Java基础——反射

1 概述

Java反射提供了一种动态获取类信息及动态修改运行规则的机制。
反射指运行时获取一个类中的信息,并控制其行为。运行时可以获取构造器对象Constructor,可以获取成员变量对象Field,可以获取方法对象Method

2 获取类对象的方式

获取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对象是对象加载之后自动创建的一个对象,在堆中。每个类加载后,虚拟机都会为其创建一个对象在堆中,有且只有一个。

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;
    }
    
	@Override
    public 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 constructor = clazz.getConstructor(int.class, String.class);
    System.out.println(constructor.getName() + " ===> " + constructor.getParameterCount());
}

报异常
Java基础——反射_第1张图片
因为无参构造是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对象来获取对应的构造器

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

1Field getField(String name);
       根据成员变量名获得对应Field对象,只能获得public修饰
2.Field getDeclaredField(String name);
       根据成员变量名获得对应Field对象,只要申明了就可以得到
3.Field[] getFields();
       获得所有的成员变量对应的Field对象,只能获得public4.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

1Method getMethod(String name,Class...args);
    根据方法名和参数类型获得对应的方法对象,只能获得public2Method getDeclaredMethod(String name,Class...args);
    根据方法名和参数类型获得对应的方法对象,包括private3Method[] getMethods();
    获得类中的所有成员方法对象,返回数组,只能获得public修饰的且包含父类的
4Method[] 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这几个对象的使用。

你可能感兴趣的:(#,Java基础知识,java,开发语言)