java反射基础(二)

反射调用

1
2
3
4
5
6
7
8
9
10
11
12
13
public void reflectMethod() {
System.out.println("反射测试成功!!!");
}
public static void main(String[] args) {
try {
Class c = Class.forName("com.reflect.ReflectTest"); // 创建Class对象
Object m = c.newInstance(); // 创建类实例对象
Method method = c.getMethod("reflectMethod"); // 获取reflectMethod方法
method.invoke(m); // 调用类实例对象方法
} catch (Exception e) {
e.printStackTrace();
}
}

Java-反射-利用反射构造Runtime类执行

可以看到先创建class对象再创建实例对象,再获取对应方法,最后再调用就可以成功调用了ReflectTemo方法,那么Runtime是否也可以这样反射呢?

按照该思路,进行编写代码

1
2
3
4
5
6
public static void main(String[] args) throws Exception {
Class c1 = Class.forName("java.lang.Runtime");
Object m = c1.newInstance();
Method method = c1.getMethod("exec", String.class);
method.invoke(m,"/System/Applications/Calculator.app/Contents/MacOS/Calculator");
}

发现报错,原因是因为Runtime类的构造方法是私有的,这里就涉及到了单例模式。比如当web应用来说,数据库连接只用建立连接一次就可以,而不是每次用到都新建连接,此时就可以将数据库连接使用的构造方法设置为私有,使用静态方法来获取

此时不能newIntance创建类对象,就可以使用getMethod通过反射获取Runtime类下的exec方法

可以看到exec有六个重载方法,第一个最简单,尝试获取第一个。

这里再说下invoke,invoke作用是执行方法,

1
2
3
4
5
6
7
public static void main(String[] args) throws Exception {
Class clazz = Class.forName("java.lang.Runtime");
Method execMethod = clazz.getMethod("exec", String.class);
Method getRuntimeMethod = clazz.getMethod("getRuntime");
Object runtime = getRuntimeMethod.invoke(clazz);
execMethod.invoke(runtime, "/System/Applications/Calculator.app/Contents/MacOS/Calculator");
}

精简一下代码

1
2
Class c1 = Class.forName("java.lang.Runtime");
c1.getMethod("exec", String.class).invoke(c1.getMethod("getRuntime").invoke(c1),"/System/Applications/Calculator.app/Contents/MacOS/Calculator");

总结一下invoke,invoke第一个参数:

如果这个方法是一个普通方法,那么第一个参数是类对象。

如果这个方法是一个静态方法,那么第一个参数就是类。

Java-反射-设置setAccessible(true)暴力访问权限

在一般情况下,我们使用反射机制不能对类的私有private字段进行操作,绕过私有权限的访问。但一些特殊场景存在例外的时候,比如我们进行序列化操作的时候,需要去访问这些受限的私有字段,这时我们可以通过调用AccessibleObject上的setAccessible()方法来允许访问。

Java.lang.reflect.AccessibleObject类是Field,Method和Constructor类对象的基类,可以提供将反射对象标记为使用它抑制摸人Java访问控制检查的功能,同时上述的反射类中的Field,Method和Constructor继承自AccessibleObject。所以我们在这些类方法基础上调用setAccessible()方法,既可对这些私有字段进行操作。

所有我们最开始的代码再修改简略下,就能成功命令执行了。

1
2
3
4
5
6
public static void main(String[] args) throws Exception {
Class c1= Class.forName("java.lang.Runtime");
Constructor m = c1.getDeclaredConstructor();
m.setAccessible(true);
c1.getMethod("exec", String.class).invoke(m.newInstance(), "/System/Applications/Calculator.app/Contents/MacOS/Calculator");
}