反射机制呢就是在程序运行时,动态的获取类(class),类的方法(method)属性(field)等。主要的注意点就是程序运行时动态的获取。
这里主要是从代码的角度来说解Java反射。在使用中咱们用的较多的几个类有Class
,Method
,Field
,Constructor
,Annotation
等。
下面咱们分别介绍下。java
获取class对象有以下三种方式:数组
//方式1 调用对象的getClass()方式,该方法属于Object类 Class<? extends Cat> aClass = cat.getClass(); //方式2 直接类.class获取。 Class<Cat> catClass = Cat.class; //方式3 经过Class类的静态方法forName获取,参数为想要获取的类的全名(含包名,否则会报ClassNotFoundException) try { Class<?> cat2 = Class.forName("xyz.magicer.Cat"); } catch (ClassNotFoundException e) { e.printStackTrace(); }
在使用中根据实际状况选择,获取Class
对象是咱们使用反射的第一步。不过仍是很简单的。获取了Class
对象后,怎么建立一个实体呢?catClass.newInstance()
不过改方法会抛出两个异常:InstantiationException
和IllegalAccessException
code
还有一种建立实体的方式是使用Constructor
,在下边在介绍。对象
类或接口的方法对应这Method
这个类。咱们经过Class对象来获取。主要方法有接口
getMethods()
: 返回Method[]
返回全部public
的方法。包含
父类或父接口的方法。getDeclaredMethods()
: 返回Method[]
返回全部的方法。包括 private
public
default
和 protected
的。不包含
父类或父接口的方法getMethod(String name, Class<?>... parameterTypes)
: 返回Method
, 根据方法名和类型获取Method。类或接口的及父类父接口公共成员方法。getDeclaredMethod(String name, Class<?>... parameterTypes)
: 返回Method
。根据方法名和类型返回Method。类和接口的全部方法。//sleep是private的 Method[] methods = catClass.getMethods(); for (Method method : methods) { //method.getName() 返回方法名,不包括修饰符,参数和返回值。 System.out.printf(method.getName()+" "); } // 打印toString getName setName setColor eat eat getAge setAge getColor wait wait wait equals hashCode getClass notify notifyAll System.out.println(); Method[] declaredMethods = catClass.getDeclaredMethods(); for (Method declaredMethod : declaredMethods) { System.out.printf(declaredMethod.getName()+" "); } //打印 toString getName setName sleep setColor eat eat getAge setAge getColor //抛出NoSuchMethodException 由于sleep的访问权限为private //Method sleep1 = catClass.getMethod("sleep", null); Method hashCode = catClass.getMethod("hashCode", null); //抛出NoSuchMethodException,由于hashCode是父类的方法。 //Method hashCode1 = catClass.getDeclaredMethod("hashCode", null); Method eat2 = catClass.getMethod("eat",null); Method sleep1 = catClass.getDeclaredMethod("sleep", null);
经过上面的代码咱们能看到,getMethods()能够获取父类的方法,可是不能获取私有方法,而getDeclaredMethod()方法不能够获取父类的方法,可是能够获取私有的方法。getMethod也是同样,能够获取父类父接口方法,可是没法获取私有方法,而getDeclaredMethod能够获取私有方法不能获取父类父接口方法。
而带s和不带s的区别是带s返回值为Method[]
,不带返回的是Method
。get
既然咱们获取到了方法(Method)固然是想调用它,那么怎么调用呢?Method有个方法invoke(Object obj, Object... args)
.
invoke接收两个参数,第一个是调用该方法的实体,第二个是方法的参数。参数类型多个时顺序要跟方法参数相同。hash
Method eat1 = catClass.getMethod("eat"); eat1.invoke(catInstance,null); //打印:我只吃小鱼干。 Method eat = catClass.getDeclaredMethod("eat"); eat.invoke(catInstance,null); //打印: 我只吃小鱼干。 Method sleep = catClass.getDeclaredMethod("sleep"); //IllegalAccessException sleep.invoke(catInstance,null);
咱们会发现当私有方法invoke调用时会抛出IllegalAccessException,不是能够获取么为何不能调用?由于方法有权限修饰符,咱们须要设置成咱们能够调用的。以下:it
sleep.setAccessible(true); sleep.invoke(catInstance,null);
在调用前设置为能够调用就解决了。
在前面咱们接触到了两个Method类的方法了(getName,和invoke)。io
获取Field对象的方式跟Method同样,用法和规律都同样。无非是如今方法改成getFields()、getDeclaredFields()、getField(String)、getDeclaredField(String)。class
能够经过set方法来设置值 public void set(Object obj, Object value)
每种基本数据类型都有的setxxx()方法。
//name 为私有 Field name = catClass.getDeclaredField("name"); name.setAccessible(true); name.set(cat,"啦啦啦"); System.out.println("\n"+cat.toString());
在使用反射设置属性的时候必定要注意,可能在代码中用到该属性的地方较多,改变了值以后引发一些意想不到的效果。
获取Constructor对象的方式跟Field和Method同样。有四个方法:
getConstructors(),getDeclaredConstructors,getConstructor(Class<?>... parameterTypes), getDeclaredConstructor(Class<?>... parameterTypes)。
获得了Constructor对象后咱们就能够调用newInstance(Object ... initargs)
方法进行初始化了,注意参数的顺序。
Constructor<Cat> constructor1 = catClass.getDeclaredConstructor(String.class, int.class, String.class); Cat cat1 = constructor1.newInstance("喵喵2", 3, "white"); System.out.println(cat1.toString());
到这里咱们看到,经过反射建立一个对象有两种方式:(1)class.newInstance()和(2)consturctor.newInstance(Object ... initargs)。那么他们有什么区别呢?
方式(1)只能调用无参构造建立对象,而且无参构造不能为私有,而方式(2)能够调用全部
Constructor<Cat> constructor1 = catClass.getDeclaredConstructor(String.class, int.class, String.class); Cat cat1 = constructor1.newInstance("喵喵2", 3, "white"); System.out.println(cat1.toString()); //调用私有构造 Constructor<Cat> constructor2 = catClass.getDeclaredConstructor(String.class); constructor2.setAccessible(true); Cat miaomiao = constructor2.newInstance("miaomiao"); System.out.println(miaomiao.toString());