DAY14
1.序列化与反序列化
序列化是指将对象的状态信息转换为可以存储或传输形式的过程.在序列化期间,对象将其当前状态写入到临时或持久性存储区.以后可以通过从存储区中读取或者反序列化对象的状态,重新创建该对象.
- 作用是为了对象的持久保存+传输
- 序列化:把对象序列化以后,保存在磁盘(比如文件)中
- 反序列化:读取磁盘(文件)中之前序列化好的数据,重新恢复成对象
- 序列化流:ObjectOutputStream
- 反序列化流:ObjectInputStream
- 创建对象:ObjectOutputStream(FOS) ObjectInputStream(FIS)
- 写方法: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);
}
}
}