Java学习Day16-IO流

本地内存与本地磁盘(本地IO)的输入(Input)输出(Output)

本地网卡/网络与外界网卡/网络(网络IO)的输入(Input)输出(Output)

输入(Input)输出(Output的媒介是内存,而不是表面上看到的磁盘和磁盘之间的输入输出

字节文件输入流的具体实现

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;

public class Demo01 {
    public static void main(String[] args) {
        //文件名需要写绝对路径,路径的分隔符用正斜杠和反斜杠都可以
        // 正斜杠写一个即可,反斜杠要写两个
        //因为一个反斜杠在字符串中是“转义符”
        //String name1 = "E:/io/hello";
        String name2 = "E:\\io\\hello";

        //文件字节输入流
        try {
            //创建文件输入流,把磁盘中的文件name2读取到内存中
            InputStream is = new FileInputStream(name2);    //我们写代码都是使用多态指针的
            //创建一个缓冲数组,用来存放读入的字节编码,容量为5
            byte[] data = new byte[5];
            //定义一个n,用来接收读取过来的字节编码
            int n;
            //定义一个空字符串,用来存放读取过来的文件信息
            String str = "";
            //来一个while循环实现读取和存放的功能
            while ((n = is.read(data)) != -1) {      //当文件读完的时候,会读到空,返回-1,所以当等于-1的时候说明读取完毕
                str += new String(data);            //用一个字符串累加读到的数据,避免读出来之后被覆盖掉
                //str += new String(data,0,n);    //这种情况不会发生多读的情况
            }
            System.out.println(str);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

字节输入输出流在读含中文字符的时候,是没法保证不会把字符破开来读的,很大概率会出现乱码的问题。

比如上文我用了5个容量的缓冲区,当读到第五个位置的时候遇到中文字符,中文字符用三个字符编码来存储,这时候就产生了乱码

那么如何解决破开读并产生乱码这个问题呢,我们可以使用字符输入输出流

字符输入输出流存储的类型是char类型,四个字节,在现实情况中我们很少会读到四个字节,所以字符输入输出流不会出现乱码的问题

字符文件输入流

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;

public class Demo02 {
    //以字符流实现,一定不会出现乱码的情况,因为不会把中文字符破开读
    //缺点:浪费存储空间,因为字符流char是四个字节四个字节四个字节实现的,有时候不一定用到四个字节所以会造成浪费
    public static void main(String[] args) {
        String name = "E:\\io\\hello";
        try {
            Reader reader = new FileReader(name);
            int d;
            while ((d = reader.read()) != -1){
                System.out.println((char)d);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

 对象输出流(对象持久化到磁盘文件)

import carshop.Car;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;

public class Demo05 {

    public static void main(String[] args) {
        //对象输出流,对象持久化到磁盘文件
        Car car = new Car();
        car.setBrand("法拉利");
        car.setColor("红色");
        car.setModel("911");

        String name = "E:\\io\\car";
        try {
            //创建底层的字节流对象
            FileOutputStream os = new FileOutputStream(name);
            //创建对象输出流,包装底层的字节流对象
            ObjectOutputStream oos = new ObjectOutputStream(os);
            oos.writeObject(car);           //把对象写入到磁盘中
            oos.flush();                    //冲刷数据,防止有数据残留在内存中
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

 对象输入流

import carshop.Car;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;

public class Demo06 {
    public static void main(String[] args) {
        //对象输入流,从磁盘读取文件反序列化还原为对象
        String name = "E:\\io\\car";
        FileInputStream is = null;      //创建文件输入流对象,并把文件传送进出
        try {
            is = new FileInputStream(name);     //初始化对象,输送文件
            ObjectInputStream ois = new ObjectInputStream(is);  //创建对象输出流,包装底层的字节流对象
            //readObject()读取磁盘文件内容并反序列化为对象,返回Object类型
            //需要手动向下转型,才能拿到car类型
            Car car = (Car) ois.readObject();
            System.out.println(car.getBrand());
            System.out.println(car.getModel());
            System.out.println(car.getColor());
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }finally {
            try {
                is.close();                         //关闭文件输入流
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }
}

 图片输入流

import java.io.*;

public class Demo07 {
    public static void main(String[] args) {
        //图片输入流
        String name = "E:\\io\\01.JPG";             //图片输入路径
        String outName = "C:\\io\\01.copy.JPG";     //图片输出路径
        FileInputStream is = null;
        DataInputStream dis = null;                 //定义在方法之外便于try-catch-finally中关闭输入流对象
        FileOutputStream os = null;
        DataOutputStream dos = null;
        try {
            //创建底层的字节流
            is = new FileInputStream(name);             //字节输入流
            //创建二级制流,包装底层的字节流
            dis = new DataInputStream(is);              //二级制输入流
            //创建底层的字节输出流
            os = new FileOutputStream(outName);        //字节输出流
            dos = new DataOutputStream(os);            //二级制输出流

            //创建底层的字节缓冲数量
            byte[] data = new byte[1024];                           //内存缓冲区
            int n ;
            while ((n = dis.read(data)) != -1){                     //循环读取图片中的二进制数据到缓冲区
                dos.write(data,0,n);                            //
            }
            dos.flush();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (dis != null) {
                    dis.close();
                }
                if (dos != null) {
                    dos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

词频测试

 生成测试文件的代码,这里我生成1000行,每行10个数的文件

import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.Random;

public class Demo03 {
    static String[] arr = {"面向对象", "封装", "继承", "多态"                 //先随机定义一个字符串数组,用来生成多词频文件
            , "数组", "父类", "抽象类", "接口", "异常", "运行时异常"
            , "编译时异常", "抽象方法", "成员方法", "构造方法","静态方法"
            ,"成员变量", "局部变量", "静态变量", "常量", "重写", "重载"
            , "向上转型", "向下转型"};
    static Random random = new Random();
    //随机生成数
     static String str = "E:\\io\\words";                                   //创建存储词频的文件
    public static void main(String[] args) {
        //创建一个词频测试文件
        //输出流:字符输出流
        try {

            Writer writer = new FileWriter(str);                            //采用字符写入磁盘文件中
            for (int i = 1; i <= 10000; i++) {                              //写入10000个词
                int index = random.nextInt(arr.length);//每循环一次生成一个随机数,作为数组的索引,注意的是给随机生成的数加个范围,范围是数组的长度,原因是避免产生数组越界的情况
                if (i%10 == 0){                                         //把创建的词频文件以10个词为一行
                    writer.write(arr[index] + "\n");
                }else {
                    writer.write(arr[index] + " ");

                }
            }
            writer.flush();                                 //冲刷流中数据残留
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

 词频测试

利用HashMap的key存储词,用value存储词出现的次数,最后用TreeMap把它们的词和次数反过来存入到TreeMap中,最后遍历TreeMap输出结果,代码如下

import java.io.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

public class Demo04 {
    public static void main(String[] args) {
        //词频测试
        HashMap<String,Integer> map = new HashMap<>();          //这个HashMap用来存储词频统计结果,指针类型用Map,目的是实现多态指针

        TreeMap<Integer,String> treeMap = new TreeMap<>();      //采用TreeMap把结果反转(key和value反向存储)并排好序

        String name = "E:\\io\\words";                      //要读取的词频文件
        try {
            Reader reader = new FileReader(name);          //从磁盘读向内存我们有字符输入流
            //用字符流对象reader作为缓冲流BufferedReader的参数
            BufferedReader bufferedReader  = new BufferedReader(reader);//BufferedReader里面有个readLine()方法是读取一行,是一个包装流,包装了Reader,底层是Reader
            //bufferedReader.readLine();
            String line = null;                 //定义一个空字符串来存取读取出来的词
            while ((line = bufferedReader.readLine()) != null) {            //循环读取,一次读取一行,当遇到null时候说明读取完毕
                //拆分字符串,String 提供了一个成员方法split(“分隔符”),返回拆分后的子串数组
                String[] arr = line.split(" ");
                for (String word : arr) {//遍历子串数组arr,取出每一个词并存入到哈希表中
                    if (map.get(word) == null) {            //用词来做key,用出现的次数做value。当词经过哈希运算后存入哈希表中,当哈希表中为空的时候,把词存进去并把次数值设计为1
                        map.put(word, 1);
                    } else {
                        map.put(word, map.get(word) + 1);   //不为空的话,一次让相同的词相加1
                    }

                }
            }
            System.out.println("词频统计结果");
            //在箭头函数的内部不能访问外部变量,除非是常量或者是线程安全的类
            //final中int count =0,定义final也不行,final值不可以修改
            //1、不使用forEach,用迭代器
            int count = 0;          //定于一个用来统计词的个数的变量
            Set<Map.Entry<String, Integer>> entries = map.entrySet();   //entrySet是Java中键值对K,V的集合,Set里面的类型是Map.Entry,一般可以通过map.entrySet()得到,通过getKey()得到K,getValue得到V
            for (Map.Entry<String, Integer> entry : entries) {        //遍历set拿到entry,每一个entry都是一组k,v对象
                String key = entry.getKey();                        //getKey()获取key
                Integer value = entry.getValue();                   //getValue()获取value

                System.out.println(key + ":" + value);              //输出key和value
                treeMap.put(value, key);                            //通过利用TreeMap实现key和value数据的反转
                count += value;                                     //累加value
            }
            System.out.println("-------------------------------------------------------------------------------------------");



            System.out.println(count);

            treeMap.forEach((k,v) -> {                  //遍历输出TreeMap中的key和value
            System.out.println(k + ":" + v);
            });
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

随机生成测试文件的时候数据为什么会有缺失

流里面有未冲刷的数据

执行冲刷指令后数据就没有发生丢失,冲刷的方法是flush()

序列化和反序列化

对象从内存中写入磁盘的过程称为对象的持久化,也称为序列化

从磁盘中读取文件到内存还原为对象的过程称为反序列化

IO流有哪几种,流怎么分类

1、流按照方向分

输入流字节输入流数据从磁盘流向内存称为读操作/输入流Input输入(Input)输出(Output)的媒介是内存,而不是表面上看到的磁盘和磁盘之间的输入输出
字符输入流
输出流字节输出流数据从内存流向磁盘称为写操作/输出流Output
字符输出流

2、流按照传输单位

传输单位

字节输入流

字节输出流

byte数据类型,一个字节,一次只能读一个字节

字符输入流

字符输出流

char数据类型,四个字节,一次可以读一个字节或者两个字节护着三个字节或者四个字节
字节输入输出流与字符输入输出流的区别

1、含中文字符的字符串,以字节流的形式读写的话可能会产生乱码,以字符流的形式读写的话一定不会产生乱码

2、字节流存储的是byte类型,一个字节

      字符流存储的是char类型,四个字节

当真正地读写数据的时候,数据不一定就能达到四个字节的规模,所以使用字符流会产生资源浪费

3、Java提供了四个基类

InputStream字节输入流基类
OutputStream字节输出流基类
Reader字符输入流基类
Writer字符输出流基类

4、常用的字节流

字节输入流FileInputStream        文件输入流
ObjectInputStream        对象输入流对象输入流和二进制输入流的底层是文件字节流
DataInputStream        二进制(图片/音频)输入流
字节输出流FileOutputStream        文件输出流
ObjectOutputStream        对象输出流对象输出流和二进制输出流的底层是文件输出流
FileOutputStream        二进制(图片/音频)输出流

5、常用的字符流

字符输入流FileReader        文件输入流
BufferedReader        带缓冲的输入流(一次第一行)带缓冲的输入流的底层是文件字符流
字符输出流FileWriter        文件输出流
BufferedReader        带缓冲的输出流(一次读一行)带缓冲的输入流的底层是文件字符流

填空题

1、流按方向来分可分为                                        

2、数据从          流向          称为写操作,也称为          

3、数据从          流向          称为读操作,也称为          

4、流按传输单位来分可分为                    

5、数据类型是char的流是          

6、数据类型是byte的流是          

7、Java提供了四个基类分别是(请英文和中文同时回答)

                                               

                                               

                                               

                                               

判断题

1、Java中磁盘与磁盘之间的信息交流不需要通过其他媒介就可以实现        (          )

2、包含中文字符的文件中,采用字符输入流从磁盘读取时,一定不会出现乱码的情况    (        )

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值