java核心基础之java反射机制详解

(一)什么是反射?

反射就是将类的各个组成部分封装为其他对象。

在详细了解反射机制之前,我们先来了解一下java代码在计算机中的运行过程:

比如当我们编写好一个类:Student.java,里面包含学生的姓名和年龄,构造方法,其他方法。

第一个阶段:Source阶段

javac会把我们写的代码编译成.class字节码文件,保存在硬盘中,这个文件中保存着该类的类名,成员名,构造方法等等。

第二个阶段:Class阶段

Class阶段会把字节码文件中的信息转化成class类对象,比如成员变量用Field[]保存,构造方法用Constructor[]保存,成员方法用Method[]保存

第三个阶段:Runtime阶段

通过new Student(),根据第二个阶段的类对象创建出Student对象

这里的第二个阶段,将类的各个组成部分封装为其他对象就是反射机制。

反射的好处:

1.可以在程序运行过程中操作对象。

2.解耦,提高程序可扩展性。

(二)获取字节码Class对象的三种方式

获取Class对象有三种方式,分别对应于前面的三个阶段:

1.对应于第一个阶段的方法是将字节码文件加载进内存中:

class.forname("全类名");

2.第二个阶段已经生成了class类对象,因此方法如下:

类名.class;

3.第三个阶段生成了对象,方法如下:

对象.getclass();

注意:

同一个字节码文件(.class)在一次程序运行过程中只会被加载一次,通过以上三种方法创建的class对象是同一个。

通过代码演示:

新建Student类:

public class Student {
    private String name;
    private int age;
    //方便后期测试的成员变量
    public int a;
    public Student(){}
    public Student(String name, int age,int a) {
        this.name = name;
        this.age = age;
        this.a=a;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", a=" + a +
                '}';
    }
}

测试获取Class对象的三种方式

public class reflectTest {
    public static void main(String[] args) throws ClassNotFoundException {
        //方法一
        Class cls1 = Class.forName("com.sdxb.reflect.Student");
        System.out.println(cls1);
        //方法二
        Class cls2 = Student.class;
        System.out.println(cls2);
        //方法三
        Student student=new Student();
        Class cls3 = student.getClass();
        System.out.println(cls3);
        //判断是否是同一对象
        System.out.println(cls1==cls2);
        System.out.println(cls1==cls3);
    }
}

(三)Class获取对象方法

1.1 获取成员变量

Field getField(String name) //获取指定名称public修饰的成员变量
Field[] getFields()  //获取所有public修饰的成员变量
Field getDeclaredField(String name) //获取指定名称成员变量
Field[] getDeclaredFields()  //获取所有成员变量

1.2 操作成员变量

Object get(Object obj)  //通过Field获取对象
void set(Object obj, Object value)  //修改Field的值

通过代码演示

public class FieldTest {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
        Class cls = Class.forName("com.sdxb.reflect.Student");
        //1.获取所有public的成员变量
        Field[] fields = cls.getFields();
        for (Field field:fields) {
            System.out.println(field);
        }
        //2.获取指定名字的public成员变量
        Field a = cls.getField("a");
        Student student=new Student();
        //3.操作Field的方法,get和set
        System.out.println(a.get(student));
        a.set(student,10);
        System.out.println(student);
        //4.获取所有成员变量
        Field[] declaredFields = cls.getDeclaredFields();
        for (Field f : declaredFields) {
            System.out.println(f);
        }
    }
}

这里的两个操作Field的方法只能操作public修饰的变量,如果需要访问其他修饰符修饰的元素,则要添加安全许可:

a.setAccessible(true);

2.1 获取构造方法

Constructor<T> getConstructor(Class<?>... parameterTypes)  //根据参数不同获取指定的public构造方法
Constructor<?>[] getConstructors()  //获取所有public构造方法
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) //根据参数不同获取指定的构造方法
Constructor<?>[] getDeclaredConstructors() //获取所有构造方法

2.2 操作构造方法

T newInstance(Object... initargs)  //创建对象

通过代码演示其中一两种操作:

public class reflectTest2 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Class cls = Class.forName("com.sdxb.reflect.Student");
        //有参构造方法
        Constructor constructor = cls.getConstructor(String.class, int.class, int.class);
        Object student = constructor.newInstance("sdxb", 24, 1);
        System.out.println(student);
        //无参构造方法
        Constructor constructor2 = cls.getConstructor();
        Object student2 = constructor2.newInstance();
        System.out.println(student2);
        //无参构造方法可以用下面的方式代替
        cls.newInstance();
    }
}

3.1 获取成员方法

Method getMethod(String name, Class<?>... parameterTypes) //根据名称和参数类型获取public方法
Method[] getMethods()  //获取所有public方法
Method getDeclaredMethod(String name, Class<?>... parameterTypes) //根据名称和参数类型获取方法
Method[] getDeclaredMethods()  //获取所有方法

3.2 成员方法的操作

Object invoke(Object obj, Object... args)  //执行成员方法

为了方便测试,我为Student类增加了两个成员方法:

public void run(){
    System.out.println("run");
}
public void run(int speed){
    System.out.println("run"+speed);
}

接着对成员方法的反射进行测试:

public class reflectTest3 {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Class cls = Student.class;
        Student student=new Student();
        //无参方法
        Method run = cls.getMethod("run");
        run.invoke(student);
        //带参数方法
        Method run2=cls.getMethod("run", int.class);
        run.invoke(student,1);
    }
}

(四)总结

java的反射机制在框架中应用十分广泛,被誉为是框架的灵魂。原因是框架是一个半成品,我们无法通过new去创建框架中定义的类,因此反射起到了很大的作用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章