基础知识 在Java中的反射机制是指在运行状态中,对于任意一个类都能够知道这个类所有的属性和方法;并且对于任意一个对象都能够调用它的任意一个方法。
在Java程序中许多对象在运行是都会出现两种类型:编译时类型和运行时类型。编译时类型由声明对象时使用的类型来决定,运行时类型由实际赋值给对象的类型决定。如:Person p=new Student(); 其中编译时类型为Person,运行时类型为Student。
使用场景 首先,既然有反射,那么先从正射开始解释。
正射:一般情况下我们使用某个类一定是知道它是什么操作用来做什么的。于是我们直接对这个类进行实例化,之后使用这个类对象进行操作。
1 2 Person person=new Person(); person.set();
反射:而反射则是一开始不知道需要初始化的对象时什么,自然也无法使用new关键字来创建对象了。例如:请求后台接口,根据url里面的信息进行相应的调用方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 public class pro8 { public static void main (String[] args) { test tt=factory("testOne" ); tt.test(); test rr=factory("testTwo" ); rr.test(); } public static test factory (String name) { test test1 = null ; try { test1=(test)Class.forName("test." +name).getDeclaredConstructor().newInstance(); }catch (Exception e) { e.printStackTrace(); } return test1; } } interface test { public void test () ; } class testOne implements test { public void test () { System.out.println("this is testOne" ); } } class testTwo implements test { public void test () { System.out.println("this is testTwo" ); } } this is testOnethis is testTwo
概念
反射是框架设计的灵魂
反射机制就是将各个组成部分封装为其他对象。
优势
可以在程序运行中,操作这些对象。
可以解耦,提高程序的可扩展性
Java代码的阶段 Java代码在计算机中经历的三个阶段
source(源代码阶段)
Java文件被javac编译成class文件,class文件会将成员变量、成员方法和构造方法等编译成不同的模块。
class文件通过classlocad类加载器来进入和下一阶段
class(类对象阶段)
通过classload类加载器,将class文件加载进内存,成员变量、成员方法和构造方法会被封装为class类中的对象。
runtime(运行阶段)
当我们需要访问Java文件的成员时,我们就可以逆向访问,通过class类的classload类加载器来获取该成员的对象。
获取class对象
创建对象实例 两种方法:
class对象调用newInstance()方法进行实例化(针对无参构造函数)
调用Constructor(构造函数)对象的newInstance()方法。(针对有参函数)
1 2 3 4 5 6 7 8 Class clazz=Class.forName("test.Person" ); Person person=(Person)clazz.newInstance(); Class clazz=Class.forName("test.Person" ); Constructor c=clazz.getDeclaredConstructors(String.class,String.class,int .class); Person person=(Person)c.newInstance("李四" ,"男" ,20 );
获取构造方法
Constructor[] getConstructors(构造函数参数类型),返回一个 Constructor对象数组,反射 所有的 public构造类对象。
Constructor getConstructors(构造函数参数类型),返回一个 Constructor对象,反射 指定的 public构造类对象。
Constructor[] getDeclaredConstructor(构造函数参数类型),返回一个 Constructor对象数组,反射 所有的 构造类对象。
Constructor getDeclaredConstructor(构造函数参数类型),返回一个 Constructor对象,反射 指定的 构造类对象。
获取成员变量(属性)(导包 java.lang.reflect.Field)
Field[] getField(),返回包含一个Field对象数组,反射所有可访问的public字段类对象。
Field getField(String name),返回一个Field对象,反射指定的public成员字段类对象。
Field[] getDeclaredFields(),返回一个Field对象,反射所有可访问 (public,private等)的已声明字段类对象。
Field getDeclaredField(String name),返回一个指定可访问 (public,private等)已声明字段类对象的一个Field对象数组。
既然有get那么就会有set方法,void set(Object obj,Object value),get(Object obj)
获取成员方法
Method[] getMethod(),返回一个Method对象数组,反射指定的public成员方法对象。
Method getMethod(String name),返回一个Method对象,反射指定的public成员方法对象。
Method[] getDeclaredMethods(),返回一个Method对象数组,反射 所有的成员方法类对象。
Method getDeclaredMethods(String name),返回一个Method对象,反射 所有的成员方法类对象。
#####
操作Constructor(构造方法)
无参构造方法:创建class对象,class对象调用newInstance方法(1.9以后方法有改变,合并为下面步骤2)
有参构造方法:创建class对象,class对象调用getConstructor方法获得Constructor对象,接着cons对象调用newInstance方法实例化
操作Field(成员变量) 拥有get和set方法,可以在任何时候对类对象进行改变取值。
首先class对象调用getField方法获得Field对象,然后该对象调用set或者get方法
void set(Object obj,Object value) / get(Object obj),取出来后记得值类型转换
setAccessible(true)暴力反射:忽略访问权限的安全检查,也就是说虽然Field对象可以获得所有成员变量但是私有变量其实是不能使用的,所以在获得私有成员变量对象Field后需要:
Field对象.setAccessible(true) ,接着就可以继续调用set或者get方法了
操作Method(成员方法) 获取Method对象:class对象调用getMethod方法
执行方法:Method对象调用invoke方法 Object invoke(Object object,Object..args)
Method md=clazz.getDeclaredMethod(“test”);
md.invoke(oo);oo是实例化对象名
小结:为了动态加载类对象,基本上全部需要先获取class对象,然后调用相应模块的方法获得该对象,接着配合实例化对象使用。因为直接的操作全部是class对象没有代入真正的对象。
下面是分界线~