java反射
java反射是指是在运行状态中,对于任意一个类都可以通过它的Class字节码文件得到对应的的Class类型的对象,从而获取到该类的所有内容,包括所有的属性与方法。当然也可以调用到它的所有内容。
那么这个字节码文件是怎么来的呢?我们在对java文件进行编译时会执行javac XXX.java,这个时候就会产生一个xxx.class文件,此即对应的字节码文件。
是如何通过Class字节码文件得到对应的的Class类型的对象的呢?类加载器。
类加载器是什么呢?
类加载器负责将class文件加载到内存中,并为之生成对应的Class对象。在java中类加载器把一个类装入JVM,需要以下几个步骤:
1.装载:查找和导入Class文件
2.链接:执行校验、准备、解析步骤,其中解析步骤是可以选择的:
a)校验:是否有正确的内部结构,并和其他类协调一致
b)准备:负责为类的静态成员分配内存,并设置默认初始化值
c)解析:将类的二进制数据中的符号引用替换为直接引用
3.初始化:对类的静态变量、静态代码执行初始化工作
类加载器的组成:
bootstrap ClassLoader 根类加载器 (负责将存话在<JAVA_HOME>\lib目录中的,或者被-Xbootclasspath参数所指定的路径中的,并且是虚拟机识别的(仅按照文件名识别,如rt.jar,名字不符合的类库即使放在lib目录中也不会被加载)类库加载到虚拟机内存了中。开发者不能直接引用启动类加载器)
extension ClassLoader 扩展类加载器(这个加载器由sun.misc.Launcher$ExtClassLoader实现,它负责加载<JAVA_HOME>\lib\ext目录中的,或者被java.ext.dirs系统变量所指定的路径中的所有类库,开发者可以直接使用扩展类加载器)
Application ClassLoader 应用程序类加载器(这个类加载器由sun.misc.Lanucher$AppClassLoader实现。由于这个类加载器是ClassLoader中的getSystemClassLoader()方法的返回值,所以一般也称它为系统类加载器。它负责加载用户类路径上所指定的类库)
这三种加载器是父子关系,加载的方式为双亲委派模型(parents delegation model)。即如果一个加载器收到了类加载的请求,首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,以此类推,只有当代加载器反馈自己无法完成这个加载请求时,子加载器才会尝试自己去加载。
扯远了
说回反射。现在我们的类加载器从字节码文件中获取到的Class对象,共有以下三种方式获取
1.Class clazz = new A().getClass()
2.Class clazz = A.class;
3.Class clazz = Class.forName(<类的全路径名,如cn.zzy.demo.A>)
开发中建议用方式三,因为类的全路径名可以写在配置文件里,这样很灵活。
那么我们如何使用反射呢?代码如下
package ReflectDemo; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; /** * Created by zuzhaoyue on 2019/2/13. */ public class ReflectTest { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException { Class clazz = Class.forName("model.Person"); ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); Class clazz4 = classLoader.loadClass("model.Person"); System.out.println(clazz); System.out.println(clazz4); //得到所有的构造方法 /*Constructor[] constructors = clazz.getDeclaredConstructors(); for(Constructor constructor:constructors){ System.out.println("构造方法:"+constructor); A a = (A) constructor.newInstance("aa",11); System.out.println("A a:"+a.getAge()); }*/ //得到单个带参构造方法,不带参的类似 Constructor constructor = clazz.getConstructor(String.class,int.class);//返回构造方法对象,通过Class对象使用其中的成员变量对象 System.out.println("带参构造方法"+constructor); Object p1 = constructor.newInstance("zzy",11); System.out.println(p1.toString()); //访问私有的构造方法 Constructor privateConstructor = clazz.getDeclaredConstructor(); System.out.println("私有构造方法:" + privateConstructor); privateConstructor.setAccessible(true);//暴力访问 Object p2 = privateConstructor.newInstance(); System.out.println(p2.toString()); /*//获取成员变量 Field[] fields = clazz.getDeclaredFields();//获取所有的成员变量 for(Field field:fields){ System.out.println("成员变量:" + field); }*/ //给成员变量赋值 Field name = clazz.getDeclaredField("name"); name.setAccessible(true);//因为是私有的,所以又来暴力访问了 name.set(p2,"aaaaa");//给p2的name属性赋值 System.out.println(p2.toString()); Field age = clazz.getDeclaredField("age"); age.setAccessible(true); age.set(p2,13); System.out.println(p2.toString()); //method的调用 // Method[] methods = clazz.getMethods();//获取自己的及父亲的方法 Method[] methods = clazz.getDeclaredMethods();//获取自己的方法,不包括父亲的 for(Method method:methods){ String name1 = method.getName(); Class[] arg = method.getParameterTypes(); System.out.println("方法名:" + name1); for(Class a: arg){ System.out.println("参数类型:" + a); } } Method method = clazz.getDeclaredMethod("say");//获取一个方法 method.invoke(p2);//调用 Method method1 = clazz.getMethod("say2",String.class); method1.invoke(p2,"祖祖侠"); Method method2 = clazz.getMethod("say3",String.class); Object object = method2.invoke(p2,"mm"); System.out.println("返回值:" + object); } }
package model; /** * Created by zuzhaoyue on 2019/2/14. */ public class Person { private String name; private int age; private Person() { } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } public Person(String name, int age) { this.name = name; this.age = age; } public void say(){ System.out.println("大家好,我是" + this.name + ",今年" + age + "岁!"); } public void say2(String friend){ System.out.println("大家好,我是" + this.name + ",今年" + age + "岁!" + "我最好的朋友是" + friend); } public String say3(String enName){ System.out.println("英文名是"+enName+"....瞎说一气,重点是返回值。"); return "hello world"; } }
方法具体怎么使用不是那么重要,api文档里都能找到,关键是理解反射是从字节码文件获取到Class对象,然后获取到相应的Method,Field,Constructor对象,与new Person().say()等使用是反着来的,就可以了。