一些不知道该总结到哪里的 Java 零碎知识点。
泛型
泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。比如我们要写一个排序方法,能够对整型数组、字符串数组甚至其他任何类型的数组进行排序,我们就可以使用 Java 泛型。
-
泛型类和泛型方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18class Test<T>{
public void show(T t){
System.out.println(t);
}
public void print(T t){
System.out.println(t);
}
}
// 使用
public static void main(String[] args) {
Test<String> d = new Test<String>();
d.show("java");
d.print("Object-C");
Test<Integer> e = new Test<Integer>();
e.show(2);
e.print(new Integer(5));
}泛型类定义的类型,在整个类中有效,如果被方法使用,那么泛型类的对象明确要操作的具体类型后,所有操作的类型就已经固定了。例如上面的 d 只能操作 String。定义到方法上就可以解决这个问题。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19class Test{
public <T> void show(T t){
System.out.println(t);
}
public <T> void print(T t){
System.out.println(t);
}
public <U,T> void sum(U u,T t){
System.out.println(u+" version is "+t);
}
}
// 使用
public static void main(String[] args) {
Test d = new Test();
d.show("java");
d.print(5);
d.sum("java", new Double(8));
} -
泛型方法的好处
- 泛型方法可以让不同方法操作不同类型,且类型还不确定。
- 与泛型类不同,泛型方法的类型参数只能在它锁修饰的泛型方法中使用。
-
创建一个泛型方法
1
2访问修饰符 [static][final] <类型参数列表> 返回值类型 方法名([形式参数列表])
// [] 表示可有可无 -
泛型接口
1
interface 接口名<类型参数表>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24interface showInterface<T>{
public void show(T t);
}
// 实现类型确定
class ShowClass implements showInterface<String>{
public void show(String t){
System.out.println("show:"+t);
}
}
// 实现类型不确定
class ShowClass<T> implements showInterface<T>{
public void show(T t){
System.out.println("show:"+t);
}
}
public static void main(String[] args) {
ShowClass<Integer> obj = new ShowClass<Integer>();
obj.show(6);
/*
ShowClass obj = new ShowClass();
obj.show("java");
*/
} -
泛型通配符
1
2
3
4
5
6
7
8
9
10
11public static void main(String[] args) {
ArrayList<String> a1 = new ArrayList<String>();
a1.add("a");
a1.add("b");
a1.add("c");
ArrayList<Integer> a2 = new ArrayList<Integer>();
a2.add(1);
a2.add(2);
a2.add(3);
}进一步修改,定义一个泛型方法:
1
2
3
4
5
6
7public static <T>void vistor(ArrayList<T> a){
Iterator<T> iterator = a.iterator();
while(iterator.hasNext()){
T t = iterator.next();
System.out.println(t);
}
}如果是泛型类,是不允许泛型定义在 static 上面的。
1
2
3
4
5
6//占位符,也称为通配符。表示元素类型可以匹配任意类型
public static void sop(ArrayList<?> a){
for(Iterator<?> it = a.iterator();it.hasNext();){
System.out.println(it.next());
}
}-
类型通配符上限定(? extends T),可以接受 T 和 T 的子类
1
2
3
4
5
6public static void printMethod(ArrayList<? extends Person> a1){
Iterator<? extends Person> it = a1.iterator();
while(it.hasNext()){
System.out.println(it.next().getNmae());
}
} -
类型通配符下限定(? super T),接受 T 类型或者 T 的父类型
-
序列化
-
保存(持久化)对象及其状态到内存或者硬盘
-
序列化以字节数组保存,静态成员不保存。对象序列化保存的是对象的”状态”,即它的成员变量。由此可知,对象序列化不会关注类中的静态变量。
-
要想将父类对象也序列化,就要让父类也实现 Serializable 接口。
实现方式
- Serializable 实现序列化
- ObjectOutputStream 和 ObjectInputStream 对对象进行序列化和反序列化
- 在类中增加 writeObject 和 readObject 方法实现自定义序列化策略
阻止序列化
- 在变量声明前加上 Transient 关键字,可以阻止该变量被序列化到文件中,在被反序列化后,transient 变量的值被设为初始值,如 int 型的是 0,对象型的是 null。
- 服务器端给客户端发送序列化对象数据,对象中有一些数据是敏感的,比如密码字符串等,希望对该密码字段在序列化时,进行加密,而客户端如果拥有解密的密钥,只有在客户端进行反序列化时,才可以对密码进行读取,这样可以一定程度保证序列化对象的数据安全。
深拷贝和浅拷贝
直接赋值复制
A a1 = a2;
这实际上复制的是引用,也就是说 a1 和 a2 指向的是同一个对象,所以 a1 变化的时候,a2 实际也在发生变化。
浅拷贝
创建一个新对象,然后将当前对象的非静态字段复制到该新对象,如果字段是值类型的,那么对该字段执行复制;如果该字段是引用类型的话,则复制引用但不复制引用的对象。因此,原始对象及其副本引用同一个对象。
深拷贝
深拷贝不仅复制对象本身,而且复制对象包含的引用指向的所有对象。
序列化
在 Java 语言里深复制一个对象,常常可以先使对象实现 Serializable 接口,然后把对象(实际上只是对象的一个拷贝)写到一个流里,再从流里读出来,便可以重建对象。