本文转自Java反射并加以修改

概念

JAVA反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。反射被视为动态语言的关键。
反射主要是指程序可以访问,检测和修改它本身状态或行为的一种能力,并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义。

程序中一般的对象的类型都是在编译期就确定下来的,而Java反射机制可以动态地创建对象并调用其属性,这样的对象的类型在编译期是未知的。即我们可以通过反射机制直接创建对象,即使这个对象的类型在编译期是未知的。

反射的核心是JVM在运行时才动态加载类或调用方法/访问属性,它不需要事先(写代码的时候或编译期)知道运行对象是谁。

反射机制主要提供以下功能:

  1. 在运行时判断任意一个对象所属的类;
  2. 在运行时构造任意一个类的对象;
  3. 在运行时判断任意一个类所具有的成员变量和方法;
  4. 在运行时调用任意一个对象的方法;
  5. 生成动态代理。

重点:是运行时而不是编译时

实际使用:

  1. 当我们在使用IDE(如Eclipse,IDEA)时,当我们输入一个对象或类并想调用它的属性或方法时,一按点号,编译器就会自动列出它的属性或方法,这里就会用到反射。
  2. 反射最重要的用途就是开发各种通用框架。
    很多框架(比如Spring)都是配置化的(比如通过XML文件配置JavaBean,Action之类的),为了保证框架的通用性,它们可能需要根据配置文件加载不同的对象或类,调用不同的方法,这个时候就必须用到反射——运行时动态加载需要加载的对象。

对于框架开发人员来说,反射虽小但作用非常大,它是各种容器实现的核心。而对于一般的开发者来说,不深入框架开发则用反射用的就会少一点,不过了解一下框架的底层机制有助于丰富自己的编程思想,也是很有益的。

Class类

Class可以说是反射能够实现的基础

  1. class关键字是在声明java类时使用的;而 Class 是java JDK提供的一个类,一个描述类的类(也就是描述类本身),封装了描述方法的Method,描述字段的Filed,描述构造器的Constructor等属性,完整路径为 java.lang.Class
  2. 对于每一个类,Java虚拟机都会初始化出一个Class类型的实例,每当我们编写并且编译一个新创建的类就会产生一个对应Class对象,并且这个Class对象会被保存在同名.class文件里。
  3. 当我们new一个新对象或者引用静态成员变量时,Java虚拟机(JVM)中的类加载器系统会将对应Class对象加载到JVM中,然后JVM再根据这个类型信息相关的Class对象创建我们需要实例对象或者提供静态变量的引用值。

Class对象构造器是私有的,只有JVM才可以调用这个构造函数创建Class的对象
4. 每个class(注意class是小写,代表普通类)类,无论创建多少个实例对象,在JVM中都对应同一个Class对象。
5. Class是反射能够实现的基础的另一个原因是:Java反射包java.lang.reflect中的所有类都没有public构造方法,要想获得这些类实例,只能通过Class类获取。所以说如果想使用反射,必须得获得Class对象。

基本运用

获取Class对象

  1. 通过类的类型获取Class对象,基本类型同样可以使用这种方法

    1
    2
    3
    4
    //Class<?>是一个泛型表示,用于获取一个类的类型。
    Class<?> c = boolean.class;

    Class<?> classInt = Integer.TYPE;
  2. 通过对象实例获取对应Class对象Object.getClass()–对于基本类型无法使用这种方法

    1
    2
    StringBuilder str = new StringBuilder("123");
    Class<?> klass = str.getClass();
  3. 使用Class类的forName静态方法(通过类的全限定名)

    1
    public static Class<?> forName(String className)

    在JDBC开发中常用此方法加载数据库驱动:
    要使用全类名来加载这个类,一般数据库驱动的配置信息会写在配置文件中。加载这个驱动前要先导入jar包

    1
    Class.forName(driver);
  4. 另外还有一些反射方法可以获取Class对象,但前提是你已经获取了一个Class对象

    1
    Class.getSuperclass()//获得给定类的父类Class
    • Class.getClasses()
    • Class.getDeclaredClasses()
    • Class.getDeclaringClass()
    • Class.getEnclosingClass()
    • java.lang.reflect.Field.getDeclaringClass()
    • java.lang.reflect.Method.getDeclaringClass()
    • java.lang.reflect.Constructor.getDeclaringClass()

示例:通过Class获取类修饰,类型等

20200815140112

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
public class TestReflection {
private static final String TAG = "Reflection";

public void testReflection() {
Class<?> c = HashMap.class;
//获取类名
System.out.println(TAG + "Class : " + c.getCanonicalName());
//获取类限定符
System.out.println(TAG + "Modifiers : " + Modifier.toString(c.getModifiers()));
//获取类泛型信息
TypeVariable<?>[] tv = c.getTypeParameters();
if (tv.length != 0) {
StringBuilder parameter = new StringBuilder("Parameters : ");
for (TypeVariable<?> t : tv) {
parameter.append(t.getName());
parameter.append(" ");
}
System.out.println(TAG + parameter.toString());
} else {
System.out.println(TAG + " -- No Type Parameters --");
}
//获取类实现的所有接口
Type[] intfs = c.getGenericInterfaces();
if (intfs.length != 0) {
StringBuilder interfaces = new StringBuilder("Implemented Interfaces : ");
for (Type intf : intfs) {
interfaces.append(intf.toString());
interfaces.append(" ");
}
System.out.println(TAG + interfaces.toString());
} else {
System.out.println(TAG + " -- No Implemented Interfaces --");
}
//获取类继承数上的所有父类
List<Class> l = new ArrayList<>();
printAncestor(c, l);
if (l.size() != 0) {
StringBuilder inheritance = new StringBuilder("Inheritance Path : ");
for (Class<?> cl : l) {
inheritance.append(cl.getCanonicalName());
inheritance.append(" ");
}
System.out.println(TAG + inheritance.toString());
} else {
System.out.println(TAG + " -- No Super Classes --");
}
//获取类的注解(只能获取到 RUNTIME 类型的注解)
Annotation[] ann = c.getAnnotations();
if (ann.length != 0) {
StringBuilder annotation = new StringBuilder("Annotations : ");
for (Annotation a : ann) {
annotation.append(a.toString());
annotation.append(" ");
}
System.out.println(TAG + annotation.toString());
} else {
System.out.println(TAG + " -- No Annotations --");
}
}

private static void printAncestor(Class<?> c, List<Class> l) {
Class<?> ancestor = c.getSuperclass();
if (ancestor != null) {
l.add(ancestor);
printAncestor(ancestor, l);
}
}

public static void main(String[] args) {
new TestReflection().testReflection();
}
}
1
2
3
4
5
6
7
// 输出结果
// ReflectionClass : java.util.HashMap
// ReflectionModifiers : public
// ReflectionParameters : K V
// ReflectionImplemented Interfaces : java.util.Map<K, V> interface java.lang.Cloneable interface java.io.Serializable
// ReflectionInheritance Path : java.util.AbstractMap java.lang.Object
// Reflection -- No Annotations --

判断是否为某个类的实例

一般地,我们用instanceof关键字来判断是否为某个类的实例。同时我们也可以借助反射中Class对象的isInstance()方法来判断是否为某个类的实例,它是一个Native方法:

1
public native boolean isInstance(Object obj);

创建实例

通过反射来生成对象主要有两种方式。

  1. 使用Class对象的newInstance()方法来创建Class对象对应类的实例。

    利用newInstance创建对象:调用的类必须有无参的构造器

    1
    2
    3
    4
    5
    6
    7
    8
    //Class<?>代表任何类的一个Class对象。
    //使用这个Class对象可以为其他类进行实例化
    //因为jvm加载类以后自动在堆区生成一个对应的*.Class对象
    //该对象用于让JVM对进行所有*对象实例化。
    Class<?> c = String.class;
    //Class<?> 中的 ? 是通配符,其实就是表示任意符合泛类定义条件的类,和直接使用 Class效果基本一致。
    //但是这样写更加规范,在某些类型转换时可以避免不必要的 unchecked 错误。
    Object str = c.newInstance();
  2. 先通过Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建实例。这种方法可以用指定的构造器构造类的实例。

    1
    2
    3
    4
    5
    6
    //获取String所对应的Class对象
    Class<?> c = String.class;
    //获取String类带一个String参数的构造器
    Constructor constructor = c.getConstructor(String.class);
    //根据构造器创建实例
    Object obj = constructor.newInstance("23333");

Member

类成员主要包括构造函数变量方法,Java中的操作基本都和这三者相关,而Member的这三个实现类就分别对应他们。

  • java.lang.reflect.Field :对应类变量
  • java.lang.reflect.Method :对应类方法
  • java.lang.reflect.Constructor :对应类构造函数

反射就是通过这三个类才能在运行时改变对象状态

突破java的权限检测

Java运行时会进行访问权限检查,private类型的变量无法进行直接访问
java.lang.reflect.AccessibleObject
AccessibleObject为我们提供了一个方法 setAccessible(boolean flag),该方法的作用就是可以取消 Java 语言访问权限检查。
所以任何继承AccessibleObject的类的对象都可以使用该方法取消 Java 语言访问权限检查。(final类型变量也可以通过这种办法访问)

1
public final class Field extends AccessibleObject implements Member

Field、Method和Constructor都是继承AccessibleObject

测试类

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
//注解类,可用于表示方法,可以通过反射获取注解的内容。
//Java注解的实现是很多注框架实现注解配置的基础
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface Invoke {
}

// UserBean的父类PersonBean
class PersonBean {
private String name;
int id;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}

// UserBean实现的接口User
interface User {
public void login();
}


public class UserBean extends PersonBean implements User {
@Override
public void login() {
System.out.println("login");
}

public void eat(String food) {
System.out.println("eat food " + food);
}

public void eat(String... foods) {
StringBuilder s = new StringBuilder();
for (String food : foods) {
s.append(food);
s.append(" ");
}
System.out.println("eat food " + s.toString());
}

class B {
}

public String userName;
protected int i;
static int j;
@Deprecated
private int k;
private long userId;

public UserBean() {
}

public UserBean(String userName, long userId) {
this.userName = userName;
this.userId = userId;
}

@Override
public String getName() {
return userName;
}

public long getId() {
return userId;
}

@Invoke
public static void staticMethod(String devName, int a) {
System.out.printf("Hi %s, I'm a static method", devName);
}

@Invoke
public void publicMethod() {
System.out.println("I'm a public method");
}

@Invoke
private void privateMethod() {
System.out.println("I'm a private method");
}

@Override
public String toString() {
return "UserBean{" +
"userName='" + userName + '\'' +
", userId=" + userId +
'}';
}
}

Field

通过Field你可以访问给定对象的类变量,包括获取变量的类型、修饰符、注解、变量名、变量的值或者重新设置变量值,即使变量是private的。

获取Field

Class提供了4种方法获得给定类的Field。

1
2
3
4
5
6
7
8
getDeclaredField(String name)
// 获取指定的变量(只要是声明的变量都能获得,包括private)
getField(String name)
// 获取指定的变量(只能获得public的)
getDeclaredFields()
// 获取所有声明的变量(包括private)
getFields()
// 获取所有的public变量
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 void testField() {
Class c = UserBean.class;
Field[] fields = c.getDeclaredFields();
for (Field f : fields) {
StringBuilder builder = new StringBuilder();
//获取名称
builder.append("filed name = ");
builder.append(f.getName());
//获取类型
builder.append(" type = ");
builder.append(f.getType());
//获取修饰符
builder.append(" modifiers = ");
builder.append(Modifier.toString(f.getModifiers()));
//获取注解
Annotation[] ann = f.getAnnotations();
if (ann.length != 0) {
builder.append(" annotations = ");
for (Annotation a : ann) {
builder.append(a.toString());
builder.append(" ");
}
} else {
builder.append(" -- No Annotations --");
}
System.out.println(builder.toString());
}
}

// 输出结果:
// filed name = userName type = class java.lang.String modifiers = public -- No Annotations --
// filed name = i type = int modifiers = protected -- No Annotations --
// filed name = j type = int modifiers = static -- No Annotations --
// filed name = k type = int modifiers = private annotations = @java.lang.Deprecated()
// filed name = userId type = long modifiers = private -- No Annotations --

获取、设置变量值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public void setField() {
UserBean userBean = new UserBean("Tom", 2);
Class c = userBean.getClass();
try {
Field fieldName = c.getField("userName");
//注意获取private变量时,需要用getDeclaredField
Field fieldId = c.getDeclaredField("userId");
fieldName.setAccessible(true);
//反射获取名字, 年龄
String name = (String) fieldName.get(userBean);
long age = fieldId.getLong(userBean);
System.out.println("before set, UserBean userName = " + name + " userId = " + age);
//反射重新set名字和年龄
fieldName.set(userBean, "Timmy");
fieldId.setInt(userBean, 3);
System.out.println("after set, UserBean " + userBean.toString());
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
}

// 输出结果:
// before set, UserBean userName = Tom userId = 2
// after set, UserBean UserBean{userName='Timmy', userId=3}

Method

获取方法

Class依然提供了4种方法获取Method:

1
2
3
4
5
6
7
8
9
10
11
getDeclaredMethods()
// 获取所有声明的方法
// 包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。
getMethods()
// 获取所有的public方法
// 包括其继承类的公用方法。
getDeclaredMethod(String name, Class<?>... parameterTypes)
// 根据方法名获得指定的方法,参数name为方法名,参数parameterTypes为方法的参数类型
// 如 getDeclaredMethod(“eat”, String.class)
getMethod(String name, Class<?>... parameterTypes)
// 根据方法名获取指定的public方法,其它同上

注意:获取带参数方法时,如果参数类型错误会报NoSuchMethodException,对于参数是泛型的情况,泛型须当成Object处理(Object.class)

getMethods()和getDeclaredMethods()区别:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public void getMethod() {
Class clazz = UserBean.class;
// getDeclaredMethod*()获取的是类自身声明的所有方法,包含public、protected和private方法。
System.out.println("------共有方法------");
// getDeclaredMethod*()获取的是类自身声明的所有方法,包含public、protected和private方法。
// getMethod*()获取的是类的所有共有方法,这就包括自身的所有public方法,和从基类继承的、从接口实现的所有public方法。
for (Method method : clazz.getMethods()) {
String name = method.getName();
System.out.println(name);
//打印出了UserBean.java的所有方法以及父类的方法
}
System.out.println("------独占方法------");
for (Method method : clazz.getDeclaredMethods()) {
String name = method.getName();
System.out.println(name);
}
}

获取所有方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public void getAllMethods() {
Class c = UserBean.class;
for (Method method : c.getDeclaredMethods()) {
//依次获得方法的修饰符,返回类型和名称,外加方法中的参数
// private static
StringBuilder methodString = new StringBuilder(Modifier.toString(method.getModifiers()) + " ");
// void
methodString.append(method.getReturnType().getSimpleName()).append(" ");
// staticMethod
methodString.append(method.getName()).append("(");
Class[] parameters = method.getParameterTypes();
Parameter[] p = method.getParameters();
for (Class parameter : parameters) {
// String
methodString.append(parameter.getSimpleName()).append(" ");
}
methodString.append(")");
System.out.println(methodString.toString());
}
}

获取方法返回类型

  • getReturnType():获取目标方法返回类型对应的Class对象
  • getGenericReturnType():获取目标方法返回类型对应的Type对象

这两个方法有啥区别呢?

  1. getReturnType()返回类型为Class,getGenericReturnType()返回类型为Type, Class实现Type。

  2. 返回值为普通简单类型如Object, int, String等,getGenericReturnType()返回值和getReturnType()一样

    例如public String function1(),那么各自返回值为:

    • getReturnType():class java.lang.String
    • getGenericReturnType():class java.lang.String
  3. 返回值为泛型
    例如public T function2(),那么各自返回值为:

    • getReturnType():class java.lang.Object
    • getGenericReturnType():T
  4. 返回值为参数化类型
    例如public Class<String> function3(),那么各自返回值为:

    • getReturnType():class java.lang.Class
    • getGenericReturnType():java.lang.Class<java.lang.String>

    其实反射中所有形如getGenericXXX()的方法规则都与上面所述类似。

获取方法参数类型

  • getParameterTypes():获取目标方法各参数类型对应的Class对象
  • getGenericParameterTypes():获取目标方法各参数类型对应的Type对象

返回值为数组,区别同上 “方法返回类型的区别” 。

获取方法声明抛出的异常的类型

  • getExceptionTypes():获取目标方法抛出的异常类型对应的Class对象
  • getGenericExceptionTypes():获取目标方法抛出的异常类型对应的Type对象

返回值为数组,区别同上

获取方法参数名称

.class文件中默认不存储方法参数名称,如果想要获取方法参数名称,需要在编译的时候加上-parameters参数。(构造方法的参数获取方法同样)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 这里的m可以是普通方法Method,也可以是构造方法Constructor
// 获取方法所有参数
Parameter[] params = m.getParameters();
for (int i = 0; i < params.length; i++) {
Parameter p = params[i];
// 获取参数类型
p.getType();
// 获取参数名称,如果编译时未加上`-parameters`,返回的名称形如`argX`
// X为参数在方法声明中的位置,从0开始
p.getName();
// 获取参数修饰符
p.getModifiers();
// .class文件中是否保存参数名称, 编译时加上`-parameters`返回true,反之flase
p.isNamePresent();
}

获取方法修饰符

方法与Filed等类似

1
method.getModifiers();

几个Method方法

  1. method.isVarArgs():判断方法参数是否是可变参数

    1
    2
    public Constructor<T> getConstructor(Class<?>... parameterTypes)  //返回true
    public Constructor<T> getConstructor(Class<?> [] parameterTypes) //返回flase
  2. method.isSynthetic():判断是否是复合方法,个人理解复合方法是编译期间编译器生成的方法,并不是源代码中有的方法

  3. method.isBridge():判断是否是桥接方法,桥接方法是 JDK 1.5 引入泛型后,为了使Java的泛型方法生成的字节码和 1.5 版本前的字节码相兼容,由编译器自动生成的方法

通过反射调用方法

反射通过Method的invoke()方法来调用目标方法。第一个参数为需要调用的目标类对象,如果方法为static的,则该参数为null。后面的参数都为目标方法的参数值,顺序与目标方法声明中的参数顺序一致。

1
2
public native Object invoke(Object obj, Object... args)
throws IllegalAccessException, IllegalArgumentException, InvocationTargetException

注意:如果方法是private的,可以使用method.setAccessible(true)方法绕过权限检查

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
public void testMethod(){
Class<?> c = UserBean.class;
try {
//构造UserBean实例
Constructor constructor = c.getConstructor(String.class, long.class);
Object userBean = constructor.newInstance( "Jack", 3);
//调用无参方法
Method sleep = c.getDeclaredMethod("login");
sleep.invoke(userBean);
//调用定项参数方法
Method eat = c.getDeclaredMethod("eat", String.class);
eat.invoke(userBean, "meat");
//调用不定项参数方法
//不定项参数可以当成数组来处理
Class[] argTypes = new Class[] { String[].class };
Method varargsEat = c.getDeclaredMethod("eat", argTypes);
String[] foods = new String[]{
"meat", "vegetable"
};
varargsEat.invoke(userBean, (Object)foods);
} catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
e.printStackTrace();
}
}

// login
// eat food meat
// eat food meat vegetable

被调用的方法本身所抛出的异常在反射中都会以InvocationTargetException抛出。换句话说,反射调用过程中如果异常InvocationTargetException抛出,说明反射调用本身是成功的,因为这个异常是目标方法本身所抛出的异常。

Constructor

和Method一样,Class也为Constructor提供了4种方法获取

1
2
3
4
5
6
7
8
getDeclaredConstructor(Class<?>... parameterTypes)
// 获取指定构造函数,参数parameterTypes为构造方法的参数类型
getConstructor(Class<?>... parameterTypes)
// 获取指定public构造函数,参数parameterTypes为构造方法的参数类型
getDeclaredConstructors()
// 获取所有声明的构造方法
getConstructors()
// 获取所有的public构造方法

构造方法的名称、限定符、参数、声明的异常等获取方法都与Method类似,请参照Method。

之前见过,有两种方式创建实例

  1. java.lang.reflect.Constructor.newInstance()
    2, Class.newInstance()

区别:

  1. Class.newInstance()仅可用来调用无参的构造方法;Constructor.newInstance()可以调用任意参数的构造方法
  2. Class.newInstance()会将构造方法中抛出的异常不作处理原样抛出;Constructor.newInstance()会将构造方法中抛出的异常都包装成InvocationTargetException抛出。
  3. Class.newInstance()需要拥有构造方法的访问权限;Constructor.newInstance()可以通过setAccessible(true)方法绕过访问权限访问private构造方法。
1
2
3
4
5
6
7
Class<?> c = UserBean.class;
try {
Constructor constructor = c.getConstructor(String.class, long.class);
UserBean userBean = (UserBean) constructor.newInstance( "Jack", 3);
} catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
e.printStackTrace();
}

注意:反射不支持自动封箱,传入参数时要小心(自动封箱是在编译期间的,而反射在运行期间)

数组和枚举

数组和枚举也是对象,但是在反射中,对数组和枚举的创建、访问和普通对象有那么一丢丢的不同,所以Java反射为数组和枚举提供了一些特定的API接口。

数组

数组类型

数组类型:数组本质是一个对象,所以它也有自己的类型。
例如对于int[] intArray,数组类型为class [I。数组类型中的[个数代表数组的维度,例如[代表一维数组,[[代表二维数组;[后面的字母代表数组元素类型,I代表int,一般为类型的首字母大写(long类型例外,为J)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// byte类型一维数组
class [B
// short类型一维数组
class [S
// int类型一维数组
class [I
// char类型一维数组
class [C
// long类型一维数组,J代表long类型,因为L被引用对象类型占用了
class [J
// float类型一维数组
class [F
// double类型一维数组
class [D
// 引用类型一维数组
class [Lcom.dada.Season
// 引用类型二维数组
class [[Ljava.lang.String
1
2
3
4
5
6
7
//获取一个变量的类型
Class<?> c = field.getType();
//判断该变量是否为数组
if (c.isArray()) {
//获取数组的元素类型
c.getComponentType()
}

创建和初始化数组

Java反射为我们提供了java.lang.reflect.Array类用来创建和初始化数组。

1
2
3
4
5
6
//创建数组,参数componentType为数组元素的类型,后面不定项参数的个数代表数组的维度,参数值为数组长度
Array.newInstance(Class<?> componentType, int... dimensions)
//设置数组值,array为数组对象,index为数组的下标,value为需要设置的值
Array.set(Object array, int index, int value)
//获取数组的值,array为数组对象,index为数组的下标
Array.get(Object array, int index)

例:用反射创建int[] array = new int[]{1, 2}

1
2
3
Object array = Array.newInstance(int.class, 2);
Array.setInt(array , 0 , 1);
Array.setInt(array , 1 , 2);

注意:反射支持数据自动转换,但不允许数据强制转换。意思是对于上述set方法,你可以在int类型数组中 set short类型数据,但不可以set long类型数据,否则会报IllegalArgumentException

多维数组

Java反射没有提供能够直接访问多维数组元素的API,但你可以把多维数组当成数组的数组处理。

1
2
3
4
5
6
7
8
Object matrix = Array.newInstance(int.class, 2, 2);
Object row0 = Array.get(matrix, 0);
Object row1 = Array.get(matrix, 1);

Array.setInt(row0, 0, 1);
Array.setInt(row0, 1, 2);
Array.setInt(row1, 0, 3);
Array.setInt(row1, 1, 4);

1
2
3
4
5
6
7
8
9
10
11
Object matrix = Array.newInstance(int.class, 2);
Object row0 = Array.newInstance(int.class, 2);
Object row1 = Array.newInstance(int.class, 2);

Array.setInt(row0, 0, 1);
Array.setInt(row0, 1, 2);
Array.setInt(row1, 0, 3);
Array.setInt(row1, 1, 4);

Array.set(matrix, 0, row0);
Array.set(matrix, 1, row1);

枚举

枚举隐式继承自java.lang.Enum,Enum继承自Object,所以枚举本质也是一个类,也可以有成员变量,构造方法,方法等;对于普通类所能使用的反射方法,枚举都能使用;另外java反射额外提供了几个方法为枚举服务。
枚举隐式继承自java.lang.Enum,Enum继承自Object,所以枚举本质也是一个类,也可以有成员变量,构造方法,方法等;对于普通类所能使用的反射方法,枚举都能使用;另外java反射额外提供了几个方法为枚举服务。

1
2
3
4
5
6
Class.isEnum()
// Indicates whether this class represents an enum type
Class.getEnumConstants()
// Retrieves the list of enum constants defined by the enum in the order they're declared
java.lang.reflect.Field.isEnumConstant()
// Indicates whether this field represents an element of an enumerated type

其他方法

注解中常用方法:

1
2
3
4
5
6
7
8
// 获取class对象的所有注解
Annotation[] annotations = (Annotation[]) class1.getAnnotations();
// 获取class对象指定注解
Annotation annotation = (Annotation) class1.getAnnotation(Deprecated.class);
// 获取class对象的直接超类
Type genericSuperclass = class1.getGenericSuperclass();
// 获取class对象的所有接口的type集合
getGenericInterfaces();

获取Class对象其他信息方法:

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
// 判断是否是基础类型
boolean isPrimitive = class1.isPrimitive();
// 判断是否是集合类
boolean isArray = class1.isArray();
// 判断是否是注解类
boolean isAnnotation = class1.isAnnotation();
// 判断是否是接口类
boolean isInterface = class1.isInterface();
// 判断是否是枚举类
boolean isEnum = class1.isEnum();
// 判断是否是匿名内部类
boolean isAnonymousClass = class1.isAnonymousClass();
// 判断是否被某个注解类修饰
boolean isAnnotationPresent = class1.isAnnotationPresent(Deprecated.class);
// 获取class名字 包含包名路径
String className = class1.getName();
// 获取class的包信息
Package aPackage = class1.getPackage();
// 获取class类名
String simpleName = class1.getSimpleName();
// 获取class访问权限
int modifiers = class1.getModifiers();
// 内部类
Class<?>[] declaredClasses = class1.getDeclaredClasses();
// 外部类
Class<?> declaringClass = class1.getDeclaringClass();
// 返回类加载器
ClassLoader ClassLoader = class1.getClassLoader()

// 获取某类所有的父类
getSuperclass():
// 获取某类所有实现的接口
getInterfaces():

静态元素

静态的类,方法,字段和实例类,方法,字段完全不一样,因为它无需初始化类就可以直接使用。

反射缺点

  • 性能问题。因为反射是在运行时而不是在编译时,所有不会利用到编译优化,同时因为是动态生成,因此,反射操作的效率要比那些非反射操作低得多。
  • 安全问题。使用反射技术要求程序必须在一个没有安全限制的环境中运行。如果一个程序必须在有安全限制的环境中运行,如Applet,那么这就是个问题了。
  • 代码问题。由于反射允许代码执行一些在正常情况下不被允许的操作(比如访问私有的属性和方法),所以使用反射可能会导致意料之外的副作用--代码有功能上的错误,降低可移植性。反射代码破坏了抽象性,因此当平台发生改变的时候,代码的行为就有可能也随着变化。

参考

Java反射
Java基础13:反射详解

代理模式