1.Class类

1.1有三种方式获取到Class的对象

1.2 可以使用 "=="

1.3 newInstance() 方法

1.4 forName 与 newInstance() 配合

2. 分析类

2.1 Field、Method 和 Constructor

3. 运行时获取域的值

4. 调用方法


1.Class类

在java运行时系统中,每当成功加载了一个类,都会为之创建一个Class类型的对象来存储这个类的信息。比如:

在Person类被加载成功时,java运行时系统会为之创建一个Class对象,这个对象存储了Person类的相关信息。

public Person

{

    private String name;

    public Person(String name) {this.name = name;}

    public String getName() { return name;}

}

1.1有三种方式获取到Class的对象

第一种:

Person p = new Person();

Class class = p.getClass();

使用上面的方式即可获取到这个对象。最常用的Class方法是getName。

System.out.println(p.getClass().getName() + " : " + p.getName());

第二种:

如果已知类名,则可以通过Class.forName(className)获取到Class对象。

String className = "java.lang.String";

Class cl = Class.forName(className);

第三种:

如果T是任意的Java类型(或void关键字),T.class将代表匹配的对象。例如:

Class cl1 = Random.class;

Class cl2 = int.class;

Class cl3 = Double[].class;

注:一个Class表示的是一个类型,但是并不一定是类。例如,int就不是一个类。

1.2 可以使用 "=="

虚拟机为每个类型管理一个Class对象,所以不需要使用equal(),可以直接使用==进行两个类对象的比较。

if(p.getClass() == Person.class)

1.3 newInstance() 方法

可以使用newInstance()方法来动态创建一个类的实例。例如:

p.getClass().newInstance();

newInstance调用默认的构造器。如果没有默认构造器,即存在有参数的构造器,则会报错。所以上文中的Person类的构造方法要改成

public Person() {this.name = "a";}

如果需要使用有参数的构造器,则可以使用Constructor类中的newInstance()方法,见下文。

1.4 forName 与 newInstance() 配合

forName与newInstance()配合可以根据字符串创建对象;

String s = "java.util.Random";

Object o = Class.forName(s).newInstance;

 

2. 分析类

2.1 Field、Method 和 Constructor

在java.lang.reflect包中,有三个类Field、Method和Constructor分别描述类的域、方法和构造器。

通过Class类中的getFields、getMethods和getConstructors可获得类提供的public类型的域、方法和构造器,其中包括父类的public成员。

通过Class类中的getDeclareFields、getDeclareMethods和getDeclaredConsturctors可获得类中声明的全部的域、方法和构造器,包括非公有的。但是不包括父类的任何成员。

Field[] getFields();

Field[] getDeclaredFields();

Method[] getMethods();

Method[] getDeclaredMethods();

Constructor[] geConstructors();

Constructor[] getDeclaredConstructors();

Field、Method、Constructor的方法 :

String getName(); //返回描述Field、Method、Constructor的字符串

Class getType(); //返回描述Field所属类型的Class对象

int getModifiers(); //返回描述Field、Method、Constructor修饰符的整型值。再通过Modifier类中的静态方法对这个整型值进行分析。例如,Modifier.isPublic()判断是否是公有,Modifier.isFinal()是否是final。

Class[ ] getParameterTypes(); //返回描述Method、Constructor参数类型的数组。

Class[] getReturnType(); //返回一个Method的返回值类型。

 

3. 运行时获取域的值

利用反射机制可以查看运行时对象域的值,方式如下:

Class c = Person.class; // 获取Person的类型

Field f = c.getDeclaredField("name"); //使用此方法可以获取指定域的类型

Person p = new Person("a"); //创建Person对象

Object v = f.get(p); //通过Field的get方法即可获得内容

实际上这段代码有一个问题,由于name是一个私有域,f.get()会抛出IllegalAccessException异常。反射默认行为受限于Java的访问控制。要达到能访问的目的,需要调用Field、Method或Constructor对象的setAccessible方法。例如:

f.setAccessible(true); // 执行之后,就可以访问了

setAccessible 方法是 AccessibleObject 类中的一个方法, 它是 Field、 Method 和 Constructor 类的公共超类。

get方法还有一个需要解决的问题,上面代码中name域的类型是String所以可以传给Object。如果是数值类怎么办?数值类不是对象,例如,double。这时,可以使用Field类中的getDouble()方法,也可以直接调用get()方法,如果直接调用get(),则会通过自动包装,把值包装成Double。

可以读取也可以设置。f.set(obj,value)可以将obj对象的域设置成新值。

4. 调用方法

类似于上一节中通过Field的get()方法获取域的值。可以通过Method的invoke()方法调用方法。注意,由于

但是,由于同一个方法可能有不同的参数,所以获取方法需要额外的参数。

Method getMethod(String name, Class... parameterTypes)

举例如下:

public Person

{

    private String name;

    public Person(String name) {this.name = name;}

    public String getName() { return name;}

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

    public static void printHello() { System.out.println("Hello")}

}

Class cl = Person.class; // 获取类型

Method m1 = cl.getMethod("getName"); // 获取getName方法

Method m2 = cl.getMethod("setName",String.class); //获取setName方法

Method m3 = cl.getMethod("printHello"); //获取printHello方法

Person p = new Person("PersonA");

String s1 = (String)m1.invoke(p); //执行getName

System.out.println("s1:" + s1);

m2.invoke(p4, "personB"); //执行setName

System.out.println("name:" + p.getName());

m3.invoke(null);//执行printHello,静态方法,参数可填null

结果:

s1:personA

name:personB

hello