1. 入门概念
概念: 之前我们都是先设计一个类,然后通过这个类的实例来获取属性和方法,而其实我们还可以先得到这个类的Class对象,然后再通过Class对象获取属性和方法,这样的好处是可以获取所有属性和方法,即使是private修饰的。
反射是java动态性的一种体现。
1.1 获取Class对象
概念: 获取Class对象有三种方式:
instance.getClass()
:要求必须得先有实例。类名.class
:很便捷,但是属于硬编码,对于JDK中自带的类型,推荐使用这种。Class.forName(qualifiedName)
:不是硬编码,对于自定义的类型,推荐使用这种。
源码: /javase-oop/
- src:
c.y.reflect.GetClassTest
/**
* @author yap
*/
public class GetClassTest {
@Test
public void getClassMethod() throws ClassNotFoundException {
GetClassTest instance = new GetClassTest();
Class<?> classA = instance.getClass();
Class<?> classB = GetClassTest.class;
String qualifiedName = "com.yap.reflect.GetClassTest";
Class<?> classC = Class.forName(qualifiedName);
System.out.println(classA.hashCode());
System.out.println(classB.hashCode());
System.out.println(classC.hashCode());
}
}
1.2 数组的Class对象
概念: 判断数组的Class对象是否相同的时候,只比较它们的类型和维度。
源码: /javase-oop/
- src:
c.y.reflect.ArrayClassTest
/**
* @author yap
*/
public class ArrayClassTest {
@Test
public void arrayTypeTest() {
Class<?> classA = int[].class;
Class<?> classB = int[][][].class;
Class<?> classC = double[].class;
System.out.println(classA.hashCode());
System.out.println(classB.hashCode());
System.out.println(classC.hashCode());
}
}
2. 反射构造方法
2.1 获取构造方法
概念: 通过Class获取构造方法的相关API方法:
Constructor<?>[] getConstructors()
:获取类的所有public修饰的构造方法。Constructor<?>[] getDeclaredConstructors()
:获取类的所有构造方法。Constructor<T> getConstructor()
:获取指定的一个public构造方法。Constructor<T> getDeclaredConstructor()
:获取指定的一个构造方法。
源码: /javase-oop/
- src:
c.y.reflect.ReflectConstructorTest.reflectConstructor()
/**
* @author yap
*/
public class ReflectConstructorTest {
static class Demo {
public Demo(int num, String str) {
System.out.println("public constructor...");
}
private Demo() {
System.out.println("private constructor...");
}
public void sayHello() {
System.out.println("hello!");
}
}
private Class<?> klass = Demo.class;
@Test
public void reflectConstructor() throws NoSuchMethodException {
System.out.println(Arrays.toString(klass.getConstructors()));
System.out.println(Arrays.toString(klass.getDeclaredConstructors()));
System.out.println(klass.getConstructor(int.class, String.class));
System.out.println(klass.getDeclaredConstructor());
}
}
2.2 使用构造方法
概念: Constructor类的常用API:
newInstance()
:等同于使用new来调用构造器的效果。setAccessible(true);
:开启私有访问权限。
源码: /javase-oop/
- src:
c.y.reflect.ReflectConstructorTest.usePublicConstructor()
/**
* @author yap
*/
public class ReflectConstructorTest {
static class Demo {
public Demo(int num, String str) {
System.out.println("public constructor...");
}
private Demo() {
System.out.println("private constructor...");
}
public void sayHello() {
System.out.println("hello!");
}
}
private Class<?> klass = Demo.class;
@Test
public void usePublicConstructor() throws Exception {
Constructor<?> constructor = klass.getConstructor(int.class, String.class);
// Demo demo = new Demo(10, "hello");
Demo demo = (Demo) constructor.newInstance(10, "hello");
demo.sayHello();
}
}
- src:
c.y.reflect.ReflectConstructorTest.usePrivateConstructor()
/**
* @author yap
*/
public class ReflectConstructorTest {
static class Demo {
public Demo(int num, String str) {
System.out.println("public constructor...");
}
private Demo() {
System.out.println("private constructor...");
}
public void sayHello() {
System.out.println("hello!");
}
}
private Class<?> klass = Demo.class;
@Test
public void usePrivateConstructor() throws Exception {
Constructor<?> constructor = klass.getDeclaredConstructor();
constructor.setAccessible(true);
Demo demo = (Demo) constructor.newInstance();
demo.sayHello();
}
}
3. 反射成员属性
3.1 获取成员属性
概念: 通过Class获取成员属性的相关API方法:
Field[] getFields()
:获取类的所有public修饰的属性。Field[] getDeclaredFields()
:获取类的所有属性。Field getField("属性名")
:获取指定的一个public属性。Field getDeclaredField()
:获取指定的一个属性。
源码: /javase-oop/
- src:
c.y.reflect.ReflectFieldTest.reflectField()
/**
* @author yap
*/
public class ReflectFieldTest {
private Class<?> klass = Demo.class;
static class Demo {
public String name;
private int gender = 0;
}
@Test
public void reflectField() throws NoSuchFieldException {
System.out.println(Arrays.toString(klass.getFields()));
System.out.println(Arrays.toString(klass.getDeclaredFields()));
System.out.println(klass.getField("name"));
System.out.println(klass.getDeclaredField("gender"));
}
}
3.2 使用成员属性
概念: Field类的常用API:
set(实例, 值)
:往哪个实例的对应属性中设置值。get(实例)
:从哪个实例中取出对应属性的值。- 对应的还有
setInt()
/getInt()
,setDouble()
/getDouble()
等方法。
源码: /javase-oop/
- src:
c.y.reflect.ReflectFieldTest.usePublicField()
/**
* @author yap
*/
public class ReflectFieldTest {
private Class<?> klass = Demo.class;
static class Demo {
public String name;
private int gender = 0;
}
@Test
public void usePublicField() throws Exception {
// FieldTest instanceA = new FieldTest();
// FieldTest instanceB = new FieldTest();
Object instanceA = klass.getDeclaredConstructor().newInstance();
Object instanceB = klass.getDeclaredConstructor().newInstance();
Field nameField = klass.getField("name");
// instanceA.name = "赵四"
// instanceB.name = "刘能"
nameField.set(instanceA, "赵四");
nameField.set(instanceB, "刘能");
// instanceA.name
// instanceB.name
System.out.println(nameField.get(instanceA));
System.out.println(nameField.get(instanceB));
}
}
- src:
c.y.reflect.ReflectFieldTest.usePrivateField()
/**
* @author yap
*/
public class ReflectFieldTest {
private Class<?> klass = Demo.class;
static class Demo {
public String name;
private int gender = 0;
}
@Test
public void usePrivateField() throws Exception {
Object instance = klass.getDeclaredConstructor().newInstance();
Field ageField = klass.getDeclaredField("gender");
ageField.setAccessible(true);
ageField.setInt(instance, 0);
System.out.println(ageField.get(instance));
}
}
4. 反射成员方法
4.1 获取成员方法
概念: 通过Class获取成员方法的相关API方法:
Method[] getMethods()
:获取类的所有public修饰的方法,包括继承下来的public方法。Method[] getDeclaredMethods()
:获取类的所有方法,不包括继承下来的方法。Method getMethod()
:获取指定的一个public方法。Method getDeclaredMethod()
:获取指定的一个方法。
源码: /javase-oop/
- src:
c.y.reflect.ReflectMethodTest.reflectMethods()
/**
* @author yap
*/
public class ReflectMethodTest {
private Class<?> klass = Demo.class;
static class Demo {
public static void methodA(String str, int num) {
System.out.println("I am methodA..." + str + num);
}
private static void methodB() {
System.out.println("I am methodB...");
}
}
@Test
public void reflectMethods() throws NoSuchMethodException {
System.out.println(Arrays.toString(klass.getMethods()));
System.out.println(Arrays.toString(klass.getDeclaredMethods()));
System.out.println(klass.getMethod("methodA", String.class, int.class));
System.out.println(klass.getDeclaredMethod("methodB"));
}
}
4.2 使用成员方法
概念: Method回调的API为 方法对象.invoke(实例, 参数)
:
- p1:调用哪个实例中的这个方法,并传入对应参数,如果是静态方法,传入null即可。
- p2:调用方法时传入的参数列表对应的Class对象。
源码: /javase-oop/
- src:
c.y.reflect.ReflectMethodTest.usePublicMethod()
/**
* @author yap
*/
public class ReflectMethodTest {
private Class<?> klass = Demo.class;
static class Demo {
public static void methodA(String str, int num) {
System.out.println("I am methodA..." + str + num);
}
private static void methodB() {
System.out.println("I am methodB...");
}
}
@Test
public void usePublicMethod() throws Exception {
Method methodA = klass.getMethod("methodA", String.class, int.class);
methodA.invoke(klass.getDeclaredConstructor().newInstance(), "赵四", 58);
}
}
- src:
c.y.reflect.ReflectMethodTest.usePrivateMethod()
/**
* @author yap
*/
public class ReflectMethodTest {
private Class<?> klass = Demo.class;
static class Demo {
public static void methodA(String str, int num) {
System.out.println("I am methodA..." + str + num);
}
private static void methodB() {
System.out.println("I am methodB...");
}
}
@Test
public void usePrivateMethod() throws Exception {
Method methodB = klass.getDeclaredMethod("methodB");
methodB.setAccessible(true);
methodB.invoke(klass.getDeclaredConstructor().newInstance());
}
}
5. 反射注解
概念: 注解信息可以从类,属性或方法上获取,常用API方法如下:
getAnnotations()
:获取类,属性或方法上的所有的注解,包括继承来的。getDeclaredAnnotations()
:获取类,属性或方法上的注解,不包括继承来的。getAnnotation(注解名.class)
:获取类,属性或方法上的某个注解,包括继承来的。getDeclaredAnnotation(注解名.class)
:获取类,属性或方法上的某个注解,不包括继承来的。
源码: /javase-oop/
- src:
c.y.reflect.ReflectAnnotationTest
/**
* @author yap
*/
public class ReflectAnnotationTest {
@Documented
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(value = RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String name();
int age() default 18;
String[] course() default {"语文", "数学"};
}
@MyAnnotation(name = "赵四")
static class Student {
@MyAnnotation(name = "刘能")
public void method() {
}
}
@Test
public void reflectAnnotation() {
Class<?> klass = Student.class;
MyAnnotation annotation = klass.getDeclaredAnnotation(MyAnnotation.class);
System.out.println(annotation.age());
System.out.println(annotation.name());
System.out.println(Arrays.toString(annotation.course()));
}
}
获取方法或属性的注解,需要先获取到方法和属性对象,然后再调用获取注解的方法。
6. 动态操作
概念: 之前我们编译和运行java代码,都是通过IDE操作来完成的,如果我有一个需求,是从客户端传递到服务器中一个java文件,或者一段java代码,你接受到之后,利用代码来编译运行它,这个过程就是动态操作(JDK1.6引入):
- 动态编译需要使用
javax.tools.ToolProvider
:static JavaCompiler getSystemJavaCompiler()
:获取系统的javac。int run(InputStream in, OutputStream out, OutputStream err, String... arguments)
:执行javac- p1:inputStream输入流,传递给javac的数据,如果为null,表示使用
System.in
标准输入。 - p2:outputStream输出流,javac返回的数据,如果为null,表示使用
System.out
标准输出。 - p3:outputStream输出流,javac返回的错误信息,如果为null,表示使用
System.err
错误输出。 - p4:String类型的不定长数组,可以传递一个或者多个java文件
- return:0代表编译成功,其他数代表编译失败
- p1:inputStream输入流,传递给javac的数据,如果为null,表示使用
- 动态运行需要使用
java.net.URL
:负责指向一个文件所在的目录,格式以file:
开头 - 动态运行需要使用
java.net.URLClassLoader
:把URL指向的class加载到内存中URLClassLoader(URL[] urls)
:构造的时候需要指定一个URL数组,规定URLClassLoader的工作范围。Class<?> loadClass(String name)
:通过类名来指定加载哪个class到内存中。void close()
:所有的类加载器在使用完毕之后都需要关闭。
源码: /javase-oop/
- src:
c.y.reflect.DynamicOperaTest
/**
* 先在D盘创建一个HelloWorld.java文件
*
* @author yap
*/
public class DynamicOperaTest {
@Test
public void dynamicCompile() {
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
String filePath = "D:" + File.separator + "HelloWorld.java";
int result = compiler.run(null, null, null, filePath);
System.out.println(result == 0 ? "compile success" : "compile fail");
}
@Test
public void dynamicRun() throws Exception {
URL url = new URL("file:" + File.separator + "D:" + File.separator);
URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{url});
Class<?> klass = urlClassLoader.loadClass("HelloWorld");
Method method = klass.getMethod("main", String[].class);
/*
Use `Object` instead of `String[]`
Because `new String[]{"a", "b"}` will be split into two parameters: "a" and "b"
The two parameters are wrong for main()
*/
method.invoke(null, (Object) new String[]{"a", "b"});
urlClassLoader.close();
}
}
如果客户端传递过来的不是一个java文件,而是一段java代码,则可以先用IO流技能将这段java代码输出到一个临时的java文件中,然后在动态编译和运行,得到结果后,别忘了销毁那个临时文件。