什么是反射
大多情况下我们通过new来对一个类进行实例化,然后操作这个类对象。这种情况下代码在未运行时就已确定要运行的类。
Employee e = new Employee();
e.setAge(18);
而反射是指一开始不知道我们要初始化的类是什么,只有在运行时才知道要操作的类是什么,并可以在运行时获取类的完整构造及对应方法。
Class clz = Class.forName("Ano.Employee");
Method method = clz.getMethod("setAge", int.class);
Constructor constructor = clz.getConstructor(); //获取反射类对象
Object object = constructor.newInstance();
method.invoke(object, 18); //通过invoke 方法调用来调用对象的方法
优点:
(1)通过运行期间的类型判断来动态加载类,可以提高代码灵活度;
(2)此外对于任意一个类,都能知道其所有属性和方法,对于任意一个对象,都可以调用其任意方法。
缺点:
(1)使用反射基本上是一种解释操作,用于字段和方法接入时要远慢于直接代码性能比直接的 java 代码要慢很多。因此Java反射机制主要应用在对灵活性和扩展性要求很高的系统框架上,普通程序不建议使用;
(2)动态操作/改变类的属性增加了类的安全隐患。
应用场景
(1)使用 JDBC 连接数据库时通过 Class.forName()通过反射加载数据库的驱动程序;
(2)Spring 框架的 IOC创建对象以及 AOP功能都和反射有联系;
反射步骤
(1)获取反射中的Class对象
在反射中,要想获取一个类或调用其方法,首先要获取类的Class对象。
获取Class类对象有三种方法:
Class clz = Class.forName("Ano.Employee"); //(1)当知道类的全路径名时
Class clz = Employee.Class; //(2)
Employee e = new Employee();
Class clz = e.getClass(); //(3)
(2)通过反射创建类对象
创建类对象主要有两种方式:
Class clz = Employee.Class;
Employee e = (Employee)clz.newInstance();
Class clz = Employee.class;
Constructor constructor = clz.getConstructor();
Employee e = (Employee)constructor.newInstance("C2y", 31);
第一种方式只能使用默认的无参构造方法,另一种则可以指定某个构造方法。
(3)通过反射查看类属性、方法、构造器
我们可以通过 Class 对象的 getFields() 方法 获取 Class 类的属性,不过无法获取私有属性。
Class clz = Employee.class;
Field[] fields = clz.getFields();
for(Field field : fields) {
System.out.println(field.getName());
}
Class 对象的 getDeclaredFields() 方法则可以获取包括私有属性在内的所有属性:
Field[] fields = clz.getDeclaredFields();
与获取属性类似,如果想获取类的私有方法,私有构造器,则必须使用带有declared 前缀的方法名。
Class clz = Employee.class;
Method[] methods = clz.getDeclaredMethods();
for(Method method : methods) {
System.out.println(method.getName());
}
调用私有方法
Class clz = Employee.class;
Employee e = (Employee)clz.newInstance();
Method privateMethod = clz.getDeclaredMethod("selfIntroduction",
String.class, int.class);
//当调用private方法时,我们需要取消安全检查
privateMethod.setAccessible(true);
privateMethod.invoke(e, "c2y", 18);
修改指定属性
Class clz = Employee.class;
Employee e = (Employee)clz.newInstance();
Field field = clz.getDeclaredField("name");
field.setAccessible(true);
field.set(e, "c2y");