第 22 章 输入输出流(I/O流)(随笔)

输入与输出简述
 
输入流(Inout Stream)与输出流(Output Stream)合称为数据流(Data Stream)

输入输出流的来源和接收者可以是文件、内存、网络连接等
 
写入数据的原理:Java程序→JVM→OS→OS调用写入数据的方法→写入成功→手动释放OS资源

读取数据的原理:Java程序→JVM→OS→OS调用读取数据的方法→读取成功→手动释放OS资源

 

▊ Java流顶层父类

它们都是抽象类,是所有相关类的超类。
定义了输入流的一些共性方法:read(), close();以及输出流的一些共性方法:write(), flush(), close()

Java的io包中的类可分为两种:字节流(Byte Stream)和字符流(Character Stream)
字节流是最底层的,因而可以处理任何类型的文件。

输入流输出流
字节流InputStreamOutputStream
字符流ReaderWriter

▊ 文件字节流
一切皆为字节(文本、图片、视频),即皆为二进制数据。
 
字节输出流

// FileOutputStream有两种构造方法(第二个参数设为true,则不会【覆盖】而是【续写】)
FileOutputStream fos = new FileOutputStream("D:\\Demo\\loli.txt");
FileOutputStream fos = new FileOutputStream(new File("D:\\Demo\\loli.txt"));


// write有三种重载:
write(int b)							 ——— 如果0-127,则查询ASCII表;否则,查询GBK表
write(byte[] b)							 ——— 数组的值被byte类型限制在了-128-127.如果遇到负数,则与接下来的字节组成两个字节,并查询GBK中文显示
write(byte[] b, int off, int len)		 ——— 指定数组的某个范围

// 写入字符串								 
可以利用getBytes()方法将字符串转化为字节数组


// demo:

fos.write(97);												// a
byte[] bytes = {65, 66, 67};	fos.write(bytes);			// ABC
byte[] bytes = {-65, 66, 67};	fos.write(bytes);			// 緽C
byte[] bytes = {65, -66, 67};	fos.write(bytes);			// A綜
fos.write("萝莉".getBytes());								// 萝莉
fos.write("\r\n".getBytes());								// 换行(linux是/n, mac是/r)

字节输入流

// 同样的两种构造方法
FileInputStream fis = new FileInputStream("D:\\Demo\\loli.txt");
FileInputStream fis = new FileInputStream(new File("D:\\Demo\\loli.txt"));

// 一次读一个字节
int content = 0;
while((content = fis.read()) != -1) {
	System.out.println(char(content));					// 将 A, B, C, D, E 依次读取出来
}

// 一次读取多个字节
byte[] bytes = new byte[2];
int len = 0;
while ((len = fis.read(bytes)) != -1) {
    System.out.println(new String(bytes, 0, len));		// 打印 AB, CD, E
}



// 注
1.读取字节时(每次read一次),指针会自动向后移动
2.read()方法的返回值就是读取的字节内容;read(byte[])方法的返回值是"有效字节的长度",读取的内容在byte[]数组中
3.byte[]数组的作用是"缓存"————我们可以控制每次读取字节的数量,并将读取的字节缓存在数组中。并根据"有效字节的长度"读取出来
  比如上面的ABCDE,如果一直读下去,这个缓冲数组的内容是:[A, B], [C, D], [E, D], [E, D]...对应的"有效字节的长度"是2, 2, 1, -1...
  其实,随着指针的移动,每次新读取的字节都会"不完全覆盖"之前的内容————[C, D]->[E, D]时的D就是未被覆盖掉的。
4.对于一个字节,用char()转换类型就好;如果是个byte[]数组,可以用利用String()构造方法

▶ 练习——复制文件

FileInputStream fis = new FileInputStream(new File("D:\\Demo\\loli.jpg"));      // 读
FileOutputStream fos = new FileOutputStream(new File("D:\\Demo\\suki.jpg"));    // 写

int len = 0;
byte[] bytes = new byte[1024];					// 使用缓冲数组
while((len = fis.read(bytes)) != -1){			// 经典写法,必须背会
    fos.write(bytes, 0, len);					// write()方法正好可以用到读出的字节数组与有效长度len
}

fos.close();       // 先关闭写
fis.close();       // 再关闭读

▊ 文件字符流

字节流更适合底层的输入输出,字符流则更适合处理一个文本文件
字符输出流

// 同样(第二个参数设为true,则不会【覆盖】而是【续写】)
FileWriter fw = new FileWriter("D:\\Demo\\suki.txt");
FileWriter fw = new FileWriter(new File("D:\\Demo\\suki.txt"));

fw.write(int c)
fw.write(char[] cbuf)						// 字符串还需要toCharArray()
fw.write(char[] cbuf, int off, int len)
fw.write(String str)						// 字符串也可以直接写!
fw.write(String str, int off, int len)

fw.flush();
fw.close();


flush与close的区别:
首先要明白,FileWriter和OutputStream的write()方法是不同的———前者是直接写到文件中(因为是字节);后者是写到内存缓冲区,之后再刷新到文件中(因为是字符)
而flush和close都有立即把缓冲区的数据刷新到文件中的作用————而close还会释放资源,流对象无法再使用了

字符输入流

 其中,对read()!=-1的理解可参考对IO流中read(buffer)!=-1的理解

// 还是一样的两种构造方法
FileReader fr = new FileReader("D:\\Demo\\loli.txt");
FileReader fr = new FileReader(new File("D:\\Demo\\loli.txt"));

// 一次读一个字符
int content = 0;
while((content = fr.read()) != -1) {
	System.out.println(char(content));						// 萝 莉 s u k i
}

// 一次读取多个字符
char[] chars = new char[2];
int len = 0;
while ((len = fr.read(chars)) != -1) {
    System.out.println(new String(chars, 0, len));		   // 萝莉s uki
}

▊ 缓冲流

利用缓冲区实现。上面的四个文件节点流,就分别对应四个缓冲处理流。

---------------------- Demo: 字符输出缓冲流 -------------------------

BufferedWriter bw = new BufferedWriter(new FileWriter(new File("D:\\Demo\\loli.txt")));
bw.write("萝莉赛高");
bw.close();


---------------------- Demo: 字符输入缓冲流 -------------------------

BufferedReader br = new BufferedReader(new FileReader(new File("D:\\Demo\\loli.txt")));
String line = null;
while((line = br.readLine()) != null){					// 多了一个好用的readLine()方法
    System.out.println(line);
}
br.close();
不再赘述 "字节缓冲流" 完整demo,只给出连接流的过程:

BufferedInputStream bi = new BufferedInputStream(new FileInputStream(new File("D:\\Demo\\loli.txt")));

BufferedOutputStream bo = new BufferedOutputStream(new FileOutputStream(new File("D:\\Demo\\loli.txt")));



注:write(),read()方法的多种重载,与上面所讲的完全一致

▊ 转换流

转换流是字节流流向字符流单向桥梁。是Reader的子类。

------------------ Demo: System.in是输入字节流,下面把它转化成字符流再用BufferedReader包装 -----------------------

BufferedReader bufferReader = new BufferedReader(new InputStreamReader(System.in));

String line = null;
while((line = bReader.readLine()) != null){
    if(line.equals("exit")){						// 如果输入"exit"就退出
        System.exit(1);
    }
	System.out.println("输入内容:" + line);			// 每次"回车"后,读取一行内容
}

bufferReader.close();

▊ 对象流

即序列化,作用是对象实例变量的状态保持

---------------------------------------- 序列化对象 ----------------------------------------------

Person person = new Person("Chino", 10);		// 这个对象必须是实现Serializable接口的

ObjectOutputStream objWriter = new ObjectOutputStream(new FileOutputStream(new File("D:\\Demo\\person.txt")));
objWriter.writeObject(person);
objWriter.close();

---------------------------------------- 反序列化对象 ----------------------------------------------

ObjectInputStream objReader = new ObjectInputStream(new FileInputStream(new File("D:\\Demo\\person.txt")));
Person person = (Object)objReader.readObject();
objReader.close();


 

▊ I/O流大家庭
 

分类字节输入流字节输出流字符输入流字符输出流
抽象基类InputStreamOutputStreamReaderReader
访问文件FileInputStreamFileOutputStreamFileReaderFileWriter
访问数组PipedInputStream

PipedOutput

Stream

CharArrayReaderCharArrayWriter
访问管道PipedReaderPipedWriter
访问字符串StringReaderStringWriter
转换流OutputStreamWriter
对象流ObjectInputStreamObjectOutputStream
过滤流FilterInputStreamFilterOutputStreamFilterReaderFilterWriter
缓冲流BufferedInputStream

BufferedOutput

Stream

BufferedReaderBufferedWriter
打印流PrintStreamPrintWriter
推回输入流PushbackInputStreamPushbackReader
数据流DataInputStream

DataOutput

Stream

注:

紫色 的是抽象基类,是无法new出对象的
粗体的流称为节点流,必须直接连接真实的物理节点;其他的称为处理流,作用是连接另一个流对其进行包装
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值