Java反射
通过实例获取
Class
信息的方法称为反射(Reflection)
class
Class类
获取一个
class
的
Class
实例有三种方法:
- 直接通过一个
的静态变量class
获取class
- 如已有一个实例变量,可通过该实例变量的
方法获取getClass()
String s = "Hello";
Class cls = s.getClass();
- 如已知道一个
的完整类名,可通过静态方法class
获取Class.forName()
反射的目的是为了获得某个实例的信息。因此,当我们拿到某个
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()
,不同的bit表示不同的含义。int
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