反射 Reflection(反射) 是 Java 程序开发语言的特征之一,反射允许运行中的Java程序在运行时获取一个类的所有信息,可以获取到任何定义的信息(包括成员变量,成员方法,构造器等),并且可以操纵类的字段、方法、构造器等部分,甚至能直接操作程序的私有属性。
获取class类对象 我们正常类加载的方式是:
而反射可以利用下面三种方法获取字节码对象:
1 2 3 4 Class.forName("类的全路径"); 类名.class 对象.getClass(); classLoader.loadClass("类的全路径");
实现反射相关API:
1 2 3 4 java.lang.Class 代表一个类 java.lang.reflect.Method 代表类的方法 java.lang.reflect.Field 代表类的成员属性 java.lang.reflect.Constructor 代表类的构造方法
举个例子
Person.java
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 public class Person { public String name; private int age; public Person(){} public Person(String name, int age) { this.name = name; this.age = age; } public Person(String name) { this.name = name; } 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; } private String show(String name) { //System.out.println(name+""); return name+"正在洗澡"; } private static void teststatic(){ System.out.println("static method start"); } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
User.java
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 public class User extends Person{ private int id; private String username; private String password; public int age; public User(){} public User(int id, String username, String password, int age) { this.id = id; this.username = username; this.password = password; this.age = age; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } private void show(String username,String password) { System.out.println("用户名:"+username+",密码:"+password); } public void study(String username) { System.out.println(username+"正在学习~"); } @Override public String toString() { return "User{" + "id=" + id + ", username='" + username + '\'' + ", password='" + password + '\'' + ", age=" + age + '}'; } }
获取class类对象的四种方法
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 public class Test { public static void main(String[] args) { //1.获取class对象 User user1 = new User(); Class c1 = user1.getClass(); System.out.println("第一种"+c1); //2.通过类的方式获取 Class c2 = User.class; System.out.println("第二种"+c2); //3.class.forName() 将字节码文件加载到内存 Class c3; try { c3 = Class.forName("User"); System.out.println("第三种"+c3); } catch (ClassNotFoundException e) { e.printStackTrace(); } //4. classLoader 类加载器 ClassLoader classLoader = Test.class.getClassLoader(); Class aClass = null; try { aClass = classLoader.loadClass("User"); } catch (ClassNotFoundException e) { e.printStackTrace(); } System.out.print("第四种"+aClass); } }
获取成员属性 1 2 3 4 getFields() 获取所有public修饰的成员属性,包括父类 getDeclaredFields() 获得当前类的所有属性,包括private getField(变量名) getDeclaredField(变量名)
获取成员属性的四个方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public void geFieldTest() throws ClassNotFoundException, NoSuchFieldException { System.out.println("getDeclaredFields方法"); Class c = Class.forName("User"); Field[] fields = c.getDeclaredFields(); for (Field f : fields) { System.out.println(f); } System.out.println("getFields方法"); Field[] fields1 = c.getFields(); for (Field field : fields1) { System.out.println(field); } System.out.println("getField方法"); Field name = c.getField("name"); System.out.println( name.toString()); System.out.println("getDeclaredField方法"); Field id2 = c.getDeclaredField("id"); System.out.println( id2.toString()); }
获取构造方法 1 2 3 4 5 获取构造方法定义信息 getConstructor(参数类型列表)//获取公开的构造方法 getConstructors()//获取所有的公开的构造方法 getDeclaredConstructors()//获取所有的构造方法,包括私有 getDeclaredConstructor(int.class,String.class)//获得当前类指定的构造方法
获取构造方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public void getConstructorTest() throws ClassNotFoundException, NoSuchMethodException { Class clazz = Class.forName("User"); System.out.println("getDeclaredConstructors方法:"); Constructor[] cs = clazz.getDeclaredConstructors(); for (Constructor con : cs) { System.out.println(con); } System.out.println("getConstructors方法:"); Constructor[] cs1 = clazz.getConstructors(); for (Constructor con : cs1) { System.out.println(con); } System.out.println("getConstructor方法:"); Constructor constructor = clazz.getConstructor(); System.out.println(constructor); System.out.println("getDeclaredConstructor方法:"); Constructor declaredConstructor1 = clazz.getDeclaredConstructor(int.class, String.class, String.class, int.class); System.out.println(declaredConstructor1); }
获取父类的构造方法:
1 2 3 4 5 6 7 8 public void getFatherConstructorTest() throws ClassNotFoundException { Class clazz = Class.forName("User"); Class superclass = clazz.getSuperclass(); Constructor[] cs = superclass.getDeclaredConstructors(); for (Constructor con : cs) { System.out.println(con); } }
获取成员方法 1 2 3 4 getMethods ()getMethod (方法名,参数类型列表)getDeclaredMethods ()getDeclaredMethod ()
获取成员方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public void getMethod() throws ClassNotFoundException, NoSuchMethodException { Class c = Class.forName("User"); System.out.println("getDeclaredMethods方法:"); Method[] method = c.getDeclaredMethods(); for (Method meth : method) { System.out.println(meth); } System.out.println("getDeclaredMethod方法:"); Method show = c.getDeclaredMethod("show", String.class,String.class); System.out.println(show); System.out.println("getMethods方法:"); Method[] method1 = c.getMethods(); for (Method meth : method1) { System.out.println(meth); } System.out.println("getMethod方法:"); Method study = c.getMethod("study", String.class); System.out.println(study); }
反射调用属性 调用属性:
1 2 3 4 5 6 7 8 9 public void getFieldValue() throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException { Class c = Class.forName("User"); Field agefield = c.getField("age"); User u = new User(1,"jack","123456",18); System.out.println(agefield.getInt(u)); Field usernamefield = c.getDeclaredField("username"); usernamefield.setAccessible(true); System.out.println(usernamefield.get(u)); }
反射调用方法 调用方法:
1 2 3 4 5 6 7 8 9 public void getMethodValue() throws ClassNotFoundException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { Class c = Class.forName("User"); Method study = c.getDeclaredMethod("study", String.class); User u = new User(1,"jack","123456",18); study.invoke(u, "jack"); Method show = c.getDeclaredMethod("show", String.class,String.class); show.setAccessible(true); show.invoke(u,"test","123"); }
想要访问show()
方法,使用getDeclaredMethod()
方法,并传入方法名,以及形参。又因为该方法为private,所以需要使用setAccessible()
设置访问权限。
注:setAccessible作用是启动和禁止访问安全检查的开关,参数为true表示反射的对象在使用时应该取消java语言访问检查,参数为false则表示反射的对象实施对java语言的访问检查。
反射创建对象 上面的例子我们是用new的方式来创建的对象,也可以通过反射创建对象
1 2 Class.newInstance() 只能够调用无参的构造方法,即默认的构造方法;要求构造方法必须是public类型的。 Constructor.newInstance() 可以根据传入的参数,调用任意的构造方法; 特定情况下可以调用私有的构造方法。
创建对象:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public void getInstance() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { // 1. 获得class对象 Class clazz = Class.forName("User"); // 2. 获取无参构造器 Constructor declaredConstructor = clazz.getDeclaredConstructor(); // 3. Constructor.newInstance() 创建对象 Object test = declaredConstructor.newInstance(); User user = (User) test; // 4. 获取方法 Method study = clazz.getDeclaredMethod("study", String.class); study.invoke(user, "jack"); Object o = clazz.newInstance(); User user1 = (User) o; Method show = clazz.getDeclaredMethod("show", String.class, String.class); show.setAccessible(true); show.invoke(user, "jack","123"); }
因为User类有两个构造方法,我们尝试用另一个有参构造器
1 2 3 4 5 6 7 8 9 10 11 12 13 public void getInstance1() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { // 1. 获得class对象 Class clazz = Class.forName("User"); // 2. 获取有参构造器 Constructor declaredConstructor2 = clazz.getDeclaredConstructor(int.class,String.class,String.class,int.class); // 3. Constructor.newInstance() 创建对象 Object test1 = declaredConstructor2.newInstance(1,"jack","123",18); User user2= (User) test1; System.out.println(user2.toString()); // 4. 获取方法 Method study1 = clazz.getDeclaredMethod("study", String.class); study1.invoke(user2,"jack"); }
结论为:在newInstance创建对象的时候,对应有参构造器,传入形参。
再尝试反射创建对象并调用public show() 私有方法
1 2 3 4 5 6 7 8 Class clazz = Class.forName("User"); Constructor declaredConstructor2 = clazz.getDeclaredConstructor(int.class,String.class,String.class,int.class); Object test1 = declaredConstructor2.newInstance(1,"jack","123",18); User user2= (User) test1; System.out.println(user2.toString()); Method show1 = clazz.getDeclaredMethod("show", String.class,String.class); show1.setAccessible(true); show1.invoke(user2,"jack","123456");
应用 反射调用命令执行常用到的Runtime类
正常使用Runtime类执行系统命令为
1 2 3 4 Process p = Runtime.getRuntime().exec("calc"); //使用ProcessBuilder类进行命令执行 new ProcessBuilder(new String[]{"calc.exe"}).start();
反射的过程首先获得Runtime类,再查看该类的构造方法
1 2 3 4 5 Class clazz = Class.forName("java.lang.Runtime"); Constructor[] declaredConstructors = clazz.getDeclaredConstructors(); for(Constructor con : declaredConstructors){ System.out.print(con); }
只有一个私有的无参构造器,获取该构造器并通过setAccessible
设置访问权限。
1 2 Constructor declaredConstructor = clazz.getDeclaredConstructor(); declaredConstructor.setAccessible(true);
有了构造器,就可以通过构造器创建对象
1 Object o = declaredConstructor.newInstance();
查看当前类的所有方法
1 2 3 4 Method[] declaredMethods = clazz.getDeclaredMethods(); for(Method meth : declaredMethods){ System.out.println(meth); }
可以看到exec()
方法对应的都是public属性,且可以传入String类型,也可以传入String[]类型,比如使用传入String类型的方法,那在getDeclaredMethod()
方法后传入的第一个参数为exec()方法名,第二个参数为String.class。
1 Method method = clazz.getDeclaredMethod("exec", String.class);
并使用invoke()方法执行exec()方法
1 method.invoke(o,"calc.exe");
所以综上,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 public void getRuntimeExec() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { // 1. 获得class对象 Class clazz = Class.forName("java.lang.Runtime"); // 2. 获取无参构造器 Constructor declaredConstructor = clazz.getDeclaredConstructor(); declaredConstructor.setAccessible(true); // 3. Constructor.newInstance() 创建对象 Object o = declaredConstructor.newInstance(); //Runtime o1 = (Runtime) o; // 4. 获取方法 Method show = clazz.getDeclaredMethod("exec", String.class); show.invoke(o,"calc.exe"); }
也可以调用传入String[] 参数类型的方法
1 2 3 4 5 6 7 8 9 public void getRuntimeExecs() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { Class clazz = Class.forName("java.lang.Runtime"); Constructor declaredConstructor = clazz.getDeclaredConstructor(); declaredConstructor.setAccessible(true); Object o = declaredConstructor.newInstance(); String[] cmd = new String[]{"cmd.exe", "-c", "calc.exe"}; Method method = clazz.getDeclaredMethod("exec", String[].class); method.invoke(o, cmd); }
同样,ProcessImpl类、ProcessBuilder类也可以反射调用执行系统命令
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 //ProcessBuilder类->获取传入List.class的构造 public void getProcessBuilder1() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, IOException { Class clazz = Class.forName("java.lang.ProcessBuilder"); Constructor declaredConstructor = clazz.getDeclaredConstructor(List.class); ArrayList<Object> lists = new ArrayList<>(); lists.add("calc.exe"); Object o = declaredConstructor.newInstance((List) lists); Method start = clazz.getDeclaredMethod("start"); start.invoke(o); } //ProcessBuilder类->获取传入String[].class的构造 public void getProcessBuilder2() throws ClassNotFoundException, IOException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { new ProcessBuilder().command("calc.exe").start(); Class clazz = Class.forName("java.lang.ProcessBuilder"); String[] cmds = new String[]{"calc.exe"}; Constructor declaredConstructors = clazz.getDeclaredConstructor(String[].class); Object o = declaredConstructors.newInstance((Object) cmds); Method command = clazz.getDeclaredMethod("start"); command.invoke(o); } //ProcessImpl类 public void getProcessImpl1() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { Class clazz = Class.forName("java.lang.ProcessImpl"); Constructor[] declaredConstructors = clazz.getDeclaredConstructors(); String[] cmds = new String[]{"calc.exe"}; Method startMethod = clazz.getDeclaredMethod("start", String[].class, Map.class, String.class, ProcessBuilder.Redirect[].class, boolean.class); startMethod.setAccessible(true); startMethod.invoke(null, cmds, null, null,null, false);//这里需要的五个参数,第一个参数为null,因为调用的方法是这个类的静态方法 }
方法清单 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 获取包名 类名 clazz.getPackage().getName()//包名 clazz.getSimpleName()//类名 clazz.getName()//完整类名 获取成员变量定义信息 getFields()//获取所有公开的成员变量,包括继承变量 getDeclaredFields()//获取本类定义的成员变量,包括私有,但不包括继承的变量 getField(变量名) getDeclaredField(变量名) 获取构造方法定义信息 getConstructor(参数类型列表)//获取公开的构造方法 getConstructors()//获取所有的公开的构造方法 getDeclaredConstructors()//获取所有的构造方法,包括私有 getDeclaredConstructor(int.class,String.class) 获取方法定义信息 getMethods()//获取所有可见的方法,包括继承的方法 getMethod(方法名,参数类型列表) getDeclaredMethods()//获取本类定义的的方法,包括私有,不包括继承的方法 getDeclaredMethod(方法名,int.class,String.class) 反射新建实例 clazz.newInstance();//执行无参构造创建对象 clazz.newInstance(666,”海绵宝宝”);//执行含参构造创建对象 clazz.getConstructor(int.class,String.class)//获取构造方法 反射调用成员变量 clazz.getDeclaredField(变量名);//获取变量 clazz.setAccessible(true);//使私有成员允许访问 f.set(实例,值);//为指定实例的变量赋值,静态变量,第一参数给null f.get(实例);//访问指定实例变量的值,静态变量,第一参数给null 反射调用成员方法 Method m = Clazz.getDeclaredMethod(方法名,参数类型列表); m.setAccessible(true);//使私有方法允许被调用 m.invoke(实例,参数数据);//让指定实例来执行该方法
参考