从控制台读写数据。在 java.io 包中使用BufferedReader、BufferedWriter、File、fileereader、FileWriter、FileInputStream、FileOutputStream、ObjectOutputStream、ObjectInputStream和PrintWriter。

I/O Streams

I/O流和Stream API 没有任何关系。
简单地说,流就是一系列的数据。

  • 当我们从一个文件中读取这个字节序列时,我们正在读取一个输入流。
  • 当我们将这个字节序列写入一个文件时,就是在写入一个输出流。
  • 文件的内容可能非常大,以至于可能无法放入内存,因此在处理流时,我们不能一次性关注整个流,而是在任何时候都只关注一小部分(逐字节或一组字节)
  • java.io 包中,我们可以找到处理字节流和字符流的类

File

把文件看作存储数据的资源(以字节或字符格式)。文件组织在目录中,除了文件,还可以包含其他目录。
文件或目录可以用一个名为 path 的 String 表示,其中分隔符字符左侧的值(在文件系统之间更改)是分隔符右侧值的父元素,如/home/documents/file.txt 或 Windows 上的c:\home\documents\file.txt

File file = new File("/home/user.properties");

有了一个 File 对象,可以使用一些方法来处理这个文件/目录

String path = ...
File file = new File(path);
if(file.exists()) {
    // Name of the file/directory
    String name = file.getName();
    // Path of its parent
    String parent = file.getParent();
    // Returning the time the file/directory was modified
    // in milliseconds since 00:00:00 GMT, January 1, 1970
    long millis = file.lastModified();

    // If the object represents a file
    if(file.isFile()) {
        // Returning the size of the file in bytes
        long size = file.length();
    }
    // If the object represents a directory
    else if(file.isDirectory()) {
        // Returns true only if the directory was created
        boolean dirCreated = file.mkdir();
        // Returns true only if the directory was created,
        // along with all necessary parent directories
        boolean dirsCreated = file.mkdirs();

        // Get all the files/directories in a directory
        // Just the names
        String[] fileNames = file.list();
        // As File instances
        File[] files = file.listFiles();
    }
    boolean wasRenamed = file.renameTo(new File("new file"));
    boolean wasDeleted = file.delete();
}

常用API

由于其API非常多,只是列举一些
image.png

  • getName()
    这只是路径名名称序列中的最后一个名称。 如果路径名的名称序列为空,则返回空字符串。
  • exists()
    此抽象路径名表示的文件或目录是否存在
  • getParent()
    返回此抽象路径名的父目录的路径名字符串,如果此路径名未命名父目录,则返回null
  • isFile()
    当且仅当此抽象路径名表示的文件存在并且是普通文件时才为true ; 否则为false
  • isDirectory()
    当且仅当此抽象路径名表示的文件存在并且是目录时才为true ; 否则为false
  • file.mkdir()
    创建由此抽象路径名命名的目录
  • mkdirs()
    当且仅当创建了目录以及所有必需的父目录时才为true ; 否则为false
  • delete()
    删除此抽象路径名表示的文件或目录。 如果此路径名表示一个目录,则该目录必须为空才能被删除
  • list()
    其只会列举出当前目录(如果不为目录则返回null)的子集文件和目录名字,不包含其他任何路径和更深层次的目录。
    image.png
    image.png

了解java.io包

image.png
java.io 包中有很多类,OCP涉及到的类中4个抽象abstract类是其他类的父类

  • 字节流byte Stream
    1.InputStream
    2.OutputStream
  • 字符流character Stream
    1.Reader
    2.Writer

其都实现了 AutoClosable,因此可以在 try-with-resources 中使用。不必手动finally释放资源
image.png
image.png

规则分类

  • Input 或 Reader 的类用于read(字节或字符)
  • Output 和 Writer 的所有类都是为了writer(字节或字符)
  • Buffered 的类,它们使用缓冲区以组(字节或字符)的形式读写数据
  • 可以进一步将 API 中的类分为包装器和非包装器。
    1.非包装类通常使用 File 或 String 的实例来创建实例
    2.包装类使用另一个流类来创建实例。
//包装类
try {
                ObjectInputStream ois =
                        new ObjectInputStream(new FileInputStream("obj.dat"));
            } catch (IOException e) {
                e.printStackTrace();
            }
  • 使用包装类不能将字节流和字符流混合
    image.png

FileInputStream

从文件中读取字节。继承InputStream抽象类
image.png
image.png

常用API

image.png

  • read()
  • 从此输入流中读取一个字节的数据。 如果尚无可用输入,则此方法会阻塞。
    返回值:数据的下一个字节,如果到达文件末尾,则为-1 。
  • read(byte b[])
    从此输入流中读取最多b.length个字节的数据到一个字节数组中。 此方法会阻塞,直到某些输入可用。
    参数:b – 读取数据的缓冲区。
    返回值:读入缓冲区的总字节数,如果由于已到达文件末尾而没有更多数据,则为-1 。
  • skip(long n)
    跳过并丢弃输入流中的n字节数据。
    image.png

创建实例

1.构造方法

//,此处没有fianlly的close,因为ObjectInputStream实现了 AutoClosable,且在 try-with-resources 中使用
try (InputStream in = new FileInputStream("c:\\file.txt")) {
    int b;
    // -1 indicates the end of the file 
    while((b = in.read()) != -1) {
        // Do something with the byte read
    }
} catch(IOException e) { /** ... */ }

也可以使用数组将信息读入

try (InputStream in = new FileInputStream("c:\\file.txt")) {
            byte[] data = new byte[1024];
            int numberOfBytesRead;
            while ((numberOfBytesRead = in.read(data)) != -1) {
                // Do something with the array data
            }
        } catch (IOException e) { /** ... */}
    }

FileOutputStream

将字节写入文件中。继承OutputStream抽象类

常用API

image.png

  • FileOutputStream(String name)
    如果文件存在但是是一个目录而不是常规文件,不存在但无法创建,或者由于任何其他原因无法打开,则抛出FileNotFoundException
  • write(int b)
    将指定字节写入此文件输出流。 实现OutputStream的write方法。
    参数:b – 要写入的字节。
  • void write(byte b[])
    将指定字节数组中的b.length个字节写入此文件输出流。
    参数:b——数据

创建实例

1.构造方法

try (OutputStream out =
        new FileOutputStream("c:\\file.txt")) {
    int b;
    // Made up method to get some data
    while((b = getData()) != -1) {
        // Writes b to the file output stream
        out.write(b);
        out.flush();
    }
} catch(IOException e) { /** ... */ }

刷新推送

写入 OutputStream 时,数据可能被内部缓存在内存中,稍后写入磁盘。如果希望确保所有数据都写入磁盘而不必关闭 OutputStream,可以每隔一段时间调用 flush ()方法
image.png
close时会flush

FileReader

从文本文件读取字符。继承抽象类Reader
image.png
其只是读取字符文件的便利类,只是多了几个构造方法而已
image

创建实例

try (Reader r = new FileReader("/file.txt")) {
    int c;
    // -1 indicates the end of the file
    while((c = r.read()) != -1) { 
        char character = (char)c;
        // Do something with the character
    }
} catch(IOException e) { /** ... */ }

FileWriter

FileWriter 将字符写入文本文件。继承 Writer抽象类

创建实例

try (Writer w = new FileWriter("/file.txt")) {
    w.write('-'); // writing a character
    // writing a string
    w.write("Writing to the file...");
} catch(IOException e) { /** ... */ }

推送流

数据可能被内部缓存在内存中,稍后写入磁盘。如果希望确保所有数据都写入磁盘而不必关闭 FileWriter,可以每隔一段时间调用 flush ()方法

BufferedReader

从字符流中读取文本。一次将一个大块读取到缓冲区中。继承Reader抽象类
image.png
这是一个包装类,通过传递一个 Reader 到它的构造函数,并且可以选择传递缓冲区的大小来创建

BufferedReader(Reader in)
BufferedReader(Reader in, int size)
try ( BufferedReader br =
     new BufferedReader( new FileReader("/file.txt") ) ) {
   String line;
   // null indicates the end of the file
   while((line = br.readLine()) != null) {
      // Do something with the line
   }
} catch(IOException e) { /** ... */ }

当 BufferedReader 关闭时,它还将关闭它从中读取的 Reader 实例。(即不必调用Reader的close方法)

BufferedWriter

将文本写入字符流,缓冲字符以提高效率。
也为包装类

创建实例

BufferedWriter(Writer out)
BufferedWriter(Writer out, int size)
try ( BufferedWriter bw =
     new BufferedWriter( new FileWriter("/file.txt") ) ) {
   w.write("Writing to the file...");
   w.newLine();
} catch(IOException e) { /** ... */ }

刷新推送

数据首先被写入缓冲区,可以调用 flush ()方法来确保在此之前写入的文本确实被写入到磁盘中。
BufferedWriter 关闭时,它也会关闭它所写入的 Writer 实例

ObjectInputStream/ ObjectOutputStream

image.png

  • ObjectOutputStream 允许将对象序列化到 OutputStream
  • ObjectInputStream 允许从 InputStream 反序列化对象。所以两者都被认为是包装器类。

序列化和反序列化

  • 对象转换为可以存储的数据格式(例如文件)的过程称为序列化
  • 将存储的数据格式转换为对象的过程称为反序列化

序列化

要序列化一个对象,它的类必须实现 java.io. Serializable。它没有方法来实现,它只是将该类的对象标记为可序列化的。
image.png
如果序列化一个不实现该接口的类,则java.io.NotSerializableException (IOException 的子类)将在运行时抛出。
image.png
image.png
Seriable接口仅仅是个标识,检查obj除了为String、enum枚举、数组之外的类时是否实现Seriable,为否则抛出NotSerializableException异常

ObjectOutputStream

  • 创建实例
    image.png
    其构造方法public只有一个
ObjectOutputStream(OutputStream out)
class Box implements java.io.Serializable {
   /** ... */
}
...
try( ObjectOutputStream oos =
       new ObjectOutputStream(
         new FileOutputStream("obj.dat") ) ) {
    Box box = new Box();
    oos.writeObject(box);
} catch(IOException e) { /** ... */ }

ObjectInputStream

反序列化文件 obj.dat,使用 ObjectInputStream 类

Object readObject()
      throws IOException, ClassNotFoundException

它返回一个 Object 类型。必须显式地强制转换对象。这可能导致在运行时抛出 ClassCastException(非检查异常)。如果找不到序列化对象的类,此方法还抛出 ClassNotFoundException (一个检查过的异常)

try (ObjectInputStream ois =
        new ObjectInputStream(
          new FileInputStream("obj.dat") ) ) {
    Box box = null;
    Object obj = ois.readObject();
    if(obj instanceof Box) {
        box = (Box)obj;
    }
} catch(IOException ioe) { /** ... */ }
catch(ClassNotFoundException cnfe) {
   /** ... */
}

PrintWriter

Writer抽象类的子类,将格式化的数据写入另一个(包装的)流,甚至是 OutputStream。
image.png

// Opens or creates the file without automatic line flushing
// and converting characters by using the default character encoding
try(PrintWriter pw = new PrintWriter("/file.txt")) {
    pw.write("Hi"); // Writing a String
    pw.write(100); // Writing a character

    // write the string representation of the argument
    // it has versions for all primitives, char[], String, and Object
    pw.print(true);
    pw.print(10);

    // same as print() but it also writes a line break as defined by
    // System.getProperty("line.separator") after the value
    pw.println(); // Just writes a new line
    pw.println("A new line...");

    // format() and printf() are the same methods
    // They write a formatted string using a format string,
    // its arguments and an optional Locale
    pw.format("%s %d", "Formatted string ", 1);
    pw.printf("%s %d", "Formatted string ", 2);
    pw.format(Locale.GERMAN, "%.2f", 3.1416);
    pw.printf(Locale.GERMAN, "%.3f", 3.1416);
} catch(FileNotFoundException e) { 
    // if the file cannot be opened or created
} 

Standard streams

Java初始化并提供三个流对象作为Java.lang.System类的公共静态字段

  • InputStream System.in
    标准输入流(通常是键盘输入)
  • PrintStream System.out
    标准输出流(通常是默认的显示输出)
  • PrintStream System.err
    标准错误输出流(通常是默认的显示输出)
    image.png
    PrintStream的功能和 PrintWriter 完全一样,但只能使用 OutputStreams

读取命令行字符

  • int
System.out.print("Enter a character: ");
try {
    int c = System.in.read();
} catch(IOException e) {
    System.err.println("Error: " + e);
}
  • String
BufferedReader br =
    new BufferedReader(new InputStreamReader(System.in));
String line = br.readLine();
// Or using the java.util.Scanner class
Scanner scanner = new Scanner(System.in);
String line = scanner.nextLine();

image.png

java.io.Console

单例
image.png
java.io.Console 类来访问程序运行所在机器的控制台。
System.console()方法获得单例
image.png
最终还是通过JavaIOAccess接口的console()方法得到的

Console console1 = System.console();
        JavaIOAccess javaIOAccess = SharedSecrets.getJavaIOAccess();
        Console console = javaIOAccess.console();
        System.out.println(console==console1);

image.png

  • 程序运行在无法访问控制台的环境中(如 IDE 或者如果您的程序作为后台进程运行) ,System.console ()将返回 null
  • 使用 readLine ()方法读取用户输入,使用 readPassword ()读取密码。
  • Reader ()和 Writer ()分别返回一个 Reader 和 Writer 的实例
Console console = System.console();
// Check if the console is available
if(console != null) {
    console.writer().println("Enter your user and password");
    String user = console.readLine("Enter user: ");
    // readPassword() hides what the user is typing
    char[] pass = console.readPassword("Password: ");
    // Clear password from memory by overwriting it
    Arrays.fill(pass, 'x');
}

总结

  • I/O流是表示文件内容的数据序列。
  • 有四个主要的抽象类,其余的类从InputStream、OutputStream、Reader和Writer扩展而来。
  • java.io类可分为:
    1.字节流或字符流
    2.输入或输出
    3.包装或非包装
  • Java初始化并提供三个流对象作为Java.lang.System类的公共静态字段:
    1.InputStream System.in标准输入流(通常是来自键盘的输入)
    2.PrintStream System.out标准输出流(通常是默认的显示输出)
    3.PrintStream System.err标准错误输出流(通常是默认的显示输出)Z

这个家伙很懒,啥也没有留下😋