反射
1. 反射概述
-
Reflection
被视为 动态语言,反射机制允许程序在执行期间借助于Reflection API 获取任何类的内部信息,并且 可以直接操作任意对象的属性和方法。 -
加载完类之后,在堆内存的方法区中包含一个Class类型的对象(一个类只有一个对象),这个对象包含完整的类的结构信息
反射:实例化对象 -->getClass()–>得到完整的类
-
框架=反射+注解+设计模式
-
java反射应用
- 在运行时 判断任意一个对象所属的类
- 在运行时 构造任意一个类的对象
- 在运行时 判断任意一个类具有的成员变量方法
- 在运行时 获得泛型信息
- 在运行时 调用任意一个对象的属性 和方法
- 在运行时 处理注解
- 生成动态代理
-
反射相关的主要API
- java.lang.Class:代表一个类
- java.lang.reflect.Method:代表类的方法
- java.lang.reflect.Field:成员变量
- java.lang.reflect.Constructor:类的构造器
2. Class类 并获取Class实例
Class clazz=Person.class;
//1.通过反射创建对象
Constructor con=clazz.getContructor(String.class,int.class);//获得构造方法
Object obj=con.netInstance("Tom",12);//获得实例
Person p=(Person)obj;
//2.通过反射可以调用属性 和方法
Field age=clazz.getDeclaredField("age");//属性
age.set(10);//给属性设置值
//3.调用方法
Method show=clazz.getDeclaredMethod("show");//第一个写方法名 后面是可变参数
show.invoke(p);//方法的调用
- 一搬方式通过new创建对象 在外部不能对象调用其内部的私有结构
- 但是 通过反射 可以调用私有属性 方法
Class clazz=Person.class;
//通过·反射 调用 私有结构 :私有构造器 方法 属性
// 1.私有构造器
Constructor cons=clazz.getDeclaredConstuctor(String.class);
cons.setAccessible(true);
Person p=(Person)cons.newInstance("Tom");//private Person(String name){this.name=name};
//2.私有属性
Field name=clazz.getDeclaredField("name");
name.setAccessible(true);// private String name;
name.set(p,"Lihua");
//3.私有方法
Method showNation =clazz.getDeclaredMethod("showNation",String.class);//
showNation.setAccessible(true);
String nation=(String)showNation.invoke(p,"China");//private String showNation(String nation) showNation的返回值就是showNation.invoke(p,"China")的返回值
3. 类加载与ClassLoader
类加载过程
- javac.exe编译生成.class文件
- java.exe 命令对某个字节码文件进行解释运行 将字节码文件加载到内存中 (类加载)
- 加载到内存中的类 称为运行时类 此运行时类 就作为Class的一个实例
ClassLoader的作用
将class文件字节码内容加载到内存中,并将这些数据静态数据转成方法区的运行时数据,然后在堆中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口
类缓存
标准的javase类加载器可以按照要求查找类,一旦某个类加载到类加载器中,它将维持加载(缓存)一段时间,不过JVM垃圾回收可以回收这些class对象
ClassLoader
-
类加载器的作用:把类装载到内存。
BootStrap ClassLoader<–Extension ClassLoader <—System ClassLoader(app) <—自定义类加载器
可以查看具体的双亲委派介绍
超好的双亲委派
4. 创建运行时类的对象
获取Class的四种方式
//1.调用运行时类的属性:.calss
Class<Person> clazz1=Person.class;//Class clazz=Person.class;
//2.通过运行时的类的对象
Person p=new Person();
Class clazz2=p1.getClass();
//3.调用Class的静态方法 forName(String classPath)
Class clazz3=Class.forName("com.test.reflect.Person");//类的全路径 要throws ClassNotFounfException
//加载到内存中的运行时类 会缓存一段时间 在此期间之间 可以通过不同的方式来获取这个缓存中的实例 唯一存在的
//4.使用类的加载器
ClassLoader classLoader=ReflectionTest.class.getClassLoader();//获取当前运行时 类的类加载器
Class clazz4=classLoader.loadClass("com.test.Person");//加载类
有哪些类型可以有Class对象
- class:外部类 成员(成员内部类 静态内部类),局部内部类,匿名内部类
- 接口
- 数组 只要数组的类型 维度一样 就是同一个Class
- 枚举
- 注解
- 基本数据类型
- void
5. 获取运行时类的完整结构
- 获取运行时类的属性结构
Class clazz1=Person.class;
//获得属性结构
Field[] fields=clazz1.getFields(); //获取当前运行是 类 及其父类中声明为public的访问权限
//getDecalredFields()获取当前运行时类中所有的属性 不包含父类中声明的属性
Field[] declaredFields=clazz1.getDecalredFields();
for(Field f:declaredFields){
//1.获取权限修饰符
int i=f.getModifiers();//返回的是 Modifier中 权限修饰符的int整数
String type=Modifier.toString(i);
//2.数据类型
Class type=f.getType();//获取数据的类型
//3.变量名
String name=f.getName();
}
- 获取运行时类的方法结构
Class clazz=Person.class;
//getMethods 获取当前运行时类 及其父类public声明的方法
Method[] methods=clazz.getMethods();
//getDeclaredMethods获取当前运行时类声明的所有方法 不包含父类的
Method[] declaredMethods=clazz.getDeclaredMethods();
for(Method m:declaredMethods){
//获取注解
Annotation[] annotations=m.getAnnotations();
//权限修饰符 方法名 方法返回值 。。。。
String quanxian=Modifier.toString(m.getModifiers());
//返回值类型
String returnType=m.getReturnType().getName();
//方法名
String methodName=m.getName();
//形参列表
//m.getParameters();
//抛出的异常 m.getExceptionsTypes()
//.....
}
还有很多获取运行时类的内部结构 ,不得不说 ,真的很强大,把类一个一个的掰开。。。。。。