一、什么是反射?
用光的反射来说明,光在遇到玻璃、水面等其他介质的时候,在分界面上又返回原来物质的一种现象。
二、应用场景
在Java的业务开发中,平常很少用到反射原理,也就不怎么接触到反射机制。但是,因为反射我们才能使用各种框架。例如Spring/Spring Boot 、MyBatis等许多框架中都用到了反射机制。
三、优点和缺点
优点:能够让代码更加的灵活,同时也为各种框架提供了便利。
缺点:我们都知道Java 的特性包括封装,恰巧反射机制就破坏了这种封装机制。同时也增加了一些安全问题。同时,反射的性能也稍微的差一点。不过在框架中这种性能差异显得微乎其微。
四、反射示例
4.1、获取Class对象的方式
通过前面的说明,我们知道一定要通过一种介质才能得到想要的对象。而这个介质就是Class类。
根据官网文档的解释,Class
类的实例表示正在运行的 Java 应用程序中的类和接口。Class
类的实例表示正在运行的 Java 应用程序中的类和接口。Class
对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的。
4.1.1 知道具体类
Class clazz = TargetClass.class ;
我们已知一个类,通过.class
这种方式就能获取到对应的Class
实例。
但是我们一般是不知道具体的类,通过遍历包下面的类来获取对象,通过此方式获取的Class
对象不会进行初始化。
4.1.2 通过Class.forName()
传入类的路径
Class clazz = Class.forName(" com.test.TargetClass");
例如在Jdbc的连接中,我们要获取驱动,一般就用这种方式来获取相应的Class
实例。
4.1.3 通过对象实例instance.getClass()
获取
TargetClass object = new TargetClass();
Class clazz = object.getClass();
4.1.4 通过类加载器传入路径
ClassLoader loader = ClassLoader.getSystemClassLoader();
Class clazz = loader.loadClass("com.base.TargetClass");
通过类加载器加载一个类获取Class对象时不会进行初始化。也就意味着构造方法和类初始化器都不会被执行。只有new对象时,这些才会执行。
五、反射的基本操作
5.1、创建一个用来反射操作的类。
public class Apple {
private String name ;
public Apple(){
name = "苹果";
}
public void show( String s ){
System.out.println( "我最喜欢:" + s );
}
private void otherShow(){
System.out.println( "我是:" + name );
}
}
5.2 使用反射
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ReflectApple {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException, NoSuchMethodException, InvocationTargetException {
//这是通过前面的四种方式来获取Apple的Class实例
Class<?> clazz = Apple.class;
Class<?> clazz2 = Class.forName("com.base.Apple");
Apple apple1 = new Apple();
Class<?> clazz3 = apple1.getClass();
ClassLoader loader = ClassLoader.getSystemClassLoader();
Class<?> clazz4 = loader.loadClass("com.base.Apple");
//我们用第一个Class实例获取要操作的类
Apple apple = (Apple)clazz.newInstance();
/**
* 用第二个Class实例获取类中的方法,修饰符为public的方法
* 返回一个包含某些 Method 对象的数组
* 如果此 Class 对象表示没有公共成员方法的类或接口,或者表示一个基本类型或 void,则此方法返回长度为 0 的数组。
*/
Method[] methods = clazz2.getMethods();
for (Method method : methods) {
System.out.println(method.getName());
}
/**
* 用第三个Class实例获取类中的字段,由于是private,所以数组的长度为0
* 返回一个包含某些 Field 对象的数组
*/
Field[] fields = clazz3.getFields();
for (Field field : fields) {
System.out.println(field.getName());
}
//获取指定的方法,同时传入参数类型
Method showMethod = clazz4.getDeclaredMethod("show" , String.class );
//调用方法,并传参
showMethod.invoke( apple , "水果");
Field field = clazz.getDeclaredField("name");
//为了对其中类的私有字段进行修改,这里取消安全检查
//值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。
field.setAccessible( true );
field.set( apple , "柠檬");
//指定获取私有方法
Method otherShowMethod = clazz.getDeclaredMethod("otherShow" );
otherShowMethod.setAccessible( true );
otherShowMethod.invoke( apple );
}
}
输出的内容如下
show
wait
wait
wait
equals
toString
hashCode
getClass
notify
notifyAll
我最喜欢:水果
我是:柠檬
如输出的结果所示,这里的show方法是我们自己定义的。其他的方法则是继承Object
。
在遍历方法的时候,并不会获取到私有方法,所以遍历时并不会显示otherShow
这个方法。
注意在getDeclaredMethod
获取指定方法的时候,如果要操作的方法中带有参数,则要加入相应的参数类型Class
实例。
以上内容是参考了书集和官方文档以及自己测试结果所写,全部为个人理解。如果有错误请批评指正。
转载请注明出处,谢谢。