Java基础-day13-序列化与反序列化·泛型·编码转换流

DAY14

1.序列化与反序列化

序列化是指将对象的状态信息转换为可以存储传输形式的过程.在序列化期间,对象将其当前状态写入到临时或持久性存储区.以后可以通过从存储区中读取或者反序列化对象的状态,重新创建该对象.

  1. 作用是为了对象的持久保存+传输
  2. 序列化:把对象序列化以后,保存在磁盘(比如文件)中
  3. 反序列化:读取磁盘(文件)中之前序列化好的数据,重新恢复成对象
  4. 序列化流:ObjectOutputStream
  5. 反序列化流:ObjectInputStream
  6. 创建对象:ObjectOutputStream(FOS) ObjectInputStream(FIS)
  7. 写方法:writeOject() 读方法: readObject()
    8. 未序列化异常:需要序列化的类必须要实现序列化接口Serializable

/*如果本类想要被序列化,必须实现可序列化接口Serializable,否则报错

  • 报错信息:java.io.NotSerializableException: cn.tedu.serializable.Student
  • Serializable是一个空接口,里面有一个方法都没有
  • 实现这个接口是为了作为一个标志,标志本类可以被序列化
  • */

package cn.tedu.serializable;

import java.io.Serializable;
/*如果本类想要被序列化,必须实现可序列化接口Serializable,否则报错
 * 报错信息:java.io.NotSerializableException: cn.tedu.serializable.Student
 * Serializable是一个空接口,里面有一个方法都没有
 * 实现这个接口是为了作为一个标志,标志本类可以被序列化
 * */
public class Student implements Serializable {

    //1.
    private String name;//姓名
    private int age;//年龄
    private String addr;//住址
    private char gender;//性别
    //无参构造
    public Student(){
        System.out.println("Student 的无参构造");
    }
    //全参构造
    public Student(String name, int age, String addr, char gender) {
        super();
        this.name = name;
        this.age = age;
        this.addr = addr;
        this.gender = gender;
        System.out.println("Student 的全参构造");
    }
    //不想看对象的地址值,想看属性值,所以添加重写toString()
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", addr='" + addr + '\'' +
                ", 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;
    }

    public String getAddr() {
        return addr;
    }

    public void setAddr(String addr) {
        this.addr = addr;
    }

    public char getGender() {
        return gender;
    }

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


package cn.tedu.serializable;
//
import java.io.*;
import java.util.Arrays;

public class TestSerializable {
    public static void main(String[] args) {
        method();//序列化
        method2();//反序列化

    }
    //序列化
    private static void method() {//序列化流
        ObjectOutputStream out = null;
        try{
            out = new ObjectOutputStream(new FileOutputStream("E:\\ready\\1.txt"));
            Student obj = new Student("乐乐",10,"快乐星球",'男');
            out.writeObject(obj);
            System.out.println("恭喜序列化成功");
        }catch(Exception e){
            e.printStackTrace();
            System.out.println("抱歉!序列化失败了!");
        }finally {

            try {
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    //反序列化
    private static void method2() {
        ObjectInputStream in = null;
        try{
            in = new ObjectInputStream(new FileInputStream("E:\\ready\\1.txt"));
            Object o = in.readObject();
            System.out.println(o);
            System.out.println("恭喜你!反序列化成功!");
        }catch(Exception e){
            System.out.println("很抱歉!反序列化失败!");
            e.printStackTrace();
        }finally {
            try {
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    
}

UID不匹配异常/反序列化异常:反序列化时持有UID与序列化时生成的UID不匹配。
解决方案:反序列化时的UID与序列化时的UID要保持一致,或者测试时一次序列操作对应一次反序列化操作,否则不匹配就报错


%@¥为什么反序列化版本号需要与序列化版本号一致?

我们在反序列化时,JVM会拿着反序列化流中的serialVersionUID与序列化时相应的实体类中的serialVersionUID来比较,如果不一致,就无法正常反序列化,出现序列化版本不一致的异常InvalidClassException。
而且我们在定义需要序列化的实体类时,如果没有手动添加UID,
Java序列化机制会根据编译的class自动生成一个,那么只有同一次编译生成的class才是一样的UID。
如果我们手动添加了UID,只要这个值不修改,就可以不论编译次数,进行序列化和反序列化操作


2.编码转换流

OutputStreamWriter(OutputStream out)把传入的字节流转成字符流
OutputStreamWriter(OutputStream out ,String charsetName)把Unicode转成其他编码输出

InputStreamReader(InputStream in) 把传入的字节流转成字符流
InputStreamReader(InputStream in,String charsetName)读取其他编码转成Unicode

/*常用编码是utf-8与gbk、ASCII

  • 如果出现乱码的情况,一定是查看与编译不符合*/
    //测试编码转换表
package cn.tedu.encoding;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
/*常用编码是utf-8与gbk
* 如果出现乱码的情况,一定是查看与编译不符合*/
//测试编码转换表
public class TestEncode {
    public static void main(String[] args) {
        OutputStreamWriter out = null;
        try{///OutputStreamWriter( FileOutputStream对象,默认字符编码格式);
            out  = new OutputStreamWriter(new FileOutputStream("E:\\ready\\1.txt"),"gbk");
            out.write("周五");
            System.out.println("执行成功");
        }catch(Exception e){
            System.out.println("失败了");
            e.printStackTrace();
        }finally {
            try {
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

3.泛型

1.泛型是为了模拟数组的元素类型检查
2.泛型通常与集合一起使用,用来约束集合中的元素的类型
3.泛型的格式:,这个Type必须是引用类型不能是基本类型
4.泛型可以帮助我们写出更加通用代码,减少代码多余,提高程序通用性
5.写泛型时一定注意格式的要求(定义集合/定义泛型方法)
List list3 = new ArrayList();
private static void print(E[ ] e ){ }
6.泛型是一个“语法糖”,可以提前报错的时机,在编译前就可以检查数据的类型,如果不是要求的类型,就报错,但是通过编译以后,泛型的作用就消失了,,也就是说编译后生成的字节码文件中没有泛型

        /*<type>---type该如何写?
        * 可以根据自己的需求设定类型,但是注意必须是引用类型,不能是基本类型*/
        //List<int> list3 = new ArrayList<int>();
        List<Integer> list3 = new ArrayList<Integer>();
        list3.add(100);
        list3.add(200);
        list3.add(300);
        System.out.println(list3);
    /*使用普通for循环的好处是可以设置循环的步长(怎么变化)*/

// for (int i = 0; i < a.length;i++ ) {
// System.out.println(a[i]);
// }
/高效for循环(增强for循环)
高效for循环
1.格式:
for(每轮遍历得到的元素的类型 遍历得到的元素的名字 : 要遍历的元素){循环体}
for(Integer i : a){ System.out.println(i) }
* 3是要遍历的元素 1每次遍历得到的元素的类型,2是遍历得到的元素的名字
* 好处:比普通for循环写法简单,效率高
* 坏处:没有办法按照下标来操作值,只能从头到尾依次遍历
/

package cn.tedu.generic;
//测试泛型的优点2
public class generic2 {
    public static void main(String[] args) {
        Integer[] a = {1, 2, 3, 4, 5, 6, 7};
        String[] b = {"大哥", "二哥", "三哥", "四哥", "五哥", "六哥", "小弟"};
        Double[] c = {6.0, 6.6, 6.66, 6.666, 6.6666};

        print(a);
        print(b);
        print(c);
    }
        /*10泛型可以实现通用代码的编写,使用E表示元素的类型Element类型
        * 泛型的语法要求:如果在方法上使用泛型,必须两处同时出现
        * 一个是传入参数的类型是泛型,另一个是返回值前的泛型类型
        * 表示这是一个泛型方法*/
        private static <I> void print (I[] e){
            for ( I f: e){
                System.out.println(f);
            }

        }


        private static void print (Double[]c){
            for (Double d: c){
                System.out.println(d);
            }
        }

        private static void print (String[]b){
            for(String s : b){
                System.out.println(s);
            }
        }

        private static void print (Integer[]a){
        /*使用普通for循环的好处是可以设置循环的步长(怎么变化)*/
//            for (int i = 0; i < a.length;i++ ) {
//                System.out.println(a[i]);
//            }
            /*高效for循环(增强for循环)
            * 格式for(1 2 :3){  System.out.println(2)}
            * 3是要遍历的元素 1每次遍历得到的元素的类型,2是遍历得到的元素的名字
            * 好处:比普通for循环写法简单,效率高
            * 坏处:没有办法按照下标来操作值,只能从头到尾依次遍历*/
            for (Integer i: a){
                System.out.println(i);
                
            }

        }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值