内容学习于:edu.aliyun.com


1. 打印流

  在java.io包里面最为原始的输出支持的操作方法是: OutputStream.write()、 Writer.write(),但是这两个操作方法实际上都有问题,功能设计上不足,因为这两个操作只支持字符串或字节数组的输出,但是从实际的开发来讲,输出的操作可能有各种数据类型。现在假设说要求你设计一一个类,这个类可以实现各种数据类型的输出。

原始工具类:

class PrintUtil implements AutoCloseable {
    private OutputStream outputStream;

    public PrintUtil(OutputStream outputStream) {
        this.outputStream = outputStream;
    }

    public void print(String str) {
        try {
            outputStream.write(str.getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void println(String str) {
        this.print(str + "\r\n");
    }

    public void print(int num) {
        this.print(String.valueOf(num));
    }

    @Override
    public void close() throws Exception {
        outputStream.close();
    }
}


public class JavaAPIDemo {
    public static void main(String[] args) throws Exception {
        PrintUtil util = new PrintUtil(new FileOutputStream(new File("d:" + File.separator + "mldn.txt")));
        util.print("张三");
        util.println(",你好");
        util.print(20);
        util.close();
    }
}

  此时的代码只是调用的形式简化了,但是本质上并没有发生任何的改变。那么这样的设计思想就称为装饰设计模式。但是既然我们都已经知道OutputStream类的输出操作不方便了,那么Java设计人员也一定明白,那么为此提供了两个输出功能类:PrintStream、PrintWriter, 观察这两个类的定义以及构造方法:

类名称 定义 构造方法
PrintStream public class PrintStream extends FilterOutputStream implements Appendable, Closeable PrintStream(OutputStream out)
PrintWriter public class PrintWriter extends Writer PrintWriter(Writer out)

  类结构如下图所示:

打印类实现:

public class JavaAPIDemo {
    public static void main(String[] args) throws Exception {
        PrintWriter util = new PrintWriter(new FileOutputStream(new File("d:" + File.separator + "mldn.txt")));
        util.print("张三");
        util.println(",你好");
        util.print(20);
        util.close();
    }
}

  JDK 1.5之后,String 类提供有format(字符串格式化方法,实际上在PrintStream、PrintWriter 类里面也提供有格式化的输出
操作方法: public PrintWriter printf(String format, Object… args)

格式化输出:

public class JavaAPIDemo {
    public static void main(String[] args) throws Exception {
        PrintWriter pw = new PrintWriter(new FileOutputStream(new File("d:" + File.separator + "mldn.txt")));
        String name = "张三";
        int age = 16;
        double salary = 8634.96479864;
        pw.printf("姓名:%s、年龄:%d、工资:%5.2f", name, age, salary);
        pw.close();
    }
}

  在以后程序进行(非二进制数据)内容输出的时候,优先考虑使用打印流完成,如果有中文建议使用PrintWriter.OutputStream只适合于输出二进制数据,如果是明文的数据一定要使用打印流完成,<mark>打印流使用装饰设计模式</mark>

2. System类对IO的支持(了解)

  System类是整个在Java学习中最为常见的一一个程序类,实际上在这个类中提供有三个与IO有关的系统常量:

  •   标准输出(显示器):public static final PrintStream out
  •   错误输出:public static final PrintStream err
  •   标准输入(键盘):public static final InputStream in

代码:

public class JavaAPIDemo {
    public static void main(String[] args) throws Exception {
        try {
            Integer.parseInt("a");
        }catch (Exception e){
            System.out.println(e);
            System.err.println(e);
        }
    }
}

结果:

java.lang.NumberFormatException: For input string: “a”
java.lang.NumberFormatException: For input string: “a”

更改错误输出位置(不建议使用):

public class JavaAPIDemo {
    public static void main(String[] args) throws Exception {
        System.setErr(new PrintStream(new FileOutputStream(new File("d:"+File.separator+"Err.txt"))));
        try {
            Integer.parseInt("a");
        }catch (Exception e){
            System.out.println(e);
            System.err.println(e);
        }
    }
}

输入流实现(不建议):

public class JavaAPIDemo {
    public static void main(String[] args) throws Exception {
        InputStream in = System.in;//该输入流为键盘输入流
        System.out.println("请输入内容:");
        byte [] data = new byte[1024];
        int len = in.read(data);
        System.out.println("输入的内容为:" + new String(data,0,len));
    }
}

结果:

请输入内容:
hadk’
输入的内容为:hadk’

  此时虽然实现了键盘数据的输入处理操作,但是这种操作却存在有如下的问题:

  • 数据开辟的空间不足,那么对于数据将无法一次性的进行读取操作,所以就需要进行重复读取;
  • 如果处理不当,则在进行中文操作的时候会存在有乱码(文字的编码被切割);

3. BufferedReader缓冲输入流

  InputStream类在进行读取时所存在的一系列问题:

  •   在进行数据读取的时候如果是二进制数据,那么肯定使用InputStream,但如果是文本数据要进行全部数据读取的时候就必须将其利用字节内存流依次进行存储而后再进行最终的转换输出;
  •   在进行键盘数据输入的时候System.in的支持不够。

  所以针对于文本数据(可能是文件中,也可能是键盘输入)的读取,在Java里面提供有一个新的支持类: BufferedReader(缓冲区字符输入流)。

  •   读入一行数据:public String readLine() throws IOException

  继承关系如下图所示:

代码:

public class JavaAPIDemo {
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        System.out.println("请输入内容:");
        String msg = br.readLine();
        System.out.println("输入的内容为:" + msg);
    }
}

结果:

输入的内容为:abc

  在Java里面并没有提供有像其它语言那样简单的键盘数据输入支持,它的输入操作一定要在掌握IO之后才可以正常的使用,另外以上在进行数据接收的时候使用的是String型进行接收,那么这样就可以继续使用String类中提供的各种方法进行数据处理。

输入数字:

public class JavaAPIDemo {

    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        System.out.println("请输入内容:");
        String msg = br.readLine();
        if (msg.matches("\\d{1,3}")){
            int age = Integer.parseInt(msg);
            System.out.println("您的年龄为:" + age);
        }else {
            System.out.println("请输入正确内容!!");
        }
    }
}

结果:

您的年龄为:55

4. Scanner类

  java.util.Scanner是从JDK1.5之后追加的一个程序类,其主要的目的是为了解决输入流的访问问题的,可以理解为BufferedReader的替代功能类,在Scanner类里面有如下几种操作方法:

  •   构造方法:Scanner(InputStream source)
  •   判断是否有数据:public boolean hasNext()
  •   取出数据:public String next()
  •   设置分割符:public Scanner useDelimiter(String pattern)

实现输入:

public class JavaAPIDemo {
    public static void main(String[] args) throws Exception {
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入您的年龄:");
        if (scanner.hasNextInt()){//判断是否有整数输入
            int age = scanner.nextInt();
            System.out.println("您的年龄:" + age);
        }else {
            System.out.println("请输入数字!!!");
        }
        scanner.close();
    }
}

结果:

请输入您的年龄:
55
您的年龄:55

  使用Scanner输入数据还有一个最大的特点是可以直接利用正则进行验证判断。

正则匹配输入:

public class JavaAPIDemo {
    public static void main(String[] args) throws Exception {
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入您的生日:");
        if (scanner.hasNext("\\d{4}-\\d{2}-\\d{2}")) {//正则匹配
            String str = scanner.next("\\d{4}-\\d{2}-\\d{2}");
            System.out.println("生日" + new SimpleDateFormat("yyyy-MM-dd").parse(str));
        } else {
            System.out.println("请输入生日!!!");
        }
        scanner.close();
    }
}

结果:

请输入您的生日:
2000-08-12
生日Sat Aug 12 00:00:00 CST 2000

  现在可以发现Scanner的整体设计要好于BufferedReader,而且要比直接使用InputStream类读取要方便。例如,现在 如果要读取一个文本文件中的所有内容信息,如果采用的是InputStream 类,那么就必须依靠内存输出流进行临时数据的保存,随后还需要判断读取的内容是否是换行。

读取文件:

public class JavaAPIDemo {
    public static void main(String[] args) throws Exception {
        Scanner scanner = new Scanner(new File("C:\\Users\\正好\\Desktop\\寒假学习\\2020-1-19"+File.separator+"比较器--笔记.txt"));
        while (scanner.hasNext()){
            scanner.useDelimiter("\n");//设置分割符
            System.out.println(scanner.next());
        }
        scanner.close();
    }
}

  <mark>在以后的开发过程之中,如果程序需要输出数据一定使用打印流,输入数据使用Scanner (BufferedReader)。</mark>