IO流总结,其他流:【转换流,对象操作流,Properties集合】

目录

转换流:【将字节流与字符流之间做转换】【进阶优化字符流】

转换流读写数据

 对象操作流

对象操作输出流 (对象序列化流)

对象操作输入流(对象反序列化流)

serialVersionUID&transient

Properties

Properties作为Map集合的使用

Properties作为Map集合的特有方法

Properties和IO流相结合的方法

Load方法

store方法


字节流的超类是InputStream,outputStream

字符流的超类:Reader,Writer 类

FileReader/Writer继承转换流,是转换流的子类

 字符流的底层仍是用到了字节流,也是一个字节一个字节的读,只是遇到汉字时【utf-8】识别到第一个值为负数,就会将三个字节结合为一体一起转为汉字。

真正读取数据的还是字节流,但创建的却是字符流。【是怎样将字符流变为字节流的?】

通过转换流 


转换流:【将字节流与字符流之间做转换】


- InputStreamReader:是从字节流到字符流的桥梁,父类是Reader

  ​    它读取字节,并使用指定的编码将其解码为字符

  ​    它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集

- OutputStreamWriter:是从字符流到字节流的桥梁,父类是Writer

  ​    是从字符流到字节流的桥梁,使用指定的编码将写入的字符编码为字节

  ​    它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集

我们以字符流的形式在内存中读取本地文件【在ieda中创的是字符流对象】,实质上也是在底层调用了字节流进行读取的【文件中存的是字节 】,只是利用了转换流将字节流转换为字符流

fileReader的使用是通过底层的转换流将文件的字节流转换为字符流进行的


转换流读写数据

 【假如有一个本地文件a.txt,win系统默认文件的编码表是GBK,而idea默认的是utf-8,所以如果使用method1方法中的方式进行读就会出现乱码】

【采用转换流来指定码表(利用其构造方法)进行读取】

注意:这是进行win系统下的文件与idea中内存进行交互,之前学的字符流,是在idea中创建的文件,码表一致,所以不会出现乱码

FileWriter和FileReader:作为转换流的子类,仅作为操作字符文件的便捷类存在。当操作的字符文件,使用的是默认编码表时可以不用父类,而直接用子类就完成操作了,简化了代码。

InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"));//默认字符集。

InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"),"GBK");//指定GBK字符集。

FileReader fr = new FileReader("a.txt");

这三句代码的功能是一样的,其中第三句最为便捷。

 注意:一旦要指定其他编码时,绝对不能用子类,必须使用字符转换流。什么时候用子类呢?

条件:

1、操作的是文件。2、使用默认编码。

 

 

  • 构造方法

    方法名说明
    InputStreamReader(InputStream in)使用默认字符编码创建InputStreamReader对象
    InputStreamReader(InputStream in,String chatset)使用指定的字符编码创建InputStreamReader对象
    OutputStreamWriter(OutputStream out)使用默认字符编码创建OutputStreamWriter对象
    OutputStreamWriter(OutputStream out,String charset)使用指定的字符编码创建OutputStreamWriter对象

由参数可知真正干活的仍是字节流 

【字符流是转换流的子类,所以使用字符流的读写方法,实际是调用了转换流的方法】

public class ConvertedDemo1 {
    public static void main(String[] args) throws IOException {
        //method1();
        //method2();

        //在JDK11之后,字符流新推出了一个构造,也可以指定编码表
        FileReader fr = new FileReader("C:\\Users\\apple\\Desktop\\a.txt", Charset.forName("gbk"));
        int ch;
        while ((ch = fr.read())!=-1){
            System.out.println((char) ch);
        }
        fr.close();
    }


    //这个方法直接读取会产生乱码
    //因为文件是GBK码表
    //而idea默认的是UTF-8编码格式.
    //所以两者不一致,导致乱码
    private static void method1() throws IOException {
        FileReader fr = new FileReader("C:\\Users\\apple\\Desktop\\a.txt");
        int ch;
        while ((ch = fr.read())!=-1){
            System.out.println((char) ch);
        }
        fr.close();
    }




    private static void method2() throws IOException {
        //如何解决乱码现象
        //文件是什么码表,那么咱们就必须使用什么码表去读取.
        //我们就要指定使用GBK码表去读取文件.
        InputStreamReader isr = new InputStreamReader(new FileInputStream("C:\\Users\\apple\\Desktop\\a.txt"),"gbk");
        int ch;
        while((ch = isr.read())!=-1){
            System.out.println((char) ch);
        }
        isr.close();


        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("C:\\Users\\apple\\Desktop\\b.txt"),"UTF-8");
        osw.write("我爱学习,谁也别打扰我");
【字符流是转换流的子类,所以使用字符流的读写方法,实际是调用了转换流的方法】
        osw.close();
    }
}

总结:转换流可以替代字符流使用 ,并且转换流还可以指定字符编码集【gbk/utf-8】


 对象操作流

案例:

public class ConvertedDemo2 {
    public static void main(String[] args) throws IOException {
        User user = new User("zhangsan","qwer");【标准JavaBean类】
        //需求:把这个用户信息保存到本地文件去.
        BufferedWriter bw = new BufferedWriter(new FileWriter("a.txt"));
        bw.write(user.getUsername());
        bw.newLine();
        bw.write(user.getPassword());
        bw.close();
    }
}
【点开文件可以看到账号密码信息,不安全】

利用对象操作流来解决此问题

对象操作流的特点:
可以把对象以字节的形式写到本地文件,直接打开文件,是读不懂的,需要再次用对象操作流读到内存中。【保证了数据的安全性】


 

对象操作流分为两类:对象操作输入流和对象操作输出流
对象操作输出流(又称为:对象序列化流)︰就是将对象写到本地文件中,或者在网络中传输对象
对象操作输入流(又称:对象反序列化流)︰把写到本地文件中的对象读到内存中,或者接收网络中传输的对象

对象操作输出流 (对象序列化流)

对象序列化介绍

  • 对象序列化:就是将对象【即此对象的所有内容】保存到磁盘中,或者在网络中传输对象

  • 这种机制就是使用一个字节序列表示一个对象,该字节序列包含:对象的类型、对象的数据和对象中存储的属性等信息

  • 字节序列写到文件之后,相当于文件中持久保存了一个对象的信息

  • 反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化


  • 构造方法

    方法名说明
    ObjectOutputStream(OutputStream out)创建一个写入指定的OutputStream的ObjectOutputStream
  • 序列化对象的方法

    方法名说明
    void writeObject(Object obj)将指定的对象写入ObjectOutputStream【参数为类的对象】

注意事项

  • 一个对象要想能被序列化【使用对象操作流进行了操作】,该对象所属的类必须必须实现Serializable 接口

  • Serializable是一个标记接口,实现该接口,不需要重写任何方法  

public class ConvertedDemo3 {
    public static void main(String[] args) throws IOException {
        User user = new User("zhangsan","qwer");

        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("a.txt"));
        oos.writeObject(user);【将自定义的user对象【所有内容】存入到文件中】
        oos.close();

    }
}
【这样就可以把对象内容存入文件中,以一堆乱码形式展示,达到保密效果】

对象操作输入流(对象反序列化流)

  • 构造方法

    方法名说明
    ObjectInputStream(InputStream in)创建从指定的InputStream读取的ObjectInputStream
  • 反序列化对象的方法

    方法名说明
    Object readObject()从ObjectInputStream读取一个对象【返回值是一个对象】
public class ConvertedDemo4 {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("a.txt"));
        User o = (User) ois.readObject();
【readobject方法将此文件里存入的对象内容读出到内存,返回的是之前被存入的对象】
        System.out.println(o);
        ois.close();

    }
}

serialVersionUID&transient

  • serialVersionUID

    • 用对象序列化流序列化了一个对象后,假如我们修改了对象所属的类文件,读取数据会不会出问题呢?

      • 会出问题,会抛出InvalidClassException异常

    • 如果出问题了,如何解决呢?

      • 重新序列化

      • 给对象所属的类加一个serialVersionUID

        • private static final long serialVersionUID = 42L;【值随便取一个】

  • transient

    • 如果一个对象中的某个成员变量的值不想被序列化,又该如何实现呢?

      • 给该成员变量加transient关键字修饰,该关键字标记的成员变量不参与序列化过程

public class User implements Serializable {
    //serialVersionUID 序列号
    //如果我们自己没有定义,那么虚拟机会根据类中的信息会自动的计算出一个序列号.
    //问题:如果我们修改了类中的信息.那么虚拟机会再次计算出一个序列号.

    //第一步:把User对象序列化到本地. --- -5824992206458892149
    //第二步:修改了javabean类. 导致 --- 类中的序列号 4900133124572371851
    //第三步:把文件中的对象读到内存. 本地中的序列号和类中的序列号不一致了.

    //解决?
    //不让虚拟机帮我们自动计算,我们自己手动给出.而且这个值不要变.

    private static final long serialVersionUID = 1L;

    private String username;
    private transient String password;

  标准javabean

    @Override
    public String toString() {
        return "User{" +
                "username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}
public class ConvertedDemo5 {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        //1.写对象
        //2.修改javabean类
        //3.读对象

        //写对象 --- 序列化
        //method1();

        //读对象 --- 反序列化
       method2();

    }

    private static void method2() throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("a.txt"));
        User o = (User) ois.readObject();
        System.out.println(o);
        ois.close();
    }

    private static void method1() throws IOException {
        User user = new User("zhangsan","qwer");
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("a.txt"));
        oos.writeObject(user);
        oos.close();
    }
}

练习: 

创建多个学生类对象写到文件中,再次读取到内存中

public class Student implements Serializable {
    

    【private static final long serialVersionUID = 100L;】
   

    private String name;
    private int age;

  JavaBean

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
}
  public static void main(String[] args) throws IOException, ClassNotFoundException {
        Student s1 = new Student("张三", 12);
        Student s2 = new Student("李四", 17);
        Student s3 = new Student("王五", 22);

        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("E:\\IdeaProject\\basic-code\\a.txt"));
        oos.writeObject(s1);
        oos.writeObject(s2);
        oos.writeObject(s3);
        oos.close();

        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("E:\\IdeaProject\\basic-code\\a.txt"));
        Object obj;



此写法会报错EOFException↓:
       /* while ((obj = ois.readObject() != null )) {
[为什么不能写 !=-1或null?  -1用于字节,null用于读一整行]
【我们不知道数据在文件中是怎样存储的】

            System.out.println(obj);
        }*/


优化:【主动处理异常】
        while (true){
            try {
                Object o = ois.readObject();
                System.out.println(o);
            } catch (EOFException e) {
               break;  等读到没数据创建了EOFException异常,执行catch的操作,break跳出循环
            }

        }
        ois.close();
    }

优化:把学生对象放入容器中,这个只需要读取一次即可 

public class Demo03 {
    /**
     *  read():
     *      读取到文件末尾返回值是 -1
     *  readLine():
     *      读取到文件的末尾返回值 null
     *  readObject():
     *      读取到文件的末尾 直接抛出异常
     *  如果要序列化的对象有多个,不建议直接将多个对象序列化到文件中,因为反序列化时容易出异常
     *      建议: 将要序列化的多个对象存储到集合中,然后将集合序列化到文件中
     */
    public static void main(String[] args) throws Exception {
        /*// 序列化
        //1.创建序列化流对象
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("myCode\\oos.txt"));
        ArrayList<Student> arrayList = new ArrayList<>();
        //2.创建多个学生对象
        Student s = new Student("佟丽娅",30);
        Student s01 = new Student("佟丽娅",30);
        //3.将学生对象添加到集合中
        arrayList.add(s);
        arrayList.add(s01);
        //4.将集合对象序列化到文件中
        oos.writeObject(arrayList);
        oos.close();*/

        // 反序列化
      	//5.创建反序列化流对象
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("myCode\\oos.txt"));
      	//6.将文件中的对象数据,读取到内存中
        Object obj = ois.readObject();
        ArrayList<Student> arrayList = (ArrayList<Student>)obj;
        ois.close();
        for (Student s : arrayList) {
            System.out.println(s.getName() + "," + s.getAge());
        }
    }
}

Properties

Properties不是IO体系中的,而是集合体系中的一员

 

 所以Properties是一个双列集合

构造方法:

Properties()
          创建一个无默认值的空属性列表

Properties作为Map集合的使用

  • Properties介绍

    • 是一个Map体系的集合类

    • 其中有跟IO相关的方法

    • 键值对的数据类型一般定义为字符串

    • Properties可以保存到流中或从流中加载

    • 属性列表中的每个键及其对应的值都是一个字符串


 将Properties当作Map集合的基本使用

public class PropertiesDemo1 {
    public static void main(String[] args) {
        Properties prop = new Properties();
        //增
        prop.put("小龙女","尹志平");
        prop.put("郭襄","杨过");
        prop.put("黄蓉","欧阳克");
        System.out.println(prop);

        //删
        //prop.remove("郭襄");
        //System.out.println(prop);

        //改
        //put --- 如果键不存在,那么就添加,如果键存在,那么就覆盖.
        prop.put("小龙女","杨过");
        System.out.println(prop);
        //查

        //Object value = prop.get("黄蓉");
        //System.out.println(value);

        //遍历
        Set<Object> keys = prop.keySet();
        for (Object key : keys) {
            Object value = prop.get(key);
            System.out.println(key + "=" + value);
        }

        System.out.println("========================");

        //装的是所有的键值对对象.
        Set<Map.Entry<Object, Object>> entries = prop.entrySet();
        for (Map.Entry<Object, Object> entry : entries) {
            Object key = entry.getKey();
            Object value = entry.getValue();
            System.out.println(key + "=" + value);
        }

    }
}

Properties作为Map集合的特有方法

  • 特有方法

    方法名说明
    Object setProperty(String key, String value)设置集合的键和值,都是String类型,底层调用 Hashtable方法 put【跟map集合的put一样】
    String getProperty(String key)使用此属性列表中指定的键搜索属性【跟map的get一样】
    Set<String> stringPropertyNames()从该属性列表中返回一个不可修改的键集,其中键及其对应的值是字符串【相当于keyset】
public class PropertiesDemo2 {
    public static void main(String[] args) {
//Object setProperty​(String key, String value) --- put
设置集合的键和值,都是String类型,底层调用 Hashtable方法 put
       

        Properties prop = new Properties();
        prop.setProperty("江苏","南京");
        prop.setProperty("安徽","南京");
        prop.setProperty("山东","济南");

        System.out.println(prop);
       


//String getProperty​(String key)  --- get
使用此属性列表中指定的键搜索属性【返回此键的值】

        String value = prop.getProperty("江苏");
        System.out.println(value);南京



//Set<String> stringPropertyNames​()  --- keySet
从该属性列表中返回一个不可修改的键集,其中键及其对应的值是字符串
返回值为set集合,集合内装的是所有的键

        Set<String> keys = prop.stringPropertyNames();
        for (String key : keys) {
            String value = prop.getProperty(key);获得键对应的值
            System.out.println(key + "=" + value);
        }

    }
}

Properties和IO流相结合的方法

Properties相当于map集合,是作用于内存中的,当想通过IO实现本地文件于内存之间信息读取时,Properties相当于代表内存,load,store方法参数为IO对象,表创建了一条通道。

从硬盘到内存,使用字符流,传给谁?传给Properties集合。load参数字符流对象,表内存与硬盘间的通道

使用store,将内存中map集合的内容写出到硬盘文件,store中字符流对象参数表使用字符流进行传输写出字符

  • 和IO流结合的方法

    方法名说明
    void load(Reader reader)从输入字符流读取属性列表(键和元素对)【 将本地文件中的键值对数据读取到集合中】
    void store(Writer writer, String comments)将此属性列表(键和元素对)写入此 Properties表中,以适合使用 load(Reader)方法的格式写入输出字符流【将集合中的数据以键值对形式保存在本地】


Load方法

public class PropertiesDemo3 {
    public static void main(String[] args) throws IOException {
        //void load​(Reader reader)                     将本地文件中的键值对数据读取到集合中
【要保证本地文件中的数据是以键值对的形式存在的】
        //void store​(Writer writer, String comments)   将集合中的数据以键值对形式保存在本地


        //读取【文件】
        Properties prop = new Properties();
        FileReader fr = new FileReader("prop.properties");【无法使用匿名对象形式写,因为还有释放资源操作】
        
       
        //prop.load(new FileReader("prop.properties"));
不能使用此语句,IO流用完后要关闭,而这种写法没法进行关闭

        prop.load(fr);【调用完了load方法之后,文件中的键值对数据已经在集合中了.】
        fr.close();

        System.out.println(prop);

执行结果:

打印出的顺序可能不一样,因为它属于map集合,是无序的


Properties 以后会把他当作配置文件用,所以文件的后缀名有要求【名字.Properties】

文件应放在当前项目下

将本地文件中的键值对数据读取到集合中

文件中的键值对写法:

 不加空格,不加分号。

直接写 :  键=值  即可


store方法

public class PropertiesDemo4 {
    public static void main(String[] args) throws IOException {
        //void load​(Reader reader)                     将本地文件中的键值对数据读取到集合中
        //void store​(Writer writer, String comments)   将集合中的数据以键值对形式保存在本地


        Properties prop = new Properties();
        prop.put("zhangsan","123");
        prop.put("lisi","456");
        prop.put("wangwu","789");

        FileWriter fw = new FileWriter("prop.properties");
        prop.store(fw,null);
        fw.close();

    }
}

第二个参数,表是一个注释。假如写一个“abc”,则注释处显示abc

 


Properties集合练习 :

  • 案例需求

    在Properties文件中手动写上姓名和年龄,读取到集合中,将该数据封装成学生对象,写到本地文件

  • 实现步骤

    • 创建Properties集合,将本地文件中的数据加载到集合中

    • 获取集合中的键值对数据,封装到学生对象中

    • 创建序列化流对象,将学生对象序列化到本地文件中

学生类: 

public class Student implements Serializable {
    private static final long serialVersionUID = 1L;

    private String name;
    private int age;

 Java Bean~~

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

 测试类:

public class Test {
  
      public static void main(String[] args) throws IOException {
        	//1.创建Properties集合,将本地文件中的数据加载到集合中
          Properties prop = new Properties();
          FileReader fr = new FileReader("prop.properties");
          prop.load(fr);
          fr.close();
  		//2.获取集合中的键值对数据,封装到学生对象中
          String name = prop.getProperty("name");
          int age = Integer.parseInt(prop.getProperty("age"));
          Student s = new Student(name,age);
  		//3.创建序列化流对象,将学生对象序列化到本地文件中
          ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("a.txt"));
          oos.writeObject(s);
          oos.close();
      }
  }
  ```

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值