java中的反射机制
概念:
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。也就是说,通过class文件对象去使用该文件的成员变量、构造方法以及成员方法。
在Class类中:
成员变量: Field 构造方法:Constructor 成员方法:Method
获取Class文件对象的方式:
方式一:Object类的getClass方法
Person p = new Person(); Class c = p.getClass();
方式二:数据类型的静态属性class
Person p = new Person(); Class c = p2.getClass();
方式三:Class类中的静态方法forName
Class c = Class.forName("Person");
在开发中我们尽量使用第三种方式,因为我们可以根据配置文件动态的获取到字节码文件对象,节省了修改的时间。
在这里举例怎样通过配置文件动态的获取字节码对象
1.创建一个txt文件,创建键值对数据:
className=com.zhousong.test.Person (value是包名+类名)
methodName=show 方法名,也可以加成员变量名
2.在com.zhousong.test包下创建Person类
3.测试类:
1 public class Test { 2 public static void main(String[] args) throws Exception { 3 // 加载键值对数据 4 Properties prop = new Properties(); 5 FileReader fr = new FileReader("test.txt"); 6 prop.load(fr); 7 fr.close(); 8 9 // 获取数据 10 String className = prop.getProperty("className"); 11 String methodName = prop.getProperty("show"); 12 13 // 反射 14 Class c = Class.forName(className); 15 16 Constructor con = c.getConstructor(); 17 Object obj = con.newInstance(); 18 19 // 调用方法 20 Method m = c.getMethod(methodName); 21 m.invoke(obj); 22 } 23 }
构造方法:
在Class类中获取构造方法比较常用的方法
public Constructor<T> getConstructor(Class<?>... parameterTypes) 这里获取的是带参的公共构造方法,返回的构造方法必须是public修饰的,当然也可以不带参数,这就要看你的构造方法是怎么写的。
Person类:
1 public class Person { 2 private String name; 3 int age; 4 public String address; 5 public Person(String name, int age, String address) { 6 super(); 7 this.name = name; 8 this.age = age; 9 this.address = address; 10 } 11 public Person() { 12 super(); 13 // TODO Auto-generated constructor stub 14 } 15 public String getName() { 16 return name; 17 } 18 public void setName(String name) { 19 this.name = name; 20 } 21 public int getAge() { 22 return age; 23 } 24 public void setAge(int age) { 25 this.age = age; 26 } 27 public String getAddress() { 28 return address; 29 } 30 public void setAddress(String address) { 31 this.address = address; 32 } 33 @Override 34 public String toString() { 35 return "Person [name=" + name + ", age=" + age + ", address=" + address + "]"; 36 } 37 38 public void show(){ 39 System.out.println("show"); 40 } 41 42 private void function(){ 43 System.out.println("function"); 44 } 45 //私有构造方法 46 private Person(String name){ 47 this.name = name; 48 } 49 }
测试类:
1 public class ReflectDemo3 { 2 public static void main(String[] args) throws Exception{ 3 //获取字节码文件对象 4 Class c = Class.forName("com.zhousong_01.Person"); 5 //获取构造方法对象,返回所有的构造方法 6 Constructor s = c.getDeclaredConstructor(String.class); 7 //暴力访问,要取消Java语言的访问检查,这样就能访问私有的构造方法 8 s.setAccessible(true); 9 //这里访问的是私有的构造方法 10 Object objc = s.newInstance("东方不败"); 11 System.out.println(objc); 12 13 } 14 }
注意点:访问私有构造方法,要设置暴力访问,取消Java语言的访问检查。
成员变量:
获取成员变量的方法:
public Field getField(String name) 这里获取的是用public修饰的单个成员变量信息,name就是要访问的成员变量。
public Field getDeclaredField (String name) 这里获取的是所有的成员变量信息
测试类:
1 public class ReflectDemo { 2 public static void main(String[] args)throws Exception { 3 //获取字节码文件对象 4 Class c = Class.forName("com.zhousong_01.Person"); 5 //获取构造方法对象,通过无参构造方法创建对象 6 Constructor cs = c.getConstructor(); 7 Object objc = cs.newInstance(); 8 System.out.println(objc); 9 //获取单个成员变量 10 Field ageField = c.getDeclaredField("age"); 11 ageField.setAccessible(true); 12 ageField.set(objc, 33); 13 System.out.println(objc); 14 15 //访问私有变量 16 Field nameField = c.getDeclaredField("name"); 17 nameField.setAccessible(true); 18 nameField.set(objc, "小明"); 19 System.out.println(objc); 20 21 Field addressField = c.getField("address"); 22 addressField.set(objc, "天朝"); 23 System.out.println(objc); 24 25 } 26 }
成员方法:
获取成员方法常用的方法:
1 public class ReflectDemo { 2 public static void main(String[] args) throws Exception { 3 // 获取字节码文件对象 4 Class c = Class.forName("cn.itcast_01.Person"); 5 //通过构造方法获取Object对象 6 Constructor con = c.getConstructor(); 7 Object obj = con.newInstance(); 8 9 // 第一个参数表示的方法名,第二个参数表示的是方法的参数的class类型 10 Method m1 = c.getMethod("show"); 11 // 返回值是Object接收,第一个参数表示对象是谁,第二参数表示调用该方法的实际参数 12 m1.invoke(obj); // 调用obj对象的m1方法 13 } 14 }
练习题:给出ArrayList<Integer>对象,添加字符串
分析:ArrayList限制类型必须是Integer类型,常规方法是无法添加字符串的,那么就要通过反射了。
1 public class ReflectDemo { 2 public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { 3 ArrayList<Integer> array = new ArrayList<Integer>(); 4 //获取ArrayList字节码对象 5 Class c = array.getClass(); 6 //将add方法可以添加的类型变成Object类型,那么就可以添加字符串了 7 Method m = c.getMethod("add", Object.class); 8 m.invoke(array, "hello"); 9 System.out.println(array); 10 } 11 }