一字节占 8 位, 最大表示 0-255
GBK 编码中一个中文字占用 2 字节一个英文占用 1 字节
UTF-8 编码中一个中文字占用 3 字节一个英文占用 1 字节
文本文件就是字节序列
普通文件也是字节序列, 字节序列归根到底是 0 1
EOF = End 读到 -1 就读到结尾
文件读写
File
java.io.File 类用于表示文件 (目录),File 类只用于表示文件(目录) 的信息(名称大小), 不能用于访问文件内容
- public static void main(String[] args) {
- File file = new File("/Users/linyuan/a.txt");
- if (file.exists())
- System.out.println("文件存在");
- else {
- try {
- System.out.println("创建新文件");
- file.createNewFile();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- RandomAccessFile
RandomAccessFile 是 Java 提供的对文件内容随机访问的实现, 可读文件, 也可写文件
支持随机访问, 可以访问文件任意位置, 一次只能读写一个字节, 写入一个 int 类型整数需要写 4 次
- try(RandomAccessFile randomAccessFile = new RandomAccessFile("/Users/linyuan/a.txt","rw")){
- randomAccessFile.write("abcd".getBytes());
- } catch (Exception e) {
- e.printStackTrace();
- }
文件打开方式
r: 只读
rw: 读写
rws: 读写, 针对文件的内容与原数据的每个更新都会强制写到存储设备
rwd: 读写, 针对文件的内容的每个更新都会强制写到存储设备
字节流
InputStream
这个抽象类是字节输入流所有类的超类, 抽象了应用程序读取数据的方式
OutputStream
这个抽象类是字节输出流所有类的超类, 抽象了应用程序写入数据的方式
FileInputStream
具体实现了从文件系统中的文件获取输入字节, 用于读取诸如图像数据之类的原始字节流
- try(FileInputStream fileInputStream = new FileInputStream("/Users/linyuan/a.txt")){
- byte[] bytes = new byte[256];
- fileInputStream.read(bytes);
- System.out.println(new String(bytes));
- } catch (FileNotFoundException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- }
- FileOutputStream
文件输出流是将数据写入文件, 用于写入诸如图像数据之类的原始字节流
- try(FileOutputStream fileOutputStream = new FileOutputStream("/Users/linyuan/a.txt")){
- fileOutputStream.write("hello".getBytes());
- } catch (FileNotFoundException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- }
- DataInputStream / DataOutputStream
对流功能的扩展, 可以更加方便的读写 intlongchar 等类型数据, 是一个包装类
BufferedInputStream
构造方法如下
- // 创建一个 BufferedInputStream 并保存其参数, 即输入流 in, 以便将来使用创建一个内部缓冲区数组并将其存储在 buf 中, 该 buf 的大小默认为 8192
- public BufferedInputStream(InputStream in);
- // 创建具有指定缓冲区大小的 BufferedInputStream 并保存其参数, 即输入流 in, 以便将来使用创建一个长度为 size 的内部缓冲区数组并将其存储在 buf 中
- public BufferedInputStream(InputStream in,int size);
在进行读操作时, 它的 read 方法表面上看仍然是读取一个字节, 但在开始时它会从硬盘读取一大堆字节到缓存区, 当你 read 时是从缓存读进一个字节到内存, 而前面所讲的 FileInputStream 则是每次 read 时都是从硬盘读到内存, 效率很慢
- try (FileInputStream fis = new FileInputStream("/Users/linyuan/Documents/a.txt");
- BufferedInputStream bis = new BufferedInputStream(fis)) {
- String content = "";
- byte[] bytes = new byte[1024];
- int flag = 0;
- while ((flag = bis.read(bytes, 0, bytes.length)) != -1) {
- content += new String(bytes, 0, flag,"UTF-8");
- }
- System.out.println(content);
- } catch (Exception e) {
- e.printStackTrace();
- }
- BufferedOutputStream
构造方法如下
- // 创建一个新的缓冲输出流, 以将数据写入指定的底层输出流
- public BufferedOutputStream(OutputStream out);
- // 创建一个新的缓冲输出流, 以将具有指定缓冲区大小的数据写入指定的底层输出流
- public BufferedOutputStream(OutputStream out,int size);
在进行写操作时, 它会先将字节写入缓冲区内, 然后在调用 flush() 方法将缓冲区内的字节刷新到硬盘中, 而前面所讲的 FileOutputStream 则是每次 write 时都是从内存写入到硬盘, 效率很慢
- try (FileOutputStream fos = new FileOutputStream("/Users/linyuan/Documents/a.txt");
- BufferedOutputStream bos = new BufferedOutputStream(fos)) {
- String content = "Java IO";
- bos.write(content.getBytes("UTF-8"));
- bos.flush();
- } catch (Exception e) {
- e.printStackTrace();
- }
- ByteArrayInputStream
它包含一个 byte 数组的缓冲区, 缓冲区中的数据可能来源于流, 读取时直接根据数组下标来获取对应的数据
关闭 ByteArrayInputStream 后流仍然可被调用, 而且不会产生任何异常
- byte[] bytes = ... //get byte array from somewhere.
- InputStream input = new ByteArrayInputStream(bytes);
- int data = input.read();
- while(data != -1) {
- //do something with data
- data = input.read();
- }
- input.close();
- ByteArrayOutputStream
该类实现了 OutputStream, 数据被写入一个 byte 数组里面, 并随着数据的不断写入而自动增长, 可使用 toByteArray() 和 toString() 获取数据
关闭 ByteArrayOutputStream 后流仍然可被调用, 而且不会产生任何异常
- try(ByteArrayOutputStream baos = new ByteArrayOutputStream(1024)){
- baos.write("linyuan".getBytes());
- }catch (Exception e){
- e.printStackTrace();
- }
- PrintStream
Java 的 PrintStream 类可以打印任意类型的数据, 打印之前会先把数据转换成字符串在进行打印, System.out 和 System.err 都是 PrintStream
- PrintStream printStream = new PrintStream(outputStream);
- printStream.print(true);
- printStream.print((int) 123);
- printStream.print((float) 123.456);
- printStream.close();
字符流
Reader
Reader 是字符输入流的抽象类
Writer
Writer 是字符输出流的抽象类
InputStreamReader
字节流 (byte) 流到字符流 (char) 流的转换, 按照编码处理
- try (FileInputStream fis = new FileInputStream("/Users/linyuan/Documents/a.txt");
- InputStreamReader isr = new InputStreamReader(fis)) {
- char[] chars = new char[10];
- int flag = isr.read(chars);
- while (flag != -1){
- System.out.println(chars);
- flag = isr.read(chars);
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- OutputStreamReader
字节流 (byte) 流到字符流 (char) 流的转换, 按照编码处理
- try (FileOutputStream fos = new FileOutputStream("/Users/linyuan/Documents/a.txt");
- OutputStreamWriter osw = new OutputStreamWriter(fos)) {
- osw.write("新的一天");
- osw.flush();
- } catch (Exception e) {
- e.printStackTrace();
- }
- FileWriter/FileReader
是对字符流功能的扩展, 可以直接对文件进行读写, 是一个包装类
- try (FileWriter fw = new FileWriter("/Users/linyuan/Documents/a.txt")) {
- fw.write("新的一天");
- fw.flush();
- } catch (Exception e) {
- e.printStackTrace();
- }
- BufferedReader / BufferedWriter
对字节流到字符流进行了缓冲操作, 并提供了 readLine(读一行)等功能
- try (FileReader fr = new FileReader("/Users/linyuan/Documents/a.txt");
- BufferedReader br = new BufferedReader(fr)) {
- String content = null;
- while ((content = br.readLine()) != null) {
- System.out.println(content);
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
序列化反序列化
涉及网络传输的应用序列化不可避免, 发送端以某种规则将消息转成 byte 数组进行发送, 接收端则以约定的规则进行 byte[] 数组的解析
对象序列化就是将 Object 转化成 byte 序列, 反之叫反序列化
被序列化对象必须实现序列化接口 Serializable
一个类实现了序列化接口, 那么其子类都可以进行序列化
对子类对象进行反序列化操作时, 如果其父类没有实现序列化接口, 那么其父类的构造函数会被调用
被 transient 修饰的关键字不会被 jvm 进行序列化, 但可以自己完成元素的序列化, 重写方法 writeObject 与 readObject
ObjectOutputStream 与 ObjectInputStream
定义 Person 类, 实现 Serializable 接口
- public class Person implements Serializable {
- private String name;
- private int age;
- public Person(String name, int age) {
- this.name = name;
- this.age = age;
- }
- // 省略 setter 和 getter 方法
- ...
- }
序列化流 ObjectOutputStream
- try(ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("/Users/linyuan/a.txt"))){
- Person person = new Person("linyuna",21);
- oos.writeObject(person);
- } catch (IOException e) {
- e.printStackTrace();
- }
反序列化 ObjectInputStream
- try(ObjectInputStream ois = new ObjectInputStream(new FileInputStream("/Users/linyuan/a.txt"))){
- Person person = (Person)ois.readObject();
- } catch (Exception e) {
- e.printStackTrace();
- }
对象引用序列化
如果要序列化的类的某个成员变量是一个非 String 类型的引用类型, 那么这个引用类型必须是可序列化的, 例如有一个 Teacher 类持有 Person 类的引用
- public class Teacher implements Serializable {
- private String name;
- private Person person;
- public Teacher(String name, Person person) {
- this.name = name;
- this.person = person;
- }
- // 省略 setter 和 getter 方法
- ...
当两个 Teacher 对象引用同一个 Person 对象的时候, 看起来似乎会向输出流中输出三个 Person 对象, 但这样的话, 当反序列化时 t1 与 t2 所引用的 Person 就不是同一个对象了
- try(ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("/Users/linyuan/a.txt"))){
- Person per = new Person("linyuan", 21);
- Teacher t1 = new Teacher("Miss Li", per);
- Teacher t2 = new Teacher("Mr Wu", per);
- oos.writeObject(t1);
- oos.writeObject(t2);
- } catch (IOException e) {
- e.printStackTrace();
- }
为了避免上述错误, Java 在序列化时会保存对象的序列化编号, 当反序列化时会检查该对象是否已被反序列化过, 如果反序列化过则直接输出序列化编号, 而不是重新序列化该对象
- try(ObjectInputStream ois = new ObjectInputStream(new FileInputStream("/Users/linyuan/a.txt"))){
- Teacher teacher1 = (Teacher)ois.readObject();
- Teacher teacher2 = (Teacher)ois.readObject();
- System.out.println(teacher1.getName());
- System.out.println(teacher2.getName());
- if (teacher1.getPerson() == teacher2.getPerson())
- System.out.println("ok");
- }catch (Exception e) {
- e.printStackTrace();
- }
来源: http://www.jianshu.com/p/3d2b2ae676eb