Java 反射机制详解

Class对象的作用

假定我们在编译期已知道了所有类型(在没有反射机制创建和使用类对象时,一般都是编译期已确定其类型,如new对象时该类必须已定义好),另外一种是反射机制,它允许我们在运行时发现和使用类型的信息。在Java中来表示运行时类型信息的就是class类,Class类是一个实实在在的类,存在于JDK的java.lang包下

public final class Class<T> implements java.io.Serializable,GenericDeclaration,Type, AnnotatedElement {
    private static final int ANNOTATION= 0x00002000;
    private static final int ENUM      = 0x00004000;
    private static final int SYNTHETIC = 0x00001000;

    private static native void registerNatives();
    static {
        registerNatives();
    }

    /*
     * Private constructor. Only the Java Virtual Machine creates Class objects.(私有构造,只能由JVM创建该类)
     * This constructor is not used and prevents the default constructor being
     * generated.
     */
    private Class(ClassLoader loader) {
        // Initialize final field for classLoader.  The initialization value of non-null
        // prevents future JIT optimizations from assuming this final field is null.
        classLoader = loader;
    }

Class类创建对象后就是Class对象,Class对象表示的是自己手动编写的类型信息,比如创建一个Shape类,那么JVM会创建一个shape对象的Class类的Class对象,该Class保存了shape对象的类型信息,实际上每个java类都有一个Class对象,每当我们编写并编译一个新的类就会产生一个对应的Class对象,并且这个Class对象会被保存在同名的.class文件中,编译后的字节码文件保存的就是Class对象,那么为什么需要一个class对象?当我们new一个对象或者引用一个静态成员变量时,JVM中的类加载器会将对应的class文件加载到JVM中,然后JVM根据这个类型信息创建我们需要的实例对象,或者提供静态变量的引用值,注意,不管创建多少个对象,JVM中只存在一个Class对象
在这里插入图片描述
我们大致可以得到几点信息

1 Class类也是类的一种,与class关键字不一样
2 手动编写的类编译后会产生一个Class对象,其表示的是类的类型信息,而这个Class对象会保存在同名的.class字节码文件中
3 通过关键字class,定义的类在内存中有且只有一个Class对象来描述其类型信息
4 Class类只有私有的构造方法,只能由JVM来创建和加载
5Class类对象的作用是运行时获取类的类型信息,这个对反射技术很重要

Class对象的加载

Class类是由JVM加载的那么加载时机是什么时候?
实际上所有的类都是第一次使用的时候动态加载到JVM中的,当程序建立第一个对类的静态成员引用时,就会加载这个使用的类,实际上是加载这个类的字节码文件,(注意,new操作创建对象也是对类的静态成员的引用,因为构造函数也是静态的),由此看来,java程序在他们开始运行之前并不会全部加载到内存中来,而是各个部分按需加载,所以在使用该类时,首先会检查该类的Class对象是否加载(类的实例对象是根据Class对象的类型信息完成的),如果还没有加载,默认的类加载器就会根据类名查找.class文件进行加载,在这个类的字节码文件被加载时,首先要接受相关的验证,以确保没有被破坏,并且不包含不良java代码(这是java安全机制检测),完全没有问题后把.class文件加载到内存中,此时相当于Class对象加载到了内存中,同时也可以创建这个类的所有实例对象

获取class对象
获取Class对象有四种方式

1        Class.forName("com.zejian.Gum");
2        Student student = new Student();
         Class<? extends Student> aClass1 = student.getClass();
3        Class字面量
         Class<Student> aClass2 = Student.class;
4 		通过ClassLoader对象的loadClass()方法
  		Class<?>  = ClassLoader.getSystemClassLoader().loadClass("com.my.test.Hello")
         

版权声明:本文为CSDN博主「一个行走的民」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/zhaominpro/article/details/89810941
前俩中获取Class对象的方式,会自动初始该类,但是最后一种不会初始化该类,最后一种相比前面俩种方法更加简单,更安全,效率更高,而且字面量这个方式不仅可以用于普通的类,还可以用于基本数据类型,数组和接口,上方我们说到,字面量不触发初始化,我们需要了解类的加载过程

类的加载过程
在这里插入图片描述

  • 加载:类加载过程的第一个阶段,通过一个类的完全限定地址查找该类的字节码文件,并利用字节码文件创建一个Class对象
  • 链接:验证字节码的安全性和完整性,准备阶段正式为静态区域分配储存空间,注意此时只是分配静态成员变量的空间,不包含成员变量,如果必要的话,解析这个类对其他类持有的引用
  • 初始化:类加载的最后阶段,如该类具有超类,则对其进行初始化,执行静态初始化器和静态初始化成员变量

关于类加载的初始化阶段,在虚拟机规范严格规定了有且只有5种场景必须对类进行初始化:

  • 使用new关键字实例化对象时,读取或者设置一个静态字段(不包括编译期常量),以及调用静态方法的时候,必须出发类加载的初始化过程(类加载的最终阶段)
  • 使用反射对类进行调用的时候,如果类还没初始化,那么就要初始化
  • 当初始化一个类的时候,如果其父类还没初始化,那么则先触发父类的初始化
  • 当java虚拟机启动时,用户需要制定一个要执行的主类(包含main方法的类),虚拟机会先初始化这个主类
  • 当使用JDK 1.7 的动态语言支持时,如果一个java.lang.invoke.MethodHandle 实例最后解析结果为REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且这个方法句柄对应类没有初始化时,必须触发其初始化(这点看不懂就算了,这是1.7的新增的动态语言支持,其关键特征是它的类型检查的主体过程是在运行期而不是编译期进行的,这是一个比较大点的话题,这里暂且打住)

理解反射技术

反射机制是指,在运行状态中,对任意一个类,都能知道这个类的所有属性和方法,对于任意一个对象都可以调用他的任意属性和方法,这种动态获取信息和动态调用方法的工能称为Java反射

获取class对象

获取一个class对象的方式
* 1.调用Object类的getClass():任何类都会继承此方法;
* 2.任何的数据类型(包括基本类型)都有一个:静态的class属性:
* Student.class;
* int.class
* 3.调用Class类的静态方法forName(String 全名限定的类名):(常用)
* 通过这个Class对象,可以获取Student类内部的成员属性、构造方法、成员方法的一些信息,
* 并能够调用它们;

     private void getclass() {
        //1
        Student student = new Student();
        Class<? extends Student> aClass1 = student.getClass();

        //2
        Class<Student> aClass2 = Student.class;

        //3
        Class<?> aClass3 = null;
        try {
            aClass3 = Class.forName("com.example.jh.rxhapp.fanshe.Student");
        } catch (ClassNotFoundException e) {

        }

        Log.d("mmm", String.valueOf(aClass1 == aClass2));
        Log.d("mmm", String.valueOf(aClass1 == aClass3));
    }

如何获取构造方法

获取构造方法,并调用:
* Class类:
* --批量的:
* Constructor[] getConstructors() :获取所有的"公有构造方法"
* Constructor[] getDeclaredConstructors() :获取全部(包括私有)的构造方法;
* --获取某个构造方法
* Constructor getConstructor(Class … parameterTypes) :获取指定的"公有构造方法";
* Constructor getDeclaredConstructor(Class … parameterTypes) :获取指定的构造方法(包括私有的);
* --调用某个构造方法:
* Constructor的 Object newInstance(Object… initargs) :调用指定构造方法,并实例化此类的对象;
* --暴力访问:如果私有成员,需要设置暴力访问;
* Constructor的setAccessible(true):不进行权限检查;

  private void getGouZao() {
        //获取class对象
        Class<?> aClass = null;
        try {
            aClass = Class.forName("com.example.jh.rxhapp.fanshe.Student1");
            System.out.println("获取所有公有构造方法");
            Constructor<?>[] constructors = aClass.getConstructors();
            for (Constructor c : constructors) {
                System.out.println(c);
            }

            System.out.println("获取所有构造方法(包括私有)");
            Constructor<?>[] declaredConstructors = aClass.getDeclaredConstructors();
            for (Constructor c : declaredConstructors) {
                System.out.println(c);
            }

            System.out.println("获取指定的公有构造方法");
            //获取参数为char的公有构造方法
            Constructor<?> constructor = aClass.getConstructor(char.class);
            //调用该构造方法
            Object o = constructor.newInstance('a');


            System.out.println("获取指定的构造方法(包括私有)");
            Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(String.class, int.class);
            //设置暴力访问
            declaredConstructor.setAccessible(true);
            Object hahah = declaredConstructor.newInstance("hahah", 11);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

如何获取成员属性

获取成员属性,并调用:
* Class类的方法:
* --批量的:
* Field[] getFields():获取所有"公有属性";
* Field[] getDeclaredFields() :获取所有成员属性(包括私有):
* --获取单个成员属性
* Field getField(String name) :获取指定的"公有属性";
* Field getDeclaredField(String name) :获取指定的属性,包括私有的;
*
* --为成员属性赋值:(注意:1.一定要先实例化此类对象;2.访问私有属性前,要设置暴力访问)
* Field的 void set(Object obj, Object value)

private void getFiled() {
        //首先获取class对象
        try {
            Class<?> aClass = Class.forName("com.example.jh.rxhapp.fanshe.Student3");
            System.out.println("获取所有的公有成员属性");
            Field[] fields = aClass.getFields();
            for (Field f : fields) {
                System.out.println(f);
            }

            System.out.println("获取所有的成员属性(包括私有)");
            Field[] declaredFields = aClass.getDeclaredFields();
            for (Field f : declaredFields) {
                System.out.println(f);
            }

            //获取对象
            Object o = aClass.getConstructor().newInstance();
            System.out.println("获取公有属性并调用");
            Field name = aClass.getField("name");
            System.out.println("赋值前打印" + o);
            name.set(o, "刘德华");
            System.out.println("赋值后打印" + o);

            System.out.println("获取所有属性并调用");
            Field age = aClass.getDeclaredField("age");
            //设置暴力访问
            age.setAccessible(true);
            //给属性赋值
            age.set(o, 99);
            System.out.println("赋值后打印" + o);


        } catch (Exception e) {
            e.printStackTrace();
        }
    }

如何获取成员方法

获取成员方法:
* Class类的方法:
* --批量的:
* Method[] getMethods():获取所有"公有方法"(包括继承的)
* Method[] getDeclaredMethods() :获取所有成员方法(包括私有):
* --获取单个成员方法
* Method getMethod(String name, Class… parameterTypes) :获取指定的"公有方法";
* Method getDeclaredMethod(String name, Class… parameterTypes) :获取指定的方法,包括私有的;
*
* --调用方法:(注意:1.要先创建此类的对象;2.调用私有方法前,要先设置暴力访问)
* Method:
* Object invoke(Object obj, Object… args) :调用Method所代表的方法:
* 返回值:此Method对象调用所代表的方法,所获取的返回值;
* 形参:obj:方法所属对象;
* args:Method所代表的那个方法的实参列表;

 private void getFangfa() {
        //获取class对象
        try {
            Class<?> aClass = Class.forName("com.example.jh.rxhapp.fanshe.Student2");

            System.out.println("获取所有的公有方法(包括继承)");
            Method[] methods = aClass.getMethods();
            for (Method m : methods) {
                System.out.println(m);
            }

            System.out.println("获取所有的成员方法(包括私有)");
            Method[] declaredMethods = aClass.getDeclaredMethods();
            for (Method m : declaredMethods) {
                System.out.println(m);
            }

            //获取对象
            Object o = aClass.getConstructor().newInstance();
            System.out.println("获取公有成员方法,带参,有返回值");
            Method show = aClass.getMethod("show", String.class, int.class);
            //调用show方法
            Object result = show.invoke(o, "刘德华", 22);
            System.out.println("调用的返回值" + result);


            System.out.println("获取私有成员方法,无参,无返回值");
            Method show3 = aClass.getDeclaredMethod("show3");
            //设置暴力访问
            show3.setAccessible(true);
            show3.invoke(o);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值