Reflection(反射)被视为动态语言 的关键,反射机制允许程序在执行期借助于Reflection API
取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
反射机制提供的功能
在运行时判断 任意一个对象所属的类
在运行时构造 任意一个类的对象
在运行时判断 任意一个类所具有的成员变量和方法
在运行时调用 任意一个对象的成员变量和方法
生成动态代理
这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
反射应用 创建一个person类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 package com.drop.test;public class Person { public String name; private int age; public Person () { super (); } public Person (String name) { this .name = name; } public Person (String name, int age) { this .name = name; this .age = age; } public String getName () { return name; } public void setName (String name) { this .name = name; } public int getAge () { return age; } public void setAge (int age) { this .age = age; } public void show () { System.out.println("人类的测试" ); } public void display (String nation) { System.out.println("国籍是:" +nation); } @Override public String toString () { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}' ; } }
没有用反射,如何创建一个类的对象,并调用其中的方法,属性。
1 2 3 4 5 6 7 8 9 @Test public void test1 () { Person p = new Person(); p.setName("test1name" ); p.setAge(10 ); System.out.println(p); p.show(); p.display("HK" ); }
通过反射构建一个类的对象,并调用其中的结构。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 @Test public void test2 () throws Exception { Class<Person> clazz = Person.class; Person p = clazz.newInstance(); System.out.println(p); Field f1 = clazz.getField("name" ); f1.set(p,"test2name" ); System.out.println(p); Field f2 = clazz.getDeclaredField("age" ); f2.setAccessible(true ); f2.set(p,20 ); System.out.println(p); Method m1 = clazz.getMethod("show" ); m1.invoke(p); Method m2 = clazz.getMethod("display" , String.class); m2.invoke(p,"CH" ); System.out.println(p);
class类 在Object类中定义了以下方法,其将被所有子类继承:
1 public final Class getClass ()
以上方法的返回值的类型是一个Class类,是java反射的源头。
正常方式 :引入需要的包名称–>通过new
实例化 –>取得实例化对象
反射方式 :实例化对象–> getClass()
方法–>得到完整的”包类”名称
创建一个类 –编译–>对应.class
–jave.exe加载(JVM类加载器)–>此.class
文件就是一个运行时类,存在缓存区,这个运行时类本身就是一个Class的实例。
1 2 3 4 5 6 @Test public void test3 () { Person p = new Person(); Class clazz = p.getClass(); System.out.println(clazz); }
1.每个运行时类只加载一次
2.有了class实例后,可做如下操作:
创建对应的运行时类的对象
获取对应的运行时类的完整结构(属性、方法、构造器、内部类、父类、所在的包、异常、注释…)
调用对应的运行时类的指定的结构(属性、方法、构造器)
反射的应用:动态代理
获取运行时类的对象四种方法 调用运行时类本身的.class
属性 1 Class<?> name1 = Runtime.class;
要明确用到类中的静态成员
通过运行时类的对象调用 getClass()
1 2 Runtime rt = Runtime.getRuntime(); Class<?> name = rt.getClass();
通过Object类中的getClass()
方法获取字节码对象,必须明确具体的类,然后创建对象。
通过Class的静态方法获取 forName()
(只需要类名) 1 Class<?> name = Class.forName("java.lang.Runtime" );
只需要有类名称即可,forName()
的静态方法JVM会装载类,并且执行static()
中代码。
通过类的加载器 1 Class<?> name = ClassLoader.getSystemClassLoader().loadClass("java.lang.Runtime" );
类似forName()
,但不同在于getSystemClassLoader().loadClass()
不执行 static()
中代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @Test public void test4 () throws ClassNotFoundException { Class clazz1 = Person.class; System.out.println(clazz1.getName()); Class clazz2 = String.class; System.out.println(clazz2.getName()); Person p = new Person(); Class clazz3 = p.getClass(); System.out.println(clazz3.getName()); String className = "com.drop.test.Person" ; Class clazz4 = Class.forName(className); System.out.println(clazz4.getName()); ClassLoader classLoader = this .getClass().getClassLoader(); Class clazz5 = classLoader.loadClass(className); System.out.println(clazz5.getName()); }
获取构造方法 在Java的任何一个类都必须有一个或多个构造方法 ,如果代码中没有创建构造方法那么在类编译的时候会自动创建一个无参数的构造方法。
getDeclaredConstructor()
1 Constructor constructor = name.getDeclaredConstructor();
没有访问构造方法权限时调用constructor.setAccessible(true);
修改访问权限就可以成功的创建出类实例。
getConstructor()
1 Constructor constructor = name.getConstructor();
无法获取到私有方法
构造方法有一个或多个参数的情况下我们应该在获取构造方法时候传入对应的参数类型数组
如:Car类
Constructor constructor = name.getDeclaredConstructor(String.class, int.class);
或者如下代码:
1 2 Class[] classes = new Class[] { String.class, int .class }; Constructor constructor = name.getDeclaredConstructor(classes);
获取类的所有构造方法 可以使用:name.getDeclaredConstructors
来获取一个Constructor
数组
后续创建类实例,同理如果有参数的情况下应该传入对应的参数值,如:constructor.newInstance("admin", "123456")
获取Class类的实例 调用Class对象的newInstance()
方法:
1 Object runtimeInstance = constructor.newInstance();
1 2 3 4 5 6 7 8 9 @Test public void test10 () throws Exception { String className = "com.drop.test.Person" ; ClassLoader classLoader = this .getClass().getClassLoader(); Class clazz = classLoader.loadClass(className); Object obj = clazz.newInstance(); Person p = (Person) obj; System.out.println(p); }
获取当前类指定的成员方法 getDeclaredMethods()
1 2 3 4 5 6 7 8 @Test public void test8 () throws ClassNotFoundException { Class<?> name = ClassLoader.getSystemClassLoader().loadClass("java.lang.Runtime" ); Method[] declaredMethods = name.getDeclaredMethods(); for (Method m:declaredMethods){ System.out.println(m); } }
能获取到当前类的所有成员方法(不包含父类)。
getMethods()
1 2 3 4 5 6 7 8 @Test public void test8 () throws ClassNotFoundException { Class<?> name = Class.forName("java.lang.Runtime" ); Method[] methods = name.getMethods(); for (Method m:methods){ System.out.println(m); } }
只能获取到当前类和父类 的所有有权限的方法(如:public
)
getMethod()
1 2 3 4 5 6 @Test public void test8 () throws NoSuchMethodException { Class<?> name = Runtime.class; Method method = name.getMethod("exec" , String.class); System.out.println(method); }
只能返回一个特定的方法,第一参数为方法名称,后面的参数为方法的参数对应Class的对象。
getDeclaredMethod()
1 2 3 4 5 6 7 @Test public void test8 () throws NoSuchMethodException { Runtime rt = Runtime.getRuntime(); Class<?> name = rt.getClass(); Method method = name.getDeclaredMethod("exec" , String.class); System.out.println(method); }
只能返回一个特定的方法,第一参数为方法名称,第二个参数名是方法参数
反射调用方法invoke
invoke调用普通方法时,传入的必须是实例化后的类。
invoke调用静态方法时,传入类即可。
获取到java.lang.reflect.Method
对象以后我们可以通过Method
的invoke
方法来调用类方法。
1 method.invoke(方法实例对象, 方法参数值,多个参数值用"," 隔开);
method.invoke
的第一个参数必须是类实例对象,如果调用的是static
方法那么第一个参数值可以传null
,因为在java中调用静态方法是不需要有类实例的,因为可以直接类名.方法名(参数)
的方式调用。
1 2 3 4 Class<?> cmdClass = curl.loadClass("com.drop.test.CMD" ); Process process = (Process) cmdClass.getMethod("exec" , String.class).invoke(null ,cmd); Process process = (Process) cmdClass.getMethod("exec" , String.class).invoke(cmdClass,cmd);
method.invoke
的第二个参数不是必须的,如果当前调用的方法没有参数,那么第二个参数可以不传,如果有参数那么就必须严格的依次传入对应的参数类型 。
调用类成员变量 Java反射不但可以获取类所有的成员变量名称,还可以无视权限修饰符实现修改对应的值。
getDeclaredFields 1 Field[] fields = clazz.getDeclaredFields();
获取当前类的所有 成员变量
1 Field field = clazz.getDeclaredField("变量名" );
获取当前类指定 的成员变量:
getFields 用法同上,区别同获取当前类指定的成员方法。
获取Runtime类Class对象 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 @Test public void test () throws Exception { System.out.println(IOUtils.toString(Runtime.getRuntime().exec("whoami" ).getInputStream(), "UTF-8" )); String className = "java.lang.Runtime" ; Class runtimeClass1 = Class.forName(className); Constructor constructor = runtimeClass1.getDeclaredConstructor(); constructor.setAccessible(true ); Object runtimeInstance = constructor.newInstance(); Method runtimeMethod = runtimeClass1.getMethod("exec" , String.class); Process process = (Process) runtimeMethod.invoke(runtimeInstance, "whoami" ); InputStream in = process.getInputStream(); System.out.println(IOUtils.toString(in, "UTF-8" )); }
java.lang.Runtime
因为有一个exec
方法可以执行本地命令