黑马程序员-JAVA.io-流初探

Java.io

IO是各种应用中最常见不过的操作了,无论是键盘输入,文件读写,还是网络访问,加密解密,都少不了对各种数据流读入送出(内存),java.io包中提供了不少定义和操作IO的工具,今天就大致看下:
java.io包内泾渭分明,很容易发现大量InputStream,OutputStream,Reader,Writer.这四大基本抽象类的子类涵盖了io包中大部分类,因为java的io包体系按照字节流和字符流,输入流和输出流分成了4大类别,也就有了4个基本抽象类。顾名思义,字节流操作的粒度是一个字节byte ,字符流操作的粒度是一个字符char,2个byte,而java内部表示字符用的是UTF16-BE编码,2个byte。而输入输出是站在内存的角度上的,进入内存便是输入,从内存送出便是输出。
如此便有这样的基本分类:输入字节流InputStream,输出字节流OutputStream,输入(读)字符流Reader,输出(写)字符流Writer
IO最常见的操作便是文件,那么先用字节流FileInputStream读取一个文件,并输出到控制台:

文件读取

        private static void testFileInputStream() {
        //这里先不初始化流,仅仅声明,这是流的一般声明方式,因为资源必须关闭
        //所以不能放在try块内
        FileInputStream fis = null;
        int by=0;
        //因为访问文件会涉及文件系统交涉,包括此路径文件存在与否,
        //是否是文件夹,是否可读,是否有权限,是否有其他程序正在使用
        //状况多多,必须做异常处理
        try {
            /* 随便指定了一个已经存在的文件,文件内容如下
             * 
-------- HAXM release 1.1 --------
 This log collects running status of HAXM driver.

             */
            fis = new FileInputStream("C:\\HaxLogs.txt");

            while((by=fis.read())!=-1){
                //因为取出的只有一个byte,只好强转成char类型以便显示
                System.out.print((char)by);
            }//控制台正常输出了文件内容

        } catch (IOException e) {
            throw new RuntimeException("Read Exception @"
                    + fis.toString() + e.getMessage());
        } finally {
            if (fis != null && fis != System.in) {
                try {
                    fis.close();
                } catch (IOException e) {
                    throw new RuntimeException("Close Exception @"
                            + fis.toString() + e.getMessage());
                }
            }
        }
    }

看来很顺利的读取了文件
其实这里有一个很大的问题,刚才用的文件是全英文的,1个byte足以表示一个字符,那么中文呢?
换了一个随便写的中文文件:结果如何?

?ò??·????????á???°??????????????°ü?¨???·????????????·???
??·?????????????·?????????·????¨??????·??????????ò????????
×????à?à??±???×??ì?????í

输出是这样的东西。。。。因为汉字的2个byte被切断了,为了能正常读取显示,这里就应该改用字符流了:

字符流读取

字符流最大的特点是字符流是带有字符集编码的,默认编码是操作系统指定的,所以无论是中文还是其他文字,都能正确读写,但是没有字体可是显示不出来咯。
这里还是读取刚才那个文件:

private static void testFileReader() {
        FileReader fis = null;
        int by=0;
        //因为访问文件会涉及文件系统交涉,包括此路径文件存在与否,
        //是否是文件夹,是否可读,是否有权限,是否有其他程序正在使用
        //状况多多,必须做异常处理
        try {
            //改用FileReader
            fis = new FileReader("S:\\新建文本文档.txt");

            while((by=fis.read())!=-1){
                //因为by是int类型,只好强转成char类型以便显示
                System.out.print((char)by);
            }//控制台正常输出了文件内容

        } catch (IOException e) {
            throw new RuntimeException("Read Exception @"
                    + fis.toString() + e.getMessage());
        } finally {
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    throw new RuntimeException("Close Exception @"
                            + fis.toString() + e.getMessage());
                }
            }
        }
    }

结果输出:

        //因为访问文件会涉及文件系统交涉,包括此路径文件存在与否,
        //是否是文件夹,是否可读,是否有权限,是否有其他程序正在使用
        //状况多多,必须做异常处理

和刚才的做法简直一模一样,还省了异步:除了把FileInputStream换为FileReader就轻松完成读取了。
但是可以发现读取过程中一次仅读取了一个字符,然后循环,这样的做法似乎太没有效率。

缓冲流

查阅Reader类的javadoc read方法还有重载形式read(char[] cbuf),那么就改改:

            fis = new FileReader("S:\\新建文本文档.txt");
            char[] buf=new char[1024];
            while((by=fis.read(buf))!=-1){
                System.out.print(new String(buf,0,by));
            }//控制台正常输出了文件内容

似乎还不错,其实还可以更进一步,而ava.io已经提供了BufferedReader来提供缓冲,方法很简单,直接包装你需要的Reader流即可:

        BufferedReader br=null;
        int by=0;
        try {
            //改用BufferedReader提供缓冲区
            br = new BufferedReader(new FileReader("S:\\新建文本文档.txt"));
            char[] buf=new char[1024];
            while((by=br.read(buf))!=-1){
                //因为取出的只有一个byte,只好强转成char类型以便显示
                System.out.print(new String(buf,0,by));
            }//控制台正常输出了文件内容

        }

这样操作读写效率就要高不少了,实际缓冲区和数组块可以按需求调节以达到更好的效果。
值得一提的是BufferedReader还有一个很好用方便的方法readLine(),顾名思义,一次读取一行,操作文本非常好用,特别是配合PrintWriter的println。

文件复制

有读就有写,写入一个文件类似的就是用Writer和OutputStream,那么这里使用缓冲字节流复制一个文件:


    /**
     * 尝试拷贝文件,无法拷贝会引发异常退出
     * 
     * @param source
     *            源文件
     * @param saveFile
     *            目标文件
     */
    public static void copyFile(File source, File saveFile) {

        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;
        try {
            // 创建输入流和输出流。
            bis = new BufferedInputStream(new FileInputStream(source));
            bos = new BufferedOutputStream(new FileOutputStream(saveFile));

            // 复制文件
            int by;
            while ((by = bis.read()) != -1) {
                bos.write(by);
            }
        } catch (IOException e) {
            throw new RuntimeException("文件读写失败");
        } finally {
            // 关闭流
            try {
                if (bis != null) {
                    bis.close();
                }
            } catch (IOException e) {
                throw new RuntimeException("输出流关闭失败");
            }
            try {
                if (bos != null) {
                    bos.close();
                }
            } catch (IOException e) {
                throw new RuntimeException("输出流关闭失败");
            }
        }
        //System.out.println("拷贝完成");
    }

流的操作对象

至此,所用流操作的都是文件和控制台,而流的应用远不仅于此
Java.io中对于流可以操作的对象总结如下:

流对象 Java.io所对应的类标识 字节流/字符流
控制台 (System.io/out) 字节
文件 File* 均有
内存 ByteArray* 字节
jvm对象 Object* 字节
jvm原始类型 Data* 字节
网络 (通过java.net连接返回) 字节
jvm String String* 字符(字节已过时)

Java.io中其它流和工具

缓冲流已经介绍过了。那么java.io还有很多工具:

转换流OutputStreamWriter & InputStreamReader

转换流可以方便的在字节流和字符流直接转换,需要注意的最重要的问题就是字符集编码。

PrintWriter

上面也提过了,PrintWriter有print 和println两个很方便的方法,甚至还有一个format方法支持c语言方式的格式化输出。

行号读取流 LineNumberReader

(LineNumberInputStream已过时)
行号读取流是缓冲读取流的子类,其特点是int getLineNumber()获得行号方法和void setLineNumber(int lineNumber)设定行号。在文本处理中有时候很有用。
需要注意的是setLineNumber只是修改当前位置的行号,并没有定位到实际的行号位置。

管道流 Piped*

管道流是连接两个流的方法,这个必须成对使用,最好是启动两个线程分别读写。

序列输入流 SequenceInputStream

这个流的特点是可以顺序连接多个输入流,按顺序逐个取,达到多个输入合一的效果。比如用来合并文件。

回退输入流 PushbackInputStream

回退输入流顾名思义可以”回退”,它包装一个输入流,在读取终止字节后,代码片段可以“取消读取”该字节,这样,输入流上的下一个读取操作将会重新读取被推回的字节。

过滤流 Filter*

Filter 包装其他的流。
Filter类本身只是简单地重写那些将所有请求传递给所包含流的所有方法,这意味着你可以继承这个流以实现过滤器和修改流的效果。

你可能感兴趣的:(黑马,学习)