Java反射
通过
Class
实例获取class
信息的方法称为反射(Reflection)
Class类
获取一个class
的Class
实例有三种方法:
- 直接通过一个
class
的静态变量class
获取
Class cls = String.class;
- 如已有一个实例变量,可通过该实例变量的
getClass()
方法获取
String s = "Hello";
Class cls = s.getClass();
- 如已知道一个
class
的完整类名,可通过静态方法Class.forName()
获取
Class cls = Class.forName("java.lang.String");
反射的目的是为了获得某个实例的信息。因此,当我们拿到某个Object
实例时,我们可以通过反射获取该Object
的class
信息:
public class Main {
public static void main(String[] args) {
printClassInfo(String[].class);
}
static void printClassInfo(Class cls) {
System.out.println("Class name: " + cls.getName());
//Class name: [Ljava.lang.String;
System.out.println("Simple name: " +cls.getSimpleName()); //Simple name: String[]
if (cls.getPackage() != null) {
System.out.println("Package name: " + cls.getPackage().getName());
}
System.out.println("is interface: " + cls.isInterface()); // is interface: false
System.out.println("is enum: " + cls.isEnum()); //is enum: false
System.out.println("is array: " + cls.isArray()); //is array: true
System.out.println("is primitive: " + cls.isPrimitive()); //is primitive: false
}
}
也可以根据一个已知的Class
实例来创建对应类型的新实例:
// 获取String的Class实例:
Class cls = String.class;
// 创建一个String实例:
String s = (String) cls.newInstance();
上述代码相当于new String()
。通过Class.newInstance()
可以创建类实例,它的局限是:只能调用public
的无参数构造方法。带参数的构造方法,或者非public
的构造方法都无法通过Class.newInstance()
被调用。
访问字段
通过
Class
实例,获取所有Field
对象。
Class
类提供了以下几个方法来获取Field
字段:
Field getField(name)
:根据字段名获取某个public
的field
(包括父类)Field getDeclaredField(name)
:根据字段名获取当前类的某个field
(不包括父类)Field[] getFields()
:获取所有public
的field
(包括父类)Field[] getDeclaredFields()
:获取当前类的所有field
(不包括父类)
一个Field
对象包含了一个字段的所有信息:
getName()
:返回字段名称,例如,"name"
;getType()
:返回字段类型,也是一个Class
实例,例如,String.class
;getModifiers()
:返回字段的修饰符,它是一个int
,不同的bit表示不同的含义。
public static void main(String[] args) throws NoSuchFieldException {
Field f = People.class.getDeclaredField("name");
f.getName(); // "name"
f.getType(); // class java.lang.String
int m = f.getModifiers();
Modifier.isFinal(m); // false
Modifier.isPublic(m); // false
Modifier.isProtected(m); // false
Modifier.isPrivate(m); // true
Modifier.isStatic(m); // false
}
public final class People{
private String name;
public People(String name) {
this.name = name;
}
}
获取字段值
对于一个People
实例,我们可以先拿到name
字段对应的Field
,再获取这个实例的name
字段的值:
public class Main {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
Object p = new People("ASHER");
Field f = p.getClass().getDeclaredField("name");
f.setAccessible(true); //别管这个字段是public还是private,一律允许访问!暴力!
System.out.println(f.get(p));
}
}
class People{
private String name;
public People(String name) {
this.name = name;
}
}
注意:上述代码People
类的name
字段是private
,如果不加**f.setAccessible(true);
**,由于Main
类无法访问Person
类的private
字段,就会得到一个IllegalAccessException
。
设置字段值
通过Field
实例不仅可以获取字段值,也可以设置字段值,通过Field.set(Object,Object)
实现,其中第一个Object
参数是指定的实例,第二个Object
参数是待修改的值:
public class Main {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
People p = new People("ASHER");
Field f = p.getClass().getDeclaredField("name");
f.setAccessible(true);
System.out.println("原名:" + f.get(p)); //原名:ASHER
f.set(p,"asher");
System.out.println("现名:" + p.getName()); //现名:asher
}
}
class People{
private String name;
public People(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
调用方法
通过
Class
实例,获取所有Method
信息。
Class
类提供了以下几个方法来获取Method
:
Method getMethod(name, Class...)
:获取某个public
的Method
(包括父类)Method getDeclaredMethod(name, Class...)
:获取当前类的某个Method
(不包括父类)Method[] getMethods()
:获取所有public
的Method
(包括父类)Method[] getDeclaredMethods()
:获取当前类的所有Method
(不包括父类)
调用方法
getMethod("substring", int.class, int.class)
就是获取这个class
类的"substring
"方法,然后传入该方法所需的两个参数的类invoke()
就是调用m
这个方法,第一个参数是指定在哪个实例上是使用该方法
String s1 = "Hello world";
Method m = s1.getClass().getMethod("substring", int.class, int.class);
String s2 = (String)m.invoke(s1,6,8); //wo
调用静态方法
调用静态方法时,由于无需指定实例对象,所以invoke
方法传入的第一个参数永远为null
下面就是调用Integer
的getMethod()
方法,将String
型的字符串转为Integer
型:
Method m = Integer.class.getMethod("parseInt", String.class);
Integer s = (Integer)m.invoke(null,"123"); //123
调用非public方法
对于非public的方法,首先要对该方法设置Method.setAccessible(true)
,然后再使用Class.getDeclaredMethod()
获取。
public class Main {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
People p = new People();
Method m = p.getClass().getDeclaredMethod("getName",null);
m.setAccessible(true);
String s = (String)m.invoke(p,null); //asher
}
}
class People{
private String name = "asher";
private String getName() {
return name;
}
}
获取继承关系
获取父类的Class
Integer
的父类类型是Number
,Number
的父类是Object
,Object
的父类是null
。
除Object
外,其他任何非interface
的Class
都必定存在一个父类类型。
Class i = Integer.class;
Class n = i.getSuperclass(); //class java.lang.Number
Class o = n.getSuperclass(); //class java.lang.Object
System.out.println(o.getSuperclass()); //null
获取interface接口
Class[] getInterfaces()
:获取当前类直接实现的所有接口(不包括父类实现的接口)Class getSuperclass()
:获取父类类型
查询String
实现的接口:
Class cla = String.class;
Class[] clas = cla.getInterfaces();
System.out.println("=====接口为:=====");
for(Class c : clas){
System.out.println(c.getName());
}
System.out.println("=====父类为:=====");
System.out.println(cla.getSuperclass().getName());
=接口为:=
java.io.Serializable
java.lang.Comparable
java.lang.CharSequence
=父类为:=
java.lang.Object
继承关系
instanceof
:判断某个实例是否是某个类型isAssignableFrom()
:判断一个Class
向上转型是否是另一个Class
// Integer i = ?
Integer.class.isAssignableFrom(Integer.class); // true,因为Integer可以赋值给Integer
// Number n = ?
Number.class.isAssignableFrom(Integer.class); // true,因为Integer可以赋值给Number
// Object o = ?
Object.class.isAssignableFrom(Integer.class); // true,因为Integer可以赋值给Object
// Integer i = ?
Integer.class.isAssignableFrom(Number.class); // false,因为Number不能赋值给Integer