Java架构师学习笔记—java基础—反射、泛型
一、反射
1.1 什么是反射
Class是反射的核心。在运行期创建对象实例。
1.2 Class文件
- 属性
- 方法(构造方法、静态方法、普通方法)
- 包路径
- 类名
…
1.3 创建Class对象的三种方式
(1)类.class
Class studentClazz = Student.class();
(2)实例.getClass()
Student student = new Student();
Class studentClazz = student.getClass();
(3)Class.forName(“类的全路径”)
Class studentClazz = Class.forName("org.dio.Student");
1.4 通过Class创建对象
(1)不使用反射
Student student = new Student();
(2)使用反射
//获取Class实例
Class studentClazz = Student.class();
//获取构造方法
//无参数构造方法
Constructor<Student> constructor1 = studentClazz.getConstructor();
//有参构造方法
Constructor<Student> constructor2 = studentClazz.getConstructor(String.Class, Integer.Class);
//创建对象
Student student = constructor2 .newInstance("dio",1);
1.5 使用反射获取属性值
(1)getField()。只能获取public的,包括从父类继承来的字段。
//通过Class对象,获得Field对象,是Student类的属性
Field nameField = studentClazz.getField("name");
//操作Field,获得属性值,获取student实例的对应属性
String name = String.valueOf(nameField.get(student));
(2)getDeclaredField()。可以获取本类所有的字段,包括private的,但是不能获取继承来的字段。其要访问private值需要使用setAccessible(true)。
//通过Class对象,获得Field对象,是Student类的属性
Field ageField = studentClazz.getField("age");
//设置属性可操作
ageField.setAccessible(true);
//操作Field,获得属性值,获取student实例的对应属性
int age= String.valueOf(ageField.get(student));
(3)其他通过Field.java获取类信息的方法
1.6 反射实战
通过反射实现一个工具类,可以让一个对象属性相同的值赋值给另一个对象。
package org.dio.javabasic.reflect;
import org.dio.javabasic.user.Student;
import java.lang.reflect.Field;
public class BeanUtils {
private static void convert(Object o1, Object o2) throws Throwable {
//获取class对象
Class clazz1 = o1.getClass();
Class clazz2 = o2.getClass();
//获得fields
Field[] fields1 = clazz1.getDeclaredFields();
Field[] fields2 = clazz2.getDeclaredFields();
//遍历赋值
for(Field field1 : fields1){
for (Field field2: fields2) {
if(field1.getName().equals(field2.getName())){
field1.setAccessible(true);
field2.setAccessible(true);
field2.set(o2, field1.get(o1));
}
}
}
}
public static void main(String[] args) throws Throwable {
Student s1 = new Student(11, "dio", true);
Student s2 = new Student();
BeanUtils.convert(s1, s2);
System.out.println("s1-->s2:"+s1+"--->"+s2);
}
}
二、泛型
2.1 什么是泛型
字面意思:泛指的类型?在编写代码时使用一些以后才指定的类型,在实例化时作为参数指明这些类型,这种就叫做反省,类似于提供一种模板。泛型使得代码简洁,提高程序健壮性,编码期完成,可读性很高。
泛型的本质是类型参数化。允许在定义类、接口、方法时使用类型形参,当使用时指定具体类型。所有使用该泛型的地方都被统一化,保证类型一致。如果未指定具体类型,默认是Object类型。集合体系中的所有类都增加了泛型,泛型也主要用于集合。
2.2 泛型的使用
泛型类、泛型接口、泛型方法、派生子类。
2.3 通配符 ?
2.3.1 < ? extends Class> 上限
只能接收Class类型及其子类,只读不写。
List<? extends Student>可以解释为,包含所有从Student继承的类型,实际上就是没有指定具体的类型,编译器无法理解到底是什么Student类型,所以不接收任何类型的Student。
List<? extends Student> list = new ArrayList<>();
list.add(new Student());//编译不通过,如下图
list.add(null);//编译通过,null可以是指所有类型
2.3.2 < ? super Class> 下限
只能接收Class类型及其父类,只读不写。
List<? super Student>可以解释为,包含Student父类的列表,实际上也没有指定到底是哪个父类,不能保证类型安全,所以就不接收
List<? super Student> list = new ArrayList<>();
list.add(new Student());//编译通过
list.add(new Person());//编译失败,如下图
2.4 类型擦除 & 桥接方法
因为jvm不能识别泛型,所以我们编写的泛型只存在于编码期,编译的时候会将泛型自动转换为Object,这就是类型擦除。
一个子类在继承(或实现)一个父类(或接口)的泛型方法时,在子类中明确指定了泛型类型,那么在编译时编译器会自动生成桥接方法。
Method.isBridge()方法来判断一个方法是否是桥接方法。
// 最初的代码
public class Node<T> {
public T data;
public void setData(T data) {
this.data = data;
}
}
public class MyNode extends Node<Integer> {
public void setData(Integer data) {
....
}
}
// jvm不识别泛型,把泛型擦除掉, 兼容老版本jdk
public class Node {
public Object data;
public void setData(Object data) {
this.data = data;
}
}
public class MyNode extends Node {
// 桥接方法,编译器自动生成
public void setData(Object data) {
setData((Integer)data)
}
public void setData(Integer data) {
....
}
}