您的位置:首页 > 编程语言 > Java开发

Java之JVM虚拟机理解和实例

2016-05-28 12:17 471 查看

开心一笑

今天上街捡了一捆芹菜,仔细一想,有芹菜就要买肉,买了肉就要有厨房,厨房有咯那就必须要个媳妇来弄菜,有个媳妇就肯定有丈母娘,你要想娶她姑娘,她就必须要开条件了,要房,要钱,要车,仔细想了哈,赶紧把芹菜扔了,,太吓人了。。

提出问题

JVM虚拟机如何加载类,已经类加载器如何使用等等???

解决问题

以下的内容来自 张老师的视频笔记 http://my.csdn.net/ricohzhanglong

JVM的结束生命周期:

1)执行System.exit()方法

2)程序正常执行结束

3)程序执行过程中出现的异常或者错误导致程序的异常终止

4)由于操作系统出现错误而导致的Java虚拟机进程终止

类的生命周期:

加载:查找并加载类的二进制数据,进入JVM虚拟机中

连接:连接包括三方面

1)验证:验证类的正确性

2)准备:为类的静态变量分配内存,并将其初始化为默认值

3)解析:把类的符合引用转化为直接引用

初始化:

为类的静态变量赋予正确的初始值,为用户赋予的正确值

例:

public class Test{
/** 按照上面的说法,a在连接准备阶段时的开始默认值0(int默认值),在初始化的时候a的值才为3 **/
public static int a = 3

}




加载.class文件的方式

1)从本地系统直接加载(常用

2)通过网络下载.class文件

3)从zip,jar文件中加载.class文件(常用

4)从专有的数据库提取.class文件

5)从java源文件动态编译为.class文件

类的加载最终的结果就是在堆区生成一份class对象

类加载器

根类加载器 Bootstrap ClassLoader

负责加载$JAVA_HOME中jre/lib/rt.jar里所有的class,由C++实现,不是ClassLoader子类

扩展类加载器 Extension ClassLoader

负责加载java平台中扩展功能的一些jar包,包括$JAVA_HOME中jre/lib/*.jar或-Djava.ext.dirs

指定目录下的jar包

运用类加载器 App ClassLoader

负责记载classpath中指定的jar包及目录中class

用户自定义加载器 Custom ClassLoader

属于应用程序根据自身需要自定义的ClassLoader,如tomcat、jboss都会根据j2ee规范自行实现

ClassLoader

加载过程中会先检查类是否被已加载,检查顺序是自底向上,从Custom ClassLoader到BootStrap ClassLoader逐层检查,只要某个classloader已加载就视为已加载此类,保证此类只所有ClassLoader加载一次。而加载的顺序是自顶向下,也就是由上层来逐层尝试加载此类。

例一:

JDK的官方文档有这么一句话,下面例子验证这句话.

public ClassLoader getClassLoader()

This method will return null in such implementations if this class was loaded by the bootstrap class loader.

package com.evada.de;

class Test{
private int id;
private String name;
}

/**
* Created by Ay on 2016/5/24.
*/
public class LambdaTest {

public static void main(String[] args) throws Exception{
/** 获得String类的class对象 **/
Class _class = Class.forName("java.lang.String");
/** 打印其类加载器 结果为null,说明是根类加载器**/
System.out.println(_class.getClassLoader());

Test test = new Test();
/** 打印上述类的类加载器  **/
System.out.println(test.getClass().getClassLoader());
//打印结果为 sun.misc.Launcher$AppClassLoader@14899482
//说明是运用类加载器加载
}
}


例二:静态变量出生化的2中形式:1)在静态变量的申明处初始化 2)在静态代码块中初始化

package com.evada.de;

class Test{
/** 在静态变量的申明处初始化 **/
public static int first = 1;
public static long second;
public static long three;
/** 在静态代码块中初始化 **/
static{
second = 100;
}
}

/**
* Created by Ay on 2016/5/24.
*/
public class LambdaTest {

public static void main(String[] args) throws Exception{
System.out.println(Test.first);//result:1
System.out.println(Test.second);//result:100
System.out.println(Test.three);//result:0
}
}


例三:

package com.evada.de.projplanning.service;

class Test{
/** 因为是final 值不会被改变,且6/3 = 2值已经确定,所以类在连接阶段的准备时期就结束了,不会初始化该类 **/
public static final int a = 6 /3;

static{
System.out.println("Hello, I am Ay!!");
}
}

/**
* Created by Ay on 2016/5/27.
*/
public class AyTest {

public static void main(String[] args) {

System.out.println(Test.a);
}
}


结果:2

解释:

public static final int a = 6 /3;在编译时是个常量,故不会对类进行初始化,所有
static静态代码块不会执行。


例四:

package com.evada.de.projplanning.service;

import java.util.Random;

class Test{

public static final int a = new Random().nextInt(100);

static{
System.out.println("Hello, I am Ay!!");
}
}

/**
* Created by Ay on 2016/5/27.
*/
public class AyTest {

public static void main(String[] args) {

System.out.println(Test.a);
}
}


结果

Hello, I am Ay!!
25


解释:

public static final int a = new Random().nextInt(100); 由于a在编译时无法确认其值,即a编译时其值是个变量,因此该类会进行初始化。所有静态代码块会被执行。


类的初始化时机:

当java虚拟机初始化一个类时,要求它的所有父类都被初始化,这条规则对接口不适用

在初始化一个类的时候,并不会先初始化它的实现接口

在初始化一个接口时,并不会初始化它的一个父接口

因此一个接口并不会因为它的实现类或者子接口初始化而初始化,只有当程序首次使用该接口的静态变量时,才会导致接口初始化。

例五:

package com.evada.de.projplanning.service;

import java.util.Random;

class Test{

public static int a = 2;

static{
System.out.println("Hello, I am Test!!");
}
}

class Child extends Test{
public static int b = 3;

static{
System.out.println("Hello,I am Child");
}

}

/**
* Created by Ay on 2016/5/27.
*/
public class AyTest {

static{
System.out.println("Hello,I am AyTest .....");
}

public static void main(String[] args) {

System.out.println(Child.b);
}
}


结果:

Hello,I am AyTest .....
Hello, I am Test!!
Hello,I am Child
3


解释:

执行main函数时,会初始化类AyTest,所有静态代码块会执行,打印Hello,I am AyTest...
执行Child.b时,是对Child的主动使用,所以会初始化Child,因为,初始化Child类时会先初始化父类,所以执行父类静态代码块,打印信息:Hello,I am Test,再执行子类静态代码块,打印:Hello,I am Child

注意,该例子和前面的例子有所区别:
例四:
public static final int a

而例五的所有变量是没有final的,这就是区别。


例六:

package com.evada.de.projplanning.service;

class Parent{

public static int a = 3;

static{
System.out.println("Hello, I am Parent!!");
}
}

class Child extends Parent{
public static int b = 4;

static{
System.out.println("Hello,I am Child");
}

}

/**
* Created by Ay on 2016/5/27.
*/
public class AyTest {

static{
System.out.println("Hello,I am AyTest .....");
}

public static void main(String[] args) {

Parent parent;
System.out.println("---------------------------");
parent = new Parent();
System.out.println(Parent.a);
/** 在这里父类已经被初始化,不会在被初始化了 **/
System.out.println(Child.b);
}
}


结果:

Hello,I am AyTest .....
---------------------------
Hello, I am Parent!!
3
Hello,I am Child
4


只有当程序访问的静态变量或静态方法确实在当前类或当前接口定义时,才可以认为是对类或者接口的主动使用

例七:

package com.evada.de.projplanning.service;

class Parent{

public static int a = 3;

static{
System.out.println("Hello, I am Parent!!");
}

public static void doSomething(){
System.out.println("parent doSomething ......");
}
}

class Child extends Parent{

static{
System.out.println("Hello,I am Child");
}

}

/**
* Created by Ay on 2016/5/27.
*/
public class AyTest {

static{
System.out.println("Hello,I am AyTest .....");
}

public static void main(String[] args) {

System.out.println(Child.a);
Child.doSomething();
}
}


结果:

Hello,I am AyTest .....
Hello, I am Parent!!
3
parent doSomething ......


读书感悟

《借东西的小人阿莉埃蒂》

别让过去的悲伤,毁掉当下的快乐

你已经是我心脏的一部分了。因为借走的是糖,还回的是心。

许多时候,让我们放不下的,其实并不是对方,而是那些逝去的共同回忆。

其他

如果有带给你一丝丝小快乐,就让快乐继续传递下去,欢迎转载,点赞,顶,欢迎留下宝贵的意见,多谢支持!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: