Java I/O学习(一)

写作目的

自学Java,Java中流的概念比较复杂,故专门作一整理。期望开始学习的童鞋,看完本文后对Java流有一个大致的认识。主要分三个小篇:

一、概述、输入/输出字节流

二、输入/输出字符流、装饰者模式与处理流

三、其他

本文首先大致介绍以下流的概念和分类,然后介绍字节流的接口,最后介绍一些三种常用的字节流,并举例说明它们的用法。

一、什么是流

首先,给一个图,给大家一个感性的认识。大家至少接触过面向对象语言,在面向对象语言中讲到输入输出(I/O)时,一般都是流。那什么是流呢?你能准确描述一下吗?其实我也不能准确描述描述,下面是我的理解。首先说说IO,IO就是将数据从一个地方搬运到另一个地方,如从一个文件运到内存中,从一台机器运到另一台机器。再来说说流,流就是隐藏这一搬运细节的一个对象(或者说将搬运细节封装起来了)。如上图所示,流对象就像桥一样,连接源数据和目标存放位置,我们程序员呢,什么都不用管,只要将源数据按照流对象规定的格式存到流对象中或从流对象读出来。

二、Java中的流

每学一门编程语言,I/O总是很头疼的一块,虽然我一直用C++写代码,但对C++的流我也没有搞懂。像控制结构、基本数据类型、运算类型等等一般都是相同的,但是流在不同的语言中会有很大差别。因为是自学,所以就把它弄清楚一些。JDK本身提供了丰富的流类型。我们首先用思维导图的方式来将这些类整理一下:

是不是很壮观,先大体上说下Java流的分类:

  • 按流向分,可分为输入流和输出流(输入/输出是针对CPU而言的);
  • 按处理数据的类型,可字节流和字符流(一般字节流就够用了,涉及到文本编码,尤其是非ASCII编码的会用到字符流)。在此,要强调的一点:磁盘上存储的所有数据都是面向字节的,字符只存在程序(或者说内存)中。
  • 按功能分,可分为节点流和处理流。节点流提供基本功能,处理流提供扩展功能,增加性能。

  所有这些类中,最重要的就是InputStream和OutputStream,接着是Reader和Writer。首先来详细介绍以下InputStream的接口。

2.1 InputStream类提供的接口

接口一、从流中读入一个字节的数据

  上面这个方法是InputStream唯一的抽象方法,子类必须实现这一方法,其功能是读入一个字节的数据。不同的输入流(有不同的数据源),读入的方式当然是不一样的,接下来要介绍的几个接口都依赖于这一接口。此接口返回值有两种可能:(1) 读到字节的值; (2) -1表示已经读到流的结束。

接口二、从流中读入若干个字节

  这个函数是从流中读入若干字节到缓存数组b[]中,返回值也有两种情况:

  (1) 实际读到字节数组b中的字节数(可能<b.length);

  (2) -1,已经到流的结尾啦,没有字节可读了!

  一般而言,上述两个函数是我们在使用输入流时,最常用的两个函数。另外,JDK还给我们提供了另一个更为灵活的读入若干字节的接口:

  这个接口与上一个接口的功能是类似的,只是增加了off和len两个参数,使用户可以指定读入字节的起始位置和长度。如果len + off > b.length,会抛出一个IndexOutofBoundsException异常。

2.2 OutputStream类提供的接口

  接下来,介绍输出流的接口。OutputStream的接口与InputStream的接口是一一对应的。你有read一个字节,那我就有write一个字节;你有read若干个字节,那我就有write若干个字节。接口如下(不做详细介绍了):

public abstract void write(int b) throws IOException;
public void write(byte b[]) throws IOException;
public void write(byte b[], int off, int len) throws IOException;

2.3 字节流的派生类及作用

2.4 流的使用

下面通过几个实例来看看第3节介绍的三种流的用法。

1. 文件输入流

 1 public static void main(String args[]) throws IOException {
 2         // 定义文件输入流
 3         FileInputStream fis = new FileInputStream("test.txt");
 4
 5         // 定义读入缓存字节
 6         byte buf[] = new byte[1024];
 7
 8         // 使用read接口读入
 9         fis.read(buf);
10
11         // 输出读入的字节
12         System.out.println(new String(buf));
13     }

【注】这里读入的buf字节数组,将其按系统默认字符集编码转为String字符串,然后输出。若文件的字符集编码与系统默认字符集编码不一致,则需要在构造String指定编码,否则可能出现乱码。

2. 字节数组输入流

  前面已经指出,该流主要用于接收并解析网络数据,在单机上没啥意思。另外,需要配合处理流(如:DataInputStream)一起使用,在这里我们通过一个简单的例子来演示一下。

 1 public static void main(String args[]) throws IOException {
 2         // 定义输入缓冲区,这部分内容通常由网络传输过来的。
 3         String sBuf = new String("ByteArrayInputStream Test.");
 4         byte buf[] = sBuf.getBytes();
 5
 6         // 定义输入字节数组流
 7         ByteArrayInputStream bais = new ByteArrayInputStream(buf);
 8
 9         // 读入其中的8个字节并输出
10         byte[] obuf = new byte[8];
11         bais.read(obuf);
12         System.out.println(new String(obuf));
13
14         // 再次强调:上述三行代码仅仅演示,一般字节数组流不单独使用。
15     }

3. 管道流

  配合使用,最好在线程中使用,便于线程的通信。为了演示,首先派生两个线程类,如下:

 1 //实现两个线程类
 2     class ThreadRead extends Thread {
 3
 4         private PipedInputStream mPis = null;
 5
 6         public ThreadRead(PipedInputStream pis) {
 7             mPis = pis;
 8         }
 9
10         // 读取管道流内容并输出
11         public void run() {
12             byte[] buf = new byte[100];
13             try {
14                 mPis.read(buf);
15             } catch (IOException e) {
16                 // TODO Auto-generated catch block
17                 e.printStackTrace();
18             }
19             System.out.println(new String(buf));
20         }
21     }
22
23     class ThreadWrite extends Thread {
24
25         PipedOutputStream mPos = null;
26
27         public ThreadWrite(PipedOutputStream pos) {
28             mPos = pos;
29         }
30
31         public void run() {
32             String s = new String("你好!PipedOutputStream.");
33             try {                mPos.write(s.getBytes());
34             } catch (IOException e) {
35                 // TODO Auto-generated catch block
36                 e.printStackTrace();
37             }
38         }
39     }
主函数如下:
 1 public class PipedStreamDemo01 {
 2
 3     public static void main(String args[]) {
 4         PipedOutputStream pos = new PipedOutputStream();
 5         PipedInputStream pis = null;
 6         try {
 7             pis = new PipedInputStream(pos);
 8         } catch (IOException e) {
 9             // TODO Auto-generated catch block
10             e.printStackTrace();
11         }
12
13         Thread th1 = new ThreadRead(pis);
14         Thread th2 = new ThreadWrite(pos);
15
16         th2.start();
17         th1.start();
18     }

三、小结

  到此,字节流大致介绍完毕。

时间: 2024-10-06 09:05:47

Java I/O学习(一)的相关文章

java/android 设计模式学习笔记(14)---外观模式

这篇博客来介绍外观模式(Facade Pattern),外观模式也称为门面模式,它在开发过程中运用频率非常高,尤其是第三方 SDK 基本很大概率都会使用外观模式.通过一个外观类使得整个子系统只有一个统一的高层的接口,这样能够降低用户的使用成本,也对用户屏蔽了很多实现细节.当然,在我们的开发过程中,外观模式也是我们封装 API 的常用手段,例如网络模块.ImageLoader 模块等.其实我们在开发过程中可能已经使用过很多次外观模式,只是没有从理论层面去了解它. 转载请注明出处:http://bl

java/android 设计模式学习笔记(10)---建造者模式

这篇博客我们来介绍一下建造者模式(Builder Pattern),建造者模式又被称为生成器模式,是创造性模式之一,与工厂方法模式和抽象工厂模式不同,后两者的目的是为了实现多态性,而 Builder 模式的目的则是为了将对象的构建与展示分离.Builder 模式是一步一步创建一个复杂对象的创建型模式,它允许用户在不知道内部构建细节的情况下,可以更精细地控制对象的构造流程.一个复杂的对象有大量的组成部分,比如汽车它有车轮.方向盘.发动机.以及各种各样的小零件,要将这些部件装配成一辆汽车,这个装配过

Java程序猿学习当中各个阶段的建议

回答阿里社招面试如何准备,顺便谈谈对于Java程序猿学习当中各个阶段的建议 引言 其实本来真的没打算写这篇文章,主要是LZ得记忆力不是很好,不像一些记忆力强的人,面试完以后,几乎能把自己和面试官的对话都给记下来.LZ自己当初面试完以后,除了记住一些聊过的知识点以外,具体的内容基本上忘得一干二净,所以写这篇文章其实是很有难度的. 但是,最近问LZ的人实在是太多了,为了避免重复回答,给自己省点力气,干脆就在这里统一回复了. 其实之前LZ写过一篇文章,但是那篇文章更多的是在讨论“面试前该不该刷题”这个

java/android 设计模式学习笔记(一)---单例模式

前段时间公司一些同事在讨论单例模式(我是最渣的一个,都插不上嘴 T__T ),这个模式使用的频率很高,也可能是很多人最熟悉的设计模式,当然单例模式也算是最简单的设计模式之一吧,简单归简单,但是在实际使用的时候也会有一些坑. PS:对技术感兴趣的同鞋加群544645972一起交流 设计模式总目录 java/android 设计模式学习笔记目录 特点 确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例. 单例模式的使用很广泛,比如:线程池(threadpool).缓存(cache).对

Java Level 2 学习的八大名著

Java Level 2 学习的八大名著 前段时间有几天难得的假期,于是把自己认为Java技术栈中的精华总结了一下,但是一直没有时间写下来,今天终于得空希望本文可以对大家有所启发.通过多个实际项目的沉淀和一些Java开源项目的源代码研究,个人认为Java语言中的精华技术主要包括如下三个方面,其实严格来说只有第一点说的是纯Java中的东西,提到二,三两点是因为这两种技术在Java技术栈中应用的比较广泛,而且对于Java技术栈的意义比较大. 1. 内存模型,并发基础设施,IOs 2. 开源MQ 3.

一位资深程序员大牛给予Java初学者的学习路线建议(转)

java学习这一部分其实也算是今天的重点,这一部分用来回答很多群里的朋友所问过的问题,那就是我你是如何学习Java的,能不能给点建议?今天我是打算来点干货,因此咱们就不说一些学习方法和技巧了,直接来谈每个阶段要学习的内容甚至是一些书籍.这一部分的内容,同样适用于一些希望转行到Java的同学. 在大家看之前,我要先声明两点.1.由于我本人是Java后端开发出身,因此所推荐的学习内容是Java Web和Java后端开发的路线,非Java Web和Java后端开发的同学请适当参考其学习思想即可,切勿照

java/android 设计模式学习笔记(7)---装饰者模式

这篇将会介绍装饰者模式(Decorator Pattern),装饰者模式也称为包装模式(Wrapper Pattern),结构型模式之一,其使用一种对客户端透明的方式来动态的扩展对象的功能,同时它也是继承关系的一种替代方案之一,但比继承更加灵活.在现实生活中也可以看到很多装饰者模式的例子,或者可以大胆的说装饰者模式无处不在,就拿一件东西来说,可以给它披上无数层不一样的外壳,但是这件东西还是这件东西,外壳不过是用来扩展这个东西的功能而已,这就是装饰者模式,装饰者的这个角色也许各不相同但是被装饰的对

java基础知识回顾之java Thread类学习(八)--java多线程通信等待唤醒机制经典应用(生产者消费者)

 *java多线程--等待唤醒机制:经典的体现"生产者和消费者模型 *对于此模型,应该明确以下几点: *1.生产者仅仅在仓库未满的时候生产,仓库满了则停止生产. *2.消费者仅仅在有产品的时候才能消费,仓空则等待. *3.当消费者发现仓储没有产品可消费的时候,会唤醒等待生产者生产. *4.生产者在生产出可以消费的产品的时候,应该通知等待的消费者去消费. 下面先介绍个简单的生产者消费者例子:本例只适用于两个线程,一个线程生产,一个线程负责消费. 生产一个资源,就得消费一个资源. 代码如下: pub

java基础知识回顾之java Thread类学习(七)--java多线程通信等待唤醒机制(wait和notify,notifyAll)

1.wait和notify,notifyAll: wait和notify,notifyAll是Object类方法,因为等待和唤醒必须是同一个锁,不可以对不同锁中的线程进行唤醒,而锁可以是任意对象,所以可以被任意对象调用的方法,定义在Object基类中. wait()方法:对此对象调用wait方法导致本线程放弃对象锁,让线程处于冻结状态,进入等待线程的线程池当中.wait是指已经进入同步锁的线程,让自己暂时让出同步锁,以便使其他正在等待此锁的线程可以进入同步锁并运行,只有其它线程调用notify方

java基础知识回顾之java Thread类学习(六)--java多线程同步函数用的锁

1.验证同步函数使用的锁----普通方法使用的锁 思路:创建两个线程,同时操作同一个资源,还是用卖票的例子来验证.创建好两个线程t1,t2,t1线程走同步代码块操作tickets,t2,线程走同步函数封装的代码操作tickets,同步代码块中的锁我们可以指定.假设我们事先不知道同步函数用的是什么锁:如果在同步代码块中指定的某个锁(测试)和同步函数用的锁相同,就不会出现线程安全问题,如果锁不相同,就会发生线程安全问题. 看下面的代码:t1线程用的同步锁是obj,t2线程在操作同步函数的资源,假设不