JAVA反射
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");
通过实例获取类的信息
System.out.println("Class name: " + cls.getName());
System.out.println("Simple name: " + cls.getSimpleName());
if (cls.getPackage() != null) {
System.out.println("Package name: " + cls.getPackage().getName());
}
System.out.println("is interface: " + cls.isInterface());
System.out.println("is enum: " + cls.isEnum());
System.out.println("is array: " + cls.isArray());
System.out.println("is primitive: " + cls.isPrimitive());
// 获取String的Class实例:
Class cls = String.class;
// 创建一个String实例:
String s = (String) cls.newInstance();
上述代码相当于new String()。
通过Class.newInstance()可以创建类实例,
它的局限是:只能调用public的无参数构造方法。
带参数的构造方法,或者非public的构造方法都无法通过Class.newInstance()被调用
动态加载
JVM在执行Java程序的时候,并不是一次性把所有用到的class全部加载到内存,而是第一次需要用到class时才加载
动态加载class的特性对于Java程序非常重要。利用JVM动态加载class的特性,
我们才能在运行期根据条件加载不同的实现类。例如,Commons Logging总是优先使用Log4j,
只有当Log4j不存在时,才使用JDK的logging。利用JVM动态加载特性,大致的实现代码如下:
// Commons Logging优先使用Log4j:
LogFactory factory = null;
if (isClassPresent("org.apache.logging.log4j.Logger")) {
factory = createLog4j();
} else {
factory = createJdkLog();
}
boolean isClassPresent(String name) {
try {
Class.forName(name);
return true;
} catch (Exception e) {
return false;
}
}
这就是为什么我们只需要把Log4j的jar包放到classpath中,Commons Logging就会自动使用Log4j的原因。
访问字段
getField(name):根据字段名获取某个public的field(包括父类)
getDeclaredField(name):根据字段名获取当前类的某个field(不包括父类)
Field[] getFields():获取所有public的field(包括父类)
Field[] getDeclaredFields():获取当前类的所有field(不包括父类)
getName():返回字段名称
getType():返回字段类型
getModifiers():返回字段的修饰符
Field f = String.class.getDeclaredField("value");
f.getName(); // "value"
f.getType(); // class [B 表示byte[]类型
int m = f.getModifiers();
Modifier.isFinal(m); // true
Modifier.isPublic(m); // false
Modifier.isProtected(m); // false
Modifier.isPrivate(m); // true
Modifier.isStatic(m); // false
获取字段的值
Object p = new Person("Xiao Ming");
Class c = p.getClass();
Field f = c.getDeclaredField("name");
// 注意name要是private是要写这句
f.setAccessible(true);
Object value = f.get(p);
System.out.println(value); // "Xiao Ming"
通过Field实例可以读取或设置某个对象的字段,如果存在访问限制,要首先调用setAccessible(true)来访问非public字段。
通过反射读写字段是一种非常规方法,它会破坏对象的封装。
调用方法
Method getMethod(name, Class...):获取某个public的Method(包括父类)
Method getDeclaredMethod(name, Class...):获取当前类的某个Method(不包括父类)
Method[] getMethods():获取所有public的Method(包括父类)
Method[] getDeclaredMethods():获取当前类的所有Method(不包括父类)
getName():返回方法名称,例如:"getScore";
getReturnType():返回方法返回值类型,也是一个Class实例,例如:String.class;
getParameterTypes():返回方法的参数类型,是一个Class数组,例如:{String.class, int.class};
getModifiers():返回方法的修饰符,它是一个int,不同的bit表示不同的含义。
调用方法
String s1 = "hello world";
Method substring = String.class.getMethod("substring", int.class);
Object invoke = substring.invoke(s1, 6);
System.out.println(invoke);
调用静态方法
如果获取到的Method表示一个静态方法,调用静态方法时,由于无需指定实例对象,
所以invoke方法传入的第一个参数永远为null。我们以Integer.parseInt(String)为例:
// 获取Integer.parseInt(String)方法,参数为String:
Method m = Integer.class.getMethod("parseInt", String.class);
// 调用该静态方法并获取结果:
Integer n = (Integer) m.invoke(null, "12345");
// 打印调用结果:
System.out.println(n);
调用非public方法
Person p = new Person();
Method m = p.getClass().getDeclaredMethod("setName", String.class);
m.setAccessible(true); //跟访问非public的字段是一样的
m.invoke(p, "Bob");
System.out.println(p.name);
多态 针对某个类型的方法调用,其真正执行的方法取决于运行时期实际类型的方法
Method m = Person.class.getMethod("hello");
m.invoke(new Student());
实际上相当于:
Person p = new Student();
p.hello();
调用构造函数
getConstructor(Class...):获取某个public的Constructor;
getDeclaredConstructor(Class...):获取某个Constructor;
getConstructors():获取所有public的Constructor;
getDeclaredConstructors():获取所有Constructor。
注意Constructor总是当前类定义的构造方法,和父类无关,因此不存在多态的问题。
调用非public的Constructor时,
必须首先通过setAccessible(true)设置允许访问。
旧:Person p = Person.class.newInstance();
新:Constructor cons1 = Person.class.getConstructor(); Person p = cons1.newInstance();
获取继承关系
Class getSuperclass():获取父类类型;
Class i = Integer.class;
Class n = i.getSuperclass();
System.out.println(n);
Class o = n.getSuperclass();
System.out.println(o);
System.out.println(o.getSuperclass());
Class[] getInterfaces():获取当前类实现的所有接口。
Class s = Integer.class;
Class[] is = s.getInterfaces();
for (Class i : is) {
System.out.println(i);
}
通过Class对象的isAssignableFrom()方法可以判断一个向上转型是否可以实现
// 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
动态代理
什么是动态代理:Java标准库提供了动态代理功能,允许在运行期动态创建一个接口的实例
通常情况我们写的就是静态代理的代码:
public interface Hello {
void morning(String name);
}
编写实现类:
public class HelloWorld implements Hello {
public void morning(String name) {
System.out.println("Good morning, " + name);
}
}
这是提前就编译好的。动态代理就是运行时候才生成一个实现接口的实例
动态代理步骤:
1. 定义一个InvocationHandler实例,它负责实现接口的方法调用
2. 通过Proxy.newProxyInstance()创建interface实例,它需要3个参数:
使用的ClassLoader,通常就是接口类的ClassLoader
需要实现的接口数组,至少需要传入一个接口进去
用来处理接口方法调用的InvocationHandler实例
将返回的Object强制转型为接口
InvocationHandler dynamic_proxy =
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("hello".equals(method.getName())) {
System.out.println("dynamic proxy");
}
return null;
}
};
Person o =
(Person)
Proxy.newProxyInstance(
Person.class.getClassLoader(), new Class[] {Person.class}, dynamic_proxy);
o.hello();