Java基础总结---IO流

二. IO流

一. 自己的理解

1.IO流的分类(比较熟悉的)

1. 字节流和字符流--最开始是字节流,也就是二进制流.但是为了便于人的读写,使用字符流将其转化为可读的Unicode编码

 字节流--(抽象父类)InputStream,OutputStream.
 (具体子类) : 对象IO流-- ObjectInputStream,ObjectOutputStream.
             文件IO流--FileInputStream,FileOutputStream. // 太久没用都忘了

 字符流--Reader,Writer.(抽象类)
 (具体子类):文件IO流--FileReader,FileWriter

2. 处理流--对上面流进行包装
包装流--BufferedInputStream,BufferedOutputStream,BufferedReader,BufferedWriter. 
转换流--InputStreamReader,OutputStreamWriter.

二. 不熟悉的地方

1.字节流的使用

1. 字节输出流的使用
class OStream {
        // 这里直接抛出IOException,抛出异常,为了版面整洁,下面也一样
    public static void main(String[] args) throws IOException{

        // 创建一个新文件
        File file = new File("/home/z/IOStream.txt");

        // OutputStream是一个抽象类,不能创建具体实例.
        // 创建字节输出流对象OutputStream
        OutputStream os = new FileOutputStream(file);

        // 创建一个字节数组,字节数组会转换成什么.
        byte[] b = new byte[]{49,65,97};

        // 开始写入
        os.write(b);

        // 关闭资源
        os.close();
    }
}
2. 字节输入流的使用
class IStream{

    public static void main(String[] args) throws IOException{
        // 获取文件位置
        File file = new File("/home/z/IOStream.txt");

        // 创建对象
        InputStream is = new FileInputStream(file);

        // 创建一个 字节数组用来读取文件内容,如果文件很大,byte数组可以设置很大.
        byte[] b = new byte[8];
        int i; // 这里用i去接收读取的返回值,但是直接判断is.read(b)!=-1也可以.

        // 读文件的时候如果到达最后没有内容,read()方法会返回-1,所以用这个来作为条件
        while(( i = is.read(b)) != -1){
            // 输出到控制台,也可以运用上输出流将其写入文件
            // 因为i是int型,所以输出来是一个Unicode整数.要用char来强转
            System.out.print((char)i+" ");
        }
        // 关闭资源
        is.close();
    }   
}

输出结果:1 A a

2.字符流的使用

1. 字符输出流的使用
class CharWriter{
    public static void main(String[] args) throws IOException{
        // 创建文件对象
        File file = new File("/home/z/writer.txt");

        // 创建字符输出流Writer对象,这里Writer也是一个抽象类,只能new具体子类实例
        Writer writer = new FileWriter(file);

        // 之前的字节流只能将ASCII的字符写出,不能写出中文.
        //本来想尝试是否能写出字符串形式的中文,结果发现OutputStream的write()方法参数只能传输字节数组,一个int值
        OutputStream os = new FileOutputStream(file);

        // 这里int值超过byte的范围0~127,在char的范围类.在String的
        os.write(34443);

        // 在文件中查看会是一个\8B 或者是一个乱码.所以字节流是不能写出中文的
        os.close();

        // 使用Writer向文件中写出字符串形式的中文
        writer.write("你好");

        // 关闭资源
        writer.close();
    }
}

writer.txt中的内容是:你好

2. 字符输入流的使用
class CharReader{
    public static void main(String[] args) throws IOException{
        //创建文件对象
        File file = new File("/home/z/writer.txt");

        // 创建字符输入流Reader对象,Reader也是一个抽象类
        Reader reader = new FileReader(file);

        // 从文件中读取内容,读取的内容都是整数,都是需要强转成char类型,才能变成字符.
        int i;
        while((i = reader.read()) != -1){
            System.out.println((char)i);
        }

        // 关闭资源
        reader.close();
    }
}

输出结果是:

3.对象字节流的使用

1. 对象输出流
/**
 * 创建一个类,如果要实现写出对象,则类需要实现Serializable借口
 */
class Animal implements Serializable{
    private int age;
    private String name;

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

    public int getAge(){
        return age;
    }

    // 重写toString()方法.
    @Override
    public String toString() {
        return "Animal [age=" + age + ", name=" + name + "]";
    }
}

class ObOStream{
    public static void main(String[] args) throws IOException{
        // 创建一个对象
        Animal animal = new Animal(10,"cat");

        // 创建一个对象输出流ObjectOutputStream
        // 无参构造器是protected,一般是不能访问的.所以需要用到下面的构造器
        // public ObjectOutputStream(OutputStream out) 

        FileOutputStream fos = new FileOutputStream(new File("/home/z/object.txt"));
        ObjectOutputStream oos = new ObjectOutputStream(fos);

        // 这么写出到文件中会出现乱码,刚开始想可能是缓存的问题,但这里是没有用到缓冲区的.
        // 这里是一个经常犯错的地方.
        oos.writeObject(animal);
        oos.close();
    }
}

百度之后,才发现写出对象的时候用的是字节流,之前说过,字节流是二进制的,所以我们写出数据的时候是用二进制写出的.当我们去用文本编辑器打开时,是不能将二进制类型转换成Unicode类型的.只有当用字符流写出时文本编辑器才会分辨出Unicode编码.

所以在进行纯文本的输入输出的时候,一般是使用字符流,其余的例如音频,视频,图片等都使用字节流

2. 对象输出流
下面用输入流读取.虽然写出的是二进制,不能识别.但是用输入流读取的时候也是二进制流,所以读出的时候就是写入的时候的样子.
class ObIStream{
    public static void main(String[] args) throws IOException,ClassNotFoundException{
        // 创建文件输入流
        FileInputStream fis = new FileInputStream(new File("/home/z/object.txt"));

        // 创建对象输入流,并将fis作为参数传入
        ObjectInputStream ois = new ObjectInputStream(fis);

        // 从文件中读取数据.转换为Animal对象对象,
        // 读取多个对象的话怎么去判断结束条件.
        Animal animal = (Animal)ois.readObject();

        ois.close();

        // 直接调用重写的toString()方法.显示在控制台
        System.out.println(animal.toString());
    }
}

输出结果:
Animal [age=10, name=cat]

3. 多个对象的输出流.
class ManyObOStream{
    public static void main(String[] args) throws IOException{
        // 两种思路是一样的,因为这个对象都能存储多个对象,且只持有一个引用.
        // 1.用一个对象数组将同一个类型的数组存储起来,写入到文件中
        // 2.用一个list将对象加入到集合中,下面使用list

        ArrayList<Animal> list = new ArrayList<>();

        // 产生多个不同的对象,使用for循环来产生下标
        for(int i = 0; i < 10; i++){
            int age = i;
            String name = "cat"+i;

            // 创建不同的Animal对象
            Animal a = new Animal(age, name);

            // 添加到list中
            list.add(a);
        }

        // 创建对象输出流对象
        FileOutputStream fos = new FileOutputStream(new File("/home/z/manyObject.txt"));
        ObjectOutputStream oos = new ObjectOutputStream(fos);

        // 将list写出到文件中
        oos.writeObject(list);
        oos.close();
    }
}
4. 多个对象输入流,将其显示到控制台
class ManyObIStream{
    public static void main(String[] args) throws IOException,ClassNotFoundException{
        // 这里输入流与之前的没有什么区别,只是将之前的读出的对象改为ArrayList类型
        FileInputStream fis = new FileInputStream(new File("/home/z/manyObject.txt"));
        ObjectInputStream ois = new ObjectInputStream(fis);
        ArrayList<Animal> list = (ArrayList<Animal>) ois.readObject();

        ois.close();

        // 将list里面数据输出
        for(int i = 0; i < list.size(); i++){
            System.out.println(list.get(i).toString());
        }

    }
}

输出结果:
Animal [age=0, name=cat0]
Animal [age=1, name=cat1]
Animal [age=2, name=cat2]
Animal [age=3, name=cat3]
Animal [age=4, name=cat4]
Animal [age=5, name=cat5]
Animal [age=6, name=cat6]
Animal [age=7, name=cat7]
Animal [age=8, name=cat8]
Animal [age=9, name=cat9]

当需要将多个对象输出到文件中时,可以将多个对象封装到一个数组或者集合中,这样写出一个数组或者集合时,将多个对象一起写出到文件中.
有个问题:ArrayList是一个引用指向它,用下标去维护.写出到文件的时候,根据输出的结果可以看出, Java 将当前引用的所有引用指向的对象都写出到了文件中.所以这里也可以实现Java中的深克隆.即内容相同,引用却不同,也就是产生了一个新的对象. 浅克隆是指只复制了引用,实际的对象是没有变的.

// TODO

为什么要定义一个int i ;(i= is.read()) != -1, 可以直接用 is.read() != -1 来判断的.

4. 处理流的使用
比较熟悉的主要是缓冲流:
字节流:BufferedInputStream,BufferedOutputStream.
字符流:BufferedReader,BufferedWriter.

class BufferedChar{
    public static void main(String[] args) throws IOException{
        public static void main(String[] args) throws IOException {
            // 重复写入一个txt文件,使用字符流,计算时间,看缓冲流和不用缓冲流的时间差
            int c1 = 0; // 用来计算不用缓冲读取的次数
            int c2 = 0; // 用来计算用缓冲读取的次数
            // 创建字符输出流对象
            FileWriter fw = new FileWriter(new File("/home/z/buffer.txt"));
            FileReader fr = new FileReader(new File("/home/z/buffer.txt"));
            // 获取系统的当前时间
            long start1 = System.currentTimeMillis();

            // 使用for循环来不停的写入
            for(int i = 0; i < 100000; i++){
                fw.write("你好,我是一个字符");
            }

            while((fr.read()) != -1) {
                c1++;
                fr.read();
            }

            long end1 = System.currentTimeMillis();


            BufferedWriter bw = new BufferedWriter(new FileWriter(new File("/home/z/buffer2.txt")));
            BufferedReader br = new BufferedReader(new FileReader(new File("/home/z/buffer2.txt")));

            char[] c = new char[1024];

            long start2 = System.currentTimeMillis();

            for(int i = 0; i < 100000; i++) {
                bw.write("你好,我是一个字符");
            }

            while(br.read() != -1) {
                c2++;
                br.read(c);// 但是这里不能输出实际读取的字符
            }

            long end2 = System.currentTimeMillis();

            System.out.println("不用缓冲的读写总时间="+(end1-start1)+"c1="+c1);
            System.out.println("使用缓冲区的读写时间="+(end2-start2)+"c2="+c2);
        }
    }
}

输出结果:
不用缓冲的读写总时间=95 c1=449666
使用缓冲区的读写时间=46 c2=869

最开始在读取的时候将读取的内容在控制台输出,如果次数过多,会造成时间差很大.所以去掉输出语句后,发现差距并不大,但是从c1 和 c2可以看出来,次数明显的减少.不过使用缓冲区的时候不能将读取的内容显示出来,只有一个readLine()方法,能够将读取的内容显示,不过readLine()是一个无参构造参数,只能根据一行来读取.

主要是在进行IO读取的时候如果数据比较大,那么对IO流的访问会很频繁,而IO流的访问时间一般会很长,所以为了降低对IO流的访问,所以将数据先存储在缓冲流里面,等读取的数据满了之后一次性写出.

5.转换流的使用

InputStreamReader:将字节流转换为字符流(An InputStreamReader is a bridge from byte streams to character streams)
OutputStreamWriter:将字符流转换为字节流(An OutputStreamWriter is a bridge from character streams to byte streams)

这里为什么会有区别?个人理解是:一般读取数据的时候用的比较多的是输入流,即InputStream,比如从网络获取的数据都是字节流.但是读取来的数据需要进行解析,就是将字节流转换为字符流,这样人们就能获取到里面的字符型数据. 而输出流的时候是人们写入字符型的数据,写出的时候将其转换为字节流,相当于二进制流,传输比较方便.所以输出流的时候需要字符转字节.

6.流的混合使用

在别的博客上看到了一个总结:

1. 用InputStream去获取输入流
2. 用InputStreamReader将字节流转换为字符流
3. 用BufferedReader开始一次性读取多个字符.

7.一些见过的流
1. DataInputStream,DataOutputStream: 不知道和其他流的区别在哪.
2. PrintStream: 标准输出流,System.out就是一个PrintStream. 不仅可以将内容输出到控制台,也可以将内容输出到文件中.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值