节点流是底层流/低级流,直接和数据源相连
而处理流(包装流)包装节点流,即可以消除不用节点流的实现差异,也可以提供更方便的方法来完成输入输出操作
可以认为,包装流是节点流的升级版本!💫
包装流的两个优点:
BufferedReader
常用于读取文本文件
使用示例:
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
/**
* 包装流
* BufferedReader常用于读取文本文件
* 包装流是节点流的升级版本
*/
public class BufferedReaderTest {
public static void main(String[] args) throws IOException {
String filePath = "D:\\hacker.txt";
BufferedReader bufferedReader = new BufferedReader(new FileReader(filePath));
String line;
// 按行读取,效率高
while ((line = bufferedReader.readLine()) != null) {
System.out.println(line);
}
// 关闭流
bufferedReader.close();
}
}
很简单,代码示例:
/**
* BufferedWriter演示
*/
public class BufferedWriterTest {
public static void main(String[] args) throws IOException {
String filePath = "D:\\hacker.txt";
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(filePath, true));
bufferedWriter.write("大河");
// 插入换行符
bufferedWriter.newLine();
bufferedWriter.write("之犬");
bufferedWriter.close();
}
}
程序示例:
拷贝图片
import java.io.*;
/**
* Buffered字节处理流
* 拷贝图片
*/
public class BufferedByteText {
public static void main(String[] args) throws IOException {
String srcFilePath = "D:\\xiao.jpg";
String destFilePath = "D:\\cheng.jpg";
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
bis = new BufferedInputStream(new FileInputStream(srcFilePath));
bos = new BufferedOutputStream(new FileOutputStream(destFilePath));
byte[] buff = new byte[1024];
int readLen = 0;
// 循环读文件
while ((readLen = bis.read(buff)) != -1) {
// 边读边写
bos.write(buff, 0, readLen);
}
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
assert bis != null;
bis.close();
assert bos != null;
bos.close();
}
}
}
Java 提供了一种对象序列化的机制,该机制中,一个对象可以被表示为一个字节序列,该字节序列包括该对象的数据、有关对象的类型的信息和存储在对象中数据的类型。
将序列化对象写入文件之后,可以从文件中读取出来,并且对它进行反序列化,也就是说,对象的类型信息、对象的数据,还有对象中的数据类型可以用来在内存中新建对象。
整个过程都是 Java 虚拟机(JVM)独立的,也就是说,在一个平台上序列化的对象可以在另一个完全不同的平台上反序列化该对象。
类 ObjectInputStream
和 ObjectOutputStream
是高层次的数据流,它们包含反序列化和序列化对象的方法。
简单来说就是,序列化和反序列化:
需要让某个对象支持序列化机制,需要保证该类是可序列化的:
该类必须实现以下两个接口之一:
Serializable
(优先选择)Externalizable
序列化演示:
/**
* ObjectOutputStream进行序列化
*/
@Test
public void Out() throws IOException {
String filePath = "D:\\data.dat";
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filePath));
oos.write(100);
oos.writeBoolean(true);
oos.writeChar('a');
oos.writeDouble(5.21);
oos.writeUTF("IMUSTCTF");
oos.writeObject(new Dog("旺财"));
oos.close();
System.out.println("数据序列化成功!");
}
反序列化演示:
dog类:
// Dog 需要实现Serializable接口才可以支持序列化操作
class Dog implements Serializable {
private String name;
private int age;
public Dog(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
反序列化类:
/**
* ObjectInputStream反序列化
*/
@Test
public void Input() throws IOException, ClassNotFoundException {
String filePath = "D:\\data.dat";
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filePath));
// 开始读取bat文件
// 注意:读取顺序要和序列化时的顺序一致!
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);
// 访问dog的私有属性
Dog dog1 = (Dog) dog;
System.out.println(dog1.getName());
// 关闭外层流
ois.close();
}
对象处理流使用细节:🎈
Serializable
接口@Serial
private static final long serialVersionUID = 1L;
static
和transient
修饰的成员不会被序列化InputStreamReader
可以将InputStream
字节流转换成Reader
字符流
/**
* InputStreamReader演示
*/
@Test
public void InputStreamReaderTest() throws IOException {
String filePath = "D:\\hacker.txt";
// FileInputStream转换为InputStreamReader
InputStreamReader isr = new InputStreamReader(new FileInputStream(filePath), StandardCharsets.UTF_8);
// 把InputStreamReader传入BufferedReader
BufferedReader br = new BufferedReader(isr);
String s = br.readLine();
System.out.println(s);
br.close();
}
OutputStreamWriter
可以将OutputStream
字节流转换成Writer
字符流
/**
* OutputStreamWriter
*/
@Test
public void OutputStreamWriterTest() throws IOException {
String filePath = "D:\\hacker.txt";
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(filePath), "gbk");
osw.write("hi,kiki!");
osw.write("我是大河!");
osw.close();
}
PrintStream
是字节打印流,代码演示:
/**
* 字节打印流
*/
@Test
public void PrintStreamTest() throws IOException {
PrintStream out = System.out;
// 默认情况下,PrintStream输出的位置是标准输出,即显示器
out.println("hello!");
out.write("你好哦".getBytes());
// 可以修改打印流输出的位置
// 比如修改打印位置为文件中
System.setOut(new PrintStream("D:\\hacker.txt"));
System.out.println("华仔华仔!");
out.close();
}
PrintWriter
是字符打印流,代码演示:
/**
* 字符打印流
*/
@Test
public void PrintWriterTest() throws IOException {
PrintWriter printWriter = new PrintWriter(System.out);
printWriter.println("你好");
// 可以修改打印流输出的位置
// 比如修改打印位置为文件中
PrintWriter pw = new PrintWriter(new FileWriter("D:\\hacker.txt"));
pw.print("我在文件里");
printWriter.close();
pw.close();
}
对于一些不经常改变的配置数据,我们可以采用Properties
文件进行保存,如保存数据库的连接信息:
请注意:配置文件的格式(键值对)
键值对之间不能有空格,值不需要使用双引号包裹!💫
ip=192.168.0.1
user=root
password=youarealoser
/**
* 读取
*/
@Test
public void read() throws IOException {
Properties properties = new Properties();
// 加载配置文件
properties.load(new FileReader("io\\mysql.properties"));
// 把 k-v 显示在控制台
properties.list(System.out);
// 根据key获取值
String password = properties.getProperty("password");
System.out.println(password);
}
/**
* 修改
*/
@Test
public void write() throws IOException {
Properties properties = new Properties();
// 设置键值对
// 如果键已经存在,则等价于修改键
properties.setProperty("user","汤姆");
properties.setProperty("passwd","youwillwin");
// 存储键值对
properties.store(new FileOutputStream("io\\mysql.properties"),"mysql test data");
}
修改后的配置文件内容:
#mysql test data
#Fri Aug 05 16:53:29 CST 2022
passwd=youwillwin
user=\u6C64\u59C6