专栏首页海纳周报【第二期】一次学透java.io

【第二期】一次学透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 条评论
登录 后参与评论

相关文章

  • 【第四期】GC专题

    我在某个技术群里发现很多人对GC的问题是最多的。确实,由于Java的GC经常会刷存在感(例如占用大量的CPU时间,full gc时直接失去响应),GC的问题就成...

    海纳
  • Java的分代式GC

    要说理解JVM的垃圾回收,什么引用计数,Copy GC,mark & compaction好像都不是必须要掌握的东西。真要说对普通的Java程序员比较重要的东西...

    海纳
  • 新生代的垃圾回收:Copy GC之基本原理

    据我所能查到的资料,基于复制的GC算法最早是Marvin Minsky提出来的。 这个算法的思路很简单,总的来说,就是把空间分成两部分,一个叫分配空间(Allo...

    海纳
  • 【计算机本科补全计划】Java学习笔记(二) 基础语法红黄蓝

    正文之前 刚才突然想起来自己CCF报名还没报名成功,就是一阵心绞痛,明晚就截止了,要是没报上,到时候怎么跟老师交差,突然想起来,如果老师问我最近干了啥,可以用准...

    用户1687088
  • 初始Java

    为了让Java能够实现跨平台,Java的发明者们增加了一个抽象层,即JVM(Java Virtual Machine,Java虚拟机),自定义一套指令并且和硬件...

    木可大大
  • 初识Java

    为了让Java能够实现跨平台,Java的发明者们增加了一个抽象层,即JVM(Java Virtual Machine,Java虚拟机),自定义一套指令并且和硬件...

    木可大大
  • 有经验的Java开发者和架构师容易犯的10个错误(上)

    首先允许我们问一个严肃的问题?为什么Java初学者能够方便的从网上找到相对应的开发建议呢?每当我去网上搜索想要的建议的时候,我总是能发现一 大堆是关于基本入门的...

    哲洛不闹
  • Java开发岗面试题--基础篇(一)

    老哥们,本期文章为大家奉上Java开发岗面试题--基础篇(一),希望对正在找工作的你有所帮助。话不多说,干货走起!!

    C you again
  • 2018年java工程师就业前景数据分析

    java语言在2017年就是一个备受争议的编程语言,有人说java语言已经过时了,java工程师已经饱和了;也有的人说java语言还是风光依旧,热度不减。但20...

    企鹅号小编
  • 别了 Java EE,开源组织将其更名为 Jakarta EE

    2017年8月,Oracle(甲骨文)决定将Java EE(Java Enterprise Edition)移交给开源组织,最后Eclipse基金会接手。

    全科

扫码关注云+社区

领取腾讯云代金券