java 类加载

反射

  • 类加载

    加载阶段:JVM在该阶段的主要目的时将字节码从不同的数据源【可能时 class文件, jar包,甚至是网络】转化为二进制字节流加载到内存,并生成一个代表该类的 Class对象

  • 连接阶段:

    • 验证阶段:

      • 为了确保Class文件的字节流中包含的信息符合jvm的要求,而且不会危害jvm安全;

      • 在 loadClass 方法中会生成一个安全管理类SecurityManager,这个类会进行验证

        • 文件格式验证,.class文件是否以魔数开头,即:oxcafebabe开头;
        • 元数据验证,字节码验证,符号引用验证;
        • 考虑使用 -Xverify:none 参数来关闭大部分类的验证措施,以提高效率;
      • 准备阶段:

        • jvm 会对静态变量分配内存并初始化【对应数据内省的默认初始化, eg 0, 0L,0.0f,null, false】

        • 这些变量都在内存的方法区【与java版本相关】中进行分配;

          public int n1 = 10;
          public static int n2 = 20;
          public static final int n3 = 30;
          //1.n1是实例属性,在准备阶段不分配内存;
          //2.n2是静态属性,在准备阶段分配内存,且默认初始化: 0;
          //3.n3是static final 常量,一旦赋值就不会改变,n3在准备阶段分配内存,而且默认初始化为 30;
          //[也可以在静态代码块中初始化,但系统不会在此阶段默认赋值]
          //当在静态代码块显式初始化的时候,必须加载类;
          //final 修饰的变量必须显式初始化,且只能赋值一次。
          
      • 解析阶段:

        • jvm 将常量池内的符号引用,替换为直接引用的过程;

          当还没由加载到内存的时候,是通过变量,符号表示相互之间的引用,

          当加载到内存后,jvm分配内存后,使用内存地址,替换以前的符号表示【符号都是在常量池内】;

  • 初始化:

    • 到了初始化,才开始真正执行类中定义的java代码,在此阶段是执行()方法的过程;

    • ()方法是由编译器按照语句在源文件中出现的顺序,依次自动收集类中静态变量的赋值动作和静态代码块中的语句,并进行合并;

    • jvm 会保证一个类的()方法【同步方法】在多线程中会被一个线程执行,其他线程需要阻塞等待,直到执行完毕【所以才会只产生一个Class对象】;

      static{
              num = 100;
          }
      public static int num = 300;
      //在加载的准备阶段,分配了num的内存,并初始化为0;
      //在初始化阶段,会将:
      num = 100;
      num = 300; //合并 =>
      num = 300;
      

通过反射获取类的结构信息:

  • Class类:

    getName();
    getSimpleName();
    getFields(); //获取所有public修饰的属性,包含本类和父类;
    getDeclaredFielss(); //获取本垒中所有属性;
    getMethods();
    getDeclaredMethods();
    getConstructors(); // 获取所有public修饰的构造器,仅本类
    getDeClaredConstructors();
    getPackage(); //以Package的形式返回 包信息;
    getSuperClass();
    getInterfaces();
    getAnnotations();
    
  • Field类:

    getModifiers();//以int形式返回修饰符;
    // 默认修饰符:0,public:1,private:2,protected:4,static: 8;final: 16,publi + static : 9;
    getType(); //属性类型的Class对象;
    getName();//返回属性名;
    
  • Method类

    getModifiers();//以int形式返回修饰符;
    getReturnType();//返回属性类型的Class对象;
    getName();//返回方法名;
    getParameterTypes();//以Class[]返回参数类型数组;
    
  • Constructor类

    getModifiers();//以int形式返回修饰符;
    getName(); // 全类名:包路径+类名
    getParameterTypes();//以Class[]返回参数类型数组;
    
  • 通过反射创建对象:

    • 调用类中的public的无参构造器;
    • 调用类中的指定构造器;
    • Class类的相关方法:
      • newInstance():调用类中的无参构造,获取对应类;
      • getConstructor(Class clazz):根据参数列表,获取对应构造器对象;
      • getDecalaredConstructor(Class clazz):根据参数列表,获取对应构造器对象;
    • Constructor类相关方法:
      • setAccessible():爆破【将private权限设置为外部类可访问状态:构造器,属性,方法】
      • newInstance(Object ...obj):调用构造器;
    • Field类的相关方法:
      • setAccessible():
      • f.set(对象, 值);
        • 为什么上传对象,因为只要类加载,对于每一个类就会存在一个Class类,数据结构【属性,构造器,方法】都在Class内,但是却可以有无数个实例对象,普通属性是和实例绑定的,所以,需要传入实例,
        • 当为静态变量的时候,实例对象可以传入null【加个方法重载多好】;
    • Method类的相关方法:
      • setAccessible():
      • invoke(对象,实参列表);
      • 如果有返回值,编译类型是:Object;
posted @   烟雨断桥  阅读(2)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!
点击右上角即可分享
微信分享提示