《疯狂java讲义》读书笔记(六):Annotation和输入输出

《疯狂java讲义》读书笔记(六):Annotation(注释)和输入/输出

(一)Annotation(注释)

​ 5个基本的Annotation如下:

  • @Override:用来指定方法覆载的,可以强制一个子类必须覆盖父类的方法。不过只能修饰方法。
  • @Deprecated:标记已过时,比如说一个类或者元素过时了。
  • @SuppressWarings:抑制编译器警告。使用该注解修饰某个类取消显示某个编译器经过,同时又修饰该类里的某个方法取消显示另一个编译器警告,那么该方法将会同时取消显示另一个编译器警告,那么该方法将会同时取消显示这两个编译器警告。
  • @SafeVarargs:开发者不希望看见堆污染的警告,可以采用该注解修饰引发该警告的方法或者构造器,抑制堆污染警告还可以使用@SuppressWarings(“unchecked”)修饰。
  • @FunctionalInterface:如果接口中只有一个抽象方法,那么该接口就是函数式接口,该注解就是用来指定某个接口必须是函数式接口的。

(二)输入/输出

1.File类

​ 不管是文件还是目录都是用File来操作,File能新建、删除、重命名文件和目录,File不能访问文件内容本身,如果需要访问文件内容本身,则需要使用输入/输出流。在目录操作相关的方法中有一个String[] list(),用来列出File对象的所有子文件名和路径名,返回String数组,该方法可以接受一个FilenameFilter参数,通过该参数可以只列出符合条件的文件,相当于是个文件过滤器。 FilenameFilter接口也是一个函数式接口,可以用lambda表达式创建实现该接口的对象。

2.理解Java的IO流

​ 从不同角度可以有多种分法。比如说输入流和输出流;字节流和字符流;节点流和处理流。

​ 输入流:只能从中读取数据,而不能向其写入数据。

​ 输出流:只能向其写入数据,而不能从中读取数据。

​ 由InputStream和Reader作为基类,而输出流则主要由OutputStream和Writer作为基类,不过都是抽象基类,没法直接创建实例。

​ 字节流和字符流的用法几乎相同,后面如果涉及到一些方法会省略描述,自行查阅API即可。字节流操作的数据单元是8位的字节,而字符流操作的数据单元是16位的字符。字节流主要是由InputStream和OutputStream作为基类,字符流是Reader和Writer作为基类。

​ 节点流是指可以从一个特定的IO设备读/写数据的流,处理流用于对一个已存在的流进行连接或封装,通过封装后的流来实现数据读/写功能。

3.对象序列化

​ 对象序列化的目标是将对象保存到磁盘里,或者允许在网络中直接传输对象,对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种二进制流持久地保存在磁盘上,通过网络将这种二进制流传输到另一个网络节点。其它程序一旦获得了这种二进制流,都可以将这种二进制流恢复成原来的Java对象。也就是说对象的序列化时将一个Java对象写入IO流,反序列化是从IO中恢复该Java对象。

​ 为了让某个类可序列化,那必须得实现Seralizable或者Externalizable接口之一。不过Java的很多类都已经是实现了Serializable,该接口是一个标记接口,实现该接口无须实现任何方法,它只是表明这个类的实例是可序列化的。

​ 下面演示以下序列化和反序列化的过程。

//新建一个Person类实现Serializble接口
public class Person implements Serializable {
	private String name;
	private int age;

	public Person(String name,int age){
		System.out.println("有参构造器");
		this.name=name;
		this.age=age;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public int getAge() {
		return age;
	}

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

下面的程序是将一个Person对象写入磁盘文件,运行之后会发现生成了一个object.txt,文件内容就是 Person对象。

//序列化
public class WriteObject {
	public static void main(String[] args) {
		try {
			ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("object.txt"));
			Person per=new Person("王二小",15);
			oos.writeObject(per);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

readObject()方法是用来读取流中的对象,运行的时候我们可以发现程序没有调用构造器,这说明反序列化机制不需要通过构造器来初始化Java对象。

//反序列化
public class ReadObject {
	public static void main(String[] args) {
		try {
			ObjectInputStream ois=new ObjectInputStream(new FileInputStream("object.txt"));
			Person p=(Person)ois.readObject();
			System.out.println("name:"+p.getName()+" age:"+p.getAge());
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
}

//运行结果
//name:王二小 age:15

​ 如果Person类中出现了一个引用类型,那么序列化会怎么样的。毋庸置疑,这个引用类型必须是可序列化的才可以,否则这个Person类不能序列化。

​ 下面再举一个例子,新建一个Teacher类。注意该类持有一个Student对象的引用,这里不新建Student类了,实际上和Person类是一样的,除了名字不一样。

public class Teacher implements Serializable {
	private String name;
	private Student student;

	public Teacher(String name,Student student){
		this.name=name;
		this.student=student;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Student getStudent() {
		return student;
	}

	public void setStudent(Student student) {
		this.student = student;
	}
}

​ 如果我想实现下面这三行代码,那么想一想,如果先序列化t1,那么系统会把t1和Student对象一起序列化, 如果在序列化t2,Student对象会在序列化一遍,如果程序在显示序列化stu,那么又得在序列化亿遍Student对象,貌似会产生3个Student对象。

Student stu=new Student("孙悟空",500);
Teacher t1=new Teacher("唐僧",stu);
Teacher t2=new Teacher("菩提祖师",stu);

​ 如果再反序列化的时候,那么还是会得到三个Student对象,就会导致t1和t2所引用的Student对象不是一个,显然这就错了。所以Java序列化机制有一种特殊的序列化算法:、

所有保存到磁盘的对象都有一个序列化编号。
当程序试图序列化一个对象的时候,会先检车该对象是否被序列化过,从未被序列化过,就会将该对象转换成字节序列然后输出。
如果某个对象序列化过,将只是直接输出一个系列化编号,而不是再次重新序列化该对象。

​ 也就是只有第一次调用writeObject()方法的时候才会将对象转换成字节序列并输出。

​ 下面来序列化看看。实际上只序列化了三个对象,t1、t2和stu。

public class WriteTeacher {

	public static void main(String[] args) {
		try {
			ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("teacher.txt"));
			Student stu=new Student("孙悟空",500);
			Teacher t1=new Teacher("唐僧",stu);
			Teacher t2=new Teacher("菩提祖师",stu);
			oos.writeObject(t1);
			oos.writeObject(t2);
			oos.writeObject(stu);
			oos.writeObject(t2);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

​ 反序列化验证。

public class WriteTeacher {

	public static void main(String[] args) {
		try {
			ObjectInputStream ois=new ObjectInputStream(new FileInputStream("teacher.txt"));
			Teacher t1=(Teacher)ois.readObject();
			Teacher t2=(Teacher)ois.readObject();
			Student stu=(Student)ois.readObject();
			Teacher t3=(Teacher)ois.readObject();
			System.out.println("t1的student引用和stu相同嘛:"+(t1.getStudent()==stu));
			System.out.println("t2的student引用和stu相同嘛:"+(t2.getStudent()==stu));
			System.out.println("t2和t3是否是同一个对象:"+(t2==t3));
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}


/**输出结果
 * t1的student引用和stu相同嘛:true
 * t2的student引用和stu相同嘛:true
 * t2和t3是否是同一个对象:true
 */

​ 多次序列化同一个Java对象,只有第一次序列化时才会把该Java对象转换成字节序列并输出,这样可能引起一个潜在的问题,就是当程序序列化一个可变对象的时候,只有第一次使用writeObject()方法输出时才会将该对象转换成字节序列并输出,当程序再次调用writeObject()方法,程序只是输出序列化编号,即使后面该对象的实例变量值改变了,改变的值也不会被输出。

​ 这个输出结果应该可以说明问题了,输出还是王二小,而不是李四。

public class Mutable implements Serializable {
	public static void main(String[] args) {
		try {
			ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("mutable.txt"));
			ObjectInputStream ois=new ObjectInputStream(new FileInputStream("mutable.txt"));
			Person per=new Person("王二小",15);
			oos.writeObject(per);
			per.setName("李四");
			oos.writeObject(per);
			Person p1=(Person)ois.readObject();
			Person p2=(Person)ois.readObject();
			System.out.println(p1==p2);
			System.out.println(p2.getName());
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
}

/*
输出结果
有参构造器
true
王二小
 */
发布了58 篇原创文章 · 获赞 5 · 访问量 6271

猜你喜欢

转载自blog.csdn.net/weixin_40992982/article/details/103991235