GENGEN
主页
vuepress
  • GIT命令
  • python+django
  • vue cli搭建项目
  • babel es6转换es5
  • docker aliyun配置
  • npm 配置
  • linux 常用命令
  • Ubuntu 下Linux 命令
  • github
  • gitee
  • csdn
  • 关于我
主页
vuepress
  • GIT命令
  • python+django
  • vue cli搭建项目
  • babel es6转换es5
  • docker aliyun配置
  • npm 配置
  • linux 常用命令
  • Ubuntu 下Linux 命令
  • github
  • gitee
  • csdn
  • 关于我
  • java基础

    • JDK8 函数式编程
    • JDK8 新特性之Date-Time
    • Servlet 源码分析
    • ArrayList 源码
    • LinkedList 源码
    • HashMap 源码
    • String 源码
    • BigDecimal 源码
    • java 类的加载
    • Class 源码
    • Synchronized锁升级
    • 事务的传播机制
    • knowledge
  • JAVA WEB

    • Java Servlet
    • 权限设计
    • logback日志的链路追踪
  • DATABASE

    • MySQL EXPLAIN详解
    • MySQL 索引
    • MySQL 表锁、行锁
    • MySQL ACID与transcation
    • 分布式事务
    • MySQL MVCC机制
    • Mysql 乐观锁与悲观锁
    • 分布式锁1 数据库分布式锁
    • 分布式锁2 Redis分布式锁
    • 分布式锁3 ZK分布式锁
  • SpringCloud

    • SpringCloud服务注册中心之Eureka
    • SpringCloud服务注册中心之Zookeeper
    • SpringCloud服务调用之Ribbon
    • SpringCloud服务调用之OpenFeign
    • SpringCloud服务降级之Hystrix
    • SpringCloud服务网关之Gateway
    • SpringCloud Config分布式配置中心
    • SpringCloud服务总线之Bus
    • SpringCloud消息驱动之Stream
    • SpringCloud链路追踪之Sleuth
    • SpringCloud Alibaba Nacos
    • SpringCloud Alibaba Sentinel
  • Spring

    • SpringBoot
    • Spring-data-jpa入门
    • SpringCloud问题
    • dispatcherServlet 源码分析
    • @SpringBootApplication注解内部实现与原理
    • spring启动初始化初始化
  • 中间件

    • 分布式协调服务器Zookeeper
    • 服务治理Dubbo
    • 分布式配置管理平台Apollo
    • 消息中间件框架Kafka
    • 分布式调度平台ElasticJob
    • 可视化分析工具Kibana
    • ElacticSearch 基础
    • ElacticSearch进阶
    • ElacticSearch集成
  • 环境部署

    • 应用容器引擎Docker
    • DockerCompose服务编排
    • 负载均衡Nginx
    • Nginx的安装配置
    • K8S基础
  • 代码片段

    • listener 监听模式
    • spingboot 整合redis
    • XSS过滤
    • profile的使用
    • ConfigurationProperties注解
  • 设计模式

    • 工厂模式
    • 单例模式
    • 装饰者模式
    • 适配器模式
    • 模板方法模式
    • 观察者模式
  • 读书笔记

    • 《Spring in Action 4》 读书笔记
    • 《高性能mysql》 读书笔记
  • NoSQL

    • Redis基础
    • Redis高级
    • Redis集群
    • Redis应用
  • MQ

    • rabbitMQ基础
    • rabbitMQ高级
    • rabbitMQ集群
  • JVM

    • JVM体系架构概述
    • 堆参数调整
    • GC 分代收集算法
    • JVM 垃圾回收器
    • JVM 相关问题
  • JUC

    • JUC总览
    • volatile关键字
    • CAS
    • ABA问题
    • collections包下线程安全的集合类
    • Lock 锁
    • LockSupport
    • AQS
    • Fork/Join分支框架
    • JUC tools
    • BlockingQueue 阻塞队列
    • Executor 线程池
    • CompletableFuture
    • 死锁以及问题定位分析
  • Shell

    • shell命令
    • shell基础
  • Activiti

    • IDEA下的Activiti HelloWord
    • 流程定义的CRUD
    • 流程实例的执行
    • 流程变量
  • VUE

    • vue基础
    • vue router
    • Vuex
    • Axios 跨域
    • dialog 弹出框使用
    • vue 动态刷新页面
    • vue 封装分页组件
    • vue 动态菜单
    • vue 常用传值
  • Solidity 智能合约

    • Solidity 基础
    • Solidity ERC-20
    • Solidity 101
  • English

    • 时态

Class 源码解析 JDK8

概述

  • Class类实现了Serializable GenericDeclaration Type AnnotatedElement 接口。

    • GenericDeclaration 是接口继承了AnnotatedElement接口,是所有“可以声明(定义)范型变量”的实体(如Class,Constructor,Method)的公共接口。也就是说只有实现了这个接口的才能在对应“实体”上声明“范型变量”。

    • Type是Java语言中所有类型的父接口。它并不是我们平常工作中经常使用的 int、String、List、Map等数据类型,而是从Java语言角度来说,对基本类型、引用类型向上的抽象;

    • AnnotatedElement 表示目前正在此 VM 中运行的程序的一个已注释元素。该接口允许反射性地读取注释。由此接口中的方法返回的所有注释都是不可变并且可序列化的。调用者可以修改已赋值数组枚举成员的访问器返回的数组;这不会对其他调用者返回的数组产生任何影响。

  • java反射是围绕Class类展开的,jvm将字节码文件加载到方法区内存中并返回一个Class对象。

  • java反射使得我们可以在程序运行时动态加载一个类,动态获取类的基本信息和定义的方法,构造函数,域等。还可以动态创建类的实例,执行类实例的方法,获取类实例的域值。反射使java这种静态语言有了动态的特性。

  • 反射是通过反编译将.class文件转换成.java,从而操纵类的属性方法。

变量

//构造器缓存
private volatile transient Constructor<T> cachedConstructor;
//实例缓存
private volatile transient Class<?>       newInstanceCallerCache;

private transient String name;

private final ClassLoader classLoader;

构造器

  • Class类构造器私有的,不允许手动创建
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类中方法主要有三类:生成实例、获取类信息、类型转换。
    • 生成实例 一类方法主要有两个:forName方法和newInstance方法。
    • 获取类信息 一类方法非常多,作用包括获取类的构造方法,已经声明的字段、方法,获取类或者方法的注解,获取类的包名、父类,以及判断类是否是数组、是否是枚举、是否是接口。
    • 类型转换 该类方法主要有两个: asSubclass方法和 cast

生成实例

  • 此方法主要是根据给定的类名,JVM将类加载、链接、初始化;生成类的实例。

  • 初始化信息和生成实例一般包含两个方法 forName 和 newInstance:

    • forName有两种声明
    public static Class<?> forName(String className)
                  throws ClassNotFoundException {
          Class<?> caller = Reflection.getCallerClass();
          return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
      }
    
    public static Class<?> forName(String name, boolean initialize,
                                     ClassLoader loader)
          throws ClassNotFoundException
      {
          Class<?> caller = null;
          SecurityManager sm = System.getSecurityManager();
          if (sm != null) {
              // Reflective call to get caller class is only needed if a security manager
              // is present.  Avoid the overhead of making this call otherwise.
              caller = Reflection.getCallerClass();
              if (sun.misc.VM.isSystemDomainLoader(loader)) {
                  ClassLoader ccl = ClassLoader.getClassLoader(caller);
                  if (!sun.misc.VM.isSystemDomainLoader(ccl)) {
                      sm.checkPermission(
                          SecurityConstants.GET_CLASSLOADER_PERMISSION);
                  }
              }
          }
          return forName0(name, initialize, loader, caller);
      }
    
    • 最终两个forName方法,都调用了forName0方法,这是一个本地方法。
    • 在forName方法实现中,出现了两个类,一个是SecurityManager,另一个是ClassLoader
      • 其中SecurityManager是jvm提供的在应用层进行安全检查的机制,应用程序可以根据策略文件被赋予一定的权限,例如是否可以读写文件,是否可以读写网络端口,是否可以读写内存,是否可以获取类加载器……。在进行特殊操作时,需要进行安全检查,从而给程序的运行安全提供一定保障。
      • 而ClassLoader则涉及到另一个大的话题:类加载,我们程序运行中使用的类都需要由类加载器来完成加载,并执行一定的初始化,随后才可以被我们使用。
  • newInstance:类加载完成后就需要实例化了,需要用到此方法。

public T newInstance()
    throws InstantiationException, IllegalAccessException
{   // 进行安全检查
    if (System.getSecurityManager() != null) {
        checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), false);
    }

  

  // 每次先检查是否有已经缓存过的构造器,如果没有,则重新获取
    if (cachedConstructor == null) {
        if (this == Class.class) {
            throw new IllegalAccessException(
                "Can not call newInstance() on the Class for java.lang.Class"
            );
        }
        try {
            Class<?>[] empty = {};
            // 获取该类已经声明的的无参构造方法
            final Constructor<T> c = getConstructor0(empty, Member.DECLARED);
           // 此处是为了使无参构造方法可以被访问,因为有时,构造方法被声明为private的
            java.security.AccessController.doPrivileged(
                new java.security.PrivilegedAction<Void>() {
                    public Void run() {
                            c.setAccessible(true);
                            return null;
                        }
                    });
             // 将获取的无参构造方法缓存        
            cachedConstructor = c;
        } catch (NoSuchMethodException e) {
            throw (InstantiationException)
                new InstantiationException(getName()).initCause(e);
        }
    }
    Constructor<T> tmpConstructor = cachedConstructor;
   // 获取构造方法的语言修饰符,诸如public,private,static,final等
    int modifiers = tmpConstructor.getModifiers();
     // 根据已经获取的语言修饰符判断是否具有访问权限,如果没有,则执行以下操作
    if (!Reflection.quickCheckMemberAccess(this, modifiers)) {
        Class<?> caller = Reflection.getCallerClass();
        if (newInstanceCallerCache != caller) {
            Reflection.ensureMemberAccess(caller, this, null, modifiers);
            newInstanceCallerCache = caller;
        }
    }
    // 整个方法的核心,使用类的构造方法来生成实例
    try {
        return tmpConstructor.newInstance((Object[])null);
    } catch (InvocationTargetException e) {
        Unsafe.getUnsafe().throwException(e.getTargetException());
        // Not reached
        return null;
    }
}
  • newInstance的具体实现,本质上是获取的类的无参构造方法,然后执行无参构造方法来生成实例。
  • 所以说反射必须依赖类的无参构造器。否则会抛出InstantiationException

获取类信息

  • 这一类方法非常多,作用包括获取类的构造方法,已经声明的字段、方法,获取类或者方法的注解,获取类的包名、父类,以及判断类是否是数组、是否是枚举、是否是接口。
  • 获取类信息之前必须先生成实例,即调用froName newInstance方法。
  • 以获取方法举例:
public class ReflectDemo {

    private String reflectString = "abc";
    //因为有 *有参构造器* 所有需要显示的定义一个无参构造器用于反射生成实例
    ReflectDemo() {
    }

    ReflectDemo(String reflectString) {
        this.reflectString = reflectString;
    }
    //1.私有方法
    private void priMethod(int a) {
        System.out.println("this is [private] method");
    }
    //2.公有方法
    public void pubMethod() {
        System.out.println("this is [public] method");
    }
    //3.有参方法
    public String getMath(int a, int b) {
        BigDecimal bigDecimalA = BigDecimal.valueOf(a);
        BigDecimal bigDecimalB = BigDecimal.valueOf(b);
        BigDecimal bigDecimal = bigDecimalA.divide(bigDecimalB, 2, RoundingMode.HALF_UP);
        return String.valueOf(bigDecimal);
    }
    //4.可变参数方法
    public String addString(String... args) {
        StringBuilder sb = new StringBuilder();
        for (String arg : args) {
            sb.append(arg).append("|");
        }
        return sb.substring(0, sb.length() - 1);
    }

}

public class App {
    public static void main(String[] args) throws Exception {
        Class<?> reflectDemo = Class.forName("com.project.ReflectDemo");
        Object o1 = reflectDemo.newInstance();
        //获取所有方法
        Method[] methods = reflectDemo.getMethods();

        //1
        Method priMethod = reflectDemo.getDeclaredMethod("priMethod");
        //功能是启用或禁用安全检查
        priMethod.setAccessible(true);
        priMethod.invoke(o1);

        //2
        Method pubMethod = reflectDemo.getMethod("pubMethod");
        pubMethod.invoke(o1);

        //3
        //因为入参是int 基本数据类型,这里也写int.class
        Method pubMethod3 = reflectDemo.getMethod("getMath", int.class, int.class);
        String invoke = (String) pubMethod3.invoke(o1, 10, 3);
        System.out.println(invoke);

        //4
        //可变参数写法,可变参数本质是一个数组,外层需要包一个new Class[]{}, invoke时候包一个new Object[]{}
        //否则报错
        Method pubMethod4 = reflectDemo.getMethod("addString", new Class[]{String[].class});
        Object invoke1 = pubMethod4.invoke(o1,new Object[]{new String[]{"123", "345"}});
        System.out.println(invoke1.toString());

    }
}
  • 获取私有方法调用,需要调用setAccessible(true);方法,关闭安全检查,这时程序不会检查访问修饰符

类型转换

  • 类型转换方法有两个 asSubclass cast
/**
 * 将类转换为它的子类Class
 * @param clazz 父类的Class
 * @param <U> 父类
 * @return U的子类Class
 */
public <U> Class<? extends U> asSubclass(Class<U> clazz) {
    // 判断clazz是否是当前类,或者是当前类的父类
    if (clazz.isAssignableFrom(this))
        return (Class<? extends U>) this;
    else
        throw new ClassCastException(this.toString());
}

/**
 * 将给定的类转换为当前Class所代表的类
 * @param obj 需要转换的类
 * @return 当前Class所代表的类
 */
public T cast(Object obj) {
    // obj不为null,并且可以被转换为当前Class代表的类
    // isInstance为native方法,类似于instanceOf的作用
    if (obj != null && !isInstance(obj))
        throw new ClassCastException();
    return (T) obj;
}

总结

  • Class.forName("") 与 ClassLoader.getSystemClassLoader().loadClass("")

    • Class.forName加载类是将类进了初始化(相当于调用无参构造函数,会调用static静态代码来初始化配置)
    • ClassLoader的loadClass并没有对类进行初始化,只是把类加载到了虚拟机中
  • 反射作用是把.class文件反编译,生成实例,动态获取实例的属性方法等。

  • spring 依赖注入也是这种反射机制获取对象

Last Updated:
Contributors: 88395515
Prev
java 类的加载
Next
Synchronized锁升级