Java反射机制
一切的操作都将使用Object完成,类,数组的引用都可以使用Object来接收
1,认识Class类
以前要是想知道一个类中的属性,成员变量,方法等等的信息的话,需要通过new这个类的对象才能得到这些信息,但是在Java中,也允许一个实例化对象找到一个类的完整的信息,这个类就是Class类,Class类是继承Object类的.
正常方式: 引入需要的"包.类"名称 -->通过new实例化-->取得实例化对象
反射方式: 实例化对象-->getClass()方法(从Object类中继承而来),这里是得到Class类对象-->得到完整的"包.类"名称
getClass()这个方法的返回值是Class类,实际上该类是Java反射的源头
在Class类中没有定义构造方法,那么得到Class类对象的方法有3种:
(1)使用forName()的静态方法实例化对象 (2) 类.class (3)对象.getClass()
package cn.reflect; class Demo{ } public class RefDemo01 { public static void main(String[] args) throws ClassNotFoundException { Class c1 = Class.forName("cn.reflect.Demo"); Class c2 = Demo.class; Class c3 = new Demo().getClass(); System.out.println("类名称:"+c1.getName()); System.out.println("类名称:"+c2); System.out.println("类名称:"+c3); } }
3种实例化Class对象的方式是一样的,但是forName()这个方式比较常用,它只需要传入类所在的位置的名称的字符串参数就可以了,这样使程序具有很大的灵活性.
2,Class类的使用
2.1通过无参构造函数实例化对象
Class类在开发中最常用的是,利用forName这个方法,传入完整的包.类的路径的字符串形式,来实例化一个类的对象
想要通过Class本身来实例化其他类的对象,则可以使用Class类中的newInstance()方法来完成,但是要保证被实例化的类中存在这一个无参的构造方法
(1)forName传入需要实例化的类的完整包.类名称
(2)使用newInstance()方法,是实例化需要被实例化的对象,需要强转
package cn.reflect; class Person{ private String name; private int age; public Person(){ }//被实例化的对象必须存在无参构造方法 public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } } public class RefInstance { public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException { Class clazz = Class.forName("cn.reflect.Person"); Person p = (Person)clazz.newInstance(); p.setName("wjd"); p.setAge(25); System.out.println("name:"+p.getName()+" age:"+p.getAge());//name:wjd age:25 } }
上述代码中,Person类并没有new一个新的对象,而是通过Class类,来完成了对Person类中的属性的访问.一般做在使用Class类来实例化对象时,一定要在类中编写无参的构造方法
2.2调用有参构造实例化对象
(1)通过Class类中的getConstructors()取得本类中的所有的构造方法
(2)向构造方法中传递一个对象数组中去,里面包含了构造方法是所需的各个参数
(3)通过Constructor实例化对象
package cn.reflect; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; class Worker{ private String name; private int age; public Worker(String name,int age){ this.setName(name); this.setAge(age); } public void setName(String name){ this.name = name; } public String getName(){ return name; } public void setAge(int age){ this.age = age; } public int getAge(){ return age; } } public class InstanceDemo01 { public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { Class c = Class.forName("cn.reflect.Worker"); Constructor[] con = c.getConstructors(); Worker worker = (Worker)con[0].newInstance("吴杰栋",25);//Worker中构造方法中有参数的,这里实例化的时候传参数,注意顺序,只有一个构造函数,所以数组中的0就可以 System.out.println("age:"+worker.getAge() +" name:"+ worker.getName()); } }
在上述代码中,newInstance()方法是Constructor类中的, 查看API可以发现这个方法中传入的参数都是对象,25其实是Integer对象(在使用是进行了自动拆箱)
在java类中是有可能存在多个构造函数的,它们只是构造函数中的参数类型不同而已,在API中,查阅到Class类中newIntance()这个类只能获取到带有无参构造函数的对象,那么带有参数的构造函数的时候,我们该如何获取该类的对象呢?
(1),构造函数没有参数的时候,产生该类对象,直接通过Class类中的newIntance()来获取
(2).构造函数有参数的时候,通过Class类中getConstructor()方法,在方法中传入构造函数参数类型的class文件,返回值是一个Constructor对象,查阅API发现在Constructor这个类中,也有一个带这参数类型的newIntance的方法,这样就能获取到带有参数类型的构造函数的类的对象了
package cn.reflect; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; //拿Person类中的构造函数 public class ReflectDemo2 { public static void main(String[] args) throws Exception { //createNewObject(); createNewObject_2(); } public static void createNewObject_2() throws NoSuchMethodException, SecurityException, ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { String name = "cn.bean.Person"; Class<?> clazz = Class.forName(name); //获取到了Person中带有参数类型的构造方法 Constructor<?> con = clazz.getConstructor(int.class,String.class); //这里通过constructor类中的方法来初始化类 Object obj = con.newInstance(23,"wjd"); } public static void createNewObject() throws Exception{ String name = "cn.bean.Person"; //找寻该文件的类文件并加载进内存,并产生class对象 Class<?> clazz = Class.forName(name); //产生该类的对象 Object obj = clazz.newInstance(); } }
3,反射的应用---得到类的属性
在反射中得到一个类的完成结构的话,使用到java.lang.reflect包中的以下几个类
(1) Constructor:表示类中的构造方法 (2)Field:表示类中的属性 (3)Method:表示类中的方法
Constructor类中常用的方法
序号 |
方法 |
描述 |
1 |
public int getModifiers() | 得到构造函数的修饰符 |
2 |
public String getName() | 得到构造函数的名称 |
3 |
public Class<?>[] getParameterTypes() | 得到构造函数中的参数类型 |
4 |
public String toString() | 返回此构造函数的信息 |
5 |
public T newInstance(Object... initargs) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException | 向构造函数中传入参数,实例化对象,就是需要拿到有参数构造函数的类的对象的时候,就需要用到这个 |
Field类中常用的方法
序号 |
方法 |
描述 |
1 | public Object get(Object obj) throws IllegalArgumentException, IllegalAccessException | 得到一个对象中属性的内容 |
2 | public void set(Object obj, Object value) throws IllegalArgumentException, IllegalAccessException | 设置指定对象中属性的具体内容 |
3 | public int getModifiers() | 得到属性的修饰符 |
4 | public String getName() | 返回此属性的名称 |
5 | public String toString() | 返回此Field类的信息 |
6 | public void setAccessible(boolean flag) throws SecurityException | 设置一个属性是否被外部访问(需要访问私有属性需要用这个方法true一下)暴力访问 |
7 | public static void setAccessible(AccessibleObject[] array, boolean flag) throws SecurityException | 设置一组属性是否被外部访问 |
Method类中常用的方法
序号 | 方法 | 描述 |
1 | public int getModifiers() | 得到方法的访问修饰符 |
2 | public String getName() | 得到方法的名称 |
3 | public Class<?>[] getParameterTypes() | 得到方法的全部参数类型 |
4 | public Class<?> getReturnType() | 得到方法的全部返回值类型 |
5 | public Class<?>[] getExceptionTypes() | 得到一个方法全部抛出的类型 |
6 | public Object invoke(Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException | 当通过反射得到类的对象,类的方法的名称,方法中的参数的时候,用这个方法,我们就能完美的运行该类 |
这三个类都是AccessibleObject类的子类
以上三个类基本覆盖了反射所需要用到的类包括类中的方法,必须要熟练记忆
在反射操作中,得到一个类中的全部属性,有两种不同的操作
(1)得到实现的接口或者父类中的公共属性 在Class类中的getFields()方法
(2)得到本类中的所有属性,包括私有属性 在Class类中的getDeclaredFields()
根据以前学过的知识,要想操作一个类中的属性,肯定得要先得到一个类的对象,那么在反射中,也不例外,也需要通过newIntance()来得到一个类的对象,这样才能去取得类中的属性
总之你想得到我的属性,必须给我对象
package cn.reflect; import java.lang.reflect.Field; //拿Person类中的字段 public class ReflectDemo3 { /** * @param args * @throws Exception */ public static void main(String[] args) throws Exception { getField(); } //拿到字节码文件的中的字段 public static void getField() throws Exception { Class<?> clazz = Class.forName("cn.bean.Person"); //拿到Person类中的age对象,返回的是一个Field的对象 //Field f = clazz.getField("age");//只能获取共有的的 Field field = clazz.getDeclaredField("age");//可以获取本类.包含私有 //对私有的属性,取消其权限的检查,暴力访问 field.setAccessible(true); //设置属性的时候,必须要用对象来设置属性,所以必须先弄个对象出来 Object obj = clazz.newInstance(); field.set(obj, 23); Object o = field.get(obj); System.out.println(o); } }
4,反射的应用---得到类中的方法
得到类中的方法,是使用Class类中的getMethod()方法,此方法返回的是一个Method类的对象数组
需要运行一个类
(1)需要先得到该类的方法,方法中的参数的class对象
(2)然后得到该类的对象
(3)调用Method中的invoke方法,将对象,和方法的参数传入该方法
package cn.reflect; import java.lang.reflect.Method; //获取Person类中的方法 public class ReflectDemo4 { public static void main(String[] args) throws Exception { // getMethod(); getMethod_2(); } public static void getMethod_2() throws Exception { Class<?> clazz = Class.forName("cn.bean.Person"); Method method = clazz.getMethod("paramMethod", String.class,int.class); //方法要运行起来,也需要对象的支持,所以要造个对象出来 Object obj = clazz.newInstance(); //Method中有个方法,能传入对象和方法,这样的话,方法就能完美的运行起来了 method.invoke(obj, "wjd",23); } public static void getMethod() throws Exception { Class<?> clazz = Class.forName("cn.bean.Person"); Method[] methods = clazz.getMethods();//这个获取的都是共有的方法 methods = clazz.getDeclaredMethods();//只能获取本类中的方法,但是包含了私有的方法 for(Method method : methods){ System.out.println(method); } } }
5,反射的应用---得到类中的的字段(暴力访问的话,可以得到类中的私有字段)
package cn.reflect; import java.lang.reflect.Field; //拿Person类中的字段 public class ReflectDemo3 { /** * @param args * @throws Exception */ public static void main(String[] args) throws Exception { getField(); } //拿到字节码文件的中的字段 public static void getField() throws Exception { Class<?> clazz = Class.forName("cn.bean.Person"); //拿到Person类中的age对象,返回的是一个Field的对象 //Field f = clazz.getField("age");//只能获取共有的的 Field field = clazz.getDeclaredField("age");//可以获取本类.包含私有 //对私有的属性,取消其权限的检查,暴力访问 field.setAccessible(true); //设置属性的时候,必须要用对象来设置属性,所以必须先弄个对象出来 Object obj = clazz.newInstance(); field.set(obj, 23); Object o = field.get(obj); System.out.println(o); } }