IO流
分类
按操作数据单位份位字节流和字符流
按数据流的流向分为:输入流、输出流
按流的角色分为:节点流、处理流/包装流
字节流
每次操作8bit
不仅可以操作二进制文件又可以操作文本文件
字节输入流
顶级父类:InputStream
字节输出流
顶级父类:OutputStream
字符流
只能操作文本文件,不能操作二进制文件
每次操作一个字符,具体多少位看编码类型
字符输入流
顶级父类:Reader
字符输出流
顶级父类:Writer
Writer相关类都是要关闭或者刷新才能保存数据
其他子类
总共有四十多个相关类,但都是由上面四个抽象基类派生出来的
由这四个类派生出来的子类的名称都是以其父类名作为子类名后缀
常用流
1.文件输入输出流
文件输入流 FileInputStream
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Oa8KP1tL-1651220002596)(屏幕截图%202022-03-19%20082730.png)]
使用代码
public class FileInputStream_ {
public static void main(String[] args) {
// readFile01();
readFile02();
}
/**
* todo 使用read(Byte[] bytes)读取文件
*/
private static void readFile02() {
String filePath = "e:\\b.txt";
int readLine = 0;
FileInputStream fileInputStream = null;
//定义一个字节数组
byte[] buf = new byte[8];//一次读取8个字节
try {
fileInputStream = new FileInputStream(filePath);
while (true) {
try {
//如果读取正常返回实际读取的字节数,可以用来创建输出数组
if ((readLine = fileInputStream.read(buf)) == -1) break;
System.out.print(new String(buf,0,readLine));
} catch (IOException e) {
e.printStackTrace();
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
try {
assert fileInputStream != null;
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* todo 使用read()读取文件
*/
public static void readFile01(){
String filePath = "e:\\b.txt";
int readData = 0;
FileInputStream fileInputStream = null;
try {
//创建一个 FileInputStream 对象,读取对应位置的文件
fileInputStream = new FileInputStream(filePath);
//从该输入流中读取一个字节的数据,如果没有输入可用,此方法将阻止
//read返回 -1 表示到达文件尾
while(true){
try {
if ((readData = fileInputStream.read()) == -1) break;
} catch (IOException e) {
e.printStackTrace();
}
System.out.print((char)readData);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
//关闭文件流释放资源
try {
assert fileInputStream != null;
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
文件输出流 FileOutputStream
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cBmyYahk-1651220002598)(屏幕截图%202022-03-19%20085302.png)]
使用代码
import java.io.FileOutputStream;
import java.io.IOException;
/**
* 使用FileOutputStream 将数据写到文件中
* @author 张
*/
public class FileOutputStream_ {
public static void main(String[] args) {
writeFile();
}
public static void writeFile(){
// todo 创建一个FileOutputStream 对象
String filePath = "e:\\b.txt";
FileOutputStream fileOutputStream = null;
try {
// todo 实例化对象
// ! new FileOutputStream(filePath);这样的创建方式是覆盖
// * new FileOutputStream(filePath , true);这样的创建方式是追加
fileOutputStream = new FileOutputStream(filePath,true);//?没有filePath会自动创建
// todo 1.写入一个字节
fileOutputStream.write('a');//写入一个字节
// todo 2.写入一个字符串
String str = "Hello World!";
// ? 将字符串转换为字节数组
fileOutputStream.write(str.getBytes());
// todo 3.指定字节长度,下面的语句就是从0到str.length()的位置相当于是输入所有
fileOutputStream.write(str.getBytes(),0,str.length());
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
assert fileOutputStream != null;
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
文件拷贝(使用InputStream和OutputStream)
/**
* @author 张
* todo 文件拷贝
*/
public class FileCopy_ {
public static void main(String[] args) {
copy();
}
public static void copy(){
String output_filePath = "e:\\a.txt";
String input_filePath = "e:\\b.txt";
FileOutputStream fileOutputStream = null;
FileInputStream fileInputStream = null;
int readData = 0;
byte[] buf = new byte[8];
try {
fileInputStream = new FileInputStream(input_filePath);
fileOutputStream = new FileOutputStream(output_filePath);
while(true){
try {
if ((readData = fileInputStream.read(buf)) == -1) break;
fileOutputStream.write(new String(buf,0,readData).getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
try {
assert fileInputStream != null;
assert fileOutputStream != null;
fileInputStream.close();
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
字符文件输入流 FileReader
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9wMff6ux-1651220002599)(屏幕截图%202022-03-19%20092230.png)]
使用代码
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
/**
* @author 张
*/
public class FileReader_ {
public static void main(String[] args) {
//m1();
m2();
}
/**
* todo 单个字符读入
*/
public static void m1(){
String filePath = "e:\\a.txt";
FileReader fileReader = null;
int readData = 0;
try {
fileReader = new FileReader(filePath);
//todo 循环读取,使用read,读单个字符
while((readData=fileReader.read()) != -1){
System.out.print((char) readData);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (fileReader != null) {
fileReader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* todo 使用read(char[] chars)读取多个字符
*/
public static void m2(){
String filePath = "e:\\c.txt";
FileReader fileReader = null;
int read_num = 0;
char[] buf = new char[8];//!FileReader是文件字符读取流,所以一定是char如果是byte就会报错
try {
fileReader = new FileReader(filePath);
while(true){
try {
if ((read_num = fileReader.read(buf)) == -1) break;
System.out.print(new String(buf,0,read_num));
} catch (IOException e) {
e.printStackTrace();
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
字符文件输出流 FileWriter
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-p6m8PGNJ-1651220002600)(屏幕截图%202022-03-19%20092356.png)]
使用代码
import java.io.FileWriter;
import java.io.IOException;
/**
* @author 张
*/
public class FileWriter_ {
public static void main(String[] args) {
m1();
}
public static void m1(){
String filePath = "e:\\note.txt";
FileWriter fileWriter = null;
try {
fileWriter = new FileWriter(filePath,true);
//todo 1)write(int):读取单个字符,int对应的是ASCII码
fileWriter.write('Z');
//todo 2)write(char[]):写入指定数组
char[] chs = {'z','y','r','f','w'};
fileWriter.write(chs);
//todo 3)write(char[],off,len):指定长度写入文件
fileWriter.write("张永锐冯雯".toCharArray(),3,2);//第二个参数是偏移量,第二个参数的写入的字符数
//todo 4)write(String):写入整个字符串
fileWriter.write("你好Java!");
//todo 5)write(String,off,len):写入部分字符串
fileWriter.write("写入部分字符串",4,3);
} catch (IOException e) {
e.printStackTrace();
} finally {
//一定要关闭流或者刷新流才能将数据写入文件
try {
if(fileWriter != null){
fileWriter.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
使用FileWrite后,必须要关闭(close)或刷新(flush),否则写入不到指定文件
2. 缓冲字节输入流
节点流和处理流
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VxqN5MIn-1651220002600)(屏幕截图%202022-03-19%20100210.png)]
处理流
处理流也叫包装流,是一种设计模式(修饰器模式)
是“连接”在已存在的流(节点流或处理流)之上,为程序提供更为强大的读写功能,也更加灵活
其构造器中可以带参数为Reader对象或者Writer对象,所以可以使用其他流
对应的BufferedReader可以接收一个Reader对象(其子类对象,例如FileReader也可以作为参数)作为构造器参数,并且使用其方法,bufferedWriter可以接收一个Writer对象(其子类对象,例如FileWrite也可以作为参数)作为构造器参数,并且可以使用其方法
有BufferedReader和BufferedWriter也就有BufferedInputStream和BufferedOutputStream,后面两个流也可以用其对应的类(InputStream或OutputStream)及子类来作为参数
下面主要展示BufferedReader和BufferedWriter,BufferedInputStream和BufferedOutputStream也类似
联系与区别
- 节点流是底层流/低级流,有固定的数据源,直接跟数据源相连
- 处理流也叫包装流,既可以消除不同节点流的实现差异,也可以提供更方便的方法来完成输入输出
- 处理流对节点流进行包装,使用了修饰器模式,不会直接与数据源相连
处理流的主要功能:
- 性能的提高:主要增加缓冲的方式来提高输入输出的效率
- 操作的便捷:处理流可能提供了一系列便捷的方法来一次输入输出大批量的数据,使用更加灵活方便
缓冲字节输入流 BufferedReader
使用代码
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
/**
* @author 张
*/
public class BufferedReader_ {
public static void main(String[] args) {
bm1();
}
public static void bm1(){
String filePath = "e:\\note.txt";
BufferedReader bufferedReader = null;
try {
//?创建bufferedReader
bufferedReader = new BufferedReader(new FileReader(filePath));
//*按行读取高效
//!当返回null时,表示文件读取完毕
String line;
while(true){
try {
if ((line = bufferedReader.readLine()) == null) break;
System.out.println(line);
} catch (IOException e) {
e.printStackTrace();
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
//关闭流,只需要关闭BufferedReader,会自动关闭File Reader
try {
if(bufferedReader != null) {
bufferedReader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
缓冲字节输出流 BufferedWriter
不能读取到换行符,所以要自己使用对象名.newLine()的方法来换行
代码使用
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
/**
* @author 张
*/
public class BufferedWriter_ {
public static void main(String[] args) {
wm1();
}
public static void wm1(){
String filePath = "e:\\a.txt";
BufferedWriter bufferedWriter = null;
try {
bufferedWriter = new BufferedWriter(new FileWriter(filePath));
bufferedWriter.write("Hello World!");
bufferedWriter.newLine();//!插入一个和系统相关的换行符
bufferedWriter.write("你好,牛人");
bufferedWriter.newLine();//!插入一个和系统相关的换行符
bufferedWriter.write("牛人,你好");
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(bufferedWriter != null){
bufferedWriter.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
注意(有关操作二进制文件和和文本文件的区别)
BufferReader和BufferWriter只能操作文本文件,不能操作二进制文件
二进制文件一般时音乐,视频,图片,.doc等
使用BufferedFileInputStream和BufferedFileOutputStream来操作二进制文件
文件拷贝(使用BufferedInputStream和BufferedOutputStream)
代码实现
import java.io.*;
/**
* @author 张
*/
public class BufferedCopy02 {
public static void main(String[] args) {
String srcPath = "e:\\鹅国小酒馆.mp4";
String destPath = "e:\\鹅国小酒馆(copy).mp4";
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
bis = new BufferedInputStream(new FileInputStream(srcPath));
bos = new BufferedOutputStream(new FileOutputStream(destPath));
byte[] buff = new byte[1024];
int readline = 0;
while((readline = bis.read(buff)) != -1){
bos.write(buff,0,readline);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(bis != null){
bis.close();
}
if(bos != null){
bos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
文件拷贝(使用BufferedReader和BufferedWriter)
代码实现
import java.io.*;
/**
* @author 张
*/
public class BufferedCopy_ {
public static void main(String[] args) {
String targetPath = "e:\\d.txt";
String sourcePath = "e:\\b.txt";
BufferedWriter bufferedWriter = null;
BufferedReader bufferedReader = null;
String line;
try {
bufferedReader = new BufferedReader(new FileReader(sourcePath));
bufferedWriter = new BufferedWriter(new FileWriter(targetPath));
while((line = bufferedReader.readLine()) != null){
bufferedWriter.write(line);
bufferedWriter.newLine();//每次读入一行再插入一行换行符
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(bufferedReader != null){
bufferedReader.close();
}
if(bufferedWriter != null){
bufferedWriter.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
3. 对象字节输入流
为什么需要这样的输入流?
- 将int num = 100 这个 int 数据保存到文件中,注意不是 100 数字,而是int 100,并且,能够从文件中直接恢复 int 100
- 将Dog dog = new Dog(“小黄” , 3)这个dog对象保存到文件中,并且能够从文件中恢复,恢复后任然是这个对象
也就是可以序列化和反序列化,并且是保存的数据类型,不只是保存值
序列化和反序列化:
- 序列化就是在保存数据时,保存数据的值和数据类型
- 反序列化就是在恢复数据时,恢复数据的值和数据类型
- 需要让某个对象支持序列化机制,则必须让其类是可序列化的,为了让某个类是可序列化的,该类必须实现如下两个接口之一:
- Serializable //标记接口(没有任何方法和属性),大多数时候都用这一个,因为不用实现方法
- Externalizable 需要实现方法
ObjectInputStream 序列化工具
序列化后,保存的文件格式,不是文本格式,而是按照他的格式来保存,所以打开文件看可能会是乱码
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.Serializable;
/**
* @author 张
*/
public class ObjectOutputStream_ {
public static void main(String[] args) {
String filePath = "e:\\data.dat";
try {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filePath));
// todo 序列化数据到e:\data.dat
oos.write(100);//int -> Integer (实现了 Serializable)
oos.writeBoolean(true);//boolean -> Boolean (实现了 Serializable)
oos.writeChar('a');//char -> Character (实现了 Serializable)
oos.writeDouble(8.5);//double -> Double (实现了 Serializable)
oos.writeUTF("牛人啊");//以UTF格式保存String对象,String (实现了 Serializable)
oos.writeObject(new Dog("小狗",10));//保存Dog类
} catch (IOException e) {
e.printStackTrace();
}
}
}
@SuppressWarnings({"all"})
class Dog implements Serializable {//!必须要实现Serializable才能被写入ObjectOutputStream
private String name;
private int age;
public static final long SerialVersionUID = 1L;
public Dog(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
ObjectOutputStream 反序列化工具
要能引用到反序列化的类(这个示例中的Dog),因为这个代码和上面一个代码在同一个包
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
/**
* @author 张
*/
public class ObjectInputStream_ {
public static void main(String[] args) throws IOException, ClassNotFoundException {
String filePath = "e:\\data.dat";
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filePath));
// todo 反序列化读取
//! 读取的顺序要和序列化的顺序保持一直否则就会抛出异常
/*
序列化顺序:
oos.write(100);
oos.writeBoolean(true);
oos.writeChar('a');
oos.writeDouble(8.5);
oos.writeUTF("牛人啊");
oos.writeObject(new Dog("小狗",10));
*/
System.out.println(ois.readInt());
System.out.println(ois.readBoolean());
System.out.println(ois.readChar());
System.out.println(ois.readDouble());
System.out.println(ois.readUTF());
Object dog = ois.readObject();//会自动转型
System.out.println(dog);
//关闭外层流
ois.close();
}
}
注意细节
- 读取顺序一致
- 要求序列化或反序列化对象,需要 实现 Serializable
- 序列化的类中建议添加SerialVersionUID,为了提高版本的兼容性
- 序列化对象时,默认会将里面所有对象都进行序列化,但除了static或transient修饰的成员
- 序列化对象时,要求里面的属性的类型也需要实现序列化接口
- 序列化具备可继承性,也就是如果某类已经实现了序列化,则它的所有子类也已经默认实现了序列化
4.标准输入输出流
介绍:
编译类型 | 运行类型 | 默认设备 | |
---|---|---|---|
System.in(标准输入) | InputStream | BufferedInputStream | 键盘 |
System.out(标准输出) | PrintStream | PrintStream | 显示器 |
其他的还有System.err(标准错误流) | |||
System类似于C++中的stdlib库中的东西,也有System.exit()[c++中的exit] |
5.转换流
java中转换流有两个——InputStreamReader和OutputStreamWriter
指定编码类型
介绍:
- InputStreamReader:Reader的子类,可以将InputStream(字节流)包装成(转换成)Reader(字符流)
- OutputSteamWriter:Writer的子类,实现将OutputStream(字节流)包装成Writer(字符流)
- 当处理存文本文件数据时,如果使用字符流效率更高,并且可以有效解决中文乱码的问题,所以建议将字节流转换成字符流
- InputStreaReader的构造器:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iLzRbjeu-1651220002601)(屏幕截图%202022-03-19%20163838.png “InputStreaReader的构造器”)] - OutputStreamReader的构造器
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wsDBMpz1-1651220002602)(屏幕截图%202022-03-19%20164100.png “OutputStreamReader的构造器”)] - 可以再使用时指定编码格式(比如 utf-8,gbk,gb2312,ISO8859-1等)
- 编码格式以枚举常量的形式包装在StandardCharsets中(调用第三个构造方法)
- 也可以直接用"gbk"来表示编码格式(调用第四个构造方法)
InputStreamReader
import java.io.*;
import java.nio.charset.StandardCharsets;
/**
* @author 张永锐
* todo 将字节流转成字符流
*/
public class InputStreamReader_ {
public static void main(String[] args) throws IOException {
String filePath = "e:\\a.txt";
//? 将 FileInputStream 转换成 InputStreamReader
//? 指定编码为utf(中文)
InputStreamReader isr = new InputStreamReader(new FileInputStream(filePath), StandardCharsets.UTF_8);
//? 把 InputStreamReader 传入 BufferedReader
BufferedReader br = new BufferedReader(isr);
// * 下面两行是一个语句,直接可以等效上面的两行代码
// BufferedReader br = new BufferedReader(new InputStreamReader(
// new FileInputStream(filePath),StandardCharsets.UTF_8));
//? 读取
String s = br.readLine();
System.out.println("读取内容 = " + s);
//? 关闭流
br.close();
}
}
OutputStreamWriter
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
/**
* @author 张
* todo 演示使用OutputStreamWrite
* todo 把 FileOutputStream 字节流转成 OutputStreamWriter
* todo 顺便演示不同方式定义编码类型
*/
public class OutputStreamWriter_ {
public static void main(String[] args) throws IOException {
String filePath = "e:\\a.txt";
String charSet = "gbk";
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(filePath), charSet);
osw.write("牛逼的牛人");
osw.close();
System.out.println("文件按照 " + charSet + " 保存成功");
//!打开文件看右下角的编码类型可以看到已经被改变了
}
}
6.打印流
PrintStream(字节打印流)和PrintWriter(字符打印流)
打印流只有输出流没有输入流
PrintStream
import java.io.IOException;
import java.io.PrintStream;
/**
* @author 张
*/
public class PrintStream_ {
public static void main(String[] args) throws IOException {
PrintStream out = System.out;
//默认情况下PrintStream 输出数据的位置是 标准输出流,即显示器
out.println("Hello PrintStream");
out.write("Hello".getBytes());
// todo 修改打印流输出的位置
System.setOut(new PrintStream("e:\\f1.txt"));//将"e:\\f1.txt"做成字符串对象也可以
System.out.println("Hello f1~");
}
}
PrintWriter
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
/**
* @author 张
*/
public class PrintWriter_ {
public static void main(String[] args) throws IOException {
PrintWriter pw = new PrintWriter(new FileWriter("e:\\f2.txt"));
pw.println("hi,f2,你好f2");
pw.close();//必须关闭
}
}
7.Properties类
父类是HashTable,所以有键值对
看看需求:
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
/**
* @author 张
* 传统方式模拟properties功能
*/
public class Properties01 {
public static void main(String[] args) throws IOException {
//读取mysql.properties 文件,并得到ip,user和pwd
BufferedReader br = new BufferedReader(new FileReader("src\\mysql.properties"));
String line ="";
while((line = br.readLine()) != null){
String[] split = line.split("=");//?根据等号划分为多部分(这里是两部分)
System.out.println(split[0] + "值是:" + split[1]);
}
br.close();
}
}
上面这样只能得到文件中的内容,但是不能得到文件中的单个配置,想要得到单个文件还要再判断,并且不好改变配置。所以就需要有以properties类来完成这个事情
介绍
- 专门用于读写配置文件的集合类
配置文件的格式:
键=值
键=值 - 注意:键值对不需要有空格,值不需要用引号引起来,默认类型是String
- 常见方法
- load:加载配置文件的键值对到properties对象
- list:将数据显示到指定设备
- getProperty(key):根据键获取值
- setProperty(key,value):根据键值对到Properties对象
- store:将Properties中的键值对存储到配置文件,如果有中文将会储存为Unicode码
像HashSet一样储存键值对,如果没有该键就新建,有该键就修改该键对应的值的内容
代码演示
以下代码只演示了读取文件
import java.io.FileReader;
import java.io.IOException;
import java.util.Properties;
/**
* @author 张
*/
public class Properties02 {
public static void main(String[] args) throws IOException {
//使用 Properties 类来读取 mysql.properties 文件
//1. 创建 Properties 对象
Properties properties = new Properties();
//2. 加载指定配置文件
properties.load(new FileReader("src\\mysql.properties"));
//3. 把k-v显示再控制台
properties.list(System.out);
//4. 根据 key 获取对应的值
String user = properties.getProperty("user");
System.out.println(user);
String ip = properties.getProperty("ip");
System.out.println(ip);
}
}
现在mysql.properties文件中的内容:
ip=192.168.100.100
user=root
pwd=123456
接下来演示修改文件
import java.io.FileWriter;
import java.io.IOException;
import java.util.Properties;
/**
* @author 张
*/
public class Properties03 {
public static void main(String[] args) throws IOException {
// todo 使用 properties 来创建、配置文件、修改配置文件内容
Properties properties = new Properties();
// todo 创建 新的键值对
properties.setProperty("charset","utf-8");//charset为键在等号左边,utf-8为值在等号右边
properties.setProperty("user","Tom");
properties.setProperty("pwd","abc111");
// todo 将新建的键值对储存进文件中
//properties.store(new FileWriter("src\\mysql.properties"),null);//!后面一个注释是注释
properties.store(new FileWriter("src\\mysql.properties"),"Hello World");//*在配置文件最前面显示Hello World
System.out.println("保存配置文件成功~");
}
}