目录
3. 节点流之一:FileReader\FileWriter
4. 节点流之二:FileInputStream\FileOutputStream
4.2 FileInputStream 与 FileOutputStream
2. IO流原理及流的分类
2.1 Java IO原理
-
Java程序中,对于数据的输入/输出操作以“
流(stream)
” 的方式进行,可以看做是一种数据的流动。
- I/O流中的I/O是
Input/Output
的缩写, I/O技术是非常实用的技术,用于处理设备之间的数据传输。如读/写文件,网络通讯等。-
输入input
:读取外部数据(磁盘、光盘等存储设备的数据)到程序(内存)中。 -
输出output
:将程序(内存)数据输出到磁盘、光盘等存储设备中。
-
2.2 流的分类
java.io
包下提供了各种“流”类和接口,用以获取不同种类的数据,并通过标准的方法
输入或输出数据。
-
按数据的流向不同分为:输入流和输出流。
-
输入流 :把数据从
其他设备
上读取到内存
中的流。-
以InputStream、Reader结尾
-
-
输出流 :把数据从
内存
中写出到其他设备
上的流。-
以OutputStream、Writer结尾
-
-
-
按操作数据单位的不同分为:字节流(8bit)和字符流(16bit)。
-
字节流 :以字节为单位,读写数据的流。
-
以InputStream、OutputStream结尾
-
-
字符流 :以字符为单位,读写数据的流。
-
以Reader、Writer结尾
-
-
-
根据IO流的角色不同分为:节点流和处理流。
-
节点流:直接从数据源或目的地读写数据
-
-
-
处理流:不直接连接到数据源或目的地,而是“连接”在已存在的流(节点流或处理流)之上,通过对数据的处理为程序提供更为强大的读写功能。
-
小结:图解
2.3 流的API
-
Java的IO流共涉及40多个类,实际上非常规则,都是从如下4个抽象基类派生的。
(抽象基类) | 输入流 | 输出流 |
---|---|---|
字节流 | InputStream | OutputStream |
字符流 | Reader | Writer |
-
由这四个类派生出来的子类名称都是以其父类名作为子类名后缀。
常用的节点流:
-
文件流: FileInputStream、FileOutputStrean、FileReader、FileWriter
-
字节/字符数组流: ByteArrayInputStream、ByteArrayOutputStream、CharArrayReader、CharArrayWriter
-
对数组进行处理的节点流(对应的不再是文件,而是内存中的一个数组)。
-
常用处理流:
-
缓冲流:BufferedInputStream、BufferedOutputStream、BufferedReader、BufferedWriter
-
作用:增加缓冲功能,避免频繁读写硬盘,进而提升读写效率。
-
-
转换流:InputStreamReader、OutputStreamReader
-
作用:实现字节流和字符流之间的转换。
-
-
对象流:ObjectInputStream、ObjectOutputStream
-
作用:提供直接读写Java对象功能
-
3. 节点流之一:FileReader\FileWriter
3.1 Reader与Writer
Java提供一些字符流类,以字符为单位读写数据,专门用于处理文本文件。不能操作图片,视频等非文本文件。
常见的文本文件有如下的格式:.txt、.java、.c、.cpp、.py等
注意:.doc、.xls、.ppt这些都不是文本文件。
3.1.1 字符输入流:Reader
java.io.Reader
抽象类是表示用于读取字符流的所有类的父类,可以读取字符信息到内存中。它定义了字符输入流的基本共性功能方法。
-
public int read()
: 从输入流读取一个字符。 虽然读取了一个字符,但是会自动提升为int类型。返回该字符的Unicode编码值。如果已经到达流末尾了,则返回-1。 -
public int read(char[] cbuf)
: 从输入流中读取一些字符,并将它们存储到字符数组 cbuf中 。每次最多读取cbuf.length个字符。返回实际读取的字符个数。如果已经到达流末尾,没有数据可读,则返回-1。 -
public int read(char[] cbuf,int off,int len)
:从输入流中读取一些字符,并将它们存储到字符数组 cbuf中,从cbuf[off]开始的位置存储。每次最多读取len个字符。返回实际读取的字符个数。如果已经到达流末尾,没有数据可读,则返回-1。 -
public void close()
:关闭此流并释放与此流相关联的任何系统资源。
注意:当完成流的操作时,必须调用close()方法,释放系统资源,否则会造成内存泄漏。
3.1.2 字符输出流:Writer
java.io.Writer
抽象类是表示用于写出字符流的所有类的超类,将指定的字符信息写出到目的地。它定义了字节输出流的基本共性功能方法。
-
public void write(int c)
:写出单个字符。 -
public void write(char[] cbuf)
:写出字符数组。 -
public void write(char[] cbuf, int off, int len)
:写出字符数组的某一部分。off:数组的开始索引;len:写出的字符个数。 -
public void write(String str)
:写出字符串。 -
public void write(String str, int off, int len)
:写出字符串的某一部分。off:字符串的开始索引;len:写出的字符个数。 -
public void flush()
:刷新该流的缓冲。 -
public void close()
:关闭此流。
注意:当完成流的操作时,必须调用close()方法,释放系统资源,否则会造成内存泄漏。
3.2 FileReader 与 FileWriter
3.2.1 FileReader
java.io.FileReader
类用于读取字符文件,构造时使用系统默认的字符编码和默认字节缓冲区。
-
FileReader(File file)
: 创建一个新的 FileReader ,给定要读取的File对象。 -
FileReader(String fileName)
: 创建一个新的 FileReader ,给定要读取的文件的名称。
举例:读取hello.txt文件中的字符数据,并显示在控制台上
/**
* 需求:读取hello.txt中的内容,显示在控制台上。
*
* 异常使用throws的方式处理不太合适
*/
@Test
public void test() throws IOException {
//1、创建File类的对象,对应着hello.txt文件
File file = new File("hello2.txt");
//2、创建输入型的字符流,用于读取数据
FileReader fr = new FileReader(file);
//3、读取数据,并显示在控制台上
int data = fr.read();
while (data!=-1){
System.out.println((char)data);
data = fr.read();
}
//4、流资源的关闭操作(必须要关闭,否则会内存泄露)
fr.close();
}
对以上案例进行优化,使用try-catch-finally处理异常,避免内存泄漏
/**
* 需求:读取hello.txt中的内容,显示在控制台上。
*
* 使用try-catch-finally的方式处理异常,确保流一定可以关闭,避免内存泄露
*/
@Test
public void test2() {
FileReader fr = null;
try {
//1、创建File类的对象,对应着hello.txt文件
File file = new File("hello2.txt");
//2、创建输入型的字符流,用于读取数据
fr = new FileReader(file);
//3、读取数据,并显示在控制台上
int data;
while ((data = fr.read()) != -1){
System.out.println((char)data);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4、流资源的关闭操作(必须要关闭,否则会内存泄露)
try {
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
继续对以上代码进行优化,每次读取多个字符放到字符数组,减少与磁盘的交互,提升效率
/**
* 需求:读取hello.txt中的内容,显示在控制台上。
*
* 对 test2()进行优化,每次读取多个字符放到字符数组中,减少了与磁盘交互的次数,提升效率。
*/
@Test
public void test3() {
FileReader fr = null;
try {
//1、创建File类的对象,对应着hello.txt文件
File file = new File("hello2.txt");
//2、创建输入型的字符流,用于读取数据
fr = new FileReader(file);
//3、读取数据,并显示在控制台上
char[] cbuffer = new char[5];
int len;
while ((len = fr.read(cbuffer))!=-1){
//遍历数组
for (int i=0;i<len;i++){
System.out.println(cbuffer[i]);
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4、流资源的关闭操作(必须要关闭,否则会内存泄露)
try {
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
不同实现方式的类比:
3.2.2 FileWriter
java.io.FileWriter
类用于写出字符到文件,构造时使用系统默认的字符编码和默认字节缓冲区。
-
FileWriter(File file)
: 创建一个新的 FileWriter,给定要读取的File对象。 -
FileWriter(String fileName)
: 创建一个新的 FileWriter,给定要读取的文件的名称。 -
FileWriter(File file,boolean append)
: 创建一个新的 FileWriter,指明是否在现有文件末尾追加内容。
/**
*
* 需求:将内存中的数据写出到指定的文件中
*/
@Test
public void test4() {
FileWriter fw = null;
try {
//1、创建File类的对象,指明要写出这个文件的名称
File file = new File("info.txt");
//2、创建输出流
fw = new FileWriter(file); // 覆盖文件的构造器,如要追加,需要使用new FileWriter(file,true)这个构造器
//3、写出的具体过程
// 输出的方法:write(String str) / write(char[] cdata)
fw.write("I love U!\n");
fw.write("you love him\n");
fw.write("太惨了。。。。。");
fw.write("太惨了。。。。。");
fw.write("太惨了。。。。。");
} catch (IOException e) {
e.printStackTrace();
} finally {
//4、关闭资源
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
运行结果:
3.2.3 小结
①
因为出现流资源的调用,为了避免内存泄漏,需要使用try-catch-finally处理异常②
对于输入流来说,File类的对象必须在物理磁盘上存在,否则执行就会报FileNotFoundException。如果传入的是一个目录,则会报IOException异常。对于输出流来说,File类的对象是可以不存在的。
> 如果File类的对象不存在,则可以在输出的过程中,自动创建File类的对象
> 如果File类的对象存在,
> 如果调用FileWriter(File file)或FileWriter(File file,false),输出时会新建File文件覆盖已有的文件
> 如果调用FileWriter(File file,true)构造器,则在现有的文件末尾追加写出内容。
4. 节点流之二:FileInputStream\FileOutputStream
如果我们读取或写出的数据是非文本文件,则Reader、Writer就无能为力了,必须使用字节流。
4.1 InputStream和OutputStream
4.1.1 字节输入流:InputStream
java.io.InputStream
抽象类是表示字节输入流的所有类的超类,可以读取字节信息到内存中。它定义了字节输入流的基本共性功能方法。
-
public int read()
: 从输入流读取一个字节。返回读取的字节值。虽然读取了一个字节,但是会自动提升为int类型。如果已经到达流末尾,没有数据可读,则返回-1。 -
public int read(byte[] b)
: 从输入流中读取一些字节数,并将它们存储到字节数组 b中 。每次最多读取b.length个字节。返回实际读取的字节个数。如果已经到达流末尾,没有数据可读,则返回-1。 -
public int read(byte[] b,int off,int len)
:从输入流中读取一些字节数,并将它们存储到字节数组 b中,从b[off]开始存储,每次最多读取len个字节 。返回实际读取的字节个数。如果已经到达流末尾,没有数据可读,则返回-1。 -
public void close()
:关闭此输入流并释放与此流相关联的任何系统资源。
说明:close()方法,当完成流的操作时,必须调用此方法,释放系统资源。
4.1.2 字节输出流:OutputStream
java.io.OutputStream
抽象类是表示字节输出流的所有类的超类,将指定的字节信息写出到目的地。它定义了字节输出流的基本共性功能方法。
-
public void write(int b)
:将指定的字节输出流。虽然参数为int类型四个字节,但是只会保留一个字节的信息写出。 -
public void write(byte[] b)
:将 b.length字节从指定的字节数组写入此输出流。 -
public void write(byte[] b, int off, int len)
:从指定的字节数组写入 len字节,从偏移量 off开始输出到此输出流。 -
public void flush()
:刷新此输出流并强制任何缓冲的输出字节被写出。 -
public void close()
:关闭此输出流并释放与此流相关联的任何系统资源。
说明:close()方法,当完成流的操作时,必须调用此方法,释放系统资源。
4.2 FileInputStream 与 FileOutputStream
4.2.1 FileInputStream
java.io.FileInputStream
类是文件输入流,从文件中读取字节。
-
FileInputStream(File file)
: 通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的 File对象 file命名。 -
FileInputStream(String name)
: 通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的路径名 name命名。
4.2.2 FileOutputStream
java.io.FileOutputStream
类是文件输出流,用于将数据写出到文件。
-
public FileOutputStream(File file)
:创建文件输出流,写出由指定的 File对象表示的文件。 -
public FileOutputStream(String name)
: 创建文件输出流,指定的名称为写出文件。 -
public FileOutputStream(File file, boolean append)
: 创建文件输出流,指明是否在现有文件末尾追加内容。
示例:
import org.junit.Test;
import java.io.*;
/**
* ClassName:IntelliJ IDEA
* Description:
*
* @Author zyjstart
* @Create:2024/10/10 17:11
*/
public class FileStreamTest {
/**
* 需求:复制一份qilongzhu.jpg文件,命名为qilongzhu_copy.jpg
*/
@Test
public void test1() {
FileInputStream fis = null;
FileOutputStream fos = null;
try {
//1、创建相关的File类的对象
File srcFile = new File("qilongzhu.jpg");
File destFile = new File("qilongzhu_copy.jpg");
//2、创建相关的字节流
fis = new FileInputStream(srcFile);
fos = new FileOutputStream(destFile);
//3、数据的读写操作
byte[] buffer = new byte[1024]; //1kb
int len; //记录每次读入到buffer中的字节数
while ((len = fis.read(buffer)) != -1){
fos.write(buffer,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4、关闭资源
try {
if (fis != null)
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (fos != null)
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
这样,我们就复制了一张图片到当前目录。