Java将流分为两类:节点流与处理流。
节点流:又称为低级流,特点:实际连接程序与另一端的“管道”,负责实际读写数据的流,IO一定是建立在某个低级流的基础上进行的。
处理流:又称为高级流,特点:不能独立存在,必须连接在其他流上,目的是当数据经过该流时对数据进行某种加工处理,简化我们的操作。
实际开发中经常串联一组高级流到某个低级流上,在读写数据的过程中以流水线式的操作对数据加工处理,这个过程也称为“流的连接”。
java.io.BufferedInputStream和BufferedOutputStream
缓冲流是一对高级流,在流连接中的作用:加快读写效率
//缓冲字节输入流
BufferedOutputStream(OutputStream out)
实例化一个缓冲字节输出流并链接在指定的字节输出流上。默认缓冲区大小8kb(内部维护的byte[] buf数组长度8192)
BufferedOutputStream(OutputStream out,int size)
实例化一个指定缓冲区大小的缓冲字节输出流并链接在指定的字节输出流上。
//缓冲字节输出流
BufferedInputStream(InputStream in)
实例化一个缓冲字节输入流并链接在指定的字节输入流上。默认缓冲区大小8kb(内部维护的byte[] buf数组长度8192)
BufferedInputStream(InputStream in,int size)
实例化一个指定缓冲区大小的缓冲字节输入流并链接在指定的字节输入流上。
缓冲流在读写数据时总是以块读写数据(默认是8kb)来保证读写效率的,并且提供了多种构造器,可以自行指定缓冲区大小。
flush()方法是被定义在java.io.Flushable中,字节输出流的超类java.io.OutputStream实现了该接口,这意味着所有的字节输出流都有flush方法。
void flush()
将缓冲区的已经缓存的数据全部写出
public class BosFlushDemo {
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream("poem.txt");
BufferedOutputStream bos = new BufferedOutputStream(fos);
//JDK14的新特性:3个双引号表示多行字符串
String line = """
大江来从万山中,山势尽与江流东。
钟山如龙独西上,欲破巨浪乘长风。
江山相雄不相让,形胜争夸天下壮。
秦皇空此瘗黄金,佳气葱葱至今王。
我怀郁塞何由开,酒酣走上城南台;
坐觉苍茫万古意,远自荒烟落日之中来!
""";
byte[] data = line.getBytes(StandardCharsets.UTF_8);
bos.write(data);
//将此时缓存的内容写出
bos.flush();
//关闭流时,只需关闭高级流,不需关闭低级流
bos.close();
}
}
对象流是一对高级流,在流连接中作用是进行对象的序列化与反序列化。
对象序列化:将一个Java对象转换为一组可以保存或传输的字节的过程
实际开发中,我们通常会将对象
无论是保存在磁盘中还是传输,都需要将对象转换为字节后才可以进行。
void writeObject(Object obj)
将给定的对象转换为一组可保存或传输的字节然后通过其链接的流将字节写出
注意,使用writeObject方法时,对象的类必须实现**Serializable**
接口,才能实现序列化对象。
**Serializable**
是签名接口,接口中没有方法,实现时不需要重写方法。
public class OOSDemo {
public static void main(String[] args) throws IOException {
String name = "小明";
int age = 22;
String gender = "男";
String[] otherInfo = new String[]{"喜欢摇滚", "喜欢吃美食"};
Person p1 = new Person(name,age,gender,otherInfo);
System.out.println(p1);
FileOutputStream fos = new FileOutputStream("person.obj");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(p1);
System.out.println("写出完毕");
oos.close();
}
}
java.io.ObjectInputStream使用对象流可以进行对象反序列化
public class OISDemo {
public static void main(String[] args) throws IOException, ClassNotFoundException {
FileInputStream fis = new FileInputStream("person.obj");
ObjectInputStream ois = new ObjectInputStream(fis);
Person p2 = (Person) ois.readObject();
System.out.println(p2);
ois.close();
}
}
当一个属性被transient关键字修饰后,该对象在进行序列化时,转换出来的字节中是不包含该属性的。忽略不必要的属性可以达到对象"瘦身"的操作。
对象瘦身可以在对象持久化时减少磁盘开销。在进行传输时可以缩短传输速度。
如果该对象不需要序列化,那么该关键字不发挥其他任何效果
java.io.Writer 所有字符输入流的超类
常用方法
void write(int c):写出一个字符,写出给定int值”低16”位表示的字符。
void write(char[] chs):将给定字符数组中所有字符写出。
void write(String str):将给定的字符串写出
void write(char[] chs,int offset,int len):将给定的字符数组中从offset处开始连续的len个字符写出
java.io.Reader 所有字符输出流的超类
常用方法
int read():读取一个字符,返回的int值“低16”位有效。当返回值为-1时表达流读取到了末尾。
int read(char[] chs):从该流中读取一个字符数组的length个字符并存入该数组,返回值为实际读取到的字符量。当返回值为-1时表达流读取到了末尾。
java.io.InputStreamReader和OutputStreamWriter是常用的字符流的实现类。
转换流是一对可以连接在字节流上的字符流,其他的高级字符流可以连接在转换流上.在流连接中起到"转换器"的作用(负责字符与字节的实际转换)
OutputStreamWriter(OutputStream out,Charset cs)
基于给定的字节输出流以及字符编码创建OSW
OutputStreamWriter(OutputStream out)
该构造方法会根据系统默认字符集创建OSW
InputStreamWriter(InputStream in,Charset cs)
基于给定的字节输入流以及字符编码创建当前转换流
InputStreamWriter(InputStream in)
该构造方法会根据系统默认字符集创建当前转换流
写出实例
public class OSWDemo {
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream("osw.txt");
OutputStreamWriter osw = new OutputStreamWriter(fos, StandardCharsets.UTF_8);
osw.write("我祈祷拥有一颗透明的心灵和会流泪的眼睛");
System.out.println("完成");
osw.close();
}
}
读入实例
public class ISRDemo {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("osw.txt");
InputStreamReader isr = new InputStreamReader(fis);
int d;
while((d = isr.read())!= -1){
System.out.print((char)d);
}
}
}
PrintWriter(File file)
PrintWriter(String path)
还支持指定字符集
PrintWriter(File file,String csn)
PrintWriter(String path,String csn)
上述构造器看似PW可以直接对文件进行操作,但是它是一个高级流,实际内部会进行流连接:
this(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(fileName))),false);
如上面工作原理图
实例
public class PWDemo1 {
public static void main(String[] args) throws FileNotFoundException {
PrintWriter pw = new PrintWriter("lyric.txt");
pw.println("我站在风口浪尖 紧握住日月旋转");
pw.println("愿烟火人间 安得太平美满");
pw.println("做人一地肝胆 做人何惧艰险");
System.out.println("写出完毕");
pw.close();
}
}
PritWriter(Writer writer)
将当前实例化的PrintWriter链接在指定的字符输出流上
PrintWriter(OutputStream out)
将当前实例化的PrintWriter链接在指定的字节输出流上
由于除了转换流外的其他字符流都不能直接连在字节流上,因此这个构造器内部会自动链接在BufferedWriter上
并且让BufferedWriter链接在转换流OutputStream上,最后再让转换流链接再指定的字节输出流上
PrintWriter支持自动行刷新,每当我们调用println方法写出一行内容后自动flush一次。
对应的构造器
PritWriter(Writer writer,boolean autoflush)
如果第二个参数为true则开启自动行刷新
缓冲字符输入流内部维护一个默认8192长度的char数组,总是以块读取文本数据保证读取效率。
缓冲输入流提供了一个按行读取文本数据的方法
String readLine()
返回一行字符串。方法内部会连续扫描若干个字符,直到遇到换行符为止,将换行符之前的内容以一个字符串形式返回。
返回的字符串中不含有最后的换行符。
返回值有三种情况:
1:正常一行内容
2:空字符串。当读取了一个空行时(这一行只有一个换行符)。
3:null。当流读取到了末尾时。
当我们第一次调用readLine()时,流并不是只读取了一行字符串,而是先进行块读操作(一次性读取8192个字符并转入到内部的char数组中),然后扫描内部的char数组,然后将第一行字符串返回。第二次调用后再继续扫描后去的内容以此类推。
/*
文件流:字节流,低级流
作用:连接文件,并将字节写入到文件中
*/
FileOutputStream fos = new FileOutputStream("pw2.txt");
/*
转换流:字符流,高级流
作用:
1:衔接字节与其他字符流
2:将写出的字符按照指定的字符集转换为字节
*/
OutputStreamWriter osw = new OutputStreamWriter(fos, StandardCharsets.UTF_8);
/*
缓冲字符流:字符流,高级流
作用:
块写文本数据保证写出效率的(内部默认有一个长度8192的char数组)
*/
BufferedWriter bw = new BufferedWriter(osw);
/*
PrintWriter:字符流,高级流
作用:
1:按行写出字符串
2:自动行刷新
*/
PrintWriter pw = new PrintWriter(bw);
小Demo:简易记事本,用户从控制台输入内容,利用PrintWriter按行写入到note2.txt中。
public class TestDemo {
public static void main(String[] args) throws FileNotFoundException {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入文本,单独输入exit时结束:");
PrintWriter pw = new PrintWriter(
new BufferedWriter(
new OutputStreamWriter(
new FileOutputStream("note2.txt")
)
)
);
while(true){
String line = scanner.nextLine();
if(line.equalsIgnoreCase("exit")){
System.out.println("程序已退出!");
break;
}
pw.write(line);
}
pw.close();
}
}
PrintWriter(bw,true)
自动刷新行,当要求即时性比较高的时候设置为true,当要求传输效率高的时候不设置为true.必须使用pw.println方法写入才能自动刷新,print方法不可以。