Java的IO

java.io包中最重要的是五个类和一个接口:File、OutputStream、InputStream、Writer、Reader和Serializable

《开实》P399


我们可以用FileInputStream(文件字符流)或FileReader(文件字节流)来读文件,这两个类可以让我们分别以字符和字节的方式来读取文件内容,但是它们都有一个不足之处,就是只能从文件头开始读,然后读到文件结束。但是有时候我们只希望读取文件的一部分,或者是说随机的读取文件,那么我们就可以利用RandomAccessFile。RandomAccessFile提供了seek()方法,用来定位将要读写文件的指针位置,我们也可以通过调用getFilePointer()方法来获取当前指针的位置




其中,字节流:OutputStream、InputStream。字符流:Writer、Reader。

字节流

抽象类,主要操作byte型数据
File file=new File("d:"+File.separator+"test.txt");
String str="Hello";
byte[] b1=str.getBytes();
byte[] b2=new byte[1024];
OutputStream out=new FileOutputStream(file);
InputStream in=new FileInputStream(file);
out.write(b1);//将数组b1中内容写到到输出流
int len=in.read(b2);//将输入流中内容读到到b2数组中



字符流

抽象类,Unicode字符,双字节
File file=new File("d:"+File.separator+"test.txt");
String str="Hello";
char[] c=new char[1024];
Writer w=new FileWriter(file);
Reader r=new FileReader(file);
w.write(str);//将字符串中内容写到到输出流
int len=r.read(c);//将输入流中内容读到到char数组中



字节流与字符流的区别

1.Reader和Writer要解决的,最主要的问题就是国际化。原先的I/O类库只支持8位的字节流,因此不可能很好地处理16位的Unicode字符流。Unicode是国际化的字符集(更何况Java内置的char就是16位的Unicode字符),这样加了Reader和Writer之后,所有的I/O就都支持Unicode了。此外新类库的性能也比旧的好。

但是,Read和Write并不是取代InputStream和OutputStream,有时,你还必须同时使用"基于byte的类"和"基于字符的类"。为此,它还提供了两个"适配器(adapter)"类。InputStreamReader负责将InputStream转化成Reader,而OutputStreamWriter则将OutputStream转化成Writer。

2.字节流在使用时直接对文件进行读写,而字符流会用到缓冲区(内存),通过缓冲区对文件进行操作。假如在分别使用字符流和字节流写文件之后都不关闭输出流,会发现,使用字节流时,文件中已存在要写入的内容;而使用字符流时,文件中是空的,这是因为在关闭输出流时会强制输出缓冲区的内容,所以关闭后文件中才会有字符(强制输出也可以用flush())。

计算机访问外部设备,要比直接访问内存慢得多,如果我们每一次 write 方法的调用都直接写到外部设备(如直接写入硬盘文件), CPU 就要花费更多的时间等待外部设备;如果我们开辟一个内存缓冲区,程序的每一次write 方法都是写到这个内存缓冲区中,只有这个缓冲区被装满后,系统才将这个缓冲区的内容一次集中写到外部设备。使用内存缓冲区有两个方面的好处,一是有效地提高了 CPU 的使用率,二是 write 并没有马上真正写入到外设,我们还有机会回滚部分写入的数据。使用缓冲区,能提高整个计算机系统的效率,但也会降低单个程序自身的效率,由于有这么一个中间缓冲区,数据并没有马上写入到目标中去,例如在网络流中,就会造成一些滞后。

3.文本文件、内存-->字符,图片声音音频文件、硬盘、传输-->字节。所以字节流使用更为广泛



ByteArrayInputStream:向内存中写入数据。ByteArrayOutputStream:将内存中的数据读出

内存操作流一般在生成一些临时信息时使用,而如果把这些临时信息保存在文件,则执行完成后还要删除文件。

使用实例:

String str="Hello";
ByteArrayInputStream bis=null;
ByteArrayOutputStream bos=null;
bis=new ByteArrayInputStream(str.getBytes());<span style="white-space:pre">	</span>//向内存中输出(写入)内容
bos=new ByteArrayOutputStream();

int temp=0;
while((temp=bis.read())!=-1){<span style="white-space:pre">	</span>//bis是InputStream的子类,所以可以使用read()
<span style="white-space:pre">	</span>char c=(char) temp;<span style="white-space:pre">	</span>//将读取的数字变成字符
<span style="white-space:pre">	</span>bos.write(Character.toLowerCase(c));<span style="white-space:pre">	</span>//变成小写
}
String s=bos.toString();<span style="white-space:pre">	</span>//取出内容,可以发现大写都变成了小写,而所有的操作都在内存中完成


DataInputStream:与平台无关的数据操作,继承自FilterInputStream类,同时实现了DataInput接口,在此接口中定义了一系列读入各种数据的方法readXXX(),如readInt()、readFloat()、readChar()。



Java的IO_第1张图片


在上面的关系图中可以看出:
1.InputStream是所有的输入字节流的父类,它是一个抽象类。
2. ByteArrayInputStream、StringBufferInputStream、FileInputStream是三种基本的介质流,它们分别将Byte数组、StringBuffer、和本地文件中读取数据。PipedInputStream是从与其它线程共用的管道中读取数据。
3. ObjectInputStream和所有FilterInputStream的子类都是装饰流(装饰器模式的主角)。

FileInputStream类是InputStream类的子类,用来处理以文件作为数据输入源的数据流。
BufferedInputStream顾名思义,就是在对流进行写入时提供一个buffer来提高IO效率。在进行磁盘或网络IO时,原始的InputStream对数据读取的过程都是一个字节一个字节操作的,而BufferedInputStream在其内部提供了一个buffer,在读数据时,会一次读取一大块数据到buffer中,这样比单字节的操作效率要高的多,特别是进程磁盘IO和对大量数据进行读写的时候。
1) ByteArrayInputStream:内存操作流。把内存中的一个缓冲区作为 InputStream 使用,将内容写入内存中(对 内存 的输入输出操作),操作byte数组;
2) StringBufferInputStream:把一个 String 对象作为 InputStream;
3) FileInputStream:文件流。把一个文件作为 InputStream,实现对文件的读取操作;
4) PipedInputStream:管道流。实现了 pipe 的概念,,主要在 线程 中使用;
5) SequenceInputStream:合并流。把多个 InputStream 合并为一个 InputStream用于将两个文件的内容合并成一个;
6) FilterInputStream:抽象类,作为”修饰器”与其他对象相连以提供有用接口;
7) ObjectInputStream: 对象输入流。把一个类对象作为InputStream.主要是为了实现对象的串行化.
8) DataInputStream:数据操作流。提供了与平台无关的数据操作
9)PushbackInputStream:回退流





转换流

OutputStreamWriter:是Writer的子类,将输出的字符流转化成字节流
InputStreamReader:是Reader的子类,将输入的字节流转化成字符流
例如,文件的操作:内存中的字符数据需要经过OutputStreamWriter变为字节流才能写进文件,读取时需要将读入的字节通过InputStreamReader变成字符流
Writer out;
out=new  OutputStreamWriter(FileOutputStream(file));//输出到文件,要用字节流
out.write("Hello");//内存中的数据是字符,所以用Writer
注意:FileOutputStream是OutputStream的子类,但 FileWriter不是Writer的子类而是OutputStreamWriter的子类。

BufferedReader:用于从缓冲区中读取内容(所有输入字节数据都是存放在缓冲区)
BufferedReader的构造方法只能接受字符流,而键盘输入System.in是字节流,所以要用转换流转换一下
BufferedReader br=null;
br=new BufferedReader(new InputStreamReader(System.in));

String s=null;
s=br.readLine();//将System.in变成字符流放入BufferedReader之后,通过readLine()等待用户输入
readLine():一次性将数据从缓冲区全部读取出来。上例中会一直等待用户输入,属于阻塞操作



Java IO 的一般使用原则 :  

一、按数据来源(去向)分类:

1 、是文件: FileInputStream, FileOutputStream, ( 字节流 )FileReader, FileWriter( 字符 )

2 、是 byte[] : ByteArrayInputStream, ByteArrayOutputStream( 字节流 )

3 、是 Char[]: CharArrayReader, CharArrayWriter( 字符流 )

4 、是 String: StringBufferInputStream, StringBufferOuputStream ( 字节流 )StringReader, StringWriter( 字符流 )

5 、网络数据流: InputStream, OutputStream,( 字节流 ) Reader, Writer( 字符流 )

二、按是否格式化输出分:

1 、要格式化输出: PrintStream, PrintWriter

三、按是否要缓冲分:

1 、要缓冲: BufferedInputStream, BufferedOutputStream,( 字节流 ) BufferedReader, BufferedWriter( 字符流 )

四、按数据格式分:

1 、二进制格式(只要不能确定是纯文本的) : InputStream, OutputStream 及其所有带 Stream 结束的子类

2 、纯文本格式(含纯英文与汉字或其他编码方式); Reader, Writer 及其所有带 Reader, Writer 的子类

五、按输入输出分:

1 、输入: Reader, InputStream 类型的子类

2 、输出: Writer, OutputStream 类型的子类

六、特殊需要:

1 、从 Stream 到 Reader,Writer 的转换类: InputStreamReader, OutputStreamWriter

2 、对象输入输出: ObjectInputStream, ObjectOutputStream

3 、进程间通信: PipeInputStream, PipeOutputStream, PipeReader, PipeWriter

4 、合并输入: SequenceInputStream

5 、更特殊的需要: PushbackInputStream, PushbackReader, LineNumberInputStream, LineNumberReader




Java多态

OutputStream的哪个子类为其实例化,就具备了向哪里输出的能力,比如用FileOutputStream是向文件输出,用PrintStream的子类System.out则是向显示器输出,这就是Java多态性的好处,根据子类的不同完成不同的任务。


http://blog.csdn.net/cruise_h/article/details/14046053





序列化(持久化)

http://www.cnblogs.com/vicenteforever/articles/1471775.html

序列化是把一个对象变成二进制的数据流的一种方法,该对象的类必须实现Serializable接口,然后就可以经过二进制数据流进行传输
Serializable接口是一个标识接口(空接口),表示一个类可以被序列化,里面没有定义任何方法。
实现了S接口后,该对象可以通过对象输出流ObjectOutputStream和对象输入流ObjectInputStream进行数据传输。

由于同一个类的不同对象的方法是一样的,只有属性不同,所以只有属性被序列化
对象的类声明了Serializable接口后,该对象所有的内容都会被序列化,如果希望自定义,则可以让该类实现Externalizable接口。或者使用transient关键字,被该关键字声明的属性不会被序列化,静态成员也不会被序列化。

你可能感兴趣的:(Java的IO)