字符缓冲流
对字符节点流的装饰,下面是字符缓冲流的构造方法
public BufferedReader(Reader in) { // private static int defaultCharBufferSize = 8192; // 内部维护了一个字符数组 // private char cb[]; this(in, defaultCharBufferSize); } public BufferedWriter(Writer out) { this(out, defaultCharBufferSize); }
字符缓冲流的特有方法
类 | 方法名 | 方法说明 |
BufferedReader | String readLine() throws IOException | 一行行读取,读取到最后一行返回null |
BufferedWriter | void newLine() throws IOException | 写一个换行符到文件中,实现换行 |
// 创建流对象 BufferedReader br = new BufferedReader(new FileReader("D:/三国/赵云.txt")); BufferedWriter bw = new BufferedWriter(new FileWriter("D:/三国/赵子龙.txt")); String line = null; while ((line = br.readLine())!=null) { System.out.println(line); bw.write(line); bw.newLine(); } // 结果 我乃常山赵子龙 于万军从中,取上将首级
缓冲流的正确姿势
缓冲流是IO流中最重要的知识点,下面通过代码实现正确用IO流的姿势
BufferedInputStream bis = null; BufferedOutputStream bos = null; try { bis = new BufferedInputStream(new FileInputStream(new File("D:/三国/视频.mp4"))); bos = new BufferedOutputStream(new FileOutputStream(new File("D:/三国/拷贝.mp4"))); int len; // 一次传输8M的文件,实际测试这里传输的大小并不影响传输的速度 byte[] data = new byte[8 * 1024]; while ((len = bis.read(data)) != -1) { bos.write(data, 0, len); } } catch (IOException e) { log.error("error", e); } finally { // finally块中关闭流,确保资源一定被关闭 if (bis != null) { try { bis.close(); } catch (IOException e) { log.error("error", e); } } if (bos != null) { try { bos.close(); } catch (IOException e) { log.error("error", e); } } }
转换流
字符编码与字符集
字符编码
计算机存储的数据都是二进制的,而我们在电脑上看到的数字、英文、汉字等都是二进制转换的结果
- 将字符转换成二进制,为编码
- 将二进制转换为字符,为解码
字符编码 就是 自然语言和二进制的对应规则
字符集
就是一个编码表,常见的字符集有ASCII字符集、GBK字符集、Unicode字符集等,具体各个编码的介绍在这里就不介绍了。
IDEA中,使用 FileReader 读取项目中的文本文件。IDEA可以设置为GBK 编码,当读取Windows系统中创建的默认的UTF8文本文件时,就会出现乱码 。
如下例
idea的字符集设置 默认是UTF-8,这里修改为GBK
运行的代码及结果
FileReader fileReader = new FileReader("D:/sanguo/utf8.txt"); int read; while ((read = fileReader.read()) != -1) { System.out.print((char)read); } // 浣犲ソ
InputStreamReader
Reader的子类,读取字节,并使用指定的字符集将其解码为字符。字符集可以自己指定,也可以使用平台的默认字符集。
构造方法如下
// 使用平台默认字符集 public InputStreamReader(InputStream in) {} // 指定字符集 public InputStreamReader(InputStream in, String charsetName) throws UnsupportedEncodingException{}
读取文件的“你好",文件默认的字符集是UTF8
// 创建流对象,默认UTF8编码 InputStreamReader isr = new InputStreamReader(new FileInputStream("D:/三国/utf8.txt")); // 创建流对象,指定GBK编码 InputStreamReader isr2 = new InputStreamReader(new FileInputStream("D:/三国/utf8.txt"), "GBK"); int read; while ((read = isr.read()) != -1) { System.out.println((char) read); } while ((read = isr2.read()) != -1) { System.out.println((char) read); } // 输出结果 你好 浣犲ソ
OutputStreamWriter
Writer的子类,使用指定的字符集将字符编码为字节。字符集可以自己指定,也可以使用平台的默认字符集。
构造方法如下
// 使用平台默认字符集 public OutputStreamWriter(OutputStream out) {} // 使用平台默认字符集 public OutputStreamWriter(OutputStream out, String charsetName) throws UnsupportedEncodingException{}
如下面的代码,将你好写入文件。写入后两个文件的字符集不一样,文件大小也不同
// 创建流对象,默认UTF8编码 OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("D:/三国/黄忠.txt")); osw.write("你好"); // 保存为6个字节 // 创建流对象,指定GBK编码 OutputStreamWriter osw2 = new OutputStreamWriter(new FileOutputStream("D:/三国/马超.txt"),"GBK"); osw2.write("你好");// 保存为4个字节
对象流
序列化
jdk提供了对象序列化的方式,该序列化机制将对象转为二进制流,二进制流主要包括对象的数据、对象的类型、对象的属性。可以将java对象转为二进制流写入文件中。文件会持久保存了对象的信息。
同理,从文件中读出对象的信息为反序列化的过程
对象想序列化,满足的条件:
- 该类必须实现 java.io.Serializable 接口, Serializable 是一个标记接口(没有任何抽象方法),不实现此接口的类将不会使任何状态序列化或反序列化,会抛出 NotSerializableException 。
- 该类的所有属性必须是可序列化的,如果有一个属性不需要可序列化的,则该属性使用transient 关键字修饰
ObjectOutputStream
该类实现将对象序列化后写出到外部设备,如硬盘文件
public ObjectOutputStream(OutputStream out) throws IOException{}
常用方法
方法名 | 方法说明 |
void writeObject(Object obj) throws IOException | 将指定的对象写出 |
如下代码,将User对象写入文件中
public class User implements Serializable { private static final long serialVersionUID = 8289102797441171947L; private String name; private Integer age; } // 下面是将对象输出到文件的核心代码 User user = new User("马超",20); // 创建序列化流对象 ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("D:/三国/马超.txt")); // 写出对象 out.writeObject(user);
注意:
- 实现了Serializable的实体一定要加一个serialVersionUID变量,这也是习惯问题,idea可以设置一下。
- serialVersionUID生成后不要改变,避免反序列化失败,改变后会抛出InvalidClassException异常
生成的文件内容如下
ObjectInputStream
该类将ObjectOutputStream写出的对象反序列化成java对象
public ObjectInputStream(InputStream in) throws IOException
常用方法
方法名 | 方法说明 |
Object readObject() throws IOException, ClassNotFoundException | 读取对象 |
ObjectInputStream in = new ObjectInputStream(new FileInputStream("D:/三国/马超.txt")); // 强转为user User user = (User) in.readObject(); System.out.println(user); // 输出内容 User(name=马超, age=20)
对象和字节数组的转换
利用对象流和字节数组流结合 ,可以实现java对象和byte[]之间的互转
// 将对象转为byte[] public static <T> byte[] t1(T t) { ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(t); return bos.toByteArray(); } // 将byte[]转为对象 public static <T> T t2(byte[] data) throws IOException, ClassNotFoundException { ByteArrayInputStream bos = new ByteArrayInputStream(data); ObjectInputStream oos = new ObjectInputStream(bos); return (T) oos.readObject(); }
管道流(了解)
管道流主要用于两个线程间的通信,即一个线程通过管道流给另一个线程发数据
注意:线程的通信一般使用wait()/notify(),使用流也可以达到通信的效果,并且可以传递数据
使用的类是如下
- PipedInputStream和PipedOutStream
- PipedReader和PipedWriter
这里使用字节流为例
class Sender implements Runnable { private PipedOutputStream pos; private String msg; public Sender(String msg) { this.pos = new PipedOutputStream(); this.msg = msg; } @Override public void run() { pos.write(msg.getBytes()); } public PipedOutputStream getPos() { return pos; } } class Receiver implements Runnable { private PipedInputStream pis; public Receiver() { this.pis = new PipedInputStream(); } @Override public void run() { byte[] data = new byte[1024]; int len; while ((len = pis.read(data)) != -1) { System.out.println(new String(data, 0, len)); } } } Sender sender = new Sender("hello"); Receiver receiver = new Receiver(); receiver.getPis().connect(sender.getPos()); new Thread(sender).start(); new Thread(receiver).start(); // 控制台输出 hello
输入与输出流(了解)
System.in和System.out代表了系统标准的输入、输出设备
默认输入设备是键盘,默认输出设备是控制台
可以使用System类的setIn,setOut方法对默认设备进行改变
我们开发中经常使用的输出到控制台上的内容的方法。
System.out.println("a"); System.out.print("b"); class System{ public final static InputStream in = null; public final static PrintStream out = null; } public PrintStream(String fileName) throws FileNotFoundException{}
数据流(了解)
主要方便读取Java基本类型以及String的数据,有DataInputStream 和 DataOutputStream两个实现类
DataOutputStream dos = new DataOutputStream(new FileOutputStream("D:/三国/周瑜.txt")); dos.writeUTF("周瑜"); dos.writeBoolean(false); dos.writeLong(1234567890L); DataInputStream dis = new DataInputStream(new FileInputStream("D:/三国/周瑜.txt")); String s = dis.readUTF(); System.out.println(s); boolean b = dis.readBoolean(); System.out.println(b); // 输出 周瑜 false
IO流总结
以上各个章节详细介绍了各个流,可见流的种类比较多,记忆确实增加了困难。但是可以通过思维导图的方式整理出来,方便记忆。
字节流的导图
字符流的导图
按照功能划分
输入、输出对应关系
结语
短期的加更计划
- NIO
- tomcat系列源码解析
文章篇幅较长,给看到这里的小伙伴点个大大的赞!由于作者水平有限,文章中难免会有错误之处,欢迎小伙伴们反馈指正。
如果觉得文章对你有帮助,麻烦 点赞、评论、转发、在看 、关注 走起
你的支持是我加更最大的动力!!!