Java——流

流(Stream)

流,看起来好像很抽象的一个概念 确实如此,但其实我们写的Java第一个程序就用到了流。我们最开始写用来hello world的System.out.println()就是一种输出流(OutputStraem),从控制读输入时用的System.in.read()则是一种输入流(InputStream)它们俩就是Java里IO流中的字节流(还有一个字符流)。

提到流我们很难不想像到溪流,流确实也像溪流那样,单方向流动,流一般也还有长度。

字节流

字节流是Java IO流中的一种,字节流下面还有OutputStreamInputStream这两个抽象类。我们尝试一下从控制台读入流:

    byte[] chars = new byte[1024];
        try {
            //read方法会返回它读到的字节数
            int len = System.in.read(chars);
            String s = new String(chars,0,len);
            System.out.println("读到了"+len+"个字节");
            System.out.println(s);
            System.out.println("s的长度为"+s.length() );
        } catch (IOException e) {
            e.printStackTrace();
        }

我们输入"aaa111"然后看看结果:

读到了7个字节
aaa111

s的长度为7

它居然说读到了7个字节,但我们只输了了6个字符。这是因为我们输入完成后,还按了一下回车,回车也算一个字节。 我们看到"aaa111"后面还有一个空行,因为我们使用的是printnl()带一个回车,然后我们的输入里面还有一个回车,所以才会有那个空行。

我们输入"你好,世界"试试:

读到了16个字节
你好,世界

s的长度为6

这次它读到了16个字节,因为unicode编码中一个中文字符算3个字节。我们输入了5个中文字符和一个回车,刚好16个字节。s的长度是按照字符个数来算的,不受不同编码不同字节数的影响,所以还是为6。

文件中的字节流

这次我们要用到FileOuputStream来测试一下文件输出流,看得出来它肯定和OutputStream有点关系:

        byte[] bytes = new byte[10];
        for (int i = 0; i < bytes.length; i++) {
            bytes[i] = (byte) i;
        }
        try {
            FileOutputStream out = new FileOutputStream("new.dat");
            out.write(bytes);
            out.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

运行完后,在项目的根目录多了发现多了一个 new.dat 文件,用二进制格式打开它发现:

00 01 02 03 04 05 06 07 08 09 

我们成功往 new.dat 文件里放了10组数字,OutPutStream对象完完整整地把循环变量以二进制的方式从内存中输出到了文件里面。

DataOutputStream

outputStream对象只能做最基本的8位二进制输出,如果我们想要输出基本数据类型,就需要借助DataOutputStream对象(这叫过滤流)

//           通过FileOutputStream来构建DataOutputStraem对象
DataOutputStream out = new DataOutputStream(
            new FileOutputStream("new.dat") );
          out.writeInt(0xcafebabe);
//            out.writeUTF("helloworld");
            out.close();
            
            DataInputStream in = new DataInputStream(
                    new FileInputStream("new.dat") );
            System.out.println(in.readInt() );
            in.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

我们通过流往文件里写了一个十六进制的数据cafebabe,我们用打开该文件,里面同样还是显示着cafebabe。好吧,文件是以十六进制格式展示的。我们再用输入流读入该文件,我们发现读入的数是682085

字符流

前面我们都是通过二进制流的方式读写文件,写入文件中的实际上是二进制文件,作为人的我们阅读起来很困难啊。尽管FileOutputStreamwriteUTF()方法允许我们写入uft字符,但是,但是我们打开文件查看发现还是uft字符在二进制中的编码。好吧。我们还是使用字符流来干这事吧。

字符流提供了两个对象,Reader和Writer,分别对应读和写。

由于Reader和Writer是两个抽象类无法直接使用,使用我们使用它们的子类 BufferedReaderBufferedWriter

让我们想让写段代码,然后当它读取自己吧😅:

 BufferedReader in  = new BufferedReader(
                   new InputStreamReader(
                           new FileInputStream("Day1/src/stream/Test.java")
                   )
           );
           String line;
           //BufferedReader的Readline()方法每次读取一行,在读到文件末尾时返回一个null,所以我们让它没读到null时循环读取每一行
           while( (line = in.readLine() )!= null)
           {
               System.out.println(line);
           }
           in.close();

输出结果:

package stream;

import java.io.*;

public class Test {
    public static void main(String[] args) {
        try {
//            BufferedWriter out = new BufferedWriter(
//                    new OutputStreamWriter(
//                            new FileOutputStream("out.txt")
//                    )
//            );
//            out.write("输出流一段中文");
           BufferedReader in  = new BufferedReader(
                   new InputStreamReader(
                           new FileInputStream("Day1/src/stream/Test.java")
                   )
           );
           String line;
           while( (line = in.readLine() )!= null)
           {
               System.out.println(line);
           }
//           out.close();
           in.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Process finished with exit code 0

成功了!成功的让代码读到了自己。

这是个成功的字符流使用案例,但其实看代码发现字符流的底层其实还是字节流。我们用了InputStreamReader对象来建立字节流与字符流的转换桥梁。可以说InputStreamReader是字节流和字符流之前的桥梁。

字符流的输出操作也基本和之前的类似,已经注释在代码中了。

Scanner

Scanner同样也能作为读入文件的对象,只要给也给他它一个输入流就行了。

Scanner in = new Scanner( new BufferedInputStream(
                   new FileInputStream("Day1/src/stream/Test.java") ) );
           String line;
           while( in.hasNextLine())
           {
               System.out.println(in.nextLine());
           }
           in.close();

这几种方式该如何选择呢?

if(二进制数据)
{
    useInputStream();
}
else if(数据是文本类型) 
{
    userReader();
}
else
{
    userScanner();
}

got it?

对象串行化

既然能通过流往文件里面写入各种类型的数据,那我们能直接将对象写进文件里面嘛?虽然可以直接把对象保存的数据通过字节流(eg DataOutputStream)写入文件。但写入一个完整的对象,听起来是不是就更🐂🍺一点呢?这种将对象以二进制存储的形式,叫对象串行化

我们要用到ObjectOutputStream,很直观的名字是吧。
我们先把对象做出来:

///对象需要实现Serializable接口才能顺利串行化
class Student implements Serializable{
    String name;
    int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

我们怎么把对象变成字节:

        Student s1 = new Student("Lee",12);
//      ObjectOutputStream对象需要建立在一个FileOutputStream的基础上
        ObjectOutputStream out = new ObjectOutputStream(
                new FileOutputStream("stu.dat")
        );
        out.writeObject(s1);
        out.close();

        ObjectInputStream in = new ObjectInputStream(
                new FileInputStream("stu.dat")
        );
        System.out.println( (Student) in.readObject() );
        in.close();

成功获得输出:

    Student{name='Lee', age=12}

学完这一节,终于能将对象保存进文件里面了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 引言:Java的IO是Java的重中之重,之前一直对各种IO流了解的模模糊糊,今天花了一天时间将所有常用的IO流均...
    cp_insist阅读 638评论 1 7
  • 最近一直在被编码问题困扰。觉得这是我“职业生涯”里过不去的坎儿,算是我的梦魇。一想到只要我搬一天的砖,它就可能折磨...
    爱秋刀鱼的猫阅读 1,026评论 0 2
  • 数据流 1、I/O流概述 大部分程序都需要输入/输出处理,比如从键盘读取数据、向屏幕中输出数据、从文件中读或者向文...
    WCT的小仙女阅读 2,421评论 0 1
  • IO 流按照数据流向分可以分为输入流和输出流;按照处理的数据类型来分可以分为字符流和字节流。字节流通常用来操作二进...
    菜鸟冲冲冲阅读 173评论 0 0
  • 1.JAVA包:Java.io 包 2.一个流可以理解为一个数据的序列。输入流表示从一个源读取数据,输出流表示向一...
    soul心声_莉阅读 241评论 0 0