20.01_IO流概述及其分类
IO流用来处理设备之间的数据传输
Java对数据的操作是通过流的方式
Java用于操作流的类都在IO包中
流按流向分为两种:输入流,输出流。
-
流按操作类型分为两种:
- 字节流 : 字节流可以操作任何数据,因为在计算机中任何数据都是以字节的形式存储的
- 字符流 : 字符流只能操作纯字符数据,比较方便。
-
字节流的抽象父类:
-
InputStream
字节输入流所有类的超类 -
OutputStream
字节输出流所有类的超类
-
-
字符流的抽象父类:
-
Reader
、Writer
-
-
3.IO程序书写
- 使用前,导入IO包中的类
- 使用时,进行IO异常处理
- 使用后,释放资源
20.02_IO流(FileInputStream)
FileInputStream input = new FileInputStream("xxoo.txt"); //可能抛出FileNotFoundException
int x ;
while( (x = input.read()) != -1){ //从文件中读取一个字节(一个字符的二进制数值)
System.out.println(x);
}
input.close(); //关闭释放资源
20.03_read()方法返回值为什么是int,而不是byte?
因为字节输入流可以操作任意类型的文件,比如图片音频等,这些文件底层都是以二进制形式的存储的,如果每次读取都返回byte,有可能在读到中间的时候遇到111111111那么这11111111是byte类型的-1,我们的程序是遇到-1就会停止不读了,后面的数据就读不到了,所以在读取的时候用int类型接收,如果11111111会在其前面补上24个0凑足4个字节,那么byte类型的-1就变成int类型的255了这样可以保证整个数据读完,而结束标记的-1就是int类型
20.04_IO流(FileOutputStream)
// FileOutputStream 创建的时候(下面这个构造方法),如果文件不存在,会自动创建,如果文件存在,就会清空文件。
FileOutputStream ouput = new FileOutputStream("xxoo.txt");
ouput.write(97); //虽然写出的是一个int数,但是在写出的时候会将前面的24个0去掉,所以写出的一个byte
ouput.write(98); //一次write()操作会覆盖原来文件的内容
ouput.close();
20.05_IO流(FileOutputStream追加)
// 使用此构造方法,代表每次写文件 都是追加 文件内容末尾
FileOutputStream fop = new FileOutputStream("xxoo.txt", true);
fop.write(100);
fop.close();
20.06_IO流(拷贝图片)
FileInputStream fis = new FileInputStream("xxoo.png"); //读文件
FileOutputStream fos = new FileOutputStream("copy.png"); //写文件
int b ;
while( (b = fis.read()) != -1 ) {
fos.write(b);;
}
fis.close();
fos.close();
20.07_IO流(拷贝大文件问题)
- 当读写一个大文件时,比如一个普通MP3文件,4Mb左右,就有4百多万个字节,而字节流一次只读写一个字节,那么拷贝一个MP3就需要读写将近9百多万次,效率将会十分低下。
20.08_IO流(字节数组拷贝之available()方法)
-
int read(byte[] b)
:一次读取一个字节数组 -
write(byte[] b)
:一次写出一个字节数组 -
available()
获取读的文件所有的字节个数 - 弊端:有可能会内存溢出
FileInputStream input = new FileInputStream("xxoo.png");
FileOutputStream output = new FileOutputStream("copy.png"); //写文件
byte[] data = new byte[input.available()]; //根据文件大小做一个字节数组
input.read(arr); //将文件上的所有字节读取到数组中
output.write(data); //将数组中的所有字节一次写到了文件上
input.close();
output.close();
20.10_IO流(定义小数组的标准格式)
FileInputStream fis = new FileInputStream("xxoo.png");
FileOutputStream fos = new FileOutputStream("copy.png");
byte[] data = new byte[1024 * 10]; //代表 10Kb
int len ;
while( (len = fis.read(data)) != -1){
fos.write(data, 0, len);
}
fos.close();
fis.close();
// write(byte[] b, int off, int len) 从byte数组 的off读,读取len个长度的有效内容
20.11_BufferedInputStream和BufferOutputStream拷贝
-
A: 缓冲思想
- 字节流一次读写一个数组的速度明显比一次读写一个字节的速度快很多,
- 这是加入了数组这样的缓冲区效果,java本身在设计的时候,
- 也考虑到了这样的设计思想(装饰设计模式后面讲解),所以提供了字节缓冲区流
-
B. BufferedInputStream
-
BufferedInputStream
内置了一个缓冲区(数组) - 从
BufferedInputStream
中读取一个字节时 -
BufferedInputStream
会一次性从文件中读取8192个, 存在缓冲区中, 返回给程序一个 - 程序再次读取时, 就不用找文件了, 直接从缓冲区中获取
- 直到缓冲区中所有的都被使用过, 才重新从文件中读取8192个
-
-
C. BufferedOutputStream
-
BufferedOutputStream
也内置了一个缓冲区(数组) - 程序向流中写出字节时, 不会直接写到文件, 先写到缓冲区中
- 直到缓冲区写满,
BufferedOutputStream
才会把缓冲区中的数据一次性写到文件里
-
-
D. 小数组的读写和带Buffered的读取哪个更快?
- 定义小数组如果是8192个字节大小和
Buffered
比较的话 - 定义小数组会略胜一筹,因为读和写操作的是同一个数组
- 而
Buffered
操作的是两个数组
- 定义小数组如果是8192个字节大小和
FileInputStream fis = new FileInputStream("xxoo.png"); //创建输入流对象
FileOutputStream fos = new FileOutputStream("copy.png"); //创建输出流对象
// 这上下四句,可以使用匿名对象写成2句
BufferedInputStream bis = new BufferedInputStream(fis); //创建缓冲区对象,对输入流进行包装
BufferedOutputStream bos = new BufferedOutputStream(fos); //创建缓冲区对象,对输出流进行包装
int data ;
while( (data = bis.read()) != -1 ) {
bos.write(data);
}
bis.close(); // 这里只需要对 缓冲对象 关闭即可,不用再去关闭 输入出流
bos.close();
20.12_IO流(flush和close方法的区别)
-
flush()
方法- 用来刷新缓冲区的,刷新后可以再次写出
-
close()
方法- 用来关闭流释放资源的的,如果是带缓冲区的流对象的close()方法,不但会关闭流,还会再关闭流之前刷新缓冲区,关闭后不能再写出
20.13_IO流(字节流读写中文)
- 字节流读取中文的问题
- 字节流在读中文的时候有可能会读到半个中文,造成乱码
- 字节流写出中文的问题
- 字节流直接操作的字节,所以写出中文必须将字符串转换成字节数组
- 写出回车换行
write("\r\n".getBytes());
20.14_IO流(流的标准处理异常代码1.6版本及其以前)
-
try finally
嵌套
以下代码了解即可,可能老项目有类似代码,现在最新eclipse(4.6.2)写如下代码依旧提示异常没有处理错误的。
FileInputStream fis = null;
FileOutputStream fos = null;
try {
fis = new FileInputStream("aaa.txt");
fos = new FileOutputStream("bbb.txt");
int b;
while((b = fis.read()) != -1) {
fos.write(b);
}
} finally {
try {
if(fis != null)
fis.close();
}finally {
if(fos != null)
fos.close();
}
}
20.15_IO流(流的标准处理异常代码1.7版本)
// 因为 InputStream 实现了 AutoCloseable接口,所以会自动关闭
// 而 tyr(){} 会自动关闭 ()里面的对象,反之,()里面的对象必须实现了 AutoCloseable接口
try(
FileInputStream in = new FileInputStream("xxoo.txt");
FileOutputStream out = new FileOutputStream("xxoo.txt");
){
int data ;
while( (data = in.read()) != -1) {
out.write(data);
}
}
在try()中创建的流对象必须实现了AutoCloseable这个接口,
如果实现了,在try后面的{}(读写代码)执行后就会自动调用,流对象的close方法将流关掉
20.16_IO流(图片加密)
// 图片加密,关键: 整数被异或两次就是整数本身
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("xo.png"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("t.png"));
int len ;
while( (len = bis.read()) != -1) {
bos.write(len ^ 10); //为什么这里 使用 ^ ? 因为一个整数被 ^ 两次,就是原来的整数
}
bis.close();
bos.close();
20.17_IO流(拷贝文件)
- 在控制台录入文件的路径,将文件拷贝到当前项目下
Scanner sc = new Scanner(System.in);
System.out.println("请输入文件的绝对路径: ");
String path = sc.nextLine();
sc.close();
File file = new File(path);
if (!file.exists() || !file.isFile()) {
System.out.println("路径不存在,或者路径指向的不是一个文件!");
return;
}
System.out.println("拷贝中...");
FileInputStream in = new FileInputStream(file); //也可以使用 BufferedInputStream
FileOutputStream out = new FileOutputStream(file.getName());
byte[] data = new byte[1024 * 10]; // 自定义缓冲大学,10Kb
int len ;
while( (len = in.read(data)) != -1) {
out.write(data, 0, len);
}
in.close();
out.close();
System.out.println("拷贝成功...");
20.18_IO流(录入数据拷贝到文件)
- 将键盘录入的数据拷贝到当前项目下的text.txt文件中,键盘录入数据当遇到quit时就退出
Scanner sc = new Scanner(System.in);
FileOutputStream out = new FileOutputStream("text.txt");
System.out.println("请输入文件内容: ");
while(true) {
String text = sc.nextLine();
if("quit".equals(text)) break ;
out.write(text.getBytes());
out.write("\r\n".getBytes());
}
System.out.println("程序结束...");
sc.close();
out.close();
END。
我是小侯爷。
在魔都艰苦奋斗,白天是上班族,晚上是知识服务工作者。
如果读完觉得有收获的话,记得关注和点赞哦。
非要打赏的话,我也是不会拒绝的。