Java基础--IO流

.1 IO流介绍

IO流式读储和读取数据的解决方法

I:input                O:output

流:像水流一样传输数据

作用:

        用读写数据(本地文件、网络)

分类1   ---- 按照流向

        输出流:程序--->文件

        输入流:文件--->程序

分类2   ---- 按照操作文件的类型

        字节流:可以操作所以类型的文件

        字符流:只能操作纯文本文件

纯文本文件

        用windows系统自带的记事本打开并且能读懂的文件

        txt文件、md文件、xml文件、lrc文件

IO流随用随创建,什么时候不用什么时候关闭

字符流

        底层就是字节流  字符流=字节流+字符集

        输入流:一次读取一个字节,遇到中文时按照编码方式读取

        输出流:底层会把数据按照指定的编码方式进行编码,变成字节再写到文件中

        对于纯文本文件进行读写操作

注:

     字节流读取中文会乱码,但是拷贝不会乱码是因为一个字节一个字节的拷贝,不会造成二进制数据的丢失,目的地的解码方式和数据源保持一样就可以了

 .2 基本流

.2.1基本字节流

.2.1.1 FileInputStream

FileInputStream  操作本地文件的字节输入流 将文件中的数据读取出来 ---写入

是字节流的基本流

读取数据

        一次一个字节

        一次多个字节,每次读取会尽可能把数组装满 本人一般字节数组设为1024*1024*5

        读到末尾返回-1

书写步骤

        创建对象、写入数据、释放资源

public class ByteStreamDemo2 {
    public static void main(String[] args) throws IOException {

        //字节输入流 FileInputStream
        //创建对象
        // 如果文件不存在,就直接报错 因为文件不存在,创建了也是空 没意义
        FileInputStream fis = new FileInputStream("D:\\JavaFile\\_8_7\\a.txt");

        //一次读取一个
        //一次读一个文件 读出来的是数据在ASCII上对应的数组
        //读到文件末尾了,read 会返回-1

        int b1 = fis.read();
        //读出来的是对应的ASCII码值 a->97  可以使用char强转
        System.out.println((char)b1);
        int b2 = fis.read();
        System.out.println(b2);
        System.out.println(fis.read());

        //循环读取  读取到空白字返回-1  会自动分行
        int b;
        while(( b= fis.read()) !=-1){
            System.out.print((char)b);
        }

        //一次读取一个字节数组的数据 一般是1024的整数倍 1024*1024*5 5MB
        //创建一个字节数组
        byte[] bytes = new byte[2];
        FileInputStream fileInputStream = new FileInputStream("D:\\JavaFile\\_8_7\\c.txt");
        //返回读取到的字节长度
        //第一回读取 a b 返回长度二
/*        int len = fileInputStream.read(bytes);
        System.out.println(len);
        System.out.println(new String(bytes));
        //第二回读取 c d 返回长度二 因为数组长度为二 覆盖了 a,b
        int len1 = fileInputStream.read(bytes);
        System.out.println(len1);
        System.out.println(new String(bytes));
        //第三回读取 e 返回长度一 因为只读到一个 所以覆盖率数组中第一个元素 c 变成了e d
        int len2 = fileInputStream.read(bytes);
        System.out.println(len2);
        System.out.println(new String(bytes));
        //第四回读取 未读取到元素返回 -1 也就没有覆盖数组中元素 还是 e d
        int len3 = fileInputStream.read(bytes);
        System.out.println(len3);
        System.out.println(new String(bytes));*/

        //优化 转成字符串是 输入长度
        //第一回读取
        int len = fileInputStream.read(bytes);
        System.out.println(len);
        System.out.println(new String(bytes,0,len));
        //第二回读取
        int len1 = fileInputStream.read(bytes);
        System.out.println(len1);
        System.out.println(new String(bytes,0,len1));
        //第三回读取
        int len2 = fileInputStream.read(bytes);
        System.out.println(len2);
        System.out.println(new String(bytes,0,len2));
        //第四回读取
        int len3 = fileInputStream.read(bytes);
        System.out.println(len3);
        if(len3 !=-1){
            System.out.println(new String(bytes,0,len3));
        }
        //释放资源
        fileInputStream.close();
        fis.close();
    }
}

.2.1.2 FileOutputStream

FileOutputStream  操作本地文件的字节输出流 将数据写进文件当中  ---写出

书写步骤

        创建对象,写出数据,释放资源

三步操作的细节

        创建对象:文件存在、文件不存在、追加写入

        写出数据:写出整数、写出字节数组、换行写

        释放资源:关闭通道

public class ByteStreamDemo1 {
    public static void main(String[] args) throws IOException {
        //写一段文字到本地文件中
        //创建对象   字节输出流对象
        //        参数是字符串表示的路径或者是File对象都可以
        //        如果文件不存在会创建一个新的文件,但是要保证父级路径是存在的
        //        如果文件已经存在,则会清空文件然后才写出数据

        //创建对象的第二个参数 false(默认) 关闭续写  true 打开续写
        FileOutputStream fos = new FileOutputStream("D:\\JavaFile\\_8_7\\a.txt",true);
        //写出数据
        //  write  方法的参数是整数,但是写在文件中的是对应的ASCII码值

        //一次写一个数据
        fos.write(97);//a的ASCII的值是97
        //一次写一组数据
        byte[] bytes = {97,98,99,100,101,102,103};
        fos.write(bytes);
        //一次写入一组数据的一部分
        // 数组  起始索引  数据个数
        fos.write(bytes,2,4);

        //换行写  再次写出一个换行符 windows:\r\n Java可以只写一个但是建议不要省略
        // Linux: \n Mac:\r
        String wrp = "\r\n";
        byte[] bytes1 = wrp.getBytes();
        fos.write(bytes1);

        //遇到一行字符串时 转化为字节数组再添加
        String str = "kankelaoyezuishuai";
        byte[] bytes2 = str.getBytes();
        fos.write(bytes2);

        //续写 创建对象时的第二个参数 把默认的false 改成true

        //释放资源
        //每次使用完流之后都要释放资源
        fos.close();
    }
}

.2.2 IO流异常处理

JDK9以后

        创建流对象1;

        创建流对象2;

        try(流1;流2){

        肯出现异常的代码;

        }catch(异常类名 变量名){

        异常的处理代码;

        }

        资源用完最终自动释放

public class ByteStreamDemo4 {
    public static void main(String[] args) throws IOException {

        //JDK9 的IO流捕获异常处理
        //创建写出对象
        FileOutputStream fos = new FileOutputStream("D:\\JavaFile\\_8_7\\b.txt");
        //创建写入对象
        FileInputStream fis = new FileInputStream("D:\\JavaFile\\_8_7\\a.txt");

        //会自动释放资源
        try (fos;fis){
            byte[] bytes = new byte[1024*1024*5];
            int len;
            while ((len = fis.read(bytes)) != -1){
                fos.write(bytes,0,len);
            }
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

.2.3基本字符流

.2.3.1 FileReader

FileReader是操作本地文件的字符输入流 将数据从文件中读取出来

操作步骤:

        创建字符输入流对象

        读取数据 按字节读取,遇到中文一次读多个字节,读取后解码,返回一个整数。读到末尾返回-1

        释放资源

public class CharStreamDemo1 {
    public static void main(String[] args) throws IOException {

        //创建字符流对象并关联本地文件  File对象 和字符串都可以
        FileReader f1 = new FileReader("D:\\JavaFile\\_8_7\\src\\CharStreamDemo\\a.txt");

        //空参read()
        //read() 字符流底层也是字节流 默认一个字节一个字节读取
        //如果遇到中文就是多个字节一读,UTF-8是一次3个字节 GBK是一次两个字节
        //在读取之后 方法的底层会进行解码然后转成十进制 将十进制作为返回值
        //想看的这些数据可以对得到的十进制数据进行char强转

        int ch;
        while ((ch = f1.read()) != -1){
            System.out.print((char)ch);
        }

        //有参read()
        char[] chars = new char[2];
        int len;
        while ((len = f1.read(chars)) != -1){
            System.out.print(new String(chars,0,len));
        }
        //释放资源
        f1.close();
    }
}

.2.3.2 FileWriter

FileWriter是操作本地文件的字符输出流 将数据写进文件当中

书写细节

        创建字符输出流对象

                参数是字符串表示的路径或者File对象都可以

                如果文件不存在会创建一个新文件,但要保证父级路径是存在的

                如果文件已经存在,则会清空文件,不想清空可以打开续写

        写数据

                参数是整数,写在本地文件中的是整数再字符集上对应的字符

        释放资源

                每次使用完流之后都要释放资源

public class CharStreamDemo2 {
    public static void main(String[] args) throws IOException {

        //可以是file对象 可以是字符串  append : true 开启续写 (默认)false 不开续写
        FileWriter fw = new FileWriter("D:\\JavaFile\\_8_7\\src\\CharStreamDemo\\b.txt");

        //一次写一个字符
        fw.write("你");

        //中文的标点符号在UTF-8也是三个字节  英文就是一个
        String str = "9527,滚出去!";
        //一次写一个字符串
        fw.write(str);
        //写入字符串的一部分
        fw.write(str,0,5);
        char[] chars = {'1','我','旧','3'};
        //写入一个字符数组
        fw.write(chars);
        //写入数组的一部分
        fw.write(chars,0,3);

        //释放资源
        fw.close();
    }
}

.3 缓冲流

 缓冲流对于字符流的提示不明显,对于字符缓冲流的特点是两个特有的方法     

        BufferedReader 字符缓冲输入流:readLine()

        BufferedWriter 字符缓冲输出流:newLine()

.3.1 字节缓冲流

BufferedInputStream  字节缓冲输入流

BufferedOutputStream  字节缓冲输出流

把基本流包装成高级流,提高读取和写出数据的性能

底层自带了长度为8192的缓冲区提高性能,默认是8kb

和基础流相比字节缓冲流拷贝文件一次一个字节数组性能最好

//因为缓冲流的底层还是字符/字节流在和文件交流 所以想要实现文件续写需要在字节/字符流里面

//字节缓冲流
//拷贝的时候会覆盖原文件内容
//BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("D:\\JavaFile\\_8_7\\src\\BufferedStreamDemo\\b.txt",true));
public class BufferedStreamDemo1 {
    public static void main(String[] args) throws IOException {

        //默认创建了长度为8192的缓冲区   可以自己设置 ,length
        // 缓存输入流和缓存输出流不是一个缓冲区 中间有介质载体传递 这个过程是在内存中发生的
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:\\JavaFile\\_8_7\\src\\BufferedStreamDemo\\a.txt"));
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("D:\\JavaFile\\_8_7\\src\\BufferedStreamDemo\\b.txt"));

/*        //还是一次操作一个字节
        int len;
        while ((len = bis.read()) != -1){
            bos.write(len);
        }*/

        //创建字节数字 一次操作多个字节
        byte[] bytes = new byte[1024*1024*5];
        int len;
        while ((len = bis.read(bytes)) != -1){
            bos.write(bytes,0,len);
        }
        bos.close();
        bis.close();
    }
}

.3.2 字符缓冲流 

BufferedReader 字符缓冲输入流

BufferedWriter 字符缓冲输出流

把基本流包装成高级流,提高读取和写出数据的性能

底层自带了长度为8192的缓冲区提高性能,默认是16kb,因为Java中一个字符是两个字节

//字符缓冲流
//字符基本流本身也带有一点缓冲区

//JAVA中底层是一个字符两个字节 字符缓冲流是创建的8192的字符缓冲区
// 也是默认覆盖的 想要实现续写也是在FileWriter对象里面加true   new FileWriter("D:\\JavaFile\\_8_7\\src\\BufferedStreamDemo\\b.txt,true
// ")
public class BufferedStreamDemo2 {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new FileReader("D:\\JavaFile\\_8_7\\src\\BufferedStreamDemo\\a.txt"));
        BufferedWriter bw = new BufferedWriter(new FileWriter("D:\\JavaFile\\_8_7\\src\\BufferedStreamDemo\\b.txt"));

        //BufferedReader读取特有的操作
        //一次读取一行 不会读到换行符需要自己println 换行
/*        String s = br.readLine();
        System.out.println(s);
        String s1 = br.readLine();
        System.out.println(s1);*/

        //循环读取 终止条件是读到null了
        String len;
        while ((len = br.readLine()) != null){
            //拷贝 BufferedWriter和BufferedReader均不会都到换行符需要手动换行
            bw.write(len);
            //根据平台去写出对应的换行符
            bw.newLine();
        }

        //释放资源
        bw.close();
        br.close();
    }
}

.4 转换流

是字符流和字节流之间的桥梁

字节流可以封装转换流变成字符流的性质

字符转换输入流:InputStreamReader

字符转换输出流:OutputStreamWriter

数据源-字节流-InputStreamReader内存-OutputStreamWriter字节流-目的地

作用:

        指定字符集读写(JDK11之后已淘汰)

        字节流想要使用字符流当中的方法

 .4.1 指定字符集读写

JDK11之后使用FileReader和FileWriter,底层还是转换流

public class ConvertStreamDemo2 {
    public static void main(String[] args) throws IOException {

        //底层还是转换流
        //读取用GBK 写入用UTF-8
        FileReader fr = new FileReader("D:\\JavaFile\\_8_7\\src\\ConvertStreamDemo\\123.txt", Charset.forName("GBK"));
        //IDEA默认是UTF-8 后面就不用添加了
        FileWriter fw = new FileWriter("D:\\JavaFile\\_8_7\\src\\ConvertStreamDemo\\123-UTF-8.txt");
        int len;
        while ((len = fr.read()) != -1){
            fw.write(len);
        }
        fw.close();
        fr.close();
    }

.4.2 字节流使用字符流方法

public class ConvertStreamDemo3 {
    public static void main(String[] args) throws IOException {
        //使用字节流区读取带有中文的文件 且不能出现乱码

        //字节流->转换流->字符缓冲流
        BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("D:\\JavaFile\\_8_7\\src\\BufferedStreamDemo\\b.txt")));
        String line;
        while ((line = br.readLine()) != null){
            System.out.println(line);
        }
        br.close();
    }
}

.5 序列化与反序列化流

序列化输出流/对象操作输出流,把一个对象写到本地文件:ObjectOutputStream

反序列化流/对象操作输入流,读取数据(写入数据):ObjectInputStream

序列化之后是一串乱码

细节:

        使用序列化流将对象写到文件时,需要让Javabean类实现Serializable接口,否则会出现NotSeroalizableException异常

        序列化流写到文件中的数据是不能修改的,一旦修改就无法再次读回来了

        序列化对象后,修改了Javabean类,再次反序列化会出问题:抛出InvalidClassException异常   解决方案:给javabean类添加serialVersionUID(序列号,版本号)

        如果一个对象中某个成员变量的值不想被序列化,则给该成员变量添加transient关键字修饰,该关键字标记的成员变量表示不参与序列化过程 

public class ObjectStreamDemo2 {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        //同时序列化多个对象时 将对象存入集合当中
        //创建对象
        Student stu1 = new Student("张三",18,"山东");
        Student stu2 = new Student("李四",18,"天津");
        Student stu3 = new Student("王五",18,"河北");

        //创建集合
        ArrayList<Student> list = new ArrayList<>();
        Collections.addAll(list,stu1,stu2,stu3);

        //写出数据  写入文件中
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:\\JavaFile\\_8_7\\src\\ObjectStreamDemo\\a.txt"));
        oos.writeObject(list);

        //写入数据 读取文件

        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:\\JavaFile\\_8_7\\src\\ObjectStreamDemo\\a.txt"));
        ArrayList<Student> list1 = (ArrayList<Student>)ois.readObject();

        for (Student student : list1) {
            System.out.println(student);
        }

    }
}
//javabean类
/*
* Serializable 标记接口  没有方法可以重写 表示可以实现序列化
* */
public class Student implements Serializable {
    //固定版本号 防止属性修改时版本号改变  私有 共享 始终 类型
    private static final long serialVersionUID = 2009558412742412647L;

    private String name;
    private Integer age;
    //如果不想某个属性进行序列号和反序列化 也就是对该属性进行保密 添加transient
    //transient 瞬态关键字,该关键字标记的成员变量不参与序列化进程 读出来的是默认初始化值
    //private transient String address;
    private String address;
    public Student() {
    }

    public Student(String name, Integer age, String address) {
        this.name = name;
        this.age = age;
        this.address = address;
    }
//省略get和set

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

 

.6 打印流

将数据打印到文件中

分类:☞ PrintStream,PrintStream两个类

字节打印流和字符打印流

        构造方法创建对象时都可以指定是否自动刷新和字符编码

        成员方法:

                write 和之前一样

                println 打印任意数据,自动刷新,自动换行

                print  打印任意数据,不换行

                printf 带有占位符的打印语句,不换行

        字节打印流默认开启自动刷新,字符打印流需要手动开启自动刷新

特点:

        打印流只操作文件目的地,不操作数据源

        特有的方法可以实现数据原样写出

                打印:97        文件中:97

        特有的写出方法,可以实现自动刷新,自动换行

                打印一次数据 = 写出+换行+刷新

注:

        System.out.println();就是一个打印流
        System类中的静态out方法
        它是一个特殊的打印流 叫做系统中的标准输出流  它是唯一的不能关闭,关闭之后后面的打印不到控制台上
        只能重新运行程序/就是重新运行JVM虚拟机
        PrintStream ps = System.out;
        ps.println("1238");
        ps.close();

//以字符打印流为例
public class PrintStreamDemo2 {
    public static void main(String[] args) throws FileNotFoundException {

        //创建字符打印流对象
        //PrintWriter pw1 = new PrintWriter(new FileWriter(),true)
        PrintWriter pw = new PrintWriter(new FileOutputStream("D:\\JavaFile\\_8_7\\src\\PrintStreamDemo\\a.txt"),true);

        //原样写出

        //换行打印
        pw.println(66);

        //不换行
        pw.print("你好");

        //带有占位符的
        pw.printf("%s说%s","小王","你好");
        pw.close();



        //注 :System.out.println();就是一个打印流
        //System类中的静态out方法
        //它是一个特殊的打印流 叫做系统中的标准输出流  它是唯一的不能关闭,关闭之后后面的打印不到控制台上
        //只能重新运行程序/就是重新运行JVM虚拟机
        PrintStream ps = System.out;
        ps.println("1238");
        ps.close();
        System.out.println("你好你好");
        ps.println("能不能打印出来");

    }
}

 

.7 字符集

计算机存储规则

        1. GB2312字符集

        2. BIG5字符集

        3. GBK字符集 windows系统默认使用的就是GBK。系统显示:ANSI

        4. Unicode字符集:国际标准字符集,IDEA默认使用的就是UTF-8

计算机中最小的存储单元是一个字节

.7.1 ASCII

ASCII编码规则:前面补0补齐8位

        要存储的英文 a

        查询ASCII对应数字97 二进制为 110 0001

        编码 0110 0001

ASCII解码规则:直接转成十进制

        01110 0001 解码

        97

        查询ASCII读到的英文为a

注ASCII字符集里面没有中文

.7.2 GBK

GBK英文编码规则:不足8位前面补0

        要存储的英文:a

        查询GBK对应数字97转换成二进制为110 0001

        编码 0110 0001

GBK英文用一个字节存储,完全兼容ASCII

GBK汉字编码规则:不需要变动

        要存储的汉字:汉                                                                            -70            -70

        查询GBK对应数字47802转换成二进制为10111010 10111010 编码10111010 10111010

        汉字两个字节存储

        高位字节二进制一定以1开头,转成十进制之后是一个负数

GBK汉字解码

        10111010 10111010 解码

        转成10进制 47802

        查询GBK 读取到的汉字为汉

.7.3 Unicode(万国码)

UTF-8编码规则:用1~4个字节保存

UTF-8 存储英文

        要存储的英文:a

        查询Unicode得到97转换成UTF-8编码01100001

UTF-9 存储汉字

        要存储的汉字:汉

        查询Unicode对应数字27721转换成十进制01101100 01001001转换成UTF-8编码为11100110 10110001 10001001

.7.4 乱码 

如何不产生乱码

        不要用字节流读取文本文件

        编码解码时要使用同一个码表,同一个编码方式

为什么会有乱码

        读取数据时未读完整个汉字

        编码和解码时的方式不统一

 .7.5 编码与解码

getBytes(),填写参数就是指定方式编码,不填参数就是默认方式编码

String(byte[] bytes, String charsetName),第二个参数可以指定编码方式,不写就是默认的

public class CharSetDemo1 {
    public static void main(String[] args) throws UnsupportedEncodingException {

        String str = "ai你哟";
        //编码

        //默认方式编码 根据编译软件的默认编码方式
        byte[] byte1 = str.getBytes();
        System.out.println(Arrays.toString(byte1));

        //指定方式编码
        byte[] byte2 = str.getBytes("GBK");//大小写都可以 尽量大写
        System.out.println(Arrays.toString(byte2));

        //解码

        //默认方式解码根据编译软件的默认解码方式
        System.out.println(new String(byte1));

        //指定方式解码
        System.out.println(new String(byte2,"GBK"));

        //编码和解码方式不同会出现乱码
        System.out.println(new String(byte2));
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值