文章目录
前言
在Java编程中,输入/输出(I/O)是与外部世界进行数据交换的基础。无论是读取文件内容、向网络发送数据还是接收用户输入,I/O流都是实现这些操作的关键。理解并熟练使用Java的I/O流对于开发高效且健壮的应用程序至关重要。
Java中的I/O流模型基于数据流的概念,它将数据的流动抽象为字节或字符的序列。这种模型简化了数据传输的过程,使得程序员可以不必关心底层的具体实现细节,如硬件设备的具体工作机制或网络协议的复杂性。通过使用流,我们可以统一地处理来自不同源(如文件、内存、网络等)的数据。
本篇博客将深入探讨Java I/O流的基本概念和核心类库,包括字节流和字符流的使用场景、如何读写数据以及如何处理I/O异常。我们将从基础入手,逐步介绍更高级的主题,如缓冲流、对象序列化以及文件处理等。无论你是Java新手还是经验丰富的开发者,本文都将为你提供有关Java I/O流的全面知识。
一、IO流的分类
二、字节流的基本流
1.字节输出流 FileOutputStream
1、功能:可以把程序中的数据写到本地文件中。
2、FileOutputStream写数据的3种方式
方法名称 | 说明 |
---|---|
void write(int b) | 一次写一个字节数据 |
void write(byte[] b) | 一次写一个字节数组数据 |
void write(byte[] b,int off,int len) | 一次写一个字节数组的部分数据 |
3、FileOutputStream书写细节
【1】创建字节输出流对象
细节1:参数是字符串表示的路径或者File对象都是可以的
细节2:如果文件不存在会创建一个新的文件,但是要保证父级路径是存在的
细节3:如果文件已经存在,则会清空文件
【2】写数据
细节:write方法的参数是整数,但是实际上写到本地文件上的是整数在ASCII上对应的字符
【3】释放数据
细节:每次使用完流后都要及时释放资源
/*
* 换行写:
* 再次写出一个换行符
* windows: \r\n
*
* 续写:
* 如果想要续写,打开续写开关即可
* 开关位置:创建对象的第二个参数
* 默认的false:表示关闭续写,此时创建对象会清空文件
* 手动传递ture;表示打开续写,此时创建的对象不会清空文件
* */
4.代码示例
//创建字节输出流对象
FileOutputStream fos = newFileOutputStream("myio\\a.text");
//写数据
fos.write(97);
//释放数据
fos.close();
/*
* 换行写:
* 再次写出一个换行符
* windows: \r\n
*
* 续写:
* 如果想要续写,打开续写开关即可
* 开关位置:创建对象的第二个参数
* 默认的false:表示关闭续写,此时创建对象会清空文件
* 手动传递ture;表示打开续写,此时创建的对象不会清空文件
* */
FileOutputStream fos = newFileOutputStream("myio\\a.text");
String str = "abc\r\n666";
byte[] arr = str.getBytes();
fos.write(arr);
fos.close;
2.字节输出流 FileInputStream
1、功能:可以读取本地文件中的数据
2、FileOutputStream写数据的3种方式
方法名称 | 说明 |
---|---|
void read() | 一次读一个字节数据 |
void read(byte[] buffer) | 一次读一个字节数组数据 |
3、FileInputStream书写细节
【1】创建字节输入流对象
细节1:如果文件不存在,就直接报错
【2】读取数据
细节1:一次读取一个字节,读出来的是数据在ASCII上对应的数字
细节2:读到文件末尾了,read方法返回-1。
【3】释放数据
细节:每次使用完流后都要及时释放资源
4.代码示例
//1.创建对象
FileInputStream fis = new FileInputStream("a.text");
//2.读取数据
int b1 = fis.read();
System.out.println((char) b1);
//3.释放
fis.close();
int len;
byte[] bytes = new byte[2];
len = fis.read(bytes);
System.out.println(new String(bytes,0,len));
len = fis.read(bytes);
System.out.println(new String(bytes,0,len));
len = fis.read(bytes);
System.out.println(new String(bytes,0,len));
三、Java中编码和解码的方法
String类中的方法 | 说明 |
---|---|
public byte[] getBytes() | 使用默认方式进行编码 |
public byte[] getBytes(String charsetName) | 使用指定方式进行编码 |
String类中的方法 | 说明 |
---|---|
String(byte[] bytes) | 使用默认方式进行解码 |
String(byte[] , String charsetName) | 使用指定方式进行解码 |
四、字符流的基本流
字符流的底层其实就是字节流
字节流=字符流+字符集
特点:输入流:一次读取一个字节,遇到中文时,一次读多个字节
输出流:底层会把数据按指定的编码方式编码,变成字节再写到文件中
字符流底层有一个缓冲区域
使用场景:对于纯文本文件进行读写操作
1.字符输入流 FileReader
1、创建字符输入流对象
构造方法 | 说明 |
---|---|
public FileReader(File file) | 创建字符输入流关联本地文件 |
public FileReader(String pathname) | 创建字符输入流关联本地文件 |
细节:如果文件不存在,就直接报错。
2、读取数据
成员方法 | 说明 |
---|---|
public int read() | 读取数据,读到末尾返回-1 |
public int read(char[] buffer) | 读取多个数据,读到末尾返回-1 |
细节1:按照字节进行读取,遇到中文,一次读取多个字节,读取都解码,返回一个整数
细节2:读到文件末尾了,read方法返回-1
3、释放资源
4、代码示例
//1.创建对象并关联本地文件
FileReader fr = new FileReader("D:\\Java\\myio\\src\\mybytestream2\\a.text");
//2.读取数据 read()
int ch;
while ((ch = fr.read()) != -1){
System.out.print((char)ch);}
//3.释放资源
fr.close();
//1.创建对象并关联本地文件
FileReader fr = new FileReader("D:\\Java\\myio\\src\\mybytestream2\\a.text");
//2.读取数据 read()
//read(chars):读取数据,解码,强转三步合并,把强转的字符放到数组当中
//相当于空参的read + 强制类型转换
char[] chars = new char[2];
int len;
while((len = fr.read(chars))!=-1){
System.out.print(new String(chars,0,len));
}
//3.释放资源
fr.close();
2.字符输出流 FileWriter
1、创建字符输出流对象
构造方法 | 说明 |
---|---|
public FileWriter(File file) | 创建字符输出流关联本地文件 |
public FileWriter(String pathname) | 创建字符输出流关联本地文件 |
public FileWriter(File file,boolean append) | 创建字符输出流关联本地文件,续写 |
public FileWriter(String pathname,boolean append) | 创建字符输出流关联本地文件,续写 |
2、写出数据
成员方法 | 说明 |
---|---|
void write(int c) | 写出一个字符 |
void write(String str) | 写出一个字符串 |
void write(String str,int off ,int len) | 写出一个字符串的一部分 |
void write(char[] cbuf) | 写出一个字符数组 |
void write(char[] cbuf,int off,int len) | 写出字符数组的一部分 |
3、释放资源
4、代码示例
FileWriter fw = new FileWriter("D:\\Java\\myio\\src\\mybytestream2\\a.text",true);
//fw.write(25105);
//fw.write("你好啊???");
//fw.write("你好啊???",0,3); //(你好啊)
char[] chars = {'a','b','c','我'};
//fw.write(chars);
//fw.write(chars,0,3);
fw.close();
五、字符流和字节流的使用场景
字节流:拷贝任意类型的文件
字符流:1、读取纯文本文件中的数据
2、往纯文本文件中写出数据
六、字节流的缓冲流
1、字节缓冲流BufferedInputStream、BufferedOutputStream
方法名称 | 说明 |
---|---|
public BufferedInputStream(InputStream is) | 把基本流包装成高级流,提高读取数据的性能 |
public BufferedOutputStream | 把基本流包装成高级流,提高读取数据的性能 |
七、字符流的缓冲流
1、字符缓冲流BufferedReader、BufferedWriter
1、字符缓冲流的构造方法
方法名称 | 说明 |
---|---|
public BufferedReader(Reader r) | 把基本流变成高级流 |
Public BufferedWriter(Writer r) | 把基本流变成高级流 |
2、字符缓冲流的特有方法
字符缓冲输入流的特有方法 | 说明 |
---|---|
public String readLine() | 读取一行数据,如果没有数据读取了,会返回null |
//1.创建字符缓冲输入流的对象
BufferedReader br = new BufferedReader(new FileReader("D:\\Java\\myio\\b.text"));
//2.读取数据
//细节:
//readLine方法在读取的时候,一次读取一整行,遇到回车换行结束
// 不会把回车换行读取到内存中
String line;
while (((line = br.readLine()) != null)){
System.out.println(line);
}
可以用于一些题目给定的数据不好直接读取的情况下将其放入文件中用字符缓冲输入流读取。
字符缓冲输出流的特有方法 | 说明 |
---|---|
public void newLine | 跨平台的换行 |
//1.创建对象
BufferedWriter bw = new BufferedWriter(new FileWriter("D:\\Java\\myio\\c.text"));
//2.写出数据
bw.write("陈梓涵真帅");
bw.newLine();
bw.write("我也觉得");
bw.newLine();
//3.释放资源
bw.close();
八、转换流
原理图
//将本地文件中的GBK文件,转成UTF-8
InputStreamReader isr = new InputStreamReader(new FileInputStream("D:\\Java\\myio\\src\\c.text"),"GBK");
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("D:\\Java\\myio\\src\\d.text"),"UTF-8");
int b;
while ((b = isr.read()) != -1){
osw.write(b);
}
osw.close();
isr.close();
九、序列化流与反序列化流
1、序列化流/对象操作输出流
可以把Java中的对象写到本地文件中,同时以无法直接看懂的格式保存,很难去修改数据。
构造方法 | 说明 |
---|---|
public ObjectOutputStream(OutputStream) | 把基本流包装成高级流 |
成员方法 | 说明 |
---|---|
public final void writeObject(Object obj) | 把对象序列化(写出)到文件中 |
2、反序列化流/对象操作输入流
可以把序列化到本地文件中的对象,读取到程序中
构造方法 | 说明 |
---|---|
public ObjectInputStream(InputStream out) | 把基本流变成高级流 |
成员方法 | 说明 |
---|---|
public Object readObject() | 把序列化到本地文件中的对象,读取到程序中来 |
3、序列化流/反序列化流的细节汇总
1、使用序列化流将对象写到文件时,需要让Javabean类实现Serializable接口。否则,会出现NotSerializableException异常
public class Student implements Serializable
2、序列化流写到文件中的数据是不能修改的,一旦修改就无法再次读回来了
3、序列化对象后,修改了Javabean类,再次反序列化,会不会有问题?会出问题,会抛出InvalidClassException异常
解决方案:给Javabean类添加serialVersionUID(序列号、版本号)
public class Student implements Serializable
4、如果一个对象中的某个成员变量的值不想被序列化,又该如何实现呢?
解决方案:给该成员变量加transient关键字修饰,该关键字标记的成员变量不参与序列化过程
//transient:瞬态关键词
//作用:不会把当前属性序列化到本地文件当中
private transient String address;
十、字节打印流
分类:打印流一般是指:PrintStream,PrintWriter两个类
特点1:打印流只操作文件目的地,不操作数据源
特点2:特有的写出方法可以实现,数据原样写出
例如:打印:97
打印:true
文件中:97
文件中:true
特点3:
特有的写出方法,可以实现自动刷新,自动换行
打印一次数据=写出+换行+刷新
构造方法 | 说明 |
---|---|
public PrintStream(OutputStream/File/String) | 关联字节输出流/文件/文件路径 |
public PrintStream(String fileName, Charset charset) | 指定字符编码 |
public PrintStream(OutputStream out, boolean autoFlush | 自动刷新 |
public PrintStream(OutputStream out, boolean autoFlush,String encoding) | 指定字符编码且自动刷新 |
字节流底层没有缓冲区,开不开自动刷新都一样
成员方法 | 说明 |
---|---|
public void write(int b) | 常规方法:规则跟之前一样,将指定的字节写出 |
public void println(Xxx xx) | 特有方法:打印任意数据,自动刷新,自动换行 |
public void print(Xxx xx) | 特有方法:打印任意数据,不换行 |
public void printf(String format, Object… args) | 特有方法:带有占位符的打印语句,不换行 |
代码示例
//字节打印流
//1.创建字节打印流的对象
PrintStream ps = new PrintStream(new FileOutputStream("D:\\Java\\myio\\b.text"),true,"UTF-8");
//2.写出数据
ps.println(97);//数据的原样写出 写出+自动刷新+自动换行
ps.print(true);
ps.println();
ps.printf("%s爱上了%s","阿珍","阿强");
//3.释放资源
ps.close();
十一、压缩流与解压缩流
见此文章:
在Java中通过压缩流与解压缩流实现文件以及文件夹的压缩与解压缩
总结
在本篇博客中,我们全面了解了Java I/O流的核心概念和用法。我们从基础的字节流和字符流开始,深入探讨了如何通过这些流进行高效的数据读写操作。通过一系列的实例和代码演示,我们学习了如何使用FileInputStream、FileOutputStream、BufferedReader和PrintWriter等类来处理文件I/O,以及如何利用ObjectInputStream和ObjectOutputStream进行对象序列化和反序列化。