`Java`反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和
属性;这种动态获取信息以及动态调用对象方法的功能成为`Java`语言的反射机制。
要想理解反射的原理,首先要了解什么是类型信息。`Java`让我们在运行时识别对象和类的信息,主要有2种方式:一种是传统的`RTTI`,
它假定我们在编译时已经知道了所有的类型信息;另一种是反射机制,它允许我们在运行时发现和使用类的信息。
- 得到`Class`对象的三种方式
```java
//1、通过对象调用 getClass() 方法来获取,通常应用在:比如你传过来一个 Object
// 类型的对象,而我不知道你具体是什么类,用这种方法
Person p1 = new Person();
Class c1 = p1.getClass();
//2、直接通过 类名.class 的方式得到,该方法最为安全可靠,程序性能更高
// 这说明任何一个类都有一个隐含的静态成员变量 class
Class c2 = Person.class;
//3、通过 Class 对象的 forName() 静态方法来获取,用的最多,
// 但可能抛出 ClassNotFoundException 异常
Class c3 = Class.forName("com.ys.reflex.Person");
```
**需要注意的是:一个类在 `JVM` 中只会有一个 `Class` 实例,即我们对上面获取的 `c1,c2,c3`进行 `equals` 比较,发现都是`true`。**
- 通过`Class`类获取成员变量、成员方法、接口、超类、构造方法等
```java
getName();//获取类的完整名字
getFields();//获取类的public类型的属性,包括继承过来的。
getDeclaredFields();//获取类的所有属性。包括private声明的属性。但不包括继承过来的属性。
getMethods();//获得类的public类型的方法。包括继承过来的。
getDeclaredMethods();//获得类的所有方法。包括private 声明的。不包括继承过来的方法。
getMethod(String name, Class[] parameterTypes);//获得类的特定方法,name参数指定方法的名字,parameterTypes 参数指定方法的参数类型。
getConstructors();//获得类的public类型的构造方法。
getConstructor(Class[] parameterTypes);//获得类的特定构造方法,parameterTypes 参数指定构造方法的参数类型。
newInstance();//通过类的不带参数的构造方法创建这个类的一个对象。
```
例子:
```java
//获得类完整的名字
String className = c2.getName();
System.out.println(className);//输出com.ys.reflex.Person
//获得类的public类型的属性。
Field[] fields = c2.getFields();
for(Field field : fields){
System.out.println(field.getName());//age
}
//获得类的所有属性。包括私有的
Field [] allFields = c2.getDeclaredFields();
for(Field field : allFields){
System.out.println(field.getName());//name age
}
//获得类的public类型的方法。这里包括 Object 类的一些方法
Method [] methods = c2.getMethods();
for(Method method : methods){
System.out.println(method.getName());//work waid equls toString hashCode等
}
//获得类的所有方法。
Method [] allMethods = c2.getDeclaredMethods();
for(Method method : allMethods){
System.out.println(method.getName());//work say
}
//获得指定的属性
Field f1 = c2.getField("age");
System.out.println(f1);
//获得指定的私有属性
Field f2 = c2.getDeclaredField("name");
//启用和禁用访问安全检查的开关,值为 true,则表示反射的对象在使用时应该取消 java 语言的访问检查;反之不取消
f2.setAccessible(true);
System.out.println(f2);
//创建这个类的一个对象
Object p2 = c2.newInstance();
//将 p2 对象的 f2 属性赋值为 Bob,f2 属性即为 私有属性 name
f2.set(p2,"Bob");
//使用反射机制可以打破封装性,导致了java对象的属性不安全。
System.out.println(f2.get(p2)); //Bob
//获取构造方法
Constructor [] constructors = c2.getConstructors();
for(Constructor constructor : constructors){
System.out.println(constructor.toString());//public com.ys.reflex.Person()
}
```
- 根据反射获取父类属性
父类:`Parent.java`
```java
public class Parent {
public String publicField = "parent_publicField";
protected String protectField = "parent_protectField";
String defaultField = "parent_defaultField";
private String privateField = "parent_privateField";
}
```
子类:`Son.java`
```java
public class Son extends Parent {
}
```
测试类:
```java
public class ReflectionTest {
@Test
public void testGetParentField() throws Exception{
Class c1 = Class.forName("com.ys.model.Son");
//获取父类私有属性值
System.out.println(getFieldValue(c1.newInstance(),"privateField"));
}
public static Field getDeclaredField(Object obj,String fieldName) {
Field field = null;
Class c = obj.getClass();
for(; c != Object.class ; c = c.getSuperclass()){
try {
field = c.getDeclaredField(fieldName);
field.setAccessible(true);
return field;
}catch (Exception e){
//这里甚么都不要做!并且这里的异常必须这样写,不能抛出去。
//如果这里的异常打印或者往外抛,则就不会执行c = c.getSuperclass(),最后就不会进入到父类中了
}
}
return null;
}
public static Object getFieldValue(Object object,String fieldName) throws Exception{
Field field = getDeclaredField(object,fieldName);
return field.get(object);
}
}
```
**通过执行上述代码,我们获得了父类的私有属性值,这里要注意的是直接通过反射获取子类的对象是不能得到父类的属性值的,
必须根据反射获得的子类 `Class` 对象在调用 `getSuperclass()` 方法获取父类对象,然后在通过父类对象去获取父类的属性值。**