LJ的Blog

学海无涯苦做舟

0%

反射

反射简介

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能成为JAVA语言的反射机制。

在《Java编程思想》中有RTTI(Run-Time Type Identification),通过运行时类型信息程序能使用基类的指针或引用来检查这些指针或引用所指对象的实际派生类型。不过RTTI的定义我是在百度百科上看到的,看了一下很多都是C++的内容,这与这本书的作者先前有C++的背景有关。不过在这里我并不想区分RTTI与我这想要学习的Java中的反射,在学习Java的过程中俺只认反射……

首先谈谈我对于反射的一些认知吧,反射对于** 客户端程序员 **来说可能并没有多大的发挥空间,而且因为反射的性能并不理想,在很多场景都是需要避免使用反射的。但是试想一下我现在要实现一个Json字符串映射到Java实体类的东西,这个时候就需要通过反射来拿到实体类的属性信息了,之后再处理Json字符串,将对应的值赋给实体类对应的属性。

在了解Java反射之前首先需要了解一个东西:Class

Class

类是程序的而一部分,每个类都有一个Class对象。在Java中,所有的类都是在对其第一次使用时,动态加载到JVM中的。因为Java的这个特性,曾经在网上看到过将Java理解为解释型语言的,不过究竟该如何理解才好,可以给出一个大神的博客:R大,感兴趣可以自行阅读。

回到本文,当程序创建第一个对类的静态成员的引用时,就会加载这个类。Java程序在它开始运行前并非完全被加载,其各个部分是在必需时才加载。

前面提到了每个类都有一个Class对象,这个类的所有对象都由其产生的Class对象产生。为了产生这个Class对象,JVM会使用类加载器将这个类加载到JVM中。

下面直接上code来说明一些问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class TestClassType {
//构造函数
public TestClassType() {

System.out.println("构造函数");

}

//静态的参数初始化
static {

System.out.println("静态的参数初始化");

int i = 100;

System.out.println("i=" + i);

}

//非静态的参数初始化
{

System.out.println("非静态的参数初始化");

}

}


public class HelloWorld {
public static void main(String... args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
Class testTypeForName = Class.forName("TestClassType");
TestClassType t = (TestClassType) testTypeForName.newInstance();
}
}

结果

当我使用Class.forName时这个类被载入内存,静态代码块的代码被执行,当我们使用Class对象创建一个该类的对象时,会先初始化非静态的属性和非静态的代码块,之后调用构造器。在《Java编程思想》说构造器也是类的静态方法,这我认为是作者的疏忽。关于这一块的讨论可以戳这里传送门

继续来补全,Class可以说是java反射的核心类,以上简单的介绍了一下Class,以下继续了解反射。

核心方法

  • 成员属性
    getFields():获得类的public类型的属性
    getDeclaredFields():获得类的所有属性
    getField(String name)
    getDeclaredField(String name)

  • 成员方法
    getMethods():获得类的public类型的方法
    getDeclaredMethods():获得类的所有方法
    getMethod(String name, Class[] parameterTypes):获得类的特定方法getDeclaredMethod(String name, Class[] parameterTypes):获得类的特定方法

  • 构造方法:
    getConstructors():获得类的public类型的构造方法。
    getDeclaredConstructors():获得类的所有构造方法。
    getConstructor(Class[] parameterTypes):获得类的特定构造方法getDeclaredConstructor(Class[] params);获得类的特定方法

下面简单的使用其中的一些方法,首先是用来测试的类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package reflect;

/**
* Created by luojun on 2017/1/9.
*/
public class Person {
public String name;
private int age;

public String getaName() {
return name;
}

public int getAge() {
return age;
}

private void setAge(int age){
this.age = age;
}

public Person(String name,int age) {
this.name = name;
this.age = age;
System.out.println("共有构造方法被调用");
}

private Person(){
System.out.println("私有构造方法被调用");
}
}

main:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package reflect;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
* Created by luojun on 2017/1/9.
* desc
*/
public class Test {
public static void main(String... args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class<Person> personClz = Person.class;
Constructor<Person> cons = personClz.getDeclaredConstructor();
cons.setAccessible(true);
Person person = cons.newInstance();

Method m1 = personClz.getDeclaredMethod("setAge", int.class);
System.out.println("使用反射获取到的方法名:" + m1.getName());
m1.setAccessible(true);
Method setAge = personClz
.getDeclaredMethod("setAge", int.class);
setAge.setAccessible(true);
setAge.invoke(person, 16);
System.out.println("使用反射初始化年龄之后年龄的值:" + person.getAge());
}
}

输出:
输出

由于自己现在并非些工具之人,所以对于反射的需求也不是很大,暂时就了解到这。