jvm学习:类的加载、连接、初始化、常量

类在jvm中有这几个过程类的加载、连接、初始化、使用、卸载

类的加载

类的加载是将class文件中的二进制数据加载到内存中,将其放在运行时的数据区:方法区内,然后在内存中创建一个

java.lang.Class对象用来封装类在方法区内的数据结构。规范没有规定Class对象放在哪里,hotspot虚拟机将其放在了方法区中。

加载.class文件的方式

从本地系统中加载

从网络下载.class文件

从zip jar文件中加载

将java原文件动态的编译为.class文件,比如jsp

类的使用

java的类主动使用,才会执行初始化

主动使用

创建类的实例

访问某个类或者接口的非final变量、对改静态变量的赋值

调用类的静态方法

反射

初始化一个类的子类

表明为启动类比如main方法

java1.7动态语言支持

例子

运行下面的例子可以先打印str1,然后打印str2。

package com.javalearn.jvm.classloader;

import lombok.extern.slf4j.Slf4j;

/**
* 对于静态字段来说,只有直接使用定义了该字段的类才会被初始化
* 当一个类加载要求父类加载完毕
*/
@Slf4j
public class MyTest { public static void main(String[] args) {
log.info(MyChild1.str2);
}
} @Slf4j
class MyParent1 {
public static String str1 = "hello"; static {
log.info("my parent static block");
}
} @Slf4j
class MyChild1 extends MyParent1 {
public static String str2 ="hello child2";
static {
log.info("my child1 static block");
}
}

结果

22:47:54.901 [main] INFO com.javalearn.jvm.classloader.MyParent1 - my parent static block
22:47:54.905 [main] INFO com.javalearn.jvm.classloader.MyChild1 - my child1 static block
22:47:54.905 [main] INFO com.javalearn.jvm.classloader.MyTest - hello child2

使用反射是主动使用,会初始化类,但ClassLoader不是主动使用

package com.learn.java.javabase.jvm.classloader;

public class ClassLoaderTest012 {
public static void main(String[] args) {
ClassLoader classLoader=ClassLoader.getSystemClassLoader();
Class<?> c1;
try {
c1 = classLoader.loadClass("com.learn.java.javabase.jvm.classloader.Test012");
System.out.println(c1);
System.out.println("--------");
c1=Class.forName("com.learn.java.javabase.jvm.classloader.Test012");
System.out.println(c1);
System.out.println("--------");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
System.out.println("--------"); }
}
class Test012{
static {
System.out.println("test012");
}
}

运行结果

class com.learn.java.javabase.jvm.classloader.Test012
--------
test012
class com.learn.java.javabase.jvm.classloader.Test012
--------
--------

初始化

类初始化的时候,会首先初始化它的父类,比如Object类

接口初始化的时候,不会初始化它的父接口

初始化是按照上下文的顺序初始化

package com.learn.java.javabase.jvm.classloader;

import java.util.Random;

public class ClassLoaderInterfaceTest {
public static void main(String[] args) {
// Paraent01 paraent01 =new Paraent01();
// Paraent01 paraent02 =new Paraent01();
// Child01 child01 =new Child01();
//System.out.println(Paraent01.paraent01);
System.out.println(Child01.childInt);
}
} class Paraent01 {
//常量会在编译期间放到调用类的常量池中
public static final int paraent01=10;
//public static int paraent01=10;
//静态代码块随着类的加载只会执行一次 static {
System.out.println("paraent01");
}
} class Child01 extends Paraent01 {
public static int childInt =5;
static {
System.out.println("child01");
}
}

结果

paraent01
child01
5

常量

常量会在编译期加载使用类的常量池中

下面的例子Child01的常量在test类使用的时候,已经放入到常量池。说明

final变量的调用不是主动使用,并不会导致父类的初始化

package com.learn.java.javabase.jvm.classloader;

import java.util.Random;

public class ClassLoaderInterfaceTest {
public static void main(String[] args) {
// Paraent01 paraent01 =new Paraent01();
// Paraent01 paraent02 =new Paraent01();
// Child01 child01 =new Child01();
//System.out.println(Paraent01.paraent01);
System.out.println(Child01.childInt);
}
} class Paraent01 {
//常量会在编译期间放到调用类的常量池中
public static final int paraent01=10;
//public static int paraent01=10;
//静态代码块随着类的加载只会执行一次 static {
System.out.println("paraent01");
}
} class Child01 extends Paraent01 {
public static final int childInt =5;
static {
System.out.println("child01");
}
}

输出结果

5
上一篇:用sublime编译C++的方法


下一篇:greendao 3.1.0在android studio中配置