【第二期】一次学透java.io

java.io是新手学习Java的第一个难点。因为这个package中的东西比较多,也比较复杂,另外加上一些接口太过于面向对象了,更加增大了学习的难度。这一期,我针对这个问题专门探讨一下,通过三篇文章,大家就可以完全地掌握java.io这个包了。

理解流

要掌握java.io,必须要掌握的一个概念就是输入输出流。 数据流是一串连续不断的数据的集合,就象水管里的水流,在水管的一端一点一点地供水,而在水管的另一端看到的是一股连续不断的水流。数据写入程序可以是一段、一段地向数据流管道中写入数据,这些数据段会按先后顺序形成一个长的数据流。对数据读取程序来说,看不到数据流在写入时的分段情况,每次可以读取其中的任意长度的数据,但只能先读取前面的数据后,再读取后面的数据。不管写入时是将数据分多次写入,还是作为一个整体一次写入,读取时的效果都是完全一样的。 为什么要有这种抽象呢?我们知道,数据的来源是多种多样的,可能来自文件,也可能来自网络,或者内存,数据可能是有结构的(比如xml),也可能是无结构的,比如简单的文本。所以,如何在语言的层面进行统一的抽象就显得至关重要了。Java中使用了输入输出流这个概念来对所有的数据进行抽象。 根据数据流向的不同,又分为输入流和输出流。输入流是指数据从外部流入当前Java程序,而输出流是指数据从当前的Java程序流出到外部。 在Java中,代表输入流的interface是InputStream,代表输出流的interface是OutputStream。

标准输入输出

命令行参数

从键盘上读入数据,最简捷的方式就是通过命令行参数。可能很多同学在第一次写Java程序的时候,对main方法的参数就会有疑问,不知道它是干啥的。其实这个参数主要就是为了处理命令行参数的,例如:

public class TestIO {
    public static void main(String[] args) {
        for (int i = 0; i < args.length; i++) {
            System.out.println(args[i]);
        }
    }
}

我们可以通过

java TestIO apple banana pear

来观察一下参数是如何输入到Java程序中的。

标准输入输出错误

大家知道,在控制台程序中,有三个可以进行输入输出的通道,我们通常称之为标准输入,标准输出和标准错误。在C语言中,我们会以stdin, stdout, stderr来代指。在Java中,也有这样的东西,分别是:

java.lang.System   
public final class System  extends Object{   
   static  PrintStream  err;//标准错误流(输出)  
   static  InputStream  in;//标准输入(键盘输入流)  
   static  PrintStream  out;//标准输出流(显示器输出流)  
}  

通过查看JDK源代码,可以看到,System.java 里,out是这么定义的:

public final static PrintStream out = null;

可见,out 是一个 static 变量,所以我们才可以使用类名直接引用它。

在Java语言中,所有的输入都被抽象成了输入流(InputStream),所有的输出都被抽象成了输出流(OutputStream)。以OutputStream为例,它的几个子类,PrintStream可以向控制台上输出字节,FileOutputStream可以向文件中写入字节,SocketOutputStream可以向网络连接上写入字节,等等,它们都是OutputStream的子类。

与之相对应的InputStream也有各种子类分别负责不同的功能。只是Output负责向外写,而Input负责向程序里读。

把字节的输入输出抽象成一个连续的流,确实形象了很多。有了IO,我们的程序终于可以与外界进行交互了。例如:

        byte[] buf = new byte[512];
        System.out.println("hey, may I have your name, please? ");
        int n = 0;
        try {
            n = System.in.read(buf);
        } catch (IOException e) {
            e.printStackTrace();
        }
        System.out.print("hello, ");
        System.out.write(buf, 0, n);

前三个字节是一组,通过UTF-8(我们会在后续的课程中陆续介绍编码的知识)的解码,可以得到前三个字节代表的十进制数是28023,这刚好就是中文字符“海”字的 unicode 码。可见,直接的字节操作对非ascii的字符会比较麻烦。例如,程序读入一个名字,想判断这个名字的姓氏是否为李,如果是字节的操作,我们就得先把读到的这些字节,解码到 unicode,或者反过来,把“李”编码为UTF-8再进行比较。这显然太麻烦了,编解码这么机械的工作,干嘛不让机器替我们做呢?

基于这个想法,Java引入了一个可以把字节流转成字符流的适配器——InputStreamReader。请继续观看下一篇文章,适配器模式。

Scanner

文章的最后,我还想额外提一下Scanner类。这是一个用于输入的辅助类,是从Java1.5开始引入的。在那之前,如果我想从标准输入里读两个数,并把它们的和打出来。用Java就得这么写:

public class TestIO {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String s = br.readLine();
        String[] ss = s.split(" ");
        int a = Integer.parseInt(ss[0]);
        int b = Integer.parseInt(ss[1]);
        System.out.print(a + b);
    }
}

输入

1 2

输入

3

相比起来,C语言的写法就比较简捷:

int main() {
  int a, b;
  scanf("%d %d", &a, &b);
  printf("%d\n", a + b);
}

Java为了解决这种数字的输入,就引入了一个叫做Scanner的类,但这个类被视为一个工具类,因为它不是一种流式处理,所以在JDK中,它被放到了java.util包下了。使用Scanner,代码可以化简如下:

public class TestIO {
    public static void main(String[] args) throws IOException {
        Scanner cin = new Scanner(System.in);
        int a = cin.nextInt();
        int b = cin.nextInt();
        System.out.println(a + b);
    }
}

本文分享自微信公众号 - HinusWeekly(gh_4b8b4eda4e40)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2017-12-22

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏JAVA高级架构

java面试需要掌握知识点

重点知识 由于我面试的JAVA开发工程师,针对于JAVA,需要理解的重点内容有: JVM内存管理机制和垃圾回收机制(基本每次面试都会问,一定要搞得透彻) JVM...

39650
来自专栏企鹅号快讯

编程语言学啥?当然首选Python啦!千字长文教你如何入门Python!

1.1 流程控制之for循环 ? 1.2 开发工具IDE 1.2.1 为何要用IDE ? 很多语言都有比较流行的开发工具,比如JAVA 的Eclipse, C#...

49260
来自专栏余林丰

Effective Java通俗理解(下)

第31条:用实例域代替序数   枚举类型有一个ordinal方法,它范围该常量的序数从0开始,不建议使用这个方法,因为这不能很好地对枚举进行维护,正确应该是利用...

31190
来自专栏JavaEdge

设计模式实战-迭代器模式

迭代器是为容器服务的,那什么是容器呢? 能容纳对象的所有类型都可以称之为容器,例如Collection集合类型、Set类型等,迭代器模式就是为解决遍历这些容器中...

37920
来自专栏owent

“C++的90个坑”-阅读笔记

C++确实是一门复杂的语言。包括之前查看了一些C++11的文档和做了一些实践和总结,越来越觉得C++是门神奇的语言,也是个陷阱多多的语言。 我现在开发过程中最...

10510
来自专栏搞前端的李蚊子

获取Object对象的length

所有JS程序猿(甚至不止JS)都知道,数组(Array)是有length的,通过length属性,可以很方便的获取数组的长度。可以说,只要使用到了数组,就必会使...

439110
来自专栏项勇

笔记45 | 代码性能优化建议[转]

15060
来自专栏恰童鞋骚年

【译】.NET中六个重要的概念:栈、堆、值类型、引用类型、装箱和拆箱

  一来是为了感受国外优秀技术社区知名博主的高质量文章,二来是为了复习对.NET技术的基础拾遗达到温故知新的效果,最后也是为了锻炼一下自己的英文读写能力。因为是...

8520
来自专栏向治洪

python 日期与时间

###python 日期与时间 (time,datetime包) [toc] #####概述 在应用程序的开发过程中,难免要跟日期、时间处理打交道。如:记录一个...

571100
来自专栏灯塔大数据

干货 | 数据科学入门必读:如何使用正则表达式?

有时候,这些数据中会包含大量文本语料。比如,假如我们需要搞清楚「xxx文件 」中谁给谁发送过邮件,那么我们就要筛查 1150 万份文档!我们可以采用人工方式,亲...

13720

扫码关注云+社区

领取腾讯云代金券