0%

Java中级-IO

I/O文件对象

  • 使用绝对路径或相对路径创建File对象File file = new File("path")
  • File类的常用方法
    • exists():是否存在
    • isDirectory():是否为目录
    • isFile():是否为文件
    • length():获取文件长度
    • lastModified():文件的最后修改时间
    • renameTo():文件重命名
    • list():以String[]返回当前文件夹下的所有文件(不包括子文件及子文件夹)
    • listFiles():以Files[]返回当前文件夹下的所有文件(不包括子文件及子文件夹)
    • getParent():以String返回所在文件夹
    • getParentFile():以File返回所在文件夹
    • mkdir():创建单级目录
    • mkdirs():创建多级目录
    • createNewFile():创建空文件
    • delete():删除文件
    • deleteOnExit():JVM结束的时候删除文件,用于临时文件的删除

文件输入流
  • 建立文件输入流,把数据从硬盘的文件,读取到JVM(内存),需要使用try-catch

    1
    2
    File f = new FIle(path);
    FileInputStream fis = new FileInputStream(f);
字节流:以单个字节为基本单位,常用来处理二进制数据
  • InputStream为字节输入流(抽象类),FileInputstream是它的子类,以此类进行文件读取

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.IOException;
    public class TestStream {
    public static void main(String[] args) {
    try {
    File f =new File("test.txt"); //文本内容为 AB
    FileInputStream fis =new FileInputStream(f);
    byte[] all =new byte[(int) f.length()];
    //以字节流的形式读取文件所有内容
    fis.read(all);
    for (byte b : all) {
    System.out.println(b); //读取到的为每个字符的ASCII码,即65 66
    System.out.prinln((char) b);//读取到的为文本的实际内容
    }
    fis.close();
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    }
  • OutputStream为字节输出流(抽象类),FileOutputStream为它的子类,用于向文件写入数据

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.IOException;
    public class TestStream {
    public static void main(String[] args) {
    try {
    File f = new File("test.txt");
    byte data[] = { 88, 89 };//等价于byte[] data = {(byte) 'A',(byte) 'B'};
    // 创建基于文件的输出流,如果没有文件会进行创建操作
    FileOutputStream fos = new FileOutputStream(f);
    // 把数据写入到输出流
    fos.write(data);
    fos.close();
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    }
  • 标准的流关闭:严谨的时候使用

    • 把流的引用声明在try的外面FileInputStream fis = null;
    • 在finally关闭之前,要先判断引用是否为空,在进行关闭
    • 关闭的时候,需要再一次进行try-catch处理
  • 无需手动关闭流的方法

    • 使用try-catch-resource,即把FileInputStream声明在try的括号体内,不论结果最终都会自己释放掉,只要自身实现了AutoCloseable接口
字符流
  • 读取文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    import java.io.File;
    import java.io.FileReader;
    public class TestStream {
    public static void main(String[] args) {
    File f =new File("test.txt");
    try (FileReader fr = new FileReader(f)){
    char[] data = new char[(int) f.length()];//一个cahr占两个字节
    fr.read(data);
    for (char c: data){
    System.out.println(c);
    }
    } catch (Exception e) {
    e.printStackTrace();
    }
    }
    }
  • 写入文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    import java.io.File;
    import java.io.FileWriter;
    public class TestStream {
    public static void main(String[] args) {
    File f =new File("test.txt");
    try (FileWriter fw = new FileWriter(f)){
    char[] data = "hahaha".toCharArray();
    fw.write(data);
    } catch (Exception e) {
    e.printStackTrace();
    }
    }
    }
编码
  • 常见编码

    • ISO-8859-1 ASCII 数字和西欧字母 包含了ASCII
    • GBK GB2312 BIG5 中文 其中GB2312是简体中文,BIG5是繁体中文,GBK同时包含简繁中文及日文,其中中文均占两个字节
    • UNICODE (统一码,万国码) 包含了所有的文字,世界上所有的文字
  • UNICODE和UTF

    • Unicode因为要包含所有的数据,因此每个字符都占了4个字节,如果完全按照UNICODE的方式来存储数据,就会有很大的浪费
    • UTF则是UNICODE的减肥般,如UTF-8对数字和字母只使用了一个字节,而对汉字就使用三个字节
  • Java采用的是Unicode

  • 使用FileInputStream字节流正确读取中文(文本采用GBK编码,即中文占两个字节)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    import java.io.File;
    import java.io.FileInputStream;
    public class TestStream {
    public static void main(String[] args) {
    File file = new File("test.txt");
    try (FileInputStream fis = new FileInputStream(file)) {
    byte[] data = new byte[(int) file.length()];
    fis.read(data);
    for (byte b : data){
    int i = b&0x000000ff; //gbk中中文只占两个字节,只需读取后两个
    System.out.println(Integer.toHexString(i)); //转为16进制形式
    }
    System.out.println(new String(data,"GBK")); //将字符数组以GBK编码形式输出
    } catch (Exception e) {
    //TODO: handle exception
    }
    }
    }
  • 使用FileReader字符流正确读取中文

    • FileReader得到的是字符,所以已经是把字节根据某种编码识别成字符了

    • FileReader使用的编码方式是Charset.defaultCharset()的返回值,如果是中文的操作系统,就是返回GBK。

    • FileReader无法手动设置编码方式(但实测JDK11可以使用FileReader(file,Charset.forName(“GBK”)) 手动指定编码格式),也可InputStreamReader来代替即new InputStreamReader(new FileInputStream(f),Charset.forName(UTF-8));

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
          import java.io.File;
      import java.io.FileInputStream;
      import java.io.FileReader;
      import java.io.InputStreamReader;
      import java.nio.charset.Charset;

      public class TestStream {
      public static void main(String[] args) {
      File file = new File("test.txt"); //文件采用GBK编码
      System.out.println("默认编码方式为:"+Charset.defaultCharset());
      //Linux中,当前默认为UTF-8
      try (FileReader fr = new FileReader(file)) {
      char[] data = new char[(int) file.length()];
      fr.read(data); //默认采用UTF-8读取,会乱码
      System.out.println(new String(data));
      } catch (Exception e) {
      //TODO: handle exception
      }
      try (InputStreamReader isr = new InputStreamReader(new FileInputStream(file),Charset.forName("GBK"))){ //采用GBK方式读取,不会乱码
      char[] data = new char[(int) file.length()];
      isr.read(data);
      System.out.println(new String(data));

      } catch (Exception e) {
      //TODO: handle exception
      }
      }
      }

      image-20200910192712105

缓存流
  • 字节流和字符流的介质是硬盘,在每一次读写的时候都会访问硬盘,频率较高实性能表现不佳

  • 缓存流在读取的时候,会一次性读比较多的数据到缓存中,以后的每次读取都在缓存中房问知道缓存数据读取完毕再去硬盘读取

  • 缓存流在写入的时候,会先把数据写到缓存区,知道缓存区达到一定的量才把这些数据一起写入硬盘,减少I/O操作

  • 读取数据

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    import java.io.BufferedReader;
    import java.io.File;
    import java.io.FileReader;

    public class TestStream {
    public static void main(String[] args) {
    File file = new File("test.txt");
    try (FileReader fr = new FileReader(file);
    BufferedReader br = new BufferedReader(fr)) {//br需建立在一个存在的流的基础上
    while(true){
    String line = br.readLine();
    if(null == line) break;
    System.out.println(line);
    }
    } catch (Exception e) {
    //TODO: handle exception
    }
    }
    }
  • 写出数据:尽量使用PrintWriter而非BufferedWriter(如BW在write后要手动newLine,PW直接使用println,且参数可为任意类型)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    import java.io.File;
    import java.io.FileWriter;
    import java.io.PrintWriter;

    public class TestStream {
    public static void main(String[] args) {
    File file = new File("test.txt");
    try (FileWriter fw = new FileWriter(file);
    PrintWriter pw = new PrintWriter(file)) {
    pw.println("123");
    pw.println("234");
    } catch (Exception e) {
    //TODO: handle exception
    }
    }
    }
  • 立即写入数据:使用PrintWriter.flush()方法

数据流(格式化读写):
  • 通过DataOutputStream格式化顺序写入,(只能)通过DataInputStream格式化顺序读出(因为DataOutputStream写的时候会进行标记,其他无法正确读取)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    import java.io.DataInputStream;
    import java.io.DataOutputStream;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;

    public class TestStream {
    public static void main(String[] args) {
    write();
    read();
    }
    private static void read() {
    File f =new File("test.txt");
    try (
    FileInputStream fis = new FileInputStream(f);
    DataInputStream dis =new DataInputStream(fis);
    ){
    boolean b= dis.readBoolean();
    int i = dis.readInt();
    String str = dis.readUTF();
    System.out.println("读取到布尔值:"+b);
    System.out.println("读取到整数:"+i);
    System.out.println("读取到字符串:"+str);
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    private static void write() {
    File f =new File("test.txt");
    try (
    FileOutputStream fos = new FileOutputStream(f);
    DataOutputStream dos =new DataOutputStream(fos);
    ){
    dos.writeBoolean(true);
    dos.writeInt(300);
    dos.writeUTF("123 this is gareen");
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    }

    image-20200910215247303

对象流
  • 对象流是把一个对象以流的形式传输给其他介质(如硬盘),一个对象以流的形式进行传输,叫做序列化。该对象所对应的类,必须实现Serializable接口

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    import java.io.Serializable;

    public class TestStream{
    public static void main(String[] args) {
    Heroo h = new Heroo();
    h.name = "garen";
    h.hp = 123;
    File file = new File("garen.lol");
    try (FileOutputStream fos = new FileOutputStream(file);
    ObjectOutputStream oos = new ObjectOutputStream(fos);//对象输出流
    FileInputStream fis = new FileInputStream(file);
    ObjectInputStream ois = new ObjectInputStream(fis)){//对象输入流
    oos.writeObject(h);
    Heroo hh = (Heroo) ois.readObject();
    System.out.println(hh.name);
    System.out.println(hh.hp);
    } catch (Exception e) {
    //TODO: handle exception
    }
    }
    }

    class Heroo implements Serializable{
    private static final long serialVersionUID = 1L;
    public String name;
    public float hp;

    }

System.in

  • 只能进行单个字符的读取

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    import java.io.IOException;
    import java.io.InputStream;

    public class TestStream{
    public static void main(String[] args) {
    try(InputStream is = System.in){
    while(true){
    int i = is.read();
    System.out.println(i);
    }
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    }

Scanner

  • 可以进行逐行读取

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    import java.util.Scanner;

    public class TestStream{
    public static void main(String[] args) {
    Scanner scanner = new Scanner(System.in);
    while(true){
    String line = scanner.nextLine();
    System.out.println(line);
    }
    }
    }

流关系图

5678