javaIO流(四)--输入与输出支持

一.打印流
  如果现在要想通过程序实现内容的输出,核心的本质一定要依靠OutputStream类来支持但是OutputStream类有一个最大的缺点,这个类的数据输出操作功能有限,所有的数据一定要转为字节数组后才可以进行才操作:public void write(byte b[]) throws IOException,假设说项目中可能输出的是long,double,date,在这样的情况下就必须将这些数据转变为字节的形式来进行处理,这样的处理一定是非常麻烦的.所以在开发之中最初的时候为了解决此类的重复操作,往往会由开发者自行定义一些功能类来简化输出操作:

1 class PrintUtil {    //实现一些常用数据的山输出
2     private OutputStream output;        //不管如何进行输出操作,核心就是OutputStream
3     
4     public void print(String str) throws IOException {      //输出字符串
5         this.output.write(str.getBytes());
6     }
7 }

--在我们的输出过中,我们的输出操作不可能只会向文件,内存,管道中单一的进行输出,所针对的输出环境可能是非常多的,因此我们无法限定输出环境,因此如果我们在PrintUtil类中直接定义了OutputStream类的实例化对象,那么这个类所完成的代码就将造成严重的耦合(这个类只能对指定的类进行输出使用),既然OutputStream类有很多的子类可以被我们使用,最好的解决办法就是通过构造方法,外部传入OutputStream的对象,我们可以模仿java打印流的实现思路来完善我们的代码:

 1 class PrintUtil implements AutoCloseable {    //实现一些常用数据的山输出
 2     private OutputStream output;        //不管如何进行输出操作,核心就是OutputStream
 3 
 4     public PrintUtil(OutputStream output) {
 5         this.output = output;
 6     }
 7 
 8     public void print(long num) throws IOException {
 9         this.print(String.valueOf(num));
10     }
11 
12     public void print(String str) throws IOException {      //输出字符串
13         this.output.write(str.getBytes());
14     }
15 
16     public void println(long num) throws IOException {
17         this.println(String.valueOf(num));
18     }
19 
20     public void println(String str) throws IOException {
21         this.print(str + "\r\n");
22     }
23 
24     @Override
25     public void close() throws Exception {
26         output.close();
27     }
28 }
29 
30 public class Demo {
31     public static void main(String[] args) throws Exception {
32         File file = new File("d:" + File.separator + "java_test" + File.separator + "demo01.txt");
33         PrintUtil printUtil = new PrintUtil(new FileOutputStream(file));
34         printUtil.println("第一次输出: 你好");
35         printUtil.println("第二此输出: 你也好");
36         printUtil.print(",你喜欢写代码吗?");
37         printUtil.print(123456);
38         printUtil.close();
39     }
40 }

--打开文件查看输出结果

第一次输出: 你好
第二此输出: 你也好
,你喜欢写代码吗?123456

--在整个的操作过程中,打印流的设计思想的本质在于提高已有类的功能,OutputStream类是唯一可以提供输出的操作标标准类,所以应该以其为核心根本,但是这个类输出的操作功能有限,所以不方便进行输出各个数据类型,那么就为他作出了一层包装.上述的代码设计思想那么我们就可以称其为"装饰设计模式".
--既然我们已经发现了原始的OutputStream功能的不足,java的设计者也一定可以发现这个问题,所以在java,io包中为我们提供了打印流:
  PrintStream(JDK1.0):  字节打印流

--可以发现在PrintStream的构造方法中又传递了OutputStream,这样的结构形式和代理模式是非常相似的,但是其主要的区别是:代理是围绕接口展开的,代理的时候调用的方法一定是接口中的方法而装饰设计模式中所使用的方法一定不是OutputStream中的方法,
  PrintWriter(JDK1.1):  字符打印流

--可以发现PrintWriter的构造方法中,除了可以接收OutputStream以外,还可以接收Writer等参数.
--范例:使用PrintWriter来实现数据的输出操作:

 1 public class PrintWriterDemo {
 2     public static void main(String[] args) throws FileNotFoundException, UnsupportedEncodingException {
 3         File file = new File("d:" + File.separator + "java_test" + File.separator + "demo01.txt");
 4         PrintWriter printWriter = new PrintWriter(new FileOutputStream(file));
 5         printWriter.println("第一次输出: 你好");
 6         printWriter.println("第二此输出: 你也好");
 7         printWriter.print(",你喜欢写代码吗?");
 8         printWriter.print(123456);
 9         printWriter.close();
10     }
11 }

 

 

--PrintWriter在JDK1.5之后追加了格式化输出操作的支持:public PrintWriter printf(String format, Object ... args)

--范例:格式化输出

 1 public class PrintWriterDemo {
 2     public static void main(String[] args) throws FileNotFoundException, UnsupportedEncodingException {
 3         File file = new File("d:" + File.separator + "java_test" + File.separator + "demo01.txt");
 4         PrintWriter printWriter = new PrintWriter(new FileOutputStream(file));
 5         String name = "张三";
 6         int age = 75;
 7         double money = 45612.5236856;
 8         printWriter.printf("姓名:%s,年龄:%d,收入:%9.2f", name, age, money);
 9         printWriter.close();
10     }
11 }

--打开文件查看输出结果 

姓名:张三,年龄:75,收入: 45612.52

--相比直接使用OutputStream类,那么使用PrintWriter,PrintStream类的处理操作会更加的简单,因此只要是程序进行内容输出的时候应全部使用打印流.

二.System类的IO的支持
  System类是一个系统类,而且是一直都在被使用的系统类,而且在这个类中定义有三个常量:
--System类中的静态常量:
  标准输出(显示器):public final static PrintStream out
  错误输出:public final static PrintStream err
  标准输入(键盘):public final static InputStream in
--范例:查看标准输出与错误输出

 1 public class OutAndErrorDemo {
 2     public static void main(String[] args) {
 3         try {
 4             Integer.valueOf("a");
 5         }catch (Exception e){
 6             System.out.println(e);
 7             System.err.println(e);
 8         }
 9     }
10 }

--运行结果

java.lang.NumberFormatException: For input string: "a"
java.lang.NumberFormatException: For input string: "a"

Process finished with exit code 0

---我们所使用的编译器可以为我们进行一些细微的差别优化(颜色),如果使用命令行去编译执行我们的java代码,那么我们将无法区别两者输出显示的差别
--System.out以及System.err是同一种类型的,最早设置两个输出的操作是有目的的:System.out是输出那些希望用户可以看见的信息,而System.err是输出那些不希望用户看见的信息.如果有需要,现在也可以修改输出的位置:
--修改out的输出位置:

 1 public class OutAndErrorDemo {
 2     public static void main(String[] args) throws FileNotFoundException {
 3         File file = new File("d:" + File.separator + "java_test" + File.separator + "demo02.txt");
 4         System.setErr(new PrintStream(
 5                 new FileOutputStream(file)));
 6         try {
 7             Integer.valueOf("a");
 8         }catch (Exception e){
 9             System.out.println(e);
10             System.err.println(e);
11         }
12     }
13 }

--在System类中还提供有一个in的常量,而这个常量对应的是标准输入设备键盘的输出处理,可以实现键盘的数据输入:

1 public class SystemInDemo {
2     public static void main(String[] args) throws IOException {
3         InputStream input = System.in;      //此时的输入流为键盘输入
4         System.out.println("请输入信息:");
5         byte[] data = new byte[1024];
6         int len = input.read(data);
7         System.out.println("输入内容为: " + new String(data,0,len));
8     }
9 }

--执行结果

请输入信息:
你好
输入内容为: 你好


Process finished with exit code 0

--这样的键盘输入处理,本身是有缺陷的,如果我们接受的数组长度不足,那么只能接受部分数据,所以输入有可能需要进行重复的输入流数据接受,而且在接收的时候,还有可能会牵扯到输入中文的情况,如果我们对中文的处理不当,则有可能造成乱码问题.

三.BufferedReader缓冲流

  BufferedReader类提供的是一个缓冲字符输入流的概念,也就是说利用BufferedReader类可以很好的解决输入流数据的读取问题,实际上java.io所提供的Buffer类还有BufferedOutputStream,BufferedInputStream,BufferedWriter等类,但是BufferedReader这个类是在最初的时候提供的最完善的数据输入的处理(JDK1.5),在之后则出了一个功能更为强大的类.之所以我们使用BufferedReader类来处理,因为该方法中有一个重要的方法:public String readLine() throws IOExpection;以换行符为分割点读取一行数据
--BufferedReader类继承结构分析:

--要先使用此类完成一个键盘输入事件的处理,我们需要知道对于键盘输入事件,java所提供的方法就是System.in(InputStream),而BufferedReader的构造器所接受的是Reader类,因此我们需要使用到转换流:InputStreamReader:

--范例:实现键盘数据输入

1 public class BufferedReaderDemo {
2     public static void main(String[] args) throws IOException {
3         BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
4         System.out.println("请输入信息:");
5         String msg = input.readLine();      //接收输入信息
6         System.out.println("输入内容为: " + msg);
7     }
8 }

--运行结果

请输入信息:
hello你好
输入内容为: hello你好

Process finished with exit code 0

--在实际的开发之中,经常会遇见输入数据的情况,而所有输入数据的类型,都是通过String描述的,那么这样就方便了接收者进行各种处理
--范例:接收整型数据并验证

 1 public class BufferedReaderDemo {
 2     public static void main(String[] args) throws IOException {
 3         BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
 4         System.out.println("请输入您的年龄:");
 5         String msg = "";      //接收输入信息
 6         while (!(msg = input.readLine()).matches("\\d+")) {
 7             System.out.println("您输入的数据有误,请重新输入: ");
 8         }
 9         System.out.println("输入的年龄为: " + msg);
10     }
11 }

--运行结果

请输入您的年龄:
sad
您输入的数据有误,请重新输入: 
ss
您输入的数据有误,请重新输入: 
56
输入的年龄为: 56

Process finished with exit code 0

--对于现代的java开发,由键盘输入数据的情况并不多,但是作为一些基础的逻辑训练,还是可以使用键盘输入数据的,而最早的键盘输入数据的实现做法就是如上的操作

四.Scanner类
  Scanner类是在java.util下在JDK1.5之后追加的程序类,其主要的目的是为了解决输入流的访问问题的,可以理解为BufferedReader的替代产品类,此类存在如下构造方法:Scanner(InputStream source)
--常用方法:
  判断是否有数据: public boolean hasNext();
  取出数据:  public String next();
  设置分隔符:public Scanner useDelimiter(String pattern)
--范例:使用Scanner实现键盘数据输入:

 1 public class ScannerDemo {
 2     public static void main(String[] args) {
 3         Scanner scanner = new Scanner(System.in);
 4         System.out.println("请输入年龄:");
 5         if(scanner.hasNextInt()){       //是否有整数输入
 6             int age = scanner.nextInt();        //直接接收数据
 7             System.out.println("您的年龄: " + age);
 8         }else {
 9             System.out.println("您输入的不对啊");
10         }
11         scanner.close();
12     }
13 }

--运行结果

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

Process finished with exit code 0

--我们可以发现Scanner的处理会更加的简单,使用Scanner输入数据,最大的特点是直接可以结合正则使用进行自定义输入验证:

 1 public class ScannerDemo {
 2     public static void main(String[] args) throws ParseException {
 3         Scanner scanner = new Scanner(System.in);
 4         System.out.println("请输入您的生日:");
 5         if (!scanner.hasNext("\\d{4}-\\d{2}-\\d{2}")) {
 6             System.out.println("您的输入有误");
 7         }else {
 8             String birthday = scanner.next();
 9             System.out.println("您的生日为: " + new SimpleDateFormat("yyyy-MM-dd").parse(birthday));
10         }
11         scanner.close();
12     }
13 }

--运行结果

请输入您的生日:
1888-08-05
您的生日为: Sun Aug 05 00:00:00 CST 1888

Process finished with exit code 0

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

 1 public class ScannerReadFileDemo {
 2     public static void main(String[] args) throws FileNotFoundException {
 3         File file = new File("d:" + File.separator + "java_test" + File.separator + "demo01.txt");
 4         Scanner scanner = new Scanner(file);
 5         scanner.useDelimiter("\n"); //设置读取分割符
 6         while (scanner.hasNext()){
 7             System.out.println(scanner.next());
 8         }
 9         scanner.close();
10     }
11 }

--运行结果

1 姓名:张三,年龄:75,收入: 45612.52
2 姓名:张三,年龄:75,收入: 45612.52
3 大叔关于不压缩大本营
4 1561441852
5 水水水水水水水水水水水水水水
6 
7 Process finished with exit code 0

--在此我们可以进行如下总结:如果程序需要输出数据一定使用打印流,输入数据使用Scanner(BufferReader)

posted @ 2019-08-27 20:45  灰色天空_graySky  阅读(253)  评论(0编辑  收藏  举报