【JavaSE】反射

反射机制允许程序在运行态借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
在这里插入图片描述

一、Class类和获取Class的实例的几种方式

1、Class类

程序经过javac.exe命令之后,会生成一个或者多个字节码文件(.class)文件。接着就需要用java.exe命令对某个字节码进行解释运行,所谓解释运行,就是把这个字节码文件(.class)加载到内存中。这个过程就叫类的加载。而对于加载到内存中的类,我们称其为运行时类,运行时类都是Class类的实例。
JRE通过保留Class类型对象, 用来保存某个类的属性、方法和构造器、某个类到底实现了哪些接口(即类的结构)。也就是说可以通过Class类型实例,获得当前类全部结构信息。
总结:

  • Class本身也是一个类;
  • Class 对象只能由系统建立对象;
  • 一个加载的类在 JVM 中只会有一个Class实例,每个类的实例都会记得自己是由哪个 Class 实例所生成; 通过Class可以完整地得到一个类中的所有被加载的结构;
  • Class类是Reflection的根源,针对任何你想动态加载、运行的类,唯有先获得相应的Class对象;

2、class类常用方法

在这里插入图片描述

3、获取class实例方法

加载到内存中的运行时类,会缓存一定的时间。在此时间之内,我们可以通过不同的方式来获取此运行时类。
方法1: 调用运行时类属性 .class
方法2:通过运行时类的对象 getClass
方法3:Class类的静态方法,Class.forName
方法4:使用类加载器

 /**
     *  Class对象创建方式
     */
    @Test
    public void test02() throws ClassNotFoundException {

        /**
         *   运行时类
         */
        Class clazz1 = Person.class;

        /**
         *  运行时类对象
         */
        Person person = new Person();
        Class clazz2 = person.getClass();

        /**
         *  Class的静态方法 forName, ***用的最多****
         */
        Class clazz3 = Class.forName("com.lucky.Person");

        /**
         *  通过类加载器
         */
        ClassLoader classLoader = ReflectTest.class.getClassLoader();
        Class clazz4 = classLoader.loadClass("com.lucky.Person");
    }

通过几种方式获得的Class实例都是地址都相同。

System.out.println(clazz1 == clazz2);
System.out.println(clazz1 == clazz3);
System.out.println(clazz1 == clazz4);

4、可以有class的类型

a、 class:
外部类, 成员(成员内部类, 静态内部类), 局部内部类, 匿名内部类
b、 interface: 接口
c、 数组
d、 enum:枚举
e、 annotation:注解@interface
f、 primitive type:基本数据类型
g、void

 @Test
    public void test03() {
        Class c1 = Object.class;
        Class c2 = Comparable.class;
        Class c3 = String[].class;
        Class c4 = int[][].class;
        Class c5 = ElementType.class;
        Class c6 = Override.class;
        Class c7 = int.class;
        Class c8 = void.class;
        Class c9 = Class.class;

        int[] a = new int[10];
        int[] b = new int[100];
        Class c10 = a.getClass();
        Class c11 = b.getClass();
        // 只要数组的元素类型与维度一样,就是同一个Class, 即使长度不一样
        System.out.println(c10 == c11);
    }

二、ClassLoader

在这里插入图片描述
类加载的作用: 将class文件字节码内容加载到内存中, 并将这些静态数据转换成方法区的运行时数据结构, 然后在堆中生成一个代表这个类java.lang.Class对象, 作为方法区中类数据的访问入口。
类缓存: 标准的JavaSE类加载器可以按要求查找类, 但一旦某个类被加载到类加载器中, 它将维持加载(缓存) 一段时间。 不过JVM垃圾回收机制可以回收这些Class对象。
在这里插入图片描述

三、创建运行时类的对象

通过Class的newInstance() 方法。

 /**
     *  通过反射创建运行时类的对象,比如通过反射创建一个Person类的对象
     */
    @Test
    public void test04() throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        Class clazz = Class.forName("com.lucky.Person");
        clazz.newInstance();
    }

注意:
使用newInstance创建对应运行时类的对象时,内部默认调用了无参构造器。因此想要正确使用此方法,必须提供无参构造,并且属性为public。
默认无参构造器:还可以在子类继承父类,默认调用super时,保证此构造函数存在。

四、获取运行时类的完整结构

实现的全部接口

 @Test
    public void test09(){
        Class clazz = Person.class;

        Class[] interfaces = clazz.getInterfaces();
        for(Class c : interfaces){
            System.out.println(c);
        }

        System.out.println();
        //获取运行时类的父类实现的接口
        Class[] interfaces1 = clazz.getSuperclass().getInterfaces();
        for(Class c : interfaces1){
            System.out.println(c);
        }

    }

所继承的父类

  @Test
    public void test07(){
        Class clazz = Person.class;

        Class superclass = clazz.getSuperclass();
        System.out.println(superclass);
    }

全部的构造器

 @Test
    public void test08(){

        Class clazz = Person.class;
        //getConstructors():获取当前运行时类中声明为public的构造器
        Constructor[] constructors = clazz.getConstructors();
        for(Constructor c : constructors){
            System.out.println(c);
        }

        System.out.println();
        //getDeclaredConstructors():获取当前运行时类中声明的所有的构造器
        Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
        for(Constructor c : declaredConstructors){
            System.out.println(c);
        }

全部的方法

  @Test
    public void test06() throws ClassNotFoundException {
        Class clazz = Class.forName("com.lucky.Person");
        // 获取类方法
        //getMethods():获取当前运行时类及其所有父类中声明为public权限的方法
        Method[] methods = clazz.getMethods();
        for(Method m : methods){
            System.out.println(m);
        }
        System.out.println();
        //getDeclaredMethods():获取当前运行时类中声明的所有方法。(不包含父类中声明的方法)
        // 方法权限 getModifiers
        // 返回值 m.getReturnType().getName()
        // 方法名 getName
        // 参数列表 m.getParameterTypes()
        // 异常 m.getExceptionTypes()
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for(Method m : declaredMethods){
            System.out.println(m);
        }
    }

全部的Field

@Test
    public void test05() throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        Class clazz = Class.forName("com.lucky.Person");
        // 获取属性
        // 当前类属性, 也可以通过Field获取这个修饰符  类型 名称
        Field[] fields1 = clazz.getDeclaredFields();

        for (Field field : fields1) {
            System.out.println(Modifier.toString(field.getModifiers()));
            System.out.println(field.getType());
            System.out.println(field.getName());
            System.out.println();
        }
        // 当前类和父类public属性
        Field[] fields2 = clazz.getFields();
        for (Field field : fields2) {
            System.out.println(Modifier.toString(field.getModifiers()));
            System.out.println(field.getType());
            System.out.println(field.getName());
            System.out.println();
        }

    }

获取注解,获取运行类所在包

五、调用运行时类的指定结构

1、操作运行类某一指定属性

 @Test
    public void test10() throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException {
        Class clazz = Class.forName("com.lucky.Person");

        // 创建对象
        Person person = (Person) clazz.newInstance();
        // 获取指定属性
        Field field = clazz.getDeclaredField("name");
        // 保证当前属性值可以访问
        field.setAccessible(true);
        // 赋值
        field.set(person, "tom");
        System.out.println(field.get(person));

    }

2、运行某个运行时类方法


    @Test
    public void test11() throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        Class clazz = Class.forName("com.lucky.Person");

        // 创建对象
        Person person = (Person) clazz.newInstance();
        Method method = clazz.getDeclaredMethod("show", String.class);
        method.setAccessible(true);

        // 又不是静态方法,那么就要用实例来访问
        // 用 invoke来执行方法
        Object retV = method.invoke(person, "chain");
        System.out.println(retV);

        // 如果是静态方法直接用

        Method showDesc = clazz.getDeclaredMethod("showDesc");
        showDesc.setAccessible(true);
        //如果调用的运行时类中的方法没有返回值,则此invoke()返回null
        // Object retV1 = showDesc.invoke(null);
        // 不传入对象也可以,静态方法本来就不用new对象
        Object retV1 = showDesc.invoke(Person.class);
        System.out.println(retV1);//null
    }

3、获取运行时类的带泛型的父类的泛型

 @Test
    public void test12() throws ClassNotFoundException {
        Class clazz = Class.forName("com.lucky.Person");
        // 获取泛型类
        Type genericSuperclass = clazz.getGenericSuperclass();
        ParameterizedType paramType = (ParameterizedType) genericSuperclass;
        //获取泛型类型
        Type[] actualTypeArguments = paramType.getActualTypeArguments();
        System.out.println(actualTypeArguments[0].getTypeName());
        System.out.println(((Class) actualTypeArguments[0]).getName());


    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值