Java的反射机制
一、Java的反射概述
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;
这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法,所以先要获取到每一个字节码文件对应的Class类型的对象
新建Class类进行测试:
package com.hansun.entity; public class Person { public String name; public int age; public Person() { } public Person(String name, int age) { this.name = name; this.age = age; } public String show(){ return "name:"+name+", age :"+age; } }
package com.hansun.entity; public class Student extends Person { public String className; private String address; public Student() { super(); } private Student(String name, int age, String className) { super(name, age); this.className = className; } public Student(String name, int age, String className, String address) { super(name, age); this.className = className; this.address = address; } public String getClassName() { return className; } public void setClassName(String className) { this.className = className; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } private void add(int a, int b) { System.out.println(a + b); } @Override public String toString() { return "Student{" + "className='" + className + '\'' + ", address='" + address + '\'' + ", name='" + name + '\'' + ", age=" + age + '}'; } }
二、获取反射的方式
// 通过Class类中的静态方法forName,直接获取到一个类的字节码文件对象,此时该类还是源文件阶段,并没有变为字节码文件。 1、Class clazz1 = Class.forName("全限定类名"); //当类被加载成.class文件时,此时Person类变成了.class,在获取该字节码文件对象,也就是获取自己, 该类处于字节码阶段。 2、Class clazz2 = Person.class; //通过类的实例获取该类的字节码文件对象,该类处于创建对象阶段 3、Class clazz3 = p.getClass();
验证三种方式获取的Class对象是否同一个
/** * 测试三种反射得来的Class对象 是否是同一个 */ private static void testClass() throws ClassNotFoundException { // 第一种方式获取Class对象 // new 产生一个Student对象,一个Class对象。 Student stu1 = new Student(); //获取Class对象 Class stuClass = stu1.getClass(); System.out.println(stuClass.getName()); // 第二种方式获取Class对象 Class stuClass2 = Student.class; // 判断第一种方式获取的Class对象和第二种方式获取的是否是同一个 System.out.println(stuClass == stuClass2); // 第三种方式获取Class对象 // 注意此字符串必须是真实路径,就是带包名的类路径,包名.类名 Class stuClass3 = Class.forName("com.hansun.entity.Student"); // 判断三种方式是否获取的是同一个Class对象 System.out.println(stuClass3 == stuClass2); }
三、获取类Class对象中成员变量Field字段
private static void testField() throws Exception { Class cls = Class.forName("com.hansun.entity.Student"); //获取成员变量,包括子类及父类,同时只能包含公共的方法 Field[] fields = cls.getFields(); for (Field field : fields) { System.out.print(field + "\t"); System.out.print(field.getModifiers() + "\t"); System.out.print(field.getType() + "\t"); System.out.print(field.getName() + "\t"); System.out.println(); } System.out.println("========================"); //此方法返回的是当前类的所有属性,不仅仅局限于公共访问修饰符,所有的访问修饰符都可以拿到 Field[] declaredFields = cls.getDeclaredFields(); for (Field declaredField : declaredFields) { System.out.print(declaredField + "\t"); System.out.print(declaredField.getModifiers() + "\t"); System.out.print(declaredField.getType() + "\t"); System.out.println(declaredField.getName()); } System.out.println("========================"); // 反射在一定程度上破坏了封装性,需要合理使用 Field address = cls.getDeclaredField("address"); // 设置该属性是否能被访问,true表示能被访问,破坏了封装性 address.setAccessible(true); System.out.println(address.getName()); Object o = cls.newInstance(); address.set(o, "北京市"); System.out.println(((Student) o).getAddress()); System.out.println("========================"); }
四、获取类Class对象中method方法
private static void testMethod() throws Exception { Class cls = Class.forName("com.hansun.entity.Student"); // 获取该对象的普通方法,包含的方法范围是当前对象及父类对象的所有公共方法 Method[] methods = cls.getMethods(); for (Method method : methods) { System.out.print(method.getModifiers() + "\t"); System.out.print(method.getName() + "\t"); System.out.println(Arrays.toString(method.getParameterTypes())); } System.out.println("-----------------------"); // 获取当前类中所有的方法,无论什么访问修饰符 Method[] declaredMethods = cls.getDeclaredMethods(); for (Method declaredMethod : declaredMethods) { System.out.print(declaredMethod.getModifiers() + "\t"); System.out.print(declaredMethod.getName() + "\t"); System.out.println(Arrays.toString(declaredMethod.getParameterTypes())); } System.out.println("-----------------------"); Method add = cls.getDeclaredMethod("add", int.class, int.class); cls.getDeclaredMethods(); add.setAccessible(true); Object o1 = cls.newInstance(); add.invoke(o1, 123, 87); Method setAddress = cls.getDeclaredMethod("setAddress", String.class); setAddress.invoke(o1, "上海市!"); System.out.println(o1.toString()); }
五、获取类Class对象中的Constructor构造器
private static void testConstructor() throws Exception { Class cls = Class.forName("com.hansun.entity.Student"); // 获取对象的所有构造方法,只能获取公有的改造方法 Constructor[] constructors = cls.getConstructors(); for (Constructor constructor : constructors) { System.out.print(constructor.getModifiers() + "\t"); System.out.println(constructor.getName()); } System.out.println("========================"); // 获取所有的构造方法,无论是私有还是公有 Constructor[] declaredConstructors = cls.getDeclaredConstructors(); for (Constructor declaredConstructor : declaredConstructors) { System.out.print(declaredConstructor.getModifiers() + "\t"); System.out.print(declaredConstructor.getName() + "\t"); // 获取构造器中方法签名 System.out.print(Arrays.toString(declaredConstructor.getParameterTypes()) + "\t"); System.out.println(declaredConstructor.getParameterCount() + "\t"); } // 如何调用私有的构造方法呢? Constructor<Student> declaredConstructor = cls.getDeclaredConstructor(String.class, int.class, String.class); declaredConstructor.setAccessible(true); Student o2 = declaredConstructor.newInstance("sunhan", 23, "aaaaaaaaaaa"); System.out.println(o2); }