浅谈Java中的io流,小白都能看懂的io流入门篇
io流是JavaSE高级篇中的重要内容,不管是平时学习,还是面试,或是工作中,我们都会经常碰到io流,本文将带你从小白入门io流,掌握io流中的基础概念及重要内容。
File类详解
谈论io流之前,我们先来了解一下File类。
File类的理解
File,很好理解,英文翻译过来就是文件的意思。Java中的File类声明在java.io包下,由此也可以看出,File类和io流是密不可分的,互相联系的。其实,说白了,io流就是为File对象服务的。File类的一个对象,表示一个文件或一个文件目录(也就是文件夹)。
File类中涉及到关于文件或文件目录的创建、删除、重命名、修改时间、文件大小等方法,并未涉及到写入或读取文件内容的操作。如果需要读取或写入文件内容,必须使用IO流来完成。
下面讲一下File类的实例化,通常有下面几个构造器:
- File(String filePath)
- File(String parentPath,String childPath)
- File(File parentFile,String childPath)
- File(URI uri)
一般最常用的是第一个构造器,即传入一个参数,该参数为文件的路径。说到路径,这里又要引入绝对路径和相对路径了。
什么是绝对路径,什么是相对路径?
绝对路径:包含盘符在内的文件或文件目录的路径。举个栗子,在我电脑的D盘下的pictures文件夹下有个beauty文件夹,那么"D:\pictures\beauty"就是绝对路径,再比如,还在我这个电脑D盘下,还是pictures文件夹,还是该文件夹下的beauty文件夹下有个my future girlfriend.jpg的图片,
那么"D:\pictures\beauty\my future girlfriend.jpg"就是该图片文件的绝对路径。
那什么是相对路径呢?
相对路径:相较于某个路径下,指明的路径。
乍一看,有点抽象,没事,举个栗子你就明白了。
现在大家都是用IDEA开发吧,在IDEA中,如果大家开发使用JUnit中的单元测试方法测试,相对路径即为当前Module下。
如果大家使用main()测试,相对路径即为当前的Project下。
如上图,在该project下有个hello.txt文件,如果用相对路径的写法创建File对象(基于在main()方法中创建)就应该这样写
File file = new File("hello.txt");
所以说,相对路径是相对于某一路径下指明的路径,以该例子来看,就是相对于该project指明的路径。
再说一下一个写路径的小细节,路径分隔符
windows和DOS系统默认使用“\”来表示(当然也可以用"/"表示)
UNIX和URL使用“/”来表示
所以我们在写路径时可以有两种写法:
//第一种:用"/"表示
File file = new File("D:/pictures/beauty/my future girlfriend");
//第二种:用”\“表示,要用两个"\",其中第一个“\”表示转义
File file1 = new File("D:\\pictures\\beauty\\my future girlfriend")
File类的常用方法
好了,说完了File类,接下来进入今天的重头戏:io流
io流介绍
流的分类
根据不同的分类依据,io流可以分为一下3类
其中,4个抽象基类:InputStream、OutputStream、Reader、Writer,及蓝框中的类需要大家重点记忆及掌握。
重点说明的几个流结构
抽象基类 | 节点流(文件流) | 缓冲流(处理流的一种) |
---|---|---|
InputStream | FileInputStream(read(byte[] buffer)) | BufferedInputStream |
OutputStream | FileOutputStream(write(byte[] buffer,0,len)) | BufferedOutputStream |
Reader | FileReader(read(char[] cbuf)) | BufferedReader |
Writer | FileWriter(write(char[] cbuf,0,len)) | BufferedWriter |
输入输出的标准化过程
输入过程
-
创建File类的对象,指明读取的数据的来源(File类构造器参数为文件的路径),要求该文件一定要存在
-
创建相应的输入流对象,将File类的对象作为参数,传入流的构造器中
-
具体的读入数据的过程:
创建相应的byte[] buffer 或 char[] cbuf
注意:文本文件创建char[] cbuf,非文本文件创建byte[] buffer
-
关闭流资源
说明:程序中出现的异常需要用try-catch-finally处理
输出过程
-
创建File类的对象,指明写出的数据的位置。(不要求此文件一定要存在)
-
创建相应的输出流,将File类的对象作为参数,传入流的构造器中
-
具体的写出数据的过程:
write(cbuf/buffer,0,len)
-
关闭流资源
同样,程序中出现的异常需要用try-catch-finally处理
节点流
节点流也叫文件流,有FileInputStream、FileOutputStream、FileReader、FileWriter这几个类。
FileInputStream、FileOutputStream属于字节流,常用来处理图片、音频、视频等(.jpg,.mp3,.mp4,.avi,.doc,.ppt等为后缀的文件)非文本文件
FileReader、FileWriter属于字符流,常用来处理文本文件(.txt,.java,.c,.cpp等为后缀的文件)
FileReader/FileWriter的使用
FileReader的使用
案例:将该project下的hello.txt文件内容读入程序中,并输出到控制台
/*
将该project下的hello.txt文件内容读入程序中,并输出到控制台
*/
public class Test {
public static void main(String[] args) {
FileReader fr = null;
try {
//创建文件对象
//因为hello.txt文件就在这个project目录下,所以使用的相对路径
File file = new File("hello.txt");
//创建字符流对象,并将file对象作为参数传递
fr = new FileReader(file);
//创建一个字符数组存储读取的数据
char[] cbuf = new char[5];
//读取的字符长度
int len;
//read(char[] cbuf):返回每次读入cbuf数组中的字符的个数。如果达到文件末尾,返回-1
while ((len = fr.read(cbuf)) != -1){
//将char[] cbuf转换成字符串输出
String s = new String(cbuf, 0, len);
System.out.print(s);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//关闭流资源,先判断流是否为空
if (fr != null) {
try {
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
注意点:
-
read(char[] cbuf)返回每次读入字符数组中的字符个数,若读到文件末尾返回-1
-
读入的文件一定要存在,否则就会报FileNotFoundException
-
对异常的处理,一定要用try-catch-finally,在finally语句中关闭流资源,以确保流资源一定会关闭,
这样就不会导致如果前面的代码出现了异常,后面的关闭流资源不会执行的情况
FileReader读入数据操作总结
-
实例化File类对象
-
实例化FileReader流对象
-
读取操作
char[] cbuf = new char[5]; int len; while((len = fr.read(cbuf)) != -1){ //将char[] cbuf转换成字符串输出 String s = new String(cbuf, 0, len); System.out.print(s); }
-
关闭流资源
FileWriter的使用
功能:从内存中写出数据到硬盘的文件里。
说明:
-
输出操作,对应的File可以不存在的。并不会报异常
-
File对应的硬盘中的文件如果不存在,在输出的过程中,会自动创建此文件。
File对应的硬盘中的文件如果存在: 如果流使用的构造器是:FileWriter(file,false) / FileWriter(file):对原文件的覆盖 如果流使用的构造器是:FileWriter(file,true):不会对原文件覆盖,而是在原文件基础上追加内容
案例:
-
@Test
public void testFileWriter() {
FileWriter fw = null;
try {
//1.提供File类的对象,指明写出到的文件
File file = new File("hello1.txt");
//2.提供FileWriter的对象,用于数据的写出
fw = new FileWriter(file,false);
//3.写出的操作
fw.write("I have a dream!\n");
fw.write("you need to have a dream!");
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.流资源的关闭
if(fw != null){
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
缓冲流
缓冲流是处理流的一种
缓冲流涉及到的类有:
-
BufferedInputStream(缓冲字节输入流)
-
BufferedOutputStream(缓冲字节输出流)
-
BufferedReader(缓冲字符输入流)
-
BufferedWriter(缓冲字符输出流)
已经有了字节流可以解决文本和非文本文件的数据读写问题,为什么还要引入缓冲流呢?
缓冲流相比于字节流有哪些优点?
缓冲流,顾名思义,可以起到一个缓冲的作用。它内部提供了一个缓冲区,默认大小为8kb,提高了流的读写速度。
所以,相比于节点流,我们会优先使用缓冲流读写文件,提高读写速度。
关于io流的笔试面试中会经常遇到诸如文件复制的问题,这类问题可以用节点流处理,也可以使用缓冲流处理
下面将使用字节流和缓冲流实现文本文件和非文本文件的复制
/*
使用io流复制一个图片或者文本文件
图片:非文本文件,使用字节流:FileInputStream/FileOutputStream or BufferedInputStream/BufferedOutputStream
文本文件:使用字符流:FileReader/FileWriter or BufferedReader/BufferedWriter
*/
public class IoCopy {
public static void main(String[] args) throws IOException {
//创建文件对象(源文件和目标文件)
File srcFile = new File("D:\\pictures\\02.jpg"); //源文件(路径)必须存在
File destFile = new File("D:\\pictures\\02copy.jpg"); //目标文件(路径)可以不存在
// copyTextWithBuffer(srcFile,destFile);
copyImgWithBuffer(srcFile,destFile);
}
//使用字节流复制图片
public static void copyImg(File srcFile, File destFile){
//创建字节流对象
FileInputStream inputStream = null;
FileOutputStream outputStream = null;
try {
inputStream = new FileInputStream(srcFile);
outputStream = new FileOutputStream(destFile);
//创建字节数组存储读取到的数据
byte[] data = new byte[5];
int len; //字节数组的长度
//read()每次返回读取的字节的长度,到达文件末尾返回-1
while ((len = inputStream.read(data)) != -1) {
outputStream.write(data, 0, len);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//关闭资源
if(outputStream != null){
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(inputStream != null){
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//使用字符流复制文本文件
public static void copyText(File srcFile, File destFile) {
FileReader reader = null;
FileWriter writer = null;
try {
//造流
reader = new FileReader(srcFile);
writer = new FileWriter(destFile);
//创建一个字符数组存储数据
char[] data = new char[5];
int len; //读取的字符长度
//read()每次返回读取的字符的长度,到达文件末尾返回-1
while ((len = reader.read(data)) != -1) {
writer.write(data, 0, len);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//关闭字节流
if(writer != null){
try {
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(reader != null){
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//使用缓冲流复制文本文件
/*
使用缓冲流的好处:提高流的读写速度,因为内部有一个缓冲区,默认大小为8kb
*/
public static void copyTextWithBuffer(File srcFile, File destFile) {
FileReader reader = null;
FileWriter writer = null;
BufferedReader br = null;
BufferedWriter bw = null;
try {
//创建字符流对象
reader = new FileReader(srcFile);
writer = new FileWriter(destFile);
//创建缓冲流对象,把字符流对象作为参数传递
br = new BufferedReader(reader);
bw = new BufferedWriter(writer);
//复制的细节
//创建一个char[]类型数组,用于存储读取的字符数据
char[] data = new char[1024];
int len; //读取到的字符长度
//read()返回读取的字符长度,当返回-1时表示读取到了文件末尾
while ((len = br.read(data)) != -1) {
bw.write(data, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//关闭流,先关闭外层流(缓冲流),关闭外层流,内层流(节点流)会自动关闭,可以省略
if (bw != null) {
try {
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//使用缓冲流复制图片等非文本文件
public static void copyImgWithBuffer(File srcFile,File destFile){
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
//创建缓冲流及字节流对象,并传递参数
bis = new BufferedInputStream(new FileInputStream(srcFile));
bos = new BufferedOutputStream(new FileOutputStream(destFile));
//复制
byte[] data = new byte[1024];
int len;
while ((len = bis.read(data)) != -1){
bos.write(data,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//关闭流
try{
if(bos != null){
bos.close();
}
}catch (Exception e){
e.printStackTrace();
}
try{
if(bis != null){
bis.close();
}
}catch (Exception e){
e.printStackTrace();
}
}
}
}
以上就是JavaSE中io流入门的基础内容,也是io流中的核心内容。因编者本人水平有限,难免存在疏漏与不足,望广大网友指出,感谢!