Java反射提供了一种动态获取类信息及动态修改运行规则的机制。
反射指运行时获取一个类中的信息,并控制其行为。运行时可以获取构造器对象Constructor,可以获取成员变量对象Field,可以获取方法对象Method
获取Class对象有三种方式
public class Test {
public static void main(String[] args) throws ClassNotFoundException {
// Class类中的一个静态方法forName
Class clazz1 = Class.forName("com.xy.day01.Student");
System.out.println(clazz1);
// 通过类名.class
Class 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对象是对象加载之后自动创建的一个对象,在堆中。每个类加载后,虚拟机都会为其创建一个对象在堆中,有且只有一个。
涉及到的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;
}
@Override
public String toString() {
return "Student{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
@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类型的构造器
@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类型的构造器
@Test
public void getConstructor() throws NoSuchMethodException {
Class<Student> clazz = Student.class;
Constructor constructor = clazz.getConstructor();
// Constructor 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 constructor = clazz.getConstructor(int.class, String.class);
System.out.println(constructor.getName() + " ===> " + constructor.getParameterCount());
}
输出
com.xy.day02.Student ===> 2
通过传入特定的class对象来获取对应的构造器
@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类型的构造器。
@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'}
反射字段相关的api
1、Field getField(String name);
根据成员变量名获得对应Field对象,只能获得public修饰
2.Field getDeclaredField(String name);
根据成员变量名获得对应Field对象,只要申明了就可以得到
3.Field[] getFields();
获得所有的成员变量对应的Field对象,只能获得public的
4.Field[] getDeclaredFields();
获得所有的成员变量对应的Field对象,只要申明了就可以得到
@Test
public void getFields() {
Class clazz = Student.class;
Field[] fields = clazz.getFields();
for (Field field : fields) {
System.out.println(field.getName() + "===>" + field.getType());
}
}
返回为空,因为所有字段都是default的
@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
@Test
public void getField() throws NoSuchFieldException {
Class clazz = Student.class;
Field field = clazz.getField("age");
System.out.println(field.getName() + "===>" + field.getType());
}
@Test
public void getDeclaredField() throws NoSuchFieldException {
Class clazz = Student.class;
Field field = clazz.getDeclaredField("age");
System.out.println(field.getName() + "===>" + field.getType());
}
输出
age===>int
常用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
常用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;
}
}
@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
@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
狗吃骨头
吃的很开心!
反射提供了一种运行时获取类信息并操作的机制,增大了Java代码的灵活性,但是同时也破坏了封装特性,因为可以反射获取private类型的数据。
反射主要涉及Class、Field、Constructor、Method这几个对象的使用。