Java学习---Day17_IO流进阶篇

Java学习—Day17_IO流进阶篇

缓冲流(会)

  • 基本介绍

给普通的IO流, 套上一个缓冲区。 所有的使用缓冲流进行的读写操作, 都是和缓冲区进行交互的, 避免了频繁的IO操作。 这样一来, 带来的好处就是可以提高读写的效率这个缓冲区, 其实是一个数组

  • 缓冲流的作用

为了提高读写的能力,本身没有读写的能力,要想进行读写就必须借助于字符流/字节流实现.

  • 常⻅的缓冲流:
    • BufferedInputStream : 缓冲字节输入流
    • BufferedOutputStream : 缓冲字节输出流
    • BufferedReader : 缓冲字符输入流
    • BufferedWriter : 缓冲字符输出流

使用缓冲流实现读写的步骤与字符流一样,只是需要我们先通过构造方法传入一个字符流对象.同时缓冲流可以提高读写效率

  • 总结:

大家在使用流读写数据时,尽量使用缓冲流,缓冲流中尽量使用缓冲字符流,在字符缓冲流中比缓冲字节流多了readLine()newLine()方法.

缓冲字节流
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException
public class BufferedInputStreamTest {
    public static void main(String[] args) {
        // 过程和InputStream一模一样的
        // 缓冲字节输入流是需要基于一个字节输入流来进行实例化的
        // 在这里,BufferedInputStream构造方法中的InputStream对象,只是用来做当前的对象的实例化,在使用结束的时候,理论上来讲,是需要关闭的
        // 实际在使用中,使用结束后,只需要关闭BufferedInputStream即可。
        try (BufferedInputStream bufferedInputStream = new 
BufferedInputStream(new FileInputStream("file\\day26\\source"))) 
{
            // 1. 实例化一个字节数组
            byte[] array = new byte[1024];
            // 2. 声明一个整型变量,用来记录每次读取了多少个字节数据
            int length = 0;
            // 3. 循环读取
            while ((length = bufferedInputStream.read(array)) != -1) {
                // 4. 将读取到的数据转成字符串输出到控制台
                String msg = new String(array, 0, length);
                System.out.println(msg);
                catch (IOException e) {
            e.printStackTrace();
        }
    }
}
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class BufferedOutputStreamTest {
    public static void main(String[] args) {
        // 1. 实例化一个缓冲字节输出流对象
        try (BufferedOutputStream bufferedOutputStream = new 
BufferedOutputStream(new 
FileOutputStream("file\\day26\\target")))  {
            // 2. 将数据写入到输出流中
            bufferedOutputStream.write("hello 
world".getBytes());
            bufferedOutputStream.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
缓冲字符流
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class BufferedReaderTest {
    public static void main(String[] args) {
        // 借助一个字符流,实例化一个缓冲字符输入流
        try (BufferedReader bufferedReader = new 
BufferedReader(new FileReader("file\\day26\\src"))) {
            // 从流中读取数据
            char[] array = new char[100];
            int length = 0;
            while ((length = bufferedReader.read(array)) != -1) 
{
                System.out.print(new String(array, 0, length));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
public class BufferedWriterTest {
    public static void main(String[] args) {
        // 借助一个字符输出流,实例化一个缓冲字符输出流对象
        try (BufferedWriter bufferedWriter = new 
BufferedWriter(new FileWriter("file\\day26\\dst"))) {
            bufferedWriter.write("hello world");
            bufferedWriter.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
缓冲字符流中的特殊方法

BufferedReader 类中多了一个方法 readLine()

  • 意义: 读取缓冲流中的一行数据, 可以逐行读取。 一直到读取到的数据是null,表示数据读取完了, 没有下一行数据了。
  • 注意事项: readLine() 是逐行读取, 但是, 只能读取到一行中的内容, 并不能读取走换行符。
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class BufferedReaderSpecial {
    public static void main(String[] args) {
        try (BufferedReader reader = new BufferedReader(new 
FileReader("file\\day26\\src"))) {
            // 1. 定义一个字符串,用来接收每一行读取到的数据
            String line = "";
            // 2. 循环读取数据
            while ((line = reader.readLine()) != null) {
                // 3. 将读取到的数据输出
                System.out.println(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
BufferedWriter 类中多了一个方法 newLine()
  • 写换行符 ,不同的系统使用的默认换行符不一样 windows系统 \r\n linux \n
  • 意义: 无参的方法, 写一个换行符,支持跨平台(平台无关性)
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
public class BufferedWriterSpecial {
    public static void main(String[] args) {
        try (BufferedWriter bufferedWriter = new 
BufferedWriter(new FileWriter("file\\day26\\dst"))) {
            bufferedWriter.write("hello world");
            bufferedWriter.newLine();
            bufferedWriter.write("你好,世界");
            bufferedWriter.newLine();
            bufferedWriter.write("end");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
LineNumberReader

BufferedReader的子类,不能读.但是可以提高效率,特有功能:设置行号,获取行号

import java.io.FileReader;
import java.io.IOException;
import java.io.LineNumberReader;
 
public class Demo10{
    public static void main(String[] args) throws IOException {
        LineNumberReader lineNumberReader = new 
LineNumberReader(new 
FileReader("BigData2005N18\\src\\com\\qf\\test\\Demo1.java"));
        //设置行号,默认从0开始,从1开始打印
        lineNumberReader.setLineNumber(10);
        String data = null;
        while ((data = lineNumberReader.readLine()) != null) {
           
 System.out.print(lineNumberReader.getLineNumber());//获取行号
            System.out.print(data);
            System.out.println();
        }
        lineNumberReader.close();
    }
}
装饰设计模式【了解】
  • 设计模式简介

设计模式, 前人总结出来的对一些常⻅问题的解决方案,后人直接拿来使用.常用的设计模式:单例,工厂,代理,适配器,装饰,模板,观察者等,一共有23

  • 装饰设计模式

基于已经实现的功能,提供增强的功能

  • 装饰设计模式特点

装饰设计模式的由来就来自于对缓冲流的实现

从缓冲流的⻆度讲解

1.使流原来的继承体更加的简单
2.提高了效率
3.由于是在原有的基础上提高增强的功能,所以他还要属于原来的体系

Scanner类【会】

这个类, 并不是一个IO流。 是一个扫描器, 这个类最主要的作用, 是从一个文件中或者从一个流中浏览数据。 在这个类中封装了若干个方法, 方便了数据的读取。

ps:这里nextLineBufferedReader中的readLine都可以读取一行数据。 但是区别在于: 结束条件不同。

  • BufferedReader: 如果读取到的数据是null, 说明没有下一行数据了。
  • Scanner: 如果没有下一行了,再去读取,会出现异常。 所以, 此时的结束条件是 hasNextLine() 为false
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;
import java.util.regex.Pattern;
public class ScannerTest {
    public static void main(String[] args) {
        // 其实,Scanner在使用结束之后,也是需要进行关闭的。 调用close方
法。
        try (Scanner scanner = new Scanner(new 
File("file\\day26\\src"))) {
            // 读取文件中的内容
            while (scanner.hasNextLine()) {
                System.out.println(scanner.hasNextLine());
            }
        } catch (FileNotFoundException e) {
         e.printStackTrace();
        }
    }
}
标准输入输出流【会】
  • 标准输入流: System.in : “标准”输入流。此流已打开并准备提供输入数据。通常,此流对应于键盘输入或者由主机环境或用户指定的另一个输入源。
输入源:可以发送数据到内存的设备     
输出源:可以接收内存的数据的设备
  
1.当前的流已经打开并关联了输入源--键盘
2.如果不想让键盘充当输入源,可以通过setIn进行更换
3.是一个字节流
  • 标准输出流: System.out : 标准”输出流。此流已打开并准备接受输出数据。通常,此流对应于显示器输出或者由主机环境或用户指定的另一个输出目标。
标准输入流
import java.io.BufferedInputStream;
import java.io.IOException;
import java.util.Scanner;
public class SystemInTest {
    public static void main(String[] args) {
        //创建了标准输入流并关联了键盘(默认的)
        //InputStream inputStream =  System.in;
        //阻塞式方法
         //int num = inputStream.read();
        //System.out.println(num);
        
       //实例演示
        try (BufferedInputStream bis = new 
BufferedInputStream(System.in)) {
            byte[] array = new byte[128];
            int length = 0;
            while ((length = bis.read(array)) != -1) {
                String str = new String(array, 0, length);
                System.out.println(str);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
标准输出流
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
 
public class SystemOutTest {
    public static void main(String[] args) {
        PrintStream original = System.out;
        // PrintStream: 是一个打印流,可以将数据输出到指定位置。
        try (PrintStream ps = new PrintStream(new 
FileOutputStream("file\\day26\\logs", true))) {
            // ps.println("hello world!");
            // 重定向标准输出流
            System.setOut(ps);
 
            System.out.println("123");
        }
        catch (IOException e) {
            e.printStackTrace();
        } finally {
            System.setOut(original);
        }
         System.out.println("你好");
 
        // System.out;      标准输出流地址
        // System.out -> ps
    }
}
转换流【了解】

使用场景:在进行文件读取的时候, 如果项目采用的字符集和文件的字符集不同,会出现乱码的情况

输入流
import java.io.*;
public class TransforeTest {
    public static void main(String[] args) {
        read();
    }
    private static void read() {
        // 当前的项目是 utf-8, 读取的文件是 GBK
        // 如果需要以指定的字符集进行文件的读取,需要使用 
        //InputStreamReader(InputStream inputStream, String charsetName)
        try (InputStreamReader reader = new 
InputStreamReader(new FileInputStream("file\\day26\\src"), 
"GBK")) {
            char[] array = new char[128];
            int length = 0;
            while ((length = reader.read(array)) != -1) {
                System.out.println(new String(array, 0, 
length));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
输出流
import java.io.*;
public class TransforeTest {
    public static void main(String[] args) {
        write();
    }
 
    private static void write() {
        // 以指定的字符集写数据
        try (OutputStreamWriter writer = new 
OutputStreamWriter(new FileOutputStream("file\\day26\\dst", 
true), "GBK")) {
            writer.write("hello world");
            writer.write("你好,世界");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
打印流【会】
打印流分类 (除了拥有输出流的特点之外,还有打印的功能)
  • 字节打印流:PrintStream
  • 字符打印流:PrintWriter
字节打印流(字节打印支持设备)
  • 1.File类型的文件
  • 2.字符串类型的文件
  • 字节输出流
public class Demo14 {
    public static void main(String[] args) throws IOException {
      //设备:File类型的文件
//     PrintStream p1 = new PrintStream(new 
File("BigData2005N18\\test7.txt"));
      //设备:字节输出流
//     PrintStream p2 = new PrintStream(new 
FileOutputStream("BigData2005N18\\test7.txt"));
      //设备:字符串类型的文件
//     PrintStream p3 = new 
PrintStream("BigData2005N18\\test7.txt");
 
        //实例
       PrintStream p3 = new 
PrintStream("BigData2005N18\\test7.txt");
       //直接使用write方法打印,默认只支持一个字节,所以当数据超出了一个字节的范围,会出现下面的错误.
        p3.write(97);//00000000 01100001   默认会将高字节删掉  01100001-----a
        p3.write(353);//00000001 01100001  ----01100001---a
      
        //一般我们不直接使用write方法,而是使用print方法
      
        p3.println(353);//直接输出353
 
        //这是print方法的内部实现原理
         //先将353转成字符串再转成字节数组
        p3.write(String.valueOf(353).getBytes());
        p3.close();
    }
}
字符打印流
  • 字符打印流支持的设备:

    • 1.File类型的文件
    • 2.字符串类型的文件
    • 3.字节输出流
    • 4.字符写出流
  • 注意点:

方法:public PrintWriter(Writer out, boolean autoFlush)
autoFlush - boolean 变量;如果为 true,则 println、printf 或 format 方法将自动刷新输出缓冲区
但是执行print方式时需要手动刷新

public class Demo11 {
    public static void main(String[] args) throws IOException {
        PrintWriter pWriter = new PrintWriter(new 
FileWriter("test5.txt"));
        pWriter.write("bingbing");
        pWriter.close();
    }
}

编码问题【了解】

开发中的编码
  • 常用字符集

    • 中国的字符集:GBK/GB2312
    • 欧洲的:ISO8859-1
    • 通用的:UTF-8
    • 美国的:ASCII
  • 对中文的处理

    • 一个汉字:GBK:2个字节 ISO8859-1:1个字节 utf-8:3个字节 unicode:2个字节
      (内部编码)
    • 说明:GBK,UTF-8是支持中文的,ISO8859-1不支持中文
  • 编码:将字符串转化成byte序列的过程

  • 解码:是将byte序列转成字符串的过程

  • 编码错误:乱码:在执行读与写的时候,由于使用的字符集不同,造成了编码的错误.

序列化流【会】
  • 将短期存储的数据实现⻓期存储,这个过程对应的流就是序列化流
  • 数据的存储分成两类:
    • 1.短期存储:存放在内存中,随着程序的关闭而释放—对象,集合,变量,数组
    • 2.⻓期存储:存储在磁盘中,即使程序关闭了,数据仍然存在------文件
  • 序列化:将数据从内存放入磁盘,可以实现数据的⻓久保存
  • 反序列化:将数据从磁盘放回内存

注意事项:

  • ObjectInputStream、 ObjectOutputStream, 主要是用来做对象的序列化和反序列化的。
  • 序列化、 反序列化, 是对象的持久化存储的一种常用手段。
  • 所有的要序列化到本地的类的对象, 类必须实现 java.io.Serilizable 接口。
实现了Serializable接口的类可以达到的目的:
1.可以进行序列化
2.进行序列化的类的元素都必须支持序列化
3.可序列化类的所有子类型本身都是可序列化的。
4.接口本身没有方法或字段,只是用来表示可序列化的语义
  • 如果需要序列化多个文件到本地, 尽量不要序列化到一个文件中。 如果需要序列化多个文件到本地, 通常采用的方式, 是存集合。 将多个对象存入一个集合中, 将这个集合序列化到本地。
注意点:
1. ClassNotFoundException:当前的类没有找到
分析:将Person对象进行序列化之后,将Person类删除,再进行反序列化的时候出现了
异常
原因:反序列化在执行的时候依赖字节码文件,当类没有了,字节码文件无法创建,反序列
化失败
 
2.java.io.InvalidClassException  无效的类
出现的原因:没有声明自己的serialVersionUID,而使用系统的.在进行反序列化的时
候,类被改动了,系统认为现在的类
已经不是原来的类了(在使用系统的id进行识别的时候,重写给Person设置了id),认为
此类无效
 
3.使用系统的serialVersionUID与自定义的ID的区别?
使用系统的,序列化和反序列化,id不能手动设置,使用的是编译器默认生成的,一旦类
发生了改动,id会重新赋值
使用自定义的,序列化和反序列化,id不会发生改变,所以当反序列化的时候,即使对
Person类进行了一些改动,也能继续反序列化
/*
创建Person对象,并进行序列化处理
要想让Person类的对象可以实现序列化,必须让Person类实现Serializable接口
类通过实现 java.io.Serializable 接口以启用其序列化功能。
未实现此接口的类将无法使其任何状态序列化或反序列化。
注意:不仅要求当前类可序列化,而且要求当前类的所有元素本身都是可序列化的
(比如:ArrayList)
*/
class  Person implements  Serializable{
    //   /**
    //       * generated:由编译器自动生成的,后面加L表示long型
    //       */
    private static final long serialVersionUID = -7224641225172644265L;
    //      /**
    //       * default:UID是由用户自己指定的,默认值是1L]
    //      private static final long serialVersionUID = 1L;
    String name;
    int age;
    double height;
 
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
 
    public  void  show(){
        System.out.println("show-oo");
    }
 
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
 
    public void  run(){
        System.out.println("run");
    }
}
//可序列化类的所有子类型本身都是可序列化的。
class  GoodPerson extends  Person{
    double weight;
 
    public GoodPerson(String name, int age, double weight) {
        super(name, age);
        this.weight = weight;
    }
}
  • 将Person对象序列化
public class Demo13 {
    static Person p1;
    public static void main(String[] args) throws IOException, 
ClassNotFoundException {
        //序列化
        //fun1();
        //逆序列化
        fun2();
    }
    //序列化
    public  static  void  fun1() throws IOException {
        //创建序列化流
        ObjectOutputStream objectOutputStream = new 
ObjectOutputStream(new 
FileOutputStream("D:\\ideaProgram\\BigDataBK2001N06\\BigDataBK20
01N20\\copyDemo5.java"));
        //objectOutputStream.writeInt(2);
 
        //对对象进行序列化
        p1 = new Person("zhangsan",20);
        objectOutputStream.writeObject(p1);
        //可序列化类的所有子类型本身都是可序列化的。
        //objectOutputStream.writeObject(new 
GoodPerson("hah",20,20));
        //序列化后要及时关闭流
        objectOutputStream.close();
    }
    //逆序列化
    public static  void  fun2() throws IOException, 
ClassNotFoundException {
        ObjectInputStream objectInputStream = new 
ObjectInputStream(new 
FileInputStream("D:\\ideaProgram\\BigDataBK2001N06\\BigDataBK200
1N20\\copyDemo5.java"));
//        int num = objectInputStream.readInt();
//        System.out.println(num);
 
        //反序列化
        Object o = objectInputStream.readObject();
        System.out.println(o == p1);//false  说明反序列化后的对象与原来的对象是两块儿空间.
        System.out.println(o);
        //objectInputStream.close();
    }
总结
  • 合理使用序列化流和反序列化流,要与输入流与输出流配合使用
  • 进行序列化的类一定要实现Serializable接口,只要实现了接口就可以序列化.包括集合,包装类等
  • 进行序列化的类要保证当前类与内部的类都要实现Serializable接口
Properties【了解】
  • 概述

Properties也不是一个IO流, 是一个集合。 是Hashtable的子类。使用Properties主要是为了描述程序中的属性列表文件。 有时候, 我们会将一些比较简单的项目的配置信息, 以 .properties 格式的文件进行存储。 可以使用Properties对象读写 .properties 文件。

  • 注意

因为存储的是属性,属性本来就是以键值对的方式存储.这里的键和值都必须是字符串.所以不需要考虑泛型

  • Properties作用
    • HashTable的子类,所以也是键值对形式,保存,操作更容易
    • 默认键值都是字符串,所以不需要再考虑泛型
    • 提供了一批好用的方法,方便使用(load(),store(),list()等)

你可能感兴趣的:(java)