Java 类型的加载和连接都是在运行期间,这样可能会有性能开销,但是却能提供动态扩展的语言特性。
解析可能在初始化之后进行
vm规范规定了四个点必须对类进行初始化:
- 遇到new ,getstatic,putstatic, invokestatic几个指令的时候。也就是new, 使用设置静态变量,引用静态方法的时候 访问静态变量只会造成当前类的初始化,不会初始化子类,这里还要注意,对于被final修饰、在编译器已经放入常量池的静态属性不会触发初始化。 new 数组是不会初始化类的
- reflect包的方法对类进行反射调用的时候
- 初始化时要先初始化父类
- 虚拟机启动时要指定主类,主类会先初始化
过程
加载
1. 根据全名获取二进制字节流 没有指明流的来与,于是可以是jar包,网络,动态代理在运行时生成 2. 在方法区生成该类的运行时数据结构 3. 在堆上创建Class对象,作为对方法区数据访问入口
验证
要求满足字节码格式规范等
准备
为类变量分配内存并设定初始值的阶段, 不涉及实例变量,都在方法区进行。
解析
将常量池内的符号引用替换为直接引用的过程。 符号引用就是字面量, 没有指向实际的内存,只是表示知道这个类有这个引用,但具体引用到哪里还不知道 直接引用, 是连接了实际目标的符号引用
初始化
执行类client()方法的过程, 由编译器收集的类变量赋值动作和static方法块合并成的。 虚拟保证client是顺序执行的,因此要注意如果static预发快中有死循环会造成阻塞
类加载器
- 工作流程: 收到加载请求后,交给父加载器去完成, 父完不成才会尝试自己完成。 如下是个过程 Class c = findClass(name); if(c == null){ c = parent.loadClass(name); } if(c == null){ // 父不行了,自己来 c = findClass(name) } return c;
- 破坏双亲委派
- JNDI 双亲委派解决不了基础类需要调用用户代码的情况 JNDI服务由启动加载器加载,但是其用到的接口类却由各个厂商提供,启动加载器就会找不到,引入了线程上下文加载器,需要使用Thread.setContextClassLoader()进行设置。JNDI就是用了这个加载器
- OSGI, 每个boundler一个类加载器,import的类委托给export的类加载器去加载。