欢迎来到“雪碧聊技术”CSDN博客!
在这里,您将踏入一个专注于Java开发技术的知识殿堂。无论您是Java编程的初学者,还是具有一定经验的开发者,相信我的博客都能为您提供宝贵的学习资源和实用技巧。作为您的技术向导,我将不断探索Java的深邃世界,分享最新的技术动态、实战经验以及项目心得。
让我们一同在Java的广阔天地中遨游,携手提升技术能力,共创美好未来!感谢您的关注与支持,期待在“雪碧聊技术”与您共同成长!
目录
思考问题2:输入流、输出流中的出、入,这两个动词是针对谁来讲的?
一、IO流的体系
抽象类只是个代表,不能构建对象,因此我们要学它们的实现类,即上图中的黑框内容。
思考问题1:字节和字符谁大?是什么关系?
答案:字符大!因为字符可能是字母、数组、符号、汉字,这几个都属于字符范围。因此,假如一个字符是汉字时,在GBK字符集中,占2字节,在UTF-8字符集中,占3个字节。
此时这个字符的大小,无论如何都大于1字节。
因此得出结论:字符大于字节。
思考问题2:输入流、输出流中的出、入,这两个动词是针对谁来讲的?
答案:是针对内存来讲的!
举例1:字节输入流,就是将文件的内容读入内存,也相当于将磁盘内容读入内存,因为文件内容就在磁盘上,因此文件内容就等同于磁盘内容。
举例2:字节输出流,就是将内存中的内容写出到文件中(磁盘)。
综上,理解出、入这两个动词所针对的对象,有利于我们学习下面的内容。
二、文件字节输入流(FileInputStream)
1、职责
以内存为基准,可以把磁盘文件中的数据以字节的形式读入到内存中。
2、创建输入流管道
①形参为File对象
举例:
②形参为文件路径
举例:
那上面两个方法有什么区别吗?
答案:没有任何区别。通过阅读FileInputStream()方法的源码,可见本质上还是会将形参中的路径封装为一个文件对象,如下:
3、读取文件(磁盘)的内容
①一个一个字节地读
举例:
public class Test1 {
public static void main(String[] args) throws Exception {
//目标:掌握文件字节输入流读取文件中的字节数组到内存中
//1、创建文件字节输入流管道与源文件接通
//FileInputStream is = new FileInputStream(new File("file-io-app\\src\\123.txt"));
FileInputStream is = new FileInputStream("src\\d1.txt");//简化写法
//2、开始读取文件中的字节并输出
//定义一个变量记住每次读取的一个字节
int b;//记录一个字节的值(如:a的值为97)
while((b = is.read()) != -1){
System.out.print((char)b);
}
}
}
运行结果:
缺点:
- 一个一个字节地读取,太费劲,性能太差。
- 一个一个字节地读,必然导致中文乱码(因为一个中文长度不止一个字节)
举例:
②一组一组(一桶一桶)地读
举例:
public class Test2 {
public static void main(String[] args) throws Exception {
//目标:掌握文件字节输入流读取文件中的字节数组到内存中
//1、创建文件字节输入流管道与源文件接通
//FileInputStream is = new FileInputStream(new File("file-io-app\\src\\123.txt"));
FileInputStream is = new FileInputStream("src\\d1.txt");//简化写法
//2、开始读取文件中的字节并输出:每次读取多个字节
//定义一个字节数组,用于存放每次读取的字节
byte[] buffer = new byte[3];//每次读取三个字节
//定义一个变量,记录每次读取了多少个字节
int len;
while((len = is.read(buffer)) !=-1 ){
//把读取到的字节数组解码成字符串
String str = new String(buffer, 0, len);
System.out.print(str);
}
}
}
运行结果:
注意:上面不写String str = new String(buffer, 0, len);这行代码行不行?
答案:不行,会导致最后一次读到残留数据问题。如下:
注意:一组一组地读,也可能会导致中文乱码,如下:
4、使用字节流读取汉字,怎么保证输出不乱码?
①解决方案1:将字节数组定义的长度很大
定义字节数组时,让这个数组的长度足够大,能一次性读完文件中的所有字节,从而实现不截断汉字,达到输出不乱码的要求。
如下:
②解决方案2:使用特定的方法readAllBytes()
但这俩种方法只适合读小文件,如果文件过大,会导致字节数组过大,引起内存溢出问题。
5、结论
①字节流适合做数据的转移
因为人们又不看,所以乱码也没事,只要文件本身的字节没变化就行。
②字符流适合读取文本
所谓文本,就是给人看的,因此一定不能产生输出乱码问题。
读取文本内容,还得字符输入流来,毕竟这是一个字符一个字符地读,即:碰到字母就读1字节,碰到汉字就读3字节(字符集为UTF-8),此时就根本不会截断汉字,因此就不会产生输出乱码现象。
三、文件字节输出流
1、职责
以内存为基准,把内存中的数据以字节的形式写出到文件(磁盘)中。
2、创建输出流管道
3、从内存写到文件(磁盘)
①写一个字节出去
②写一个字节数组出去
③写一个字节数组的一部分出去
4、关闭流
因为IO流管道,是内存和磁盘的一个连接,而内存速度快,磁盘速度慢,如果不关闭IO流管道,会导致磁盘拖累内存的速度。也可以释放很多占用的硬件资源,如:总线、CPU等等。
5、演示
public class Test4 {
public static void main(String[] args) throws Exception {
//目标:学会使用文件字节输出流
//1、创建文件字节输出流管道与目标文件接通
//FileOutputStream os = new FileOutputStream("src\\d1.txt");
FileOutputStream os = new FileOutputStream("src\\d1.txt", true);//true表示追加写,而不是覆盖写
//2、写入数据
//public void write (int b)
os.write(97);//写一个字节
os.write('b');//写一个字节,此处会将‘b’转为字节98
//os.write('徐');//写一个字节,会报错,因为一个汉字3字节,你这里只写了一个字节
//3、写一个字节数组出去
byte[] bytes = {97,98,99,100,101,102,103,104};
os.write(bytes);
byte[] bytes1 = "我爱你中国666".getBytes();
os.write(bytes1);
byte[] bytes2 = "\r\n".getBytes();//这里\r\n也是换行符,兼容性更好而已
os.write(bytes2);
//4、写一个字节数组的一部分出去
byte[] bytes3 = "我爱中国".getBytes();
os.write(bytes3, 0, 3);//从下标为0的字节,往后写3个,即“我”(注意此处为UTF-8字符集,汉字占3字节)
//5、关闭IO流管道
os.close();//关闭IO管道,释放资源
}
}
运行效果:
6、总结
四、文件复制
1、思路
2、举例
下面我们要将D:\01.gif这张动态图,复制到C:\Users\jhj15\Pictures目录下,并令新图片名称为new01.gif
public class Test5 {
public static void main(String[] args) throws Exception {
//目标:使用字节流完成文件的复制操作
//源文件:D:\01.gif
//目标文件:C:\Users\jhj15\Pictures\new01.gif (注意:复制过去的时候必须带文件名,否则无法自动生成文件)
//调用自定义的复制文件方法
copyFile("D:\\01.gif", "C:\\Users\\jhj15\\Pictures\\new01.gif");
}
public static void copyFile(String srcPath, String destPath) throws Exception {
//1、创建字节输入流管道,与源文件接通,用来读取磁盘(文件)的字节到内存(Java程序)
FileInputStream is = new FileInputStream("D:\\01.gif");
//2、创建字节输出流管道,与目标文件接通,用来从内存(Java程序)写数据到目标文件(磁盘)
FileOutputStream os = new FileOutputStream("C:\\Users\\jhj15\\Pictures\\new01.gif");
//3、创建一个字节数组,用来存放字节(中转站)
byte[] bytes = new byte[1024];//长度设为1024
int len;//用来标记一次读取到的字节数,防止数据残留问题
while((len = is.read(bytes)) != -1){
os.write(bytes, 0 , len);//读取多少,就倒多少字节,防止数据残留
}
System.out.println("复制成功!");
}
}
运行结果:
3、结论
①字节流非常适合做文件的复制操作
任何文件的底层都是字节,字节流做复制,是一字不漏的转移全部字节,只要复制后的文件格式一致就没问题!
②字节流不适合读取汉字输出
因为字节流会截断汉字,导致输出乱码。
举例:使用UTF-8字符集时,汉字占3字节,而你使用字节流读取汉字时,若每次读取1字节,则必然输出乱码;而每次读取一个字节数组(n字节),也可能截断汉字,导致输出乱码。
五、资源释放问题
1、方式1:try-catch-finally(不推荐)
package com.itheima.IO流;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class Test5 {
public static void main(String[] args) throws Exception {
//目标:使用字节流完成文件的复制操作
//源文件:D:\01.gif
//目标文件:C:\Users\jhj15\Pictures\new01.gif (注意:复制过去的时候必须带文件名,否则无法自动生成文件)
//调用自定义的复制文件方法
copyFile("D:\\01.gif", "C:\\Users\\jhj15\\Pictures\\new01.gif");
}
public static void copyFile(String srcPath, String destPath){
FileInputStream is = null;
FileOutputStream os = null;
try {
//1、创建字节输入流管道,与源文件接通,用来读取磁盘(文件)的字节到内存(Java程序)
is = new FileInputStream("D:\\01.gif");
//2、创建字节输出流管道,与目标文件接通,用来从内存(Java程序)写数据到目标文件(磁盘)
os = new FileOutputStream("C:\\Users\\jhj15\\Pictures\\new01.gif");
//3、创建一个字节数组,用来存放字节(中转站)
byte[] bytes = new byte[1024];//长度设为1024
int len;//用来标记一次读取到的字节数,防止数据残留问题
while((len = is.read(bytes)) != -1){
os.write(bytes, 0 , len);//读取多少,就倒多少字节,防止数据残留
}
System.out.println("复制成功!");
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
try {
if(is!=null) is.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
try {
if(os!=null) os.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
优点:finally里的代码一定会执行,因此一定会关闭IO流管道资源。
缺点:代码臃肿、不优雅,因此不推荐。
2、方式2:try-with-resource(推荐)
注意:try后面的括号()内,只能定义资源。
那什么叫资源呢?答案:实现Closeable接口的类,都叫资源。
举例:
以上就是字节流的全部内容,如果想了解更多IO流的知识,请关注本博主~~
标签:文件,读取,05,内存,IO,new,os,字节 From: https://blog.csdn.net/qq_63981644/article/details/143415282