1 概述
Java反射提供了一种动态获取类信息及动态修改运行规则的机制。
反射指运行时获取一个类中的信息,并控制其行为。运行时可以获取构造器对象Constructor,可以获取成员变量对象Field,可以获取方法对象Method
2 获取类对象的方式
获取Class对象有三种方式
public class Test {public static void main(String[] args) throws ClassNotFoundException {// Class类中的一个静态方法forNameClass clazz1 = Class.forName("com.xy.day01.Student");System.out.println(clazz1);// 通过类名.classClass clazz2 = Student.class;System.out.println(clazz2);// 通过对象的getClass方法Student student = new Student();Class clazz3 = student.getClass();System.out.println(clazz3);}
}
输出
class com.xy.day01.Student
class com.xy.day01.Student
class com.xy.day01.Student
Class对象是对象加载之后自动创建的一个对象,在堆中。每个类加载后,虚拟机都会为其创建一个对象在堆中,有且只有一个。
2 反射构造方法
涉及到的api
1. Constructor getConstructor(Class... parameterTypes)根据参数匹配获取某个构造器,只能拿public修饰的构造器,几乎不用!
2. Constructor getDeclaredConstructor(Class... parameterTypes)根据参数匹配获取某个构造器,只要申明就可以定位,不关心权限修饰符,建议使用!
3. Constructor[] getConstructors()获取所有的构造器,只能拿public修饰的构造器。几乎不用!!太弱了!
4. Constructor[] getDeclaredConstructors()获取所有申明的构造器,只要你写我就能拿到,无所谓权限。建议使用!!
测试类
public class Student {int age;String name;private Student() {System.out.println("无参构造执行了");}public Student(int age, String name) {System.out.println("有参构造执行了");this.age = age;this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}@Overridepublic String toString() {return "Student{" +"age=" + age +", name='" + name + '\'' +'}';}
}
2.1 获取所有public构造器
@Test
public void getConstructors() {Class<Student> clazz = Student.class;Constructor[] constructors = clazz.getConstructors();for (Constructor constructor : constructors) {System.out.println(constructor.getName() + " ===> " + constructor.getParameterCount());}
}
输出
com.xy.day02.Student ===> 2
该方法只能获取所有的public类型的构造器
2.2 获取所有构造器
@Test
public void getDeclaredConstructors() {Class<Student> clazz = Student.class;Constructor[] constructors = clazz.getDeclaredConstructors();for (Constructor constructor : constructors) {System.out.println(constructor.getName() + " ===> " + constructor.getParameterCount());}
}
输出
com.xy.day02.Student ===> 0
com.xy.day02.Student ===> 2
该方法能够获取所有的构造器,包括private类型的构造器
2.3 获取单个public构造器
@Test
public void getConstructor() throws NoSuchMethodException {Class<Student> clazz = Student.class;Constructor constructor = clazz.getConstructor();// Constructor<Student> constructor = clazz.getConstructor(int.class, String.class);System.out.println(constructor.getName() + " ===> " + constructor.getParameterCount());
}
报异常
因为无参构造是private的,该方法只能获取public类型的构造器,由于获取不到对应构造器,所以报异常。
@Test
public void getConstructor() throws NoSuchMethodException {Class<Student> clazz = Student.class;Constructor constructor = clazz.getConstructor();// Constructor<Student> constructor = clazz.getConstructor(int.class, String.class);System.out.println(constructor.getName() + " ===> " + constructor.getParameterCount());
}
输出
com.xy.day02.Student ===> 2
通过传入特定的class对象来获取对应的构造器
2.4 获取单个构造器
@Test
public void getDeclaredConstructor() throws NoSuchMethodException {Class<Student> clazz = Student.class;Constructor<Student> constructor1 = clazz.getDeclaredConstructor();Constructor<Student> constructor2 = clazz.getDeclaredConstructor(int.class, String.class);System.out.println(constructor1.getName() + " ===> " + constructor1.getParameterCount());System.out.println(constructor2.getName() + " ===> " + constructor2.getParameterCount());
}
输出
com.xy.day02.Student ===> 0
com.xy.day02.Student ===> 2
通过该方法可以获取包括private类型的构造器。
3 构造器的使用
@Test
public void useConstructor() throws Exception {Class<Student> clazz = Student.class;Constructor<Student> constructor = clazz.getDeclaredConstructor();System.out.println(constructor.getName() + " ===> " + constructor.getParameterCount());Student s = (Student) constructor.newInstance();System.out.println(s);
}
反射无参构造器,并创建对象,报异常
由于无参构造方法是private的,所以会报异常
可以通过setAccessible方法设置。
@Test
public void useConstructor() throws Exception {Class<Student> clazz = Student.class;Constructor<Student> constructor = clazz.getDeclaredConstructor();System.out.println(constructor.getName() + " ===> " + constructor.getParameterCount());constructor.setAccessible(true);Student s = (Student) constructor.newInstance();System.out.println(s);
}
输出
com.xy.day02.Student ===> 0
无参构造执行了
Student{age=0, name='null'}
setAccessible设置为true,表示可以暴力进入,无视权限控制。false为默认,保留权限控制。
@Test
public void useConstructor() throws Exception {Class<Student> clazz = Student.class;Constructor<Student> constructor = clazz.getDeclaredConstructor(int.class, String.class);System.out.println(constructor.getName() + " ===> " + constructor.getParameterCount());Student s = (Student) constructor.newInstance(12, "Bob");System.out.println(s);
}
输出
com.xy.day02.Student ===> 2
有参构造执行了
Student{age=12, name='Bob'}
4 反射字段
反射字段相关的api
1、Field getField(String name);根据成员变量名获得对应Field对象,只能获得public修饰
2.Field getDeclaredField(String name);根据成员变量名获得对应Field对象,只要申明了就可以得到
3.Field[] getFields();获得所有的成员变量对应的Field对象,只能获得public的
4.Field[] getDeclaredFields();获得所有的成员变量对应的Field对象,只要申明了就可以得到
4.1 获取所有public字段
@Test
public void getFields() {Class clazz = Student.class;Field[] fields = clazz.getFields();for (Field field : fields) {System.out.println(field.getName() + "===>" + field.getType());}
}
返回为空,因为所有字段都是default的
4.2 获取所有字段
@Test
public void getDeclaredFields() {Class clazz = Student.class;Field[] fields = clazz.getDeclaredFields();for (Field field : fields) {System.out.println(field.getName() + "===>" + field.getType());}
}
输出
age===>int
name===>class java.lang.String
4.3 获取单个public字段
@Test
public void getField() throws NoSuchFieldException {Class clazz = Student.class;Field field = clazz.getField("age");System.out.println(field.getName() + "===>" + field.getType());
}
抛异常,因为该字段不是public的
4.4 获取单个字段
@Test
public void getDeclaredField() throws NoSuchFieldException {Class clazz = Student.class;Field field = clazz.getDeclaredField("age");System.out.println(field.getName() + "===>" + field.getType());
}
输出
age===>int
5 字段的操作
常用api有
void set(Object obj, Object value):给对象注入某个成员变量数据
Object get(Object obj):获取对象的成员变量的值。
void setAccessible(true);暴力反射,设置为可以直接访问私有类型的属性。
Class getType(); 获取属性的类型,返回Class对象。
String getName(); 获取属性的名称。
代码示例:
@Test
public void useField() throws Exception {Class clazz = Student.class;Field field = clazz.getDeclaredField("age");Constructor<Student> constructor = clazz.getDeclaredConstructor();constructor.setAccessible(true);field.setAccessible(true);Student s = (Student) constructor.newInstance();field.set(s, 23);System.out.println(s);int age = (int) field.get(s);System.out.println(age);
}
输出
无参构造执行了
Student{age=23, name='null'}
23
6 反射方法
常用api
1、Method getMethod(String name,Class...args);根据方法名和参数类型获得对应的方法对象,只能获得public的
2、Method getDeclaredMethod(String name,Class...args);根据方法名和参数类型获得对应的方法对象,包括private的
3、Method[] getMethods();获得类中的所有成员方法对象,返回数组,只能获得public修饰的且包含父类的
4、Method[] getDeclaredMethods();获得类中的所有成员方法对象,返回数组,只获得本类申明的方法。
测试类
public class Dog {private String name ;public Dog(){}public Dog(String name) {this.name = name;}public void run(){System.out.println("狗跑的贼快~~");}private void eat(){System.out.println("狗吃骨头");}private String eat(String name){System.out.println("狗吃" + name);return "吃的很开心!";}public static void inAddr(){System.out.println("在黑马学习Java!");}public String getName() {return name;}public void setName(String name) {this.name = name;}}
6.1 获取所有方法
@Test
public void getDeclaredMethods(){Class clazz = Dog.class;Method[] methods = clazz.getDeclaredMethods();for (Method method : methods) {System.out.println(method.getName() +" 返回值类型:" + method.getReturnType() + " 参数个数:" + method.getParameterCount());}
}
输出
getName 返回值类型:class java.lang.String 参数个数:0
run 返回值类型:void 参数个数:0
setName 返回值类型:void 参数个数:1
inAddr 返回值类型:void 参数个数:0
eat 返回值类型:void 参数个数:0
eat 返回值类型:class java.lang.String 参数个数:1
6.2 获取单个方法并执行
@Test
public void getDeclardMethod() throws Exception {Class clazz = Dog.class;Method m = clazz.getDeclaredMethod("eat");Method m2 = clazz.getDeclaredMethod("eat", String.class);m.setAccessible(true);m2.setAccessible(true);Dog d = new Dog();Object result = m.invoke(d);System.out.println(result);Object result2 = m2.invoke(d, "骨头");System.out.println(result2);
}
输出
狗吃骨头
null
狗吃骨头
吃的很开心!
7 总结
反射提供了一种运行时获取类信息并操作的机制,增大了Java代码的灵活性,但是同时也破坏了封装特性,因为可以反射获取private类型的数据。
反射主要涉及Class、Field、Constructor、Method这几个对象的使用。