本来想写插件化的文章,然后发现需要一些其他的知识进行支持,那就先聊一下Java中的反射机制~
平时我们做项目写代码的时候,都是拿指定的类使用其api完成各功能的开发,但是如果一开始我们并不知道要使用的是类对象是哪个,这样是没办法new一个对象出来的,这时候就需要用到反射的技术了。反射就是在运行时才知道要操作的类是什么,并且可以在运行时获取类的完整构造,并调用其对应的方法,这是Java被视为动态语言的一个关键的特性。
我们平时知道一个类,然后获取一个实例的操作很简单,直接new一下就好了,但想获取一个类就需要使用Class类的forName方法,参数是传入想要获得的类的全路径名,然后就可以获取到这个类的实例或者其构造函数。
Class<?> classAnimal = Class.forName("hunger.java.test.Animal");
Animal animal = (Animal) classAnimal.newInstance();
Constructor<?>[] constructors = classAnimal.getConstructors();
for (int i = 0; i < constructors.length; i++) {
System.out.println(constructors[i]);
}
结果:
public hunger.java.test.Animal()
public hunger.java.test.Animal(java.lang.String,int)
如果有多个构造函数,怎么获取到我们想要的构造函数呢,那就是根据参数类型进行匹配:
Constructor constructor = classAnimal.getConstructor(String.class, int.class); //注意这里int
我想获取其他的方法来执行,可以使用Class.getDeclaredMethod的方法:
Class<?> classAnimal = Class.forName("hunger.java.test.Animal");
Animal animal = (Animal) classAnimal.newInstance();
Method method1 = classAnimal.getDeclaredMethod("method1");
method1.invoke(animal);
Animal类中对应方法如下:
public void method1() {
System.out.println("this animal no color no length");
}
执行结果:
那如果是带参数的方法呢?
Method method2 = classAnimal.getDeclaredMethod("method2", String.class, int.class); //注意参数顺序和类型要对应上,不然报方法找不到的异常
method2.invoke(animal,"yellow", 10);
Animal中方法对应如下:
public void method2(String color, int length) {
System.out.println("this animal color is:" + color + ", length is:" + length);
}
执行结果:
那么有个问题,我这边访问的两个都是public方法,那我如何访问一个private的方法呢?
Method method = classAnimal.getDeclaredMethod("method");
method.setAccessible(true); //设置方法可被访问
method.invoke(animal);
Animal中对应方法如下:
public void method2(String color, int length) {
System.out.println("this animal color is:" + color + ", length is:" + length);
}
执行效果:
同样的,我要访问该类中的属性,也可以适用类似的方法:
Animal animal = (Animal) classAnimal.newInstance();
animal.setColor("default color");
animal.setLength(10);
Field field = classAnimal.getDeclaredField("color");
field.setAccessible(true);
System.out.println(field.get(animal));
field.set(animal, "blue");
System.out.println(field.get(animal));
执行效果:
我把class中常用的一些API汇总如下:
getName():获得类的完整名字。
getFields():获得类的public类型的属性。
getDeclaredFields():获得类的所有属性。包括private 声明的和继承类
getMethods():获得类的public类型的方法。
getDeclaredMethods():获得类的所有方法。包括private 声明的和继承类
getMethod(String name, Class[] parameterTypes):获得类的特定方法,name参数指定方法的名字,parameterTypes 参数指定方法的参数类型。
getConstructors():获得类的public类型的构造方法。
getConstructor(Class[] parameterTypes):获得类的特定构造方法,parameterTypes 参数指定构造方法的参数类型。
newInstance():通过类的不带参数的构造方法创建这个类的一个对象。
getModifiers():类和属性或者方法都可以使用这个方法来获取其权限修饰符。
那么反射有什么作用呢?
反射机制使得一个已知名称的class对外提供了获取其内部信息的途径,包括获取其属性和方法,这样在程序运行时,可以动态地改变其属性值或者调用方法。这样写代码时会更加灵活,降低了耦合度,但是凡事都有利有弊,虽然反射有诸多方便的地方,但是也是以牺牲性能为代价换来的,平时开发中要适当使用,否则会有很高的资源消耗。
反射的使用场景有哪些呢?
反射在我们日常开发中用的倒也不是非常多,不过有几个比较常见的场景值得说一下,像平时我们看第三方框架的源码里面就挺常见的,还有插件化、动态代理、注解里面都会有所涉及。