一,字节流
1,InputStream/OutputStream
下所有字节流的父类,也就是在装饰模式中扮演“武器”这个角色的类。所有输入字节流的父类是 InputStream,所有输出字节流的父类是 OutputStream,他们都处于java.io 包下,道这两个类都是抽象类,无法创建对象。
FileInputStream 是文件输入流,从功能上说,这是一个节点流,能够读取硬盘上的文件;而 FileOutputStream 是文件输出流,能够写入文件。
(1)FileInputStream(String filename) : 通过文件路径,获得文件输入流
(2)FileInputStream(File file) :通过文件对象,获得文件输入流。
需要注意的是,FileInputStream 在创建对象的时候,当要读取的文件不存在时,就会抛
出异常:FileNotFoundException。由于这是一个已检查异常,因此必须要处理。
除此之外,FileInputStream 还有一些值得注意的方法:
(3)close() : 这个方法顾名思义,可以用来关闭文件流,释放资源。当我们结束输入操作时,
应该调用该方法来关闭流。
(4)int read() : 无参的 read 方法。这个方法每次都从文件中读取一个字节,并且把读到的内
容返回。要强调的是,虽然返回值是一个 int 类型,但是每次读文件时只读取一个字节,这
个字节作为 int 四个字节中的最低位返回。而当读到流末尾时,返回-1。
(5)int read(byte[] bs) : 这个方法的特点是,每次调用这个方法的时候,会把读取到的数据放入 bs 数组中,一次调用尽量读取 bs.length 个字节
(6)int read(byte[] bs, int off, int len) :这个方法读取数据的时候,会让数据在数组中以下标为 off 的地方开始,并且最多只读取 len 个。
FileOutputStream
相对于 FileInputStream , FileOutputStream 更 简 单 一 些 。 首先 同 样 是研 究 一 下
FileOutputStream 的构造方法,罗列如下:
(1)FileOutputStream(String path) : 根据路径创建文件输出流
(2)0FileOutputStream(File file) : 根据文件对象创建文件输出流
append 为true时,当如果文件不存在,依然会创建新文件;而如果文件已存在,则会用追加的方式写文件
(1.1)FileOutputStream(String path, boolean append)
(2.1)FileOutputStream(File file, boolean append)
(3)close(): 关闭流
FileOutputStream 最重要的是 write 方法,其 write 方法罗列如下:
(4)void write(int v) : 这个 write 方法每次调用时写入一个字节。注意,虽然这个 write 方法接受的参数类型是 int 类型,虽然 int 类型有 4 个字节,但是这个 write 方法每次只写入 int类型中最后的那个字节。
(5)void write(byte[] bs) : 写入一个 byte 数组。
(6)void write(byte[] bs, int off, int len) : 同样是写入一个 byte 数组,所不同的是,写入这个
byte 数组的时候,数据内容从数组下标 off 的位置开始,写入 len 个字节。
(7)public byte[] getBytes():String类型转化成byte
二,过滤流基础
我们读写字符串的时候几乎不使用 Data 流。Data 流主要是用在 8 种基本类型的读写上。
DataInputStream 和 DataOutputStream,
DataOutputStream 的方法:除了有 OutputStream 中有的几个 write 方法之外,还有 writeBoolean, writeByte, writeShort … 等一系列方法,这些方法接受某一种基本类型,把基本类型写入到流中。
DataInputStream 的方法中,除了有几个 read 方法之外,还有 readBoolean,readByte,readInt 等一系列方法,这些方法能够读入若干个字节,然后拼成所需要的数据。例如 readDouble 方法,就会一次读入 8 个字节,然后把这 8 个字节拼接成一个 double 类型。
三,缓冲区 BufferedInputStream 和 BufferedOutputStream
我们每调用一次 read 或者 write 方法,都会触发一次 I/O 操作。而由于 I/O 操作要跨越 JVM 的边界,因此进行 I/O 操作的时候,事实上效率会非常低,这非常不利于程序的高效。为了让程序的效率得到提升,我们引入了缓冲机制。我们会在内存中开辟一块空间,当调用 read 或者 write 方法时,并不真正进行 I/O 操作,而是对内存中的这块空间进行操作。我们以 write 操作为例,使用了缓冲机制之后,我们调用 write 方法时,并不真正把数据写入到文件中,而是先把数据放到缓冲区里。等到缓冲区
满了之后,再一次性把数据写入文件中。
四,对象序列化
Serializable 接口和 transient 关键字
Serializable:那怎么让对象能够在流上进行传输呢?如果要让一个类成为可序列化的,只要让这个类
实现一个接口:java.io.Serializable 接口即可。只需要写上 implements Serializable 就可以了。
transient:这个关键字是一个修饰符,这个修饰符可以用来修饰属性,用 transient 修饰的属性表示:这个属性不参与序列化。
五,字符编码
计算机把字符转换为数字的过程,称之为“编码”。而读取文件的时候,则过程相反,计算机会把数字转化为文字,并绘制到屏幕上。计算机把数字转换为字符的过程,称之为“解码”。
换而言之,产生乱码的根源在于:编解码方式不一致。
//编码,指定编码方式为 GBK
byte[] bs = str.getBytes("GBK");
//解码,指定解码方式为 GBK
String str2 = new String(bs, "GBK");
六,字符流
所有输入字符流的父类是 Reader,所有输出字符流的父类是Writer。这两个类也是抽象类。有两个类 FileReader 和 FileWriter,这两个类分别表示文件输入字符流和文件输出字符流。需要注意的是,使用这两个流的时候,无法指定编解码方式。通过 FileReader 和 FileWriter 可以直接获得文件字符流。
通过这个流,可以接受一个字节流作为参数,创建一个字符流。这个对象就起到了字节流向字符流转换的功能,我们往往称之为:桥转换,在桥转换的过程中,我们还可以指定编解码方式。如果不指定的话,则编码方式采用系统默认的编码方式。
七,字符过滤流
1, BufferedReader
顾名思义,BufferedReader 提供了缓冲区功能。但是更重要的是,BufferedReader 中有
一个 readLine()方法,签名如下:
public String readLine()
这个方法也很容易理解:每次读入一行文本,并把读入的这一行文本当做返回值返回。
当读到流末尾时,返回一个 null 值。
2,PrintWriter
首先,PrintWriter 可以作为一个过滤流。这个流可以接受一个 Writer 作为参数。增强了
如下一些功能:
1、缓冲区的功能。因此使用 PrintWriter 应当及时关闭或刷新
2、写八种基本类型和字符串的功能。
3、写对象的功能。
在 PrintWriter 类中,有一系列 print 方法,这些方法能够接受八种基本类型、字符串和对象。同样的,还有一系列 println 方法,这些方法在写入数据之后,会在数据后面写入一个换行符。
要注意的是,PrintWriter 写基本类型的方式,是把基本类型转换为字符串再写入流中,与 Data 流不同。举例来说,对于 3.14 这个 double 类型的数,Data 流会把这个数拆分成 8个字节写入文件,而 PrintWriter 会把这个数字转化为字符串“3.14”,写入文件中。此外,PrintWriter 写对象的时候,写入的是对象的 toString()方法返回值,与对象序列化有本质区别。
PrintWriter 除了可以作为过滤流之外,还可以作为节点流。PrintWriter 类的构造方法中,可以直接接受一个文件名或 File 对象作为参数,直接获得一个输出到文件的 PrintWriter。当然,编码方式采用的是系统默认的编码方式。最后,PrintWriter 的构造方法可以接受一个 InputStream,也就是说,可以使用 PrintWriter进行桥转换。只不过使用 PrintWriter 进行桥转换的时候,无法指定编码方式,采用的是系统默认的编码方式。
public class TestWriter {
public static void main(String[] args) throws Exception {
FileOutputStream fout = new FileOutputStream("poem2.txt");
Writer w = new OutputStreamWriter(fout, "UTF8");
PrintWriter pw = new PrintWriter(w);
pw.println("一个人在清华园");
pw.println("我写的 Java 程序");
pw.println("是全天下");
pw.println("最面向对象的");
pw.close();
}
}
八,字节流与字符流区别
其实底层都是字节,我们使用字符是为了处理一些文本和字符串提高性能,如果是音频文件、图片、歌曲,就用字节流好点,如果是关系到中文(文本)的,用字符流好点
1,字节流操作的基本单元为字节;字符流操作的基本单元为Unicode码元(大小2字节)。
2,字节流默认不使用缓冲区;字符流使用缓冲区。
3,字节流通常用于处理二进制数据,实际上它可以处理任意类型的数据,但它不支持直接写入或读取Unicode码元;字符流通常处理文本数据,它支持写入及读取Unicode码元。所谓Unicode码元,也就是一个Unicode代码单元,范围是0x0000~0xFFFF。在以上范围内的每个数字都与一个字符相对应,Java中的String类型默认就把字符以Unicode规则编码而后存储在内存中。
字节(byte)字符(char)
1byte=8bit
1char=2byte=16bit
boolean=1byte
int=4byte