什么是反射?Java反射机制?
反射主要是指程序可以访问、检测和修改其本身状态或行为的一种能力。
Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性。使用反射使Java具有动态语言的特性,增强了程序的灵活性和可移植性。
作用
反射被广泛地用于那些需要在运行时检测或修改程序行为的程序中。
(1)在运行时判断任意一个对象所属的类型。
(2)在运行时构造任意一个类的对象。
(3)在运行时判断任意一个类所具有的成员变量和方法。
(4)在运行时调用任意一个对象的方法,甚至可以调用private方法。
==注意:上述功能都是在运行时环境中,而不是在编译时环境中==
用途
MyBatis中为Mapper接口通过反射实现具体的类。
数据库JDBC过程中,加载驱动。
Spring中通过xml配置文件加载不同的对象或类。
等等…
缺点
- 性能第一
反射包括了一些动态类型,所以 JVM 无法对这些代码进行优化。因此,反射操作的效
率要比那些非反射操作低得多。我们应该避免在经常被 执行的代码或对性能要求很高的程
序中使用反射。
- 安全限制
使用反射技术要求程序必须在一个没有安全限制的环境中运行。
- 内部暴露
由于反射允许代码执行一些在正常情况下不被允许的操作(比如访问私有的属性和方
法),所以使用反射可能会导致意料之外的副作用--代码有功能上的错误,降低可移植性。
反射代码破坏了抽象性,因此当平台发生改变的时候,代码的行为就有可能也随着变化。
反射机制中的类
实现Java反射机制的API在Java.lang.reflect包下,具有以下几点。
(1)Class类:代表一个类。
(2)Filed类:代表类的成员变量。
(3)Method类:代表类的方法。
(4)Constructor类:代表类的构造方法。
(5)Array类:提供了动态创建数组及访问数组元素的静态方法。该类中的所有方法都是静态的
具体功能实现
- 反射机制获取类有三种方法,我们来获取Employee类型
//第一种方式,会抛ClassNotFoundException异常
try {
Class clazz = Class.forName("com.learn.Employee");
System.out.println(clazz.getName());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
//第二种方式,Java中每个类都有class属性
Class clazz2 = Employee.class;
//第三种方式,任何一个Java对象都有getClass 方法
Employee e = new Employee();
Class clazz3 = e.getClass();//clazz3是运行时类 (e的运行时类是Employee)
- 创建对象:获取类以后我们来创建它的对象,利用newInstance
try {
Class clazz = Class.forName("com.learn.Employee");//ClassNotFoundException异常
//创建此Class对象所表示的类的一个实例
Object object = clazz.newInstance();////调用了Employee的无参数构造方法.
//IllegalAccessException InstantiationException 异常
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
- 获取属性:分为所有的属性和指定的属性:
a.先看获取所有的属性的写法:
//获取整个类
Class c = Class.forName("java.lang.Integer");
//获取所有的属性
Field[] fields = c.getDeclaredFields();
for(Field field:fields){
System.out.println(
Modifier.toString(field.getModifiers()) +//获得属性的修饰符,例如public,static等等
" "+field.getName() );//返回由此 Field对象表示的字段的名称。
}
b.获取特定的属性
//获取类
Class c2 = Class.forName("com.learn.Employee");
//获取id属性
try {
Field id = c.getDeclaredField("id");
//实例化这个类赋给对象obj
Object obj = c.newInstance();
//打破封装
id.setAccessible(true); //使用反射机制可以打破封装性,导致了java对象的属性不安全。
//给obj对象的id属性赋值"1024"
id.set(obj, 1024); //set
//获取属性值
System.out.println(id.get(obj));
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
- 获取方法,和构造方法
方法关键字 | 含义 |
---|---|
getDeclaredMethods( | 获取所有的方法 |
getReturnType() | 获得方法的放回类型 |
getParameterTypes() | 获得方法的传入参数类型 |
getDeclaredMethod(“方法名”,参数类型.class,……) | 获得特定的方法 |
构造方法关键字 | 含义 |
getDeclaredConstructors() | 获取所有的构造方法 |
getDeclaredConstructor(参数类型.class,……) | 获取特定的构造方法 |
父类和父接口 | 含义 |
getSuperclass() | 获取某类的父类 |
getInterfaces() | 获取某类实现的接口 |
- 利用反射创建数组
其中的Array类为java.lang.reflect.Array类。
Class<?> cls = Class.forName("java.lang.String");
Object array = Array.newInstance(cls,25);
//往数组里添加内容
Array.set(array,0,"hello");
Array.set(array,1,"Java");
Array.set(array,2,"fuck");
Array.set(array,3,"Scala");
Array.set(array,4,"Clojure");
//获取某一项的内容
System.out.println(Array.get(array,3));
- 调用方法
当我们从类中获取了一个方法后,我们就可以用invoke()方法来调用这个方法。
//获取整个类
Class c = Class.forName("com.learn.Employee");
//获取此类的实例对象
Object ob = c.newInstance();
try {
//获取Employee类的function方法
Method method = c.getMethod("function", String.class);
//调用method对应的方法 => function("是的!")
String str = (String) method.invoke(ob,"是的!");
System.out.println( str );
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
其中Employee
类为
public class Employee {
private Integer id;
private String name;
public String function(String string){
return "调用function方法"+string;
}
...省略set get 方法
}
这样我们就可以获得类的各种内容,进行了反编译。对于JAVA这种先编译再运行的语言来说,反射机制可以使代码更加灵活,更加容易实现面向对象。
参考文章: