JAVA学习 API_day10(缓冲流, 打印流, 对象流)

缓冲流, 转换流, 打印流, 对象流

  • 1. 缓冲流
    • 1. 字节缓冲流
    • 2. 字符缓冲流
  • 2.打印流
    • 1.字节打印流(了解)
    • 2.字符打印流
  • 3.对象流

1. 缓冲流

1. 字节缓冲流

包装 -> 高级流: BufferedInputStream/BufferedOutputStream
Buffered: 缓冲/缓存
通常用于文件复制

扩展: 文件加密 -> 原理, 将读出来的字节做了运算后写入到新文件中
解密 -> 将加密过的文件, 再读出, 做一个还原运算, 重新写入
运算 -> 为了避免byte取值范围溢出, 同时为了避免加密解密两套代码
一般使用 ^ 运算
1 2 4 8 16 32 64
63 = 32 + 16 + 8 + 4 + 2 + 1
00111111
^ 13 = 00001101
= 00110010
^ 13 = 00001101
= 00111111
代码实现:

import org.junit.Test;

import java.io.*;
import java.util.Arrays;

public class WriteAndRead {
     
    @Test
    public void writeTest() throws IOException {
     
        File file = new File("src/com/hzt/stream/a.txt");
        System.out.println(file.getAbsolutePath());
        //获取字符串编码
        byte[] bw = "你好,世界!".getBytes("gbk");
        FileOutputStream os = new FileOutputStream(file, true);
        os.write(bw);
        os.close();
    }
    @Test
    public void readTest() throws IOException {
     
        File file = new File("src/com/hzt/stream/a.txt");
        byte[] br = new byte[3];
        FileInputStream is = new FileInputStream(file);
        int len;
        //read方法是每次读取br.length个字节,如果读到末尾返回-1,否则返回读取的字符数
        //使用写方法写入数组时要注意:
        //由于一直用同一个字符串读取,最后一次只读取了一个字节,导致后两个字节是上次读取的数据
        while((len = is.read(br)) != -1){
     
            System.out.println(len);
            System.out.println(Arrays.toString(br));
        }
        is.close();
    }

    @Test
    //字节流用于复制
    public void copy() throws IOException {
     
        File fileI = new File("src/com/hzt/stream/a.txt");
        File fileO = new File("src/com/hzt/stream/a_bak.txt");
        FileInputStream is = new FileInputStream(fileI);
        FileOutputStream os = new FileOutputStream(fileO, true);
        byte[] b = new byte[3];
        int len;
        //当读取字节数为-1时表示读取到了末尾
        while ((len = is.read(b)) != -1){
     
            //将每次读取的有效字节写入
            os.write(b, 0, len);
        }
        os.close();
        is.close();
    }
    //缓冲字节流包装字节流 大幅度提高效率
    @Test
    //字节流用于复制
    public void bufferCopy() throws IOException {
     
        //图片地址
        File fileI = new File("src/com/hzt/stream/p.jpg");
        File fileO = new File("src/com/hzt/stream/p_bak.jpg");
        //使用缓冲流包装字节流
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(fileI));
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(fileO));
        byte[] b = new byte[1024];
        int len;
        long time1 = System.currentTimeMillis();
        //当读取字节数为-1时表示读取到了末尾
        while ((len = bis.read(b)) != -1){
     
            //1.将每次读取的有效字节写入缓冲区
            //2.当缓冲区满了之后自动刷新缓冲区释放资源,同时把数据写入到内存
            bos.write(b, 0, len);
        }
        long time2 = System.currentTimeMillis();
        System.out.println(time2 - time1);
        //先刷新缓冲区,然后通知系统释放资源。流对象不可以再被使⽤了。
        bos.close();
        bis.close();
    }
    @Test
    public void flushTest() throws IOException {
     
        File fileI = new File("src/com/hzt/stream/a.txt");
        File fileO = new File("src/com/hzt/stream/a_bak.txt");
        //使用缓冲流包装字节流
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(fileI));
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(fileO));
        long time1 = System.currentTimeMillis();
        int i;
        //当读取字节数为-1时表示读取到了末尾
        while ((i = bis.read()) != -1){
     
            bos.write(i);
            bos.flush();//写在输出流后面
        }
        long time2 = System.currentTimeMillis();
        System.out.println(time2 - time1);
    }
    @Test
    //文件的加密与解密
    public void encryption(){
     
        File fileI = new File("src/com/hzt/stream/a.txt");
        File fileE = new File("src/com/hzt/stream/a_bak_encryption.txt");
        File fileO = new File("src/com/hzt/stream/a_bak.txt");
        // try(resource) {} 不管有没有异常, resource都会自动关闭, 不需要finally
        try(            //使用缓冲流包装字节流
                        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(fileI));
                        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(fileE));
                        BufferedInputStream bisE = new BufferedInputStream(new FileInputStream(fileE));
                        BufferedOutputStream bosE = new BufferedOutputStream(new FileOutputStream(fileO));) {
     

            long time1 = System.currentTimeMillis();
            //当读取字节数为-1时表示读取到了末尾
            int i;
            //当读取字节数为-1时表示读取到了末尾
            while ((i = bis.read()) != -1){
     
                //在写入时对字节进行异或操作加密,因为是逐位运算所以不会超过范围(通常运算可能会超过字节范围)
                bos.write(i ^ 1);
//                bos.flush();//写在输出流后
            }
            //解密文件
            while ((i = bisE.read()) != -1){
     
                //在写入时对字节进行异或操作解密,利用异或运算两次会复原的特性将异或加密的文件解密
                bosE.write(i ^ 1);
                bosE.flush();//写在输出流后面
            }
            long time2 = System.currentTimeMillis();
            System.out.println(time2 - time1);
        } catch (IOException e) {
     
            e.printStackTrace();
        }
    }
}

2. 字符缓冲流

包装字符流 -> 缓冲字符流
通常用来读写文件内容
BufferedReader(Reader)
String readLine(): 读取一整行, 返回null 标记读到文件末尾
代码实现:

public class BufferStreamTest {
     
    //字符缓冲流
    @Test
    public void read(){
     
        File fileI = new File("./src/com/hzt/stream/a.txt");
        //构造方法:使用缓冲流包装由字符流包装的字节流,字节输出流可以设置append, 字符流可设置字符编码
        try{
     
            BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(fileI), "utf-8"));
            int i;
            String str;
            //循环读取一个字符,返回值为-1时结束,本操作系统不识别/r
            while ((i = br.read()) != -1){
     
                System.out.print((char)i);
            }
            //循环读取一行,返回值为null时结束,""即空字符串不为null,每次读取遇到换行符结束,不包括换行符
//            while ((str = br.readLine()) != null){
     
//                System.out.println(str);
//            }
        }catch(IOException e){
     
            e.printStackTrace();
        }
    }

    @Test
    public void write(){
     
        File fileO = new File("./src/com/hzt/stream/a.txt");
        //定义一个字节缓冲输出流,参数为FileWriter,通过字符流便捷类改变可添加属性append
        try{
     
            BufferedWriter bw = new BufferedWriter(new FileWriter(fileO, true));
            bw.write("你好");
            bw.flush();
        }catch (IOException e){
     
            e.printStackTrace();
        }
    }
}

2.打印流

1.字节打印流(了解)

代码实现:

 @Test
    public void bytePrint(){
     
        File file = new File("src/com/hzt/stream/a.txt");
        //打印流用于在文件中打印出数据流中的数据
        try{
     
            //构造方法1:(文件路径,字符集)
            PrintStream ps1 = new PrintStream(file, "utf-8");
            //构造方法2:(字节缓冲流(字节流(file, append)), autoFlush, enCoding)
            //append 可拼接, autoFlush 自动刷新, enCoding 字符编码, 用BufferedOutputStream包装FileOutputStream
            PrintStream ps2 = new PrintStream(new BufferedOutputStream(new FileOutputStream(file, true)), true, "utf-8");
            ps2.println("黎明杀机");
            ps2.print("怪物猎人");
            ps2.print(":世界");
            ps2.append('a');
            //创建一个打印流向为控制台的字节打印流
            PrintStream ps3 = new PrintStream(System.out);
            ps3.println("赛博朋克2077");
            //改变控制台的打印流为ps2
            System.setOut(ps2);
            System.out.println("\n控制台输出");
        }catch (IOException e){
     
            e.printStackTrace();
        }
    }

2.字符打印流

PrintWriter
1.只能清空原文件内容, 但是可以指定字符集
PrintWriter(String fileName, String csn)
2.可以在原文件上追加内容, 但是不可以指定字符集, 可以自动刷新缓冲区
PrintWriter(OutputStream out, boolean autoFlush)
3.可以在原文件上追加内容, 可以指定字符集, 可以自动刷新缓冲区
PrintWriter(Writer out, boolean autoFlush)
void print(Object): 写出内容, 不加换行
void println(): 写出内容, 并且换行
注意: 自动刷新的功能, 只有println方法具有
print方法 必须手动刷新
代码实现:

 //字符打印流
    @Test
    public void characterPrint(){
     
        File file = new File("src/com/hzt/stream/a.txt");
        try{
     
            //构造方法1:(文件路径, 字符集)
            PrintWriter pw1 = new PrintWriter(file, "utf-8");
            //构造方法2:(字节缓冲输出流(字节输出流(file, append)), autoFlush)
            PrintWriter pw2 = new PrintWriter(new BufferedOutputStream(new FileOutputStream(file, true)), true);
            //构造方法3:(字符缓冲输出流(字符输出流(
            // 字节缓冲输出流(字节输出流(file, append)), charsetName)), autoFlush)
            PrintWriter pw3 = new PrintWriter(new BufferedWriter(new OutputStreamWriter(
                    new BufferedOutputStream(new FileOutputStream(file, true)), "utf-8")), true);
            //打印
            //不像PrintStream类,如果启用自动刷新,它只会在调用的println,printf,或format方法来完成
            pw3.println("怪物猎人:世界");
            pw3.print("怪物猎人:冰原");
            //print方法需要刷新
            pw3.flush();
        }catch (IOException e){
     
            e.printStackTrace();
        }
    }

3.对象流

对象输入输出流:
对象 -> 文件[字节] : 序列化
文件[字节] -> 对象 : 反序列化

ObjectInputStream(InputStream)
Object readObject()
ObjectOutputStream(OutputStream)
void writeObject(Object)
Serializable: 可序列化的
接口中没有常量, 也没有抽象方法, 空接口
序列化接口没有方法或字段,仅用于标识可串行化的语义。

serialVersionUID: 给类添加固定的序列版本号
transient: 修饰的变量, 在序列化的时候, 会被忽略掉
代码实现:

import org.junit.Test;

import java.io.*;
import java.util.Date;

public class ObjectStreamTest implements Serializable{
     
    //对象流
    //创建内部类类(JavaBean规范),实现序列化
    //Serializable接口中没有任何方法,只是作为可序列化的标识
    private class Student implements Serializable {
     
        private String name;
        private int age;
        private transient  char gender; // transient瞬态修饰成员,值不会被序列化
        /*Serializable 接口给需要序列化的类,提供了一个序列版本号。 serialVersionUID 该版本号
          的木的在于验证序列化的对象和对应类是否版本匹配。*/
        //所以需要定义序列号,不定义的话会随机生成,生成之后不可修改
        public static final long serialVersionUID = 13065705269l;
        public Student(){
     };

        public char getGender() {
     
            return gender;
        }

        public void setGender(char gender) {
     
            this.gender = gender;
        }

        public String getName() {
     
            return name;
        }

        public void setName(String name) {
     
            this.name = name;
        }

        public int getAge() {
     
            return age;
        }

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

        @Override
        public boolean equals(Object obj) {
     
            return super.equals(obj);
        }

        @Override
        public int hashCode() {
     
            return super.hashCode();
        }

        @Override
        public String toString() {
     
            return "name:" + this.name + " age:" + this.age + " gender:" + this.gender;
        }
    }
    //序列化
    @Test
    public void writeObject(){
     
        File file = new File("./src/com/hzt/stream/a.txt");
        Student student = new Student();
        student.setAge(20);
        student.setName("hzt");
        student.setGender('男');
        Date date = new Date();//date类是可序列化的
        try(ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file))){
     
            oos.writeObject(student);//写进去的类序列号为serialVersionUID,如果没有定义就随机生成
            oos.writeObject(date);
            oos.writeObject(null);
        }catch (IOException e){
     
            e.printStackTrace();
        }
    }
    //反序列化
/*
    当JVM反序列化对象时,能找到class文件,但是class文件在序列化对象之后发生了修改,
    那么反序列化操作也会失败,抛出一个 InvalidClassException 异常
*/
    @Test
    public void readObject(){
     
        File file = new File("./src/com/hzt/stream/a.txt");
        try(ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file))){
     
            Student student = (Student) ois.readObject();
            Date date = (Date)ois.readObject();
            Object object = (Object)ois.readObject();//对象值为null也可以读出
            System.out.println(student);
            System.out.println(date);
            System.out.println(object);
            Object end = (Object)ois.readObject();//如果读到文件末尾会抛出EOF异常(已检测异常必须处理,是IOException的子类)
            System.out.println(end);
            //因为gender属性是瞬态放,所以值在反序列化时就被忽略了
        }catch ( ClassNotFoundException | IOException e){
     
            e.printStackTrace();
        }
    }
}

你可能感兴趣的:(JAVASE,java,stream,javase)