Java 中的IO-06

回顾

其实之前我们已经学过了Java IO中的主要的一些流,剩下来的流基本上都是一个套路没有必要一个一个再去研讨。下面是Java IO中的一个体系图。 Java IO中主要分为了四大抽象类,然后各种流都是继承于这些抽象类,实现这些抽象类的方法。下面的图中有的是我每次学过的,有的是我们没看过的,有的还是Java中已经废弃了的,不过其实我们也完全没有必要按照这个图再去将其余的流再去学习一次,我们学习java IO其实是学习的Java IO 的思路。即使我们遇到一个陌生的流,我们也完全可以应付的来。

回到Hello World

我们对IO流的学习其实是源于Hello world的输出,当时我们不知道,现在我们知道了System.out其实就是一个流对象,调用println()方法就是向控制台中输出信息。而当我们想要获取键盘的输入的时候我们使用的是System.in这个流,一般情况下,我们使用的方式是

Scanner in = new Scanner(System.in);
String str = in.next();
int num = in.nextInt();

这个Scanner就是Java为了方便我们处理键盘的输入而产生的类。不过我们也可以使用我们之前的方式读取键盘的输入。

BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
String s = bufferedReader.readLine();
int a = Integer.parseInt(bufferedReader.readLine());
System.out.println(s + a);

不过可以清晰的看到这个方法是有一点儿烦的,所以说使用Scanner在读取控制台的输入还是非常的方便而且简单的。

不过这个Scanner并不是只可以读取控制的输入的,也可以读取文件。只要是流都是可以读取的。更好的是这个类有hasNext等一系列的方法,所以说上面的scanner代码可以修改一下。

Scanner in = new Scanner(System.in);
if (in.hasNext()){
    String str = in.next();
}
if (in.hasNextInt){
    int num = in.nextInt();    
}

下面来演示一下使用Scanner来读取文件的操作,其实是一样的,文件流和控制台的标准流都是流。

private static void scannerReadFile() {
    // Use Scanner to read file!
    final String path = "C:/Users/SHeR/IdeaProjects/Exercise/src/com/sher/corejava/hello/file.txt";
    Scanner scanner = null;
    try {
        scanner = new Scanner(Path.of(path), StandardCharsets.UTF_8);
        while (scanner.hasNextLine()) {
            System.out.println(scanner.nextLine());
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (scanner != null) {
            scanner.close();
        }
    }
}

System.in是一个OnputStream,但是System.out并不是OutputStream而是一个叫PrintWriter的玩意,这就是很好奇了。现在我们来看一看这个PrintWriter是一个什么玩意。

PrintWriter

看到这个类是以Writer结尾的,我们立马就知道了,这是一个字符流。不过也对,我们想控制台输入的肯定是字符啊。输入字节我们也是看不懂的,这是没必要的。先观察一下这个类的官方文档再说。

java.lang.Object 
    java.io.Writer 
        java.io.PrintWriter 
public class PrintWriter
extends Writer
  • 将对象的格式表示打印到文本输出流。这个类实现了全部在发现print种方法PrintStream 。它不包含用于编写原始字节的方法,程序应使用未编码的字节流。
  • 不像PrintStream类,如果启用自动刷新,它只会在调用的println,printf,format方法来完成,而不是当一个换行符恰好是输出。 这些方法使用平台自己的行分隔符而不是换行符。
  • 这个类中的方法不会抛出I / O异常,尽管它的一些构造函数可能。 客户可以通过调用checkError()查询是否发生错误。
PrintWriter(File file) 
// 使用指定的文件创建一个新的PrintWriter,而不需要自动的线路刷新。  
PrintWriter(File file, String csn) 
// 使用指定的文件和字符集创建一个新的PrintWriter,而不需要自动进行线条刷新。  
PrintWriter(OutputStream out) 
// 从现有的OutputStream创建一个新的PrintWriter,而不需要自动线路刷新。  
PrintWriter(OutputStream out, boolean autoFlush) 
// 从现有的OutputStream创建一个新的PrintWriter。  
PrintWriter(String fileName) 
// 使用指定的文件名创建一个新的PrintWriter,而不需要自动执行行刷新。  
PrintWriter(String fileName, String csn) 
// 使用指定的文件名和字符集创建一个新的PrintWriter,而不需要自动线路刷新。  
PrintWriter(Writer out) 
// 创建一个新的PrintWriter,没有自动线冲洗。  
PrintWriter(Writer out, boolean autoFlush) 
//创建一个新的PrintWriter。  
PrintWriter append(char c) 
// 将指定的字符附加到此作者。  
PrintWriter append(CharSequence csq) 
// 将指定的字符序列附加到此作者。  
PrintWriter append(CharSequence csq, int start, int end) 
// 将指定字符序列的子序列附加到此作者。  
boolean checkError() 
// 如果流未关闭,请刷新流并检查其错误状态。  
protected void clearError() 
// 清除此流的错误状态。  
void close() 
// 关闭流并释放与之相关联的任何系统资源。  
void flush() 
// 刷新流。  
PrintWriter format(Locale l, String format, Object... args) 
// 使用指定的格式字符串和参数将格式化的字符串写入此写入程序。  
PrintWriter format(String format, Object... args) 
// 使用指定的格式字符串和参数将格式化的字符串写入此写入程序。  
void print(boolean b) 
// 打印布尔值。  
void print(char c) 
// 打印一个字符  
void print(char[] s) 
// 打印字符数组。  
void print(double d) 
// 打印双精度浮点数。  
void print(float f) 
// 打印浮点数。  
void print(int i) 
// 打印一个整数。  
void print(long l) 
// 打印一个长整数。  
void print(Object obj) 
// 打印一个对象。  
void print(String s) 
// 打印字符串。  
PrintWriter printf(Locale l, String format, Object... args) 
// 使用指定的格式字符串和参数将格式化的字符串写入该writer的方便方法。  
PrintWriter printf(String format, Object... args) 
// 使用指定的格式字符串和参数将格式化的字符串写入该writer的方便方法。  
void println() 
// 通过写入行分隔符字符串来终止当前行。  
void println(boolean x) 
// 打印一个布尔值,然后终止该行。  
void println(char x) 
// 打印一个字符,然后终止该行。  
void println(char[] x) 
// 打印字符数组,然后终止行。  
void println(double x) 
// 打印双精度浮点数,然后终止行。  
void println(float x) 
// 打印一个浮点数,然后终止该行。  
void println(int x) 
// 打印一个整数,然后终止该行。  
void println(long x) 
// 打印一个长整型,然后终止行。  
void println(Object x) 
// 打印一个对象,然后终止该行。  
void println(String x) 
// 打印一个字符串,然后终止行。  
protected void setError() 
// 表示发生错误。  
void write(char[] buf) 
// 写入一个字符数组。  
void write(char[] buf, int off, int len) 
// 写一个字符数组的一部分。  
void write(int c) 
// 写一个字符  
void write(String s) 
// 写一个字符串  
void write(String s, int off, int len) 
// 写一个字符串的一部分。  

可以看到这个类虽然方法很多,不过基本上都是重载的方法。也就是print,printf,println这三个方法。

下面来个代码来演示一下这个流的使用。

private static void printWriterFile() {
    PrintWriter printWriter = null;
    try {
        printWriter = new PrintWriter("Hello.txt", StandardCharsets.UTF_8);
        printWriter.println("hello printWriter");
        printWriter.println("hello world\nhello core java");
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (printWriter != null) {
            printWriter.close();
        }
    }
}

从上面的构战函数可以得知,也可以从现有的OutputStream的基础上创建PrintWriter。所以上面的代码等价于

private static void printWriterFile() {
    FileOutputStream fileOutputStream = null;
    PrintWriter printWriter = null;
    try {
        fileOutputStream = new FileOutputStream(new File("hello.txt"));
        printWriter = new PrintWriter(fileOutputStream);
        printWriter.println("hello printWriter");
        printWriter.println("hello world");
        printWriter.println("hello hello");
        printWriter.flush();
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            if (fileOutputStream != null) {
                fileOutputStream.close();
            }
            if (printWriter != null) {
                printWriter.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

注意:上面代码的第十行代码是必须的,不然文件中不会产生任何的数据。其实每次输出都可以刷新一下缓冲的。

上面说了PrintWriter那么是否有PrintReader呢?答案是没有的,,,至于为什么,好好想想去伐。。。

总结

到此为此,对IO的学习基本上已经结束了。下面的一个将会讲一讲Commons IO这个玩意,那时候也就是IO真正的结束了。


一枚小菜鸡