IO流(二)

本文详细介绍了Java中的IO流,包括字符流(如FileReader和FileWriter)、缓冲流的使用,以及如何处理不同编码的乱码问题。此外,还涵盖了字符输入转换流、字符输出转换流、打印流、数据流、序列化流和对象字节流的原理与示例。
摘要由CSDN通过智能技术生成

IO流-字符流

文件字符输入流-读字符数据进来

字节流:适合复制文件等,不适合读写文本文件

字符流:适合读写文本文件内容

FileReader(文件字符输入流)
作用:以内存为基准,可以把文件中的数据以字符的形式读入到内存中去



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

public class FileReaderTest1 {
    public static void main(String[] args) {
        /**
         * 目标:掌握文件字符输入流每次读取一个字符
         */

        try (
                //1.首先创建一个文件字符输入流管道与源文件接通
                //相对路径 new FileReader 获得一个文件输入流管道 交给一个Reader类型的变量fr记住(多态的写法)
                Reader fr = new FileReader("D:\\code\\idea-javacode\\javaseproject\\io-app2\\src\\itheima01.txt"); //同时把流对象放到try后面的小括号里边去
        ) {//2.读取文本文件的内容
            /*int c;//用来记住每次读取的字符编号
            while ((c = fr.read()) != -1) {//说明读到了这个字符     fr.read()表示去这个文件输入流里面读一个字符编号给到c
                System.out.print((char) c);//直接写c表示输出一个字符编号是不行的,要把它转成字符,强转成char,这里不要换行,会导致每输出一个字符换行
            }*/
            //每次读取一个字符的形式,他的性能肯定是比较差的

            //3.用字符数组,每次读取多个字符  每次读取更多字符,系统调用就会少一些,性能是更好一点的
            //第一步要定义一个字符数组来装字符
            char[] buffer = new char[3];//方便理解 每次读三个字符
            //接着定义一个int类型的变量len
            int len;//记住每次读取了多少个字符 ,因为最后一桶水有可能没有装满这个字符,一定要读多少倒多少,那就要记住每次读取了多少个字符
            //接着再写一个while循环
            while ((len = fr.read(buffer)) != -1) {//len = fr.read(buffer) ->把桶交给read方法  调用的read方法可以每次可以读多个字符到这个字符数组种
                //进到里面来就要把字符数组里面的字符转成字符串
                //用字符串这个构造器把字符数组扔给他,从第一个字符开始(0),读了多少由len记住就倒出多少个字符,然后打印输出
                //读取多少就倒出多少
                System.out.print(new String(buffer, 0, len));//换行去掉
            }

        } catch (Exception e) {//改用EXception来拦截一切异常
            e.printStackTrace();
        }
        //对异常进行try catch  进行资源的释放
    }
}

文件字符输出流-写字符数据出去

FileWriter(文件字符输出流)
作用:以内存为基准,把内存中的数据以字符的形式写出到文件中去

 

io流每次读写一次数据,都需要进行一次系统调用,才可以把数据读写到文件里面去,每次都进行系统调用都是比较耗费系统性能的;
而文件字符输出流就优化了这个问题,当创建文件字符输出流时,会在内存中分配一块区域出来,称为缓冲区;
以后再用文件字符输出流写数据的时候,并不是把数据通过系统直接写到文件里面去,而是把数据先写到缓冲区里面去;
最后通过一种机制,把缓冲区里面的数据通过调用底层系统,在只需要写一次,就把数据全部写到文件中去,这样只需要一次系统调用,性能就会好很多。
如何通知缓冲区,把他里面的数据通过调用系统一次同步到文件里面去?
1.刷新流操作--> fw.flush()2.关闭流(包含刷新操作)fw.close()
//注意:关闭流之后就不能继续使用了,刷新流后还可以继续使用。


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

public class FileWriterTest2 {
    //io流每次读写一次数据,都需要进行一次系统调用,才可以把数据读写到文件里面去;每次都进行系统调用都是比较耗费系统性能的
    //而文件字符输出流就优化了这个问题,当创建文件字符输出流时,会在内存中分配一块区域出来,称为缓冲区;
    // 以后再用文件字符输出流写数据的时候,并不是把数据通过系统直接写到文件里面去,而是把数据先写到缓冲区里面去;
    //最后通过一种机制,把缓冲区里面的数据通过调用底层系统,在只需要写一次,就把数据全部写到文件中去,这样只需要一次系统调用,性能就会好很多
    //如何通知缓冲区,把他里面的数据通过调用系统一次同步到文件里面去?1.刷新流操作--> fw.flush()2.关闭流(包含刷新操作)fw.close()
    //注意关闭流之后就不能继续使用了,刷新流后还可以继续使用。
    public static void main(String[] args) {
        //0.创建一个文件字符输出流管道与目标文件接通

        //是一个覆盖管道
        //目标文件会自动生成,我们不需要提前去把目标文件创建出来的直接创建管道就可以了
        try (//把资源放在小括号里面,用完之后会帮我进行自动释放
             //Writer fw = new FileWriter("D:\\code\\idea-javacode\\javaseproject\\io-app2\\src\\itheima02.txt");
             //如果做数据增加  管道创建出来的时候后面还能增加一个参数
             Writer fw = new FileWriter("D:\\code\\idea-javacode\\javaseproject\\io-app2\\src\\itheima02.txt",true);//此时就变成了追加数据的管道
        ) {
            //1、public void write(int c):写一个字符出去
            fw.write('a');
            fw.write(97);
            fw.write('凡');
            fw.write("\r\n");//换行

            // 2、public void write(string c)写一个字符串出去
            fw.write("woaini");
            fw.write("我爱你");
            fw.write("\r\n");
            // 3、public void write(string c ,int pos int len):写字符串的一部分出去
            fw.write("woxihuanni", 2, 8);
            fw.write("\r\n");
            // 4、public void write(char[]buffer):写一个字符数组出去
            char[] buffer = {'l', 'z', 'x', 'w', 'y', 'f'};
            fw.write(buffer);
            fw.write("\r\n");
            // 5、public void write(char[] buffer ,int pos ,int len):写字符数组的一部分出去
            fw.write(buffer, 0, 3);
        } catch (Exception e) {
            e.printStackTrace();
        }


    }
}

字符输出流使用时的注意事项
字符输出流写出数据后,必须刷新流,或者关闭流,写出去的数据才能生效

 

如果数据没有经过刷新和关闭操作把缓冲区挤满了,等后面在写数据的时候先会把装满了的数据自动同步一次到文件中去,然后流出空间再往里面放数据,所以不用担心。

IO流-缓冲流

缓冲流的作用是用来包装所谓的原始流,然后提高原始流读写数据的性能的

字节缓冲流

作用:提高字节流读写数据的性能



import java.io.*;

/**
 * 目标:掌握字节缓冲流的作用
 */
public class BufferedInputStreamTest1 {
    public static void main(String[] args) {
        try (
                FileInputStream is = new FileInputStream("D:\\code\\idea-javacode\\javaseproject\\io-app2\\src\\itheima01.txt");
                //缓冲流的思想:不在用原始的资源流
                // 1、定义一个字节缓冲输入流包装原始的字节输入流
                //InputStream bis = new BufferedInputStream(is);
                InputStream bis = new BufferedInputStream(is, 8192 * 2);
                //这里可以把底层的缓冲池搞得更大一点,可能性能更好,这里是将底层的缓冲池做到16KB,可以自己在这个地方声明制定缓冲池的大小

                FileOutputStream os = new FileOutputStream("D:\\code\\idea-javacode\\javaseproject\\io-app2\\src\\itheima01_bak.txt");
                //2.定义一个字节缓冲输出流包装原始的字节输出流
                OutputStream bos = new BufferedOutputStream(os);//交给一个字节输出流的变量bos 多态的写法
        ) {

            /*byte[] buffer = new byte[1024];
            int len;
            while ((len = is.read(buffer)) != -1) {
            这里就不能用is和os来进行读和写了,应该用的是包装后的缓冲流dis和dos,这样的话就会利用到8KB的缓冲池,每次读8KB,再由数组进行转移
                os.write(buffer, 0, len);
            }*/
            byte[] buffer = new byte[1024];
            int len;
            while ((len = bis.read(buffer)) != -1) {
                bos.write(buffer, 0, len);
            }
            System.out.println("复制完成!!");

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}


字符缓冲流

作用:自带8K(8192)的字符缓冲池,提高字符流读写数据的性能



import java.io.BufferedReader;
import java.io.FileReader;
import java.io.Reader;

/**
 * 目的:掌握字符缓冲输入流的用法
 */

public class BufferedReaderTest2 {
    public static void main(String[] args) {
        try (
                Reader fr = new FileReader("D:\\code\\idea-javacode\\javaseproject\\io-app2\\src\\itheima04.txt");
                // 创建一个字符缓冲输入流包装原始的字符输入流
                BufferedReader br = new BufferedReader(fr);//这里不要用多态的方式写了因为后面要用新增的特有功能:按照行读

        ) {
            /*char[] buffer = new char[3];
            int len;
            while ((len = br.read(buffer)) != -1) {
                System.out.print(new String(buffer, 0, len));
            }*/
           /* System.out.println(br.readLine());
            System.out.println(br.readLine());一行一行读肯定不现实,所以需要循环
            System.out.println(br.readLine());
            System.out.println(br.readLine());*/
            String line;// 用来记住每次读取的一行数据
            while((line = br.readLine()) != null){
                System.out.println(line);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}


import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.Writer;

public class BufferedWriterTest3 {
    public static void main(String[] args) {
        try (
                //Writer fw = new FileWriter("D:\\code\\idea-javacode\\javaseproject\\io-app2\\src\\itheima02.txt");
                Writer fw = new FileWriter("D:\\code\\idea-javacode\\javaseproject\\io-app2\\src\\itheima02.txt", true);//此时就变成了追加数据的管道
                //创建一个字符缓冲输出流管道包装原始的字符缓冲输出流
                BufferedWriter bw = new BufferedWriter(fw);//因为要用新增的功能所以不用多态来写

        ) {
            //1、public void write(int c):写一个字符出去
            bw.write('a');
            bw.write(97);
            bw.write('凡');
            //bw.write("\r\n");//换行
            bw.newLine();//换行

            //2、public void write(string c)写一个字符串出去
            bw.write("woaini");
            bw.write("我爱你");
            //bw.write("\r\n");
            bw.newLine();//换行
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}


原始流、缓冲流的性能分析【重点】

字节数组是越大越好,性能相对来说会越快,但大到一定程度就不明显了。(字节数组很大的时候每次装的数据多系统调用的次数少,性能肯定会好;但数组很大的时候装数据的过程慢,倒数据的过程也要慢一点,要花更多的时间装进去,也要花更多的时间倒出来;虽然字节数组大了可能对性能没法提升了)因此在开发中,如果要提高这些流的读写性能,不一定非得用缓冲流,可以用低级字节流,把这个数组弄大,性能可能就提升了,看到多少能够性能达到较好,根据自己的经验去设置。


IO流-转换流

引出问题:不同编码读取时会乱码

如果代码编码和被读取的文本文件的编码是一致的,使用字符流读取文本文件时不会出现乱码
如果代码编码和被读取的文本文件的编码是不一致的,使用字符流读取文本文件时就会出现乱码!


字符输入转换流

InputStreamReader(字符输入转换流)

  • 解决不同编码时,字符流读取文本内容乱码的问题。
  • 解决思路:先获取文件的原始字节流,再将其按真实的字符集编码转成字符输入流,这样字符输入流中的字符就不乱码了

字符输出转换流

需要控制写出去的字符使用什么字符集编码该咋整?

  • 调用String提供的getBytes方法解决?   String data="我爱你中国abc";
  • byte[] bytes = data.getBytes("GBK")    使用”字符输出转换流”实现。

OutputStreamWriter(字符输出转换流)

  • 作用:可以控制写出去的字符使用什么字符集编码,
  • 解决思路:获取字节输出流,再按照指定的字符集编码将其转换成字符输出流,以后写出去的字符就会用该字符集编码了


IO流-打印流

 PrintStream/PrintWriter(打印流)
作用:打印流可以实现更方便、更高效的打印数据出去,能实现打印啥出去就是啥出去。

PrintStream和PrintWriter的区别

  • 打印数据的功能上是一模一样的:都是使用方便,性能高效(核心优势)
  • PrintStream继承自字节输出流OutputStream,因此支持写字节数据的方法。
  • PrintWriter继承自字符输出流Writer,因此支持写字符数据出去

(我们用的是他的打印功能,所以两者在功能上没有什么区别)

打印流的一种应用:输出语句的重定向。可以把输出语句的打印位置改到某个文件中去。


IO流-数据流

 DataOutputstream(数据输出流)

  • 允许把数据和其类型一并写出去。

DatalnputStream(数据输入流)

  • 用于读取数据输出流写出去的数据

注意:写什么读什么,一定要对应起来!

//数据输出流
import java.io.DataOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;

public class DataOutputStreamTest1 {
    public static void main(String[] args) {

        try ( //1.创建一个数据输出流包装低级字节输出流
                DataOutputStream dos =
                        new DataOutputStream(new FileOutputStream("D:\\code\\idea-javacode\\javaseproject\\io-app2\\src\\itheima10out.txt"));

                ){//这样我们就到到一个数据输出流了 然后就可以调用它的方法
            dos.writeInt(99);
            dos.writeDouble(99.9);
            dos.writeBoolean(true);
            //dos.writeByte(10);
            dos.writeUTF("我爱你义凡");


        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
//数据输入流

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

public class DataInputStreamTest2 {
    public static void main(String[] args) {
        try(
                DataInputStream dis =
                        new DataInputStream(new FileInputStream("D:\\code\\idea-javacode\\javaseproject\\io-app2\\src\\itheima10out.txt"));
                ) {
            //System.out.println(dis.readInt());
            int i = dis.readInt();
            System.out.println(i);
            double d = dis.readDouble();
            System.out.println(d);
            boolean b = dis.readBoolean();
            System.out.println(b);
            String rs = dis.readUTF();
            System.out.println(rs);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}


IO流-序列化流

对象序列化:把Java对象写入到文件中去
对象反序列化:把文件里的Java对象读出来

Objectoutputstream(对象字节输出流)
可以把Java对象进行序列化:把Java对象存入到文件中去。

注意:对象如果要参与序列化,必须实现序列化接口(java.io.Serializable)

Objectlnputstream(对象字节输入流)
可以把Java对象进行反序列化:把存储在文件中的Java对象读入到内存中来。

 如果要一次序列化多个对象,怎么办?
用一个ArrayList集合存储多个对象,然后直接对集合进行序列化即可
注意:ArrayList集合已经实现了序列化接口!

注意:对象如果需要序列化,必须实现序列化接口。(implements Serializable)  相当于一个标记,给虚拟机程序看的,只有实现了这个接口,才允许这个对象按特定的方式存入到文件中去的虚拟就会启动装载程序把对象按照既定的格式存到文件中去。


import java.io.Serializable;

//注意:对象如果需要序列化,必须实现序列化接口。  相当于一个标记,给虚拟机程序看的,只有实现了这个接口,才允许这个对象按特定的方式存入到文件中去的
//虚拟就会启动装载程序把对象按照既定的格式存到文件中去
public class User implements Serializable {
    private String loginName;
    private String userName;
    private int age;
    //transient 这个成员变量将不参与序列化
    private transient String passWord;


    public User() {
    }

    public User(String loginName, String userName, int age, String passWord) {
        this.loginName = loginName;
        this.userName = userName;
        this.age = age;
        this.passWord = passWord;
    }

    public String getLoginName() {
        return loginName;
    }

    public void setLoginName(String loginName) {
        this.loginName = loginName;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getPassWord() {
        return passWord;
    }

    public void setPassWord(String passWord) {
        this.passWord = passWord;
    }

    @Override
    public String toString() {
        return "User{" +
                "loginName='" + loginName + '\'' +
                ", userName='" + userName + '\'' +
                ", age=" + age +
                ", passWord='" + passWord + '\'' +
                '}';
    }
}
//对象字节输出流序列化对象

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;

public class Test1ObjectOutputStream {
    public static void main(String[] args) {

        //2、创建一个对象字节输出流包装原始的字节输出流
        try (
                ObjectOutputStream oos =
                        new ObjectOutputStream(new FileOutputStream("D:\\code\\idea-javacode\\javaseproject\\io-app2\\src\\itheima11out.txt"));
                ){
            //1.创建一个Java对象
            User u = new User("admin","张三",23,"888666wyf");
            //3.序列化对象到文件中去
            oos.writeObject(u);
            System.out.println("序列化对象成功!");//Java规定如果对某个对象进行序列化的话就必须让这个对象的类去实现一个接口Serializable

        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}
//对象字节输入流反序列化对象

import java.io.*;

/**
 * 目标:如何实现对象的反序列化,掌握对象字节输入流的使用
 */
public class Test2ObjectInputStream {
    public static void main(String[] args) {
        //1.必须创建一个对象字节输入流管道包装低级的字节输入流与源文件接通
        try(
                ObjectInputStream ois =
                        new ObjectInputStream(new FileInputStream("D:\\code\\idea-javacode\\javaseproject\\io-app2\\src\\itheima11out.txt"));
                ) {
            User u = (User) ois.readObject();
            //可以把文件种存储的Java对象又恢复到内存中,返回的是一个Object类型的对象
            //因为存到文件中的是一个用户对象,所以用一个User来接收,强转成用户对象就可以了
            System.out.println(u);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}


补充知识:IO框架

什么是框架?

  • 解决某类问题,编写的一套类、接口等,可以理解成一个半成品,大多框架都是第三方研发的。
  • 好处:在框架的基础上开发,可以得到优秀的软件架构,并能提高开发效率
  • 框架的形式:一般是把类、接口等编译成class形式,再压缩成一个,jar结尾的文件发行出去

什么是IO框架? 

  • 封装了Java提供的对文件、数据进行操作的代码,对外提供了更简单的方式来对文件进行操作,对数据进行读写等。

 步骤:导入commons-io-2.15.1.jar框架到项目中去。

  1. 在项目中创建一个文件夹:lib
  2. 将commons-io-2.6.jar文件复制到lib文件夹
  3. 在jar文件上点右键,选择 Add as Library ->点击OK
  4. 在类中导包使用
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值