目录
1、内部类
- 内部类可以使用外部类的所有方法和变量,即使是私有的。
- 内部类的实例一定会绑在外部类的实例上。
- 内部类的作用:让一个类(外部类)可以实现同一个接口的多次。或者让一个类可以继承父类多次。-------这样做的目的:就是为了让一个类可以保持接口的多种不同状态。
2、序列化
如果我们在一些应用中,需要保存我们某个对象到本地的话,可以使用序列化的形式来存储。
- 被序列化的对象保存了自身实例变量的值,因此在我们写入文件之中之后,再被取回来的时候,可以转换为我们原来的对象。
- 当对象被序列化时,被该对象引用的实例变量也会被序列化,所以如果它的实例变量中有类对象,那么这个类也会被序列化。
- Serializable接口又被称为marker或tag类的标记用接口,因为此接口并没有任何方法需要实现。它的唯一目的就是声明实现它的类可以被序列化。
- 如果父类可以被序列化,那么它的子类也可以被序列化(不需要明确实现Serializable接口)。
- 静态变量不会被序列化,因为所有的对象都会共享同一份静态变量值。
- 更多属性在下面的代码zho
public class Test_Serializable {
static class Student implements Serializable {
static final long serialVersionUID=23333;
String name;
int age;
/**
* 1、Student内所有类对象都应该可以被序列化,否则Student将序列化失败,并报错。
* 2、如果两个Student对象中对Person的引用指向同一个对象,那么这个Person只会被存储
* 一次,另一个Student被复原以后,会拥有该对象的指向。
*/
Person p = new Person();
/**
* 1、当然如果你不需要对该对象进行序列化的话,可以用transient标记,这样即使Teacher
* 没有实现Serializable接口,Student也能序列化成功。
*/
transient Teacher t=new Teacher();
Student(String name, int age) {
this.name = name;
this.age = age;
}
}
/**
* 1、因为Student持有对Person对象的引用,所以Person类也必须实现Serializable接口,否则
* Student对象将会序列化失败。
*/
static class Person implements Serializable{
String name;
}
static class Teacher {
String name;
}
/**
* 1、FileOutputStream是写入字节的方法,所以我们需要先把我们的对象转换成字节,
* ObjectOutputStream就是将我们的对象转换成可以写入的串流数据。
*/
private static void testWrite() throws IOException {
Student s = new Student("张三", 22);
Student s1 = new Student("李四", 12);
// 创建存取文件的FileOutputStream对象
FileOutputStream fileStream = new FileOutputStream("Student.ser");
// 创建写入对象的ObjectOutputStream对象
ObjectOutputStream os = new ObjectOutputStream(fileStream);
os.writeObject(s);
os.writeObject(s1);
os.close();
}
public static void main(String[] args) {
try {
testWrite();
testRead();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
3、serialVersionUID
当对象被序列化的同时,该对象会被添加一个类的版本识别ID。这个ID被成为serialVersionUID,它是根据类的结构信息计算出来的。
在对象被解序列化时,如果在对象被序列化之后有了不同的serialVersionUID,则还原操作会失败。
如果你认为类会被演化(序列化存储之后,修改该类),可以把serialVersionUID放在类中,就比如上面的 Student类中。
4、解序列化
* 解序列化过程: * 1、对象从stream流中读取出来; * 2、Java虚拟机通过存储信息判断出对象的class类型; * 3、Java虚拟机尝试寻找和加载对象的类。如果Java虚拟机找不到或无法加载该类, * 则Java虚拟机会抛出异常ClassNotFoundException; * 4、新的对象会被配置在堆上,但是构造函数不会被执行!执行会抹除我们对象的信息。 * 5、如果对象在继承树上有一个不可序列化的祖先类,则该不可序列化类以及在它之上 * 的类的构造函数(就算是可序列化也会执行)就会执行。一旦构造函数连锁启动之后 * 将无法停止。也就是说,从第一个不可序列化的父类开始,全部都会重新初始状态。 * 6、对象的实例变量会被还原成序列化之前的状态值。transient的变量会被赋予 * 默认值。
/**
* 解序列化过程:
* 1、对象从stream流中读取出来;
* 2、Java虚拟机通过存储信息判断出对象的class类型;
* 3、Java虚拟机尝试寻找和加载对象的类。如果Java虚拟机找不到或无法加载该类,
* 则Java虚拟机会抛出异常ClassNotFoundException;
* 4、新的对象会被配置在堆上,但是构造函数不会被执行!执行会抹除我们对象的信息。
* 5、如果对象在继承树上有一个不可序列化的祖先类,则该不可序列化类以及在它之上
* 的类的构造函数(就算是可序列化也会执行)就会执行。一旦构造函数连锁启动之后
* 将无法停止。也就是说,从第一个不可序列化的父类开始,全部都会重新初始状态。
* 6、对象的实例变量会被还原成序列化之前的状态值。transient的变量会被赋予
* 默认值。
*/
private static void testRead() throws IOException, ClassNotFoundException {
FileInputStream fileStream=new FileInputStream("Student.ser");
ObjectInputStream os=new ObjectInputStream(fileStream);
// 每次调用readObject都会都会从stream流中读取下一个对象,读取顺序与写入顺序相同,次数超出会抛出异常
Object o1=os.readObject();
Object o2=os.readObject();
os.close();
Student s1= (Student) o1;
Student s2= (Student) o2;
System.out.println("Student1.name="+s1.name);
System.out.println("Student2.name="+s2.name);
}
5、文本存储
public class Test_Write_String {
private static void testWrite() {
String s = "我想要存储在本地my.txt文件中\n";
try {
// false参数可以每次清空my.txt文件内的内容,重新写入语句
FileWriter writer = new FileWriter("my.txt", false);
writer.write(s);
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 1、BufferedWriter可以将我们要写入磁盘的文本先存储到BufferedWriter内部,缓存起来,当缓存空间占满之后,
* 开始往本地磁盘写入,这样可以减少我们访问本地磁盘的次数,加快写入的效率。
* 2、当然,如果我们向立即将BufferedWriter缓存内部的文本写入磁盘的话,可以使用 buffer.flush();
*/
private static void testBufferWrite() {
String s = "我先交由BufferedWriter存储,当它存满以后,把我写入到本地磁盘\n";
String s1 = "我基础写入缓存中1\n";
String s2 = "我基础写入缓存中2\n";
String s3 = "我基础写入缓存中3\n";
try {
// true 可以在不删除前面内容的情况下,继续写入内容
BufferedWriter buffer = new BufferedWriter(new FileWriter("my.txt", true));
buffer.write(s);
buffer.write(s1);
buffer.write(s2);
buffer.write(s3);
buffer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 1、利用BufferedReader可以提高我们的读取效率,它只有在读取BufferedReader的缓存没有任何内容的时候,
* 才会去读取磁盘。
*/
private static void testReader() {
try {
File file = new File("my.txt");
FileReader reader = new FileReader(file);
BufferedReader buffer = new BufferedReader(reader);
String result;
while ((result = buffer.readLine()) != null) {
System.out.println("\n输出:" + result);
}
buffer.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* File对象代表磁盘上的文件,或者目录的路径名称;
* 它不能读取或者代表文件中的数据。
*/
private static void testFile() {
File dir = new File("test.txt");
boolean isMk = dir.mkdir();
System.out.println("mkdir=" + isMk);
System.out.println("绝对目录=" + dir.getAbsolutePath());
if (dir.isDirectory()) {
String[] list = dir.list();
for (String s : list) {
System.out.println("目录:" + s);
}
}
}
public static void main(String[] args) {
testWrite();
testFile();
testBufferWrite();
testReader();
}
}