java反射
1.反射的理解和好处
2.Class类的理解和特点
3.通过反射获取类中的成员
4.通过反射创建对象
5.通过反射调用类中的成员
6.通过反射观察泛型的本质
1.反射的理解和好处
反射的理解
反射的好处
案例一
张三和李四共同完成一个项目
张三:设计类:Music类(√)、Word类(×)
李四:创建对象,并调用里面的方法
public void method(){
Music m = new Music();
m.work();
Word w = new Word();//编译错误
w.work();
}
//使用反射
public void method(Object o){
Object obj = o.getClass().newInstance();
o.getClass().getMethod("work").invoke();
}
静态加载:编译期加载,也就是说编译时加载所有类,如果该类不存在,则直接报编译错误,所以依赖性太强。
动态加载:(反射)运行期加载。运行时加载用到的类,编译时如果该类不存在,不报编译错误,降低了依赖性
类的加载过程
1、装载:通过类加载器将字节码文件读入到方法区中,并为之开辟Class类的对象
2、链接:将读入到方法区的字节码文件中的二进制数据合并到JRE
3、初始化:将类中的静态成员进行初始化
类加载器
一)类加载器分类
启动类加载器
引导类加载器
扩展类加载器
系统类加载器
用户自定义加载器
二)类加载器加载的机制
先从下往上 ,检测该类是否被加载器加载过。
然后再从上往下,依次判断类加载器是否可以加载该类!
获取类加载器
1、获取系统类加载器
ClassLoader loader = ClassLoader.getSystemClassLoader();
2、获取扩展类加载器
ClassLoader extLoader = loader.getParent();
3、获取引导类加载器
ClassLoader bootLoader = extLoader.getParent();
4、获取某个类所使用的类加载器
ClassLoader cl = 对应类的Class类对象.getClassLoader();
2.Class类的理解和特点 ★
-
问:自定义的类,是一个对象吗?
答:是 -
问:自定义的类,是哪个类型的对象?
答:java.lang.Class类 -
问:Class类的对象如何创建
答:不是new出来的,而是系统自动创建的,我们可以通过一定的方式获取对象的引用
特点
1.Class本身也是一个类
2.Class 对象只能由系统建立对象
3.一个类在 JVM 中只会有一个Class实例
4.一个Class对象对应的是一个加载到JVM中的一个.class文件
5.每个类的实例都会记得自己是由哪个 Class 实例所生成
6.通过Class可以完整地得到一个类中的完整结构
获取Class类对象的三种方式
1、Class.forName(全类名);
2、对象名.getClass()
3、类名.class
3.通过反射获取类中的成员
Class c = String.class;
一、获取类中的属性
Field[] fields = c.getFields();//获取public修饰的所有属性,包含从父类继承来的,不限于直接父类
Field[] fields = c.getDeclaredFields();//获取本类中定义的所有属性,不问修饰符
二、获取类中的方法
Method[] methods = c.getMethods();//获取public修饰的所有方法,包含从父类继承来的,不限于直接父类
Method[] methods = c.getDeclaredMethods();//获取本类中定义的所有方法,不问修饰符
三、获取类中的构造器
Constructor[] cons = c.getDeclaredConstructors();//获取本类中定义的所有构造器,不问修饰符
四、获取类所在的包
Package package = c.getPackage();
五、获取当前类的父类
Class parent = c.getSuperClass();
六、获取当前类的接口
Class[] interfaces = c.getInterfaces();
七、获取当前类带有泛型的父类
Type t = c.getGenericSuperClass();//Type是 Java 编程语言中所有类型的公共高级接口。它们包括原始类型、参数化类型、数组类型、类型变量和基本类型。
ParameterizedType pt = (ParameterizedType)t;
Class c = (Class)(pt.getActualArguments()[0]);//获取泛型列表中的第一个泛型参数
String name = c.getSimpleName();//第一个泛型参数的类型
八、获取当前类带泛型的接口
Type[] ts = c.getGenericInterfaces();
for(Type t:ts){
if(!(t instanceof ParameterizedType))
continue;
ParameterizedType pt = (ParameterizedTypse)t;
Class c = (Class)(pt.getActualArguments()[0]);
String name = c.getSimpleName();
}
九、获取当前类的注解
Annotation[] anns = c.getAnnotations();
for(Annotation a : anns){
System.out.pringln(a.annotationType().getsimpleName())
}
4.通过反射创建对象
方式一:调用无参构造器【推荐使用】
Class c = Class.forName("Student全类名");
Object o = c.newInstance();
方式二:调用有参构造器
Class c = Class.forName("Student全类名");
Constructor con = c.getDeclaredConstructor(String.class);
con.setAccessible(true);
Object o = con.newInstance("john");
5.通过反射调用类中的属性和方法
一、属性
1.获取指定的Field对象
Field field =clazz.getDeclaredField("属性名");
2.创建对象
Object obj = clazz.newInstance();
3.暴力破解
field.setAccessible(true);
4.为属性赋值
field.set(obj,value);
5.读取属性
Object value = field.get(obj);
二、方法
1.获取指定的Method对象
Method method = clazz.getDeclaredMethod(方法名,Class...cs);
2.创建对象
Object obj = clazz.newInstance();
3.暴破
method.setAccessible(true);
4.调用方法
method.invoke(obj,Object...obj);
6.通过反射观察泛型的本质
public class MethodDemo2 {
public static void main(String[] args) {
ArrayList<Integer> list1 = new ArrayList<Integer>();
ArrayList<String> list2 = new ArrayList<String>();
Class c1 = list1.getClass();
Class c2 = list2.getClass();
System.out.println(c1 == c2);
}
}
因为list1和list2属于两个不同的对象,由此我们推断c1和c2也是不相等的两个类类型,但实际上结果输出true;因为反射将类的字节码文件加载内存,相当于字节码的执行阶段,那么c1和c2肯定属于执行阶段的比较,所以,编译之后集合的泛型是去泛型化的,所有的集合类的类类型都相等,泛型就不存在了,泛型只是在编译的时候约束元素的类型,只在编译阶段有效,所以我们可以利用反射的原理,绕过编译,让list1也可以存放不同类型的元素:
try {
System.out.println(list1.size());
Method method = c1.getMethod("add", Object.class);
method.invoke(list1, "12234");
System.out.println(list1.size());
} catch (NoSuchMethodException | SecurityException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}