反射概念:
java反射机制是在运行状态中,对于任意一个类,都能知道所有属性和方法
对于任意一个对象都能调用它的任意一个方法和属性,这种动态获取和调用的功能称为java的反射机制
实际作用:
已经完成一个java程序,但是想再添加新功能,又不能修改源码,这时候就用到反射机制了
获取class文件的三种方式:
简单地自定义一个Person类:
package demo; public class Person { public String name; private int age; public Person() { } public Person(String name, int age) { this.name = name; this.age = age; } private Person(int age, String name) { this.name = name; this.age = age; } public void eat() { System.out.println("人吃饭"); } public void sleep(String s, int a, double d) { System.out.println("人在睡觉" + s + "....." + a + "....." + d); } private void playGame() { System.out.println("人在打游戏"); } public String toString() { return "Person [name=" + name + ", 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; } }
获取person类class文件(三种方法本质上获取的是同一个对象):
package demo; public class ReflectDemo { public static void main(String[] args) { //1.对象获取 Person p = new Person(); Class c = p.getClass(); System.out.println(c); //2.类名获取 Class c1 = Person.class; System.out.println(c1); System.out.println(c==c1);//true System.out.println(c.equals(c1));//true //只存在一个class文件,两种方式都是获得同一个对象 //3.Class类的静态方法,参数注意带着包名防止重名 try { Class c2 = Class.forName("demo.Person"); System.out.println(c2); } catch (ClassNotFoundException e) { e.printStackTrace(); } } }
反射获取空参构造方法:
package demo; import java.lang.reflect.Constructor; public class ReflectDemo { public static void main(String[] args) throws ClassNotFoundException { Class c = Class.forName("demo.Person"); function1(c); function2(c); } private static void function1(Class c) { // 获得所有公共权限(public)的构造器 Constructor[] cons = c.getConstructors(); for (Constructor con : cons) { System.out.println(con); // 输出: // public demo.Person(java.lang.String,int) // public demo.Person() } } public static void function2(Class c) { // 获取空参构造器并执行(toString方法) try { Constructor con = c.getConstructor(); Object obj = con.newInstance(); System.out.println(obj); // 输出:Person [name=null, age=0] } catch (Exception ex) { ex.printStackTrace(); } } }
反射获取有参构造方法:
package demo; import java.lang.reflect.Constructor; public class ReflectDemo { public static void main(String[] args) throws Exception { Class c = Class.forName("demo.Person"); Constructor con = c.getConstructor(String.class, int.class); System.out.println(con); Object object = con.newInstance("张三", 20); System.out.println(object); } } /*输出: public demo.Person(java.lang.String,int) Person [name=张三, age=20] */
发现上边的方式代码量偏大,快捷一些的方式:
前提:被反射的类,必须具有空参构造方法,且构造方法权限必须是public
package demo; public class ReflectDemo { public static void main(String[] args) throws Exception { Class c = Class.forName("demo.Person"); // Class类中定义方法, T newInstance() 直接创建被反射类的对象实例 Object obj = c.newInstance(); System.out.println(obj); } }
反射获取私有构造方法(日常开发不建议使用,这种方法了解即可):
package demo; import java.lang.reflect.Constructor; public class ReflectDemo { public static void main(String[] args) throws Exception { Class c = Class.forName("demo.Person"); // 获取所有构造方法(包括私有的) // Constructor[] cons = c.getDeclaredConstructors(); Constructor con = c.getDeclaredConstructor(int.class, String.class); con.setAccessible(true);// 取消权限,破坏了封装性 Object object = con.newInstance(18, "张三"); System.out.println(object); // Person [name=张三, age=18] } }
反射获取类的成员变量并修改:
package demo; import java.lang.reflect.Constructor; import java.lang.reflect.Field; public class ReflectDemo { public static void main(String[] args) throws Exception { Class c = Class.forName("demo.Person"); // 获得所有public成员 Field[] fields = c.getFields(); // 获得所有成员变量 Field[] fields2 = c.getDeclaredFields(); for (Field field : fields2) { System.out.println(field); } // 获取指定成员变量并修改 Field field = c.getField("name"); Object object = c.newInstance(); field.set(object, "张三"); System.out.println(object); // 输出:Person [name=张三, age=0] } }
反射获取成员方法并执行:
package demo; import java.lang.reflect.Method; public class ReflectDemo { public static void main(String[] args) throws Exception { Class c1 = Class.forName("demo.Person"); // 获得所有public方法 /* * Method[] methods = c1.getMethods(); * for(Method method:methods){ * System.out.println(method); } */ // 获取指定方法运行(空参) Method method = c1.getMethod("eat"); Object obj = c1.newInstance(); method.invoke(obj); // 输出:人吃饭 // 有参 Method method1 = c1.getMethod("sleep", String.class, int.class, double.class); Object obj1 = c1.newInstance(); method1.invoke(obj1, "休息", 10, 10.11); // 输出:人在睡觉休息.....10.....10.11 // 可以利用前面提到的方法暴力运行私有方法 } }
反射的泛型擦除:
package demo; import java.lang.reflect.Method; import java.util.ArrayList; public class ReflectTest { public static void main(String[] args) throws Exception { ArrayList<String> array = new ArrayList<String>(); // 通常添加方式 array.add("a"); array.add("1"); Class class1 = array.getClass(); Method method = class1.getMethod("add", Object.class); method.invoke(array, 100); method.invoke(array, 666.666); method.invoke(array, 0.1); System.out.println(array); // 输出:[a, 1, 100, 666.666, 0.1] } }
反射实现通过配置文件运行:
有时候想改源码,但是不能改源码,可以这样做:
自定义三个类:
package demo; public class Person { public void eat(){ System.out.println("人在吃饭"); } }
package demo; public class Student { public void study(){ System.out.println("学生在学习"); } }
package demo; public class Worker { public void job(){ System.out.println("上班族在工作"); } }
配置文件:config.properties
#className=demo.Student
#methodName=study
className=demo.Person
methodName=eat
#className=demo.Worker
#methodName=job
测试类:
package demo; import java.io.FileReader; import java.lang.reflect.Method; import java.util.Properties; /* * 调用Person方法,调用Student方法,调用Worker方法 * 类不清楚,方法也不清楚 * 通过配置文件实现此功能 * 运行的类名和方法名字,以键值对的形式,写在文本中 * 运行哪个类,读取配置文件即可 * 实现步骤: * 1. 准备配置文件,键值对 * 2. IO流读取配置文件 Reader * 3. 文件中的键值对存储到集合中 Properties * 集合保存的键值对,就是类名和方法名 * 4. 反射获取指定类的class文件对象 * 5. class文件对象,获取指定的方法 * 6. 运行方法 */ public class Test { public static void main(String[] args) throws Exception{ //IO流读取配置文件 FileReader r = new FileReader("config.properties"); //创建集合对象 Properties pro = new Properties(); //调用集合方法load,传递流对象 pro.load(r); r.close(); //通过键获取值 String className = pro.getProperty("className"); String methodName = pro.getProperty("methodName"); //反射获取指定类的class文件对象 Class c = Class.forName(className); Object obj = c.newInstance(); //获取指定的方法名 Method method = c.getMethod(methodName); method.invoke(obj); } }