文章导航PC6首页软件下载单机游戏安卓资源苹果资源

pc软件新闻网络操作系统办公工具编程服务器软件评测

安卓新闻资讯应用教程刷机教程安卓游戏攻略tv资讯深度阅读综合安卓评测

苹果ios资讯苹果手机越狱备份教程美化教程ios软件教程mac教程

单机游戏角色扮演即时战略动作射击棋牌游戏体育竞技模拟经营其它游戏游戏工具

网游cf活动dnf活动lol周免英雄lol礼包

手游最新动态手游评测手游活动新游预告手游问答

您的位置:首页单机游戏角色扮演 → 怪物猎人世界新装备介绍 怪物猎人世界新装备新系统一览

突袭_JVM学习五:JVM之类加载器之编译常量和主动使用

在学习了前面几节的内容后,相信大家已经对JAVA 虚拟机 加载类的过程有了一个认识和了解,那么本节,我们就继续进一步巩固前面所学知识和特殊点。

一、类的初始化回顾

类在初始化的时候,静态变量的声明语句以及静态代码块都被看作类的初始化语句,Java虚拟机会按照初始化语句在类文件中的先后顺序来依次加载它们。

上图中a的初始化其实经过了四步
1、a被初始化为默认值0
2、a被赋予正确的初始值1
3、执行静态代码块,将a的值赋为2
4、执行静态代码块,将a的值赋为4
因此最终的执行结果是4,一个类只能被一个ClassLoader加载一次,只有没有被加载过的类或者已经被卸载的类才可能被再次加载。类的初始化步骤如下:
1、假如这个类还没有被加载和连接,那就先进行加载和连接
2、假如存在直接的父类,并且这个父类还没有被初始化则先初始化直接的父类
3、假如类中存在初始化语句,那就依次执行初始化语句。
注意:Java虚 拟机在初始化一个类的时候要求它的父类已经被初始化,但是这条规则并不适应于接口!在初始化一个类的时候并不会初始化他所实现的接口!在初始化一个接口的 时候也不会去初始化他的父接口!因此一个父接口并不会因为他的实现类或者子接口的初始化而初始化,只有当程序使用该接口特定的静态变量的时候才会去初始化 这个接口!

我们上面的例子印证了第三点,对于前两点我们知道我们构造一个类的时候假设它有父类,就会默认调用父类的无参构造方法,然后就初始化了父类,而初始化之前必须进行加载和连接!
我们来看一个具体的程序来验证上面的结论,代码如下:

wo men shang mian di li zi yin zheng le di san dian, dui yu qian liang dian wo men zhi dao wo men gou zao yi ge lei de shi hou jia she ta you fu lei, jiu hui mo ren diao yong fu lei de wu can gou zao fang fa, ran hou jiu chu shi hua le fu lei, er chu shi hua zhi qian bi xu jin xing jia zai he lian jie! wo men lai kan yi ge ju ti de cheng xu lai yan zheng shang mian di jie lun, dai ma ru xia:

class Parent{

    static int a=1;

    static{

       System.out.println("Parent"s static block");

    }

}


class Child extends Parent{

    static int b=2;

    static{

       System.out.println("Child"s static block");

    }

}


public class InitTestCase {

    static{

       System.out.println("InitTestCase"s static block");

    }

    public static void main(String[] args) {

       System.out.println(Child.b);

    }

}

这个程序很简单,我们一猜就能知道结果,因为是InitTestCase启动类,因此优先初始化!然后调用子类Child的b静态变量,Child继承自Parent,因此先初始化父类Parent,所以运行结果是:

修改代码如下:

class Parent{

    static int a=1;

    static{

       System.out.println("Parent"s static block");

    }

}


class Child extends Parent{

    static int b=2;

    static{

       System.out.println("Child"s static block");

    }
}



public class InitTestCase {

    static{

       System.out.println("InitTestCase"s static block");

    }

    public static void main(String[] args) {

       Parent parent;

       System.out.println("===== split line =====");

       parent=new Child();

       System.out.println(Parent.a);

       System.out.println(Child.b);

    }

}

为了能够看清楚parent具体的初始化状态,我们加入split line来隔开程序段,这样又会返回怎样的结果呢?
刚开始的Parent是否会初始化?
我们之前已经说的很清楚了,只有主动使用的6种情况会导致类的初始化,因此刚开始parent不会初始化,因为InitTestCase是启动类,所以最先初始化,然后是分隔符,然后初始化子类Child,初始化Child的时候发现继承了Parent,所以先初始化Parent,然后初始化Child,然后再次调用parent的静态变量,因为Parent已经初始化了,一个类只能被一个ClassLoader初始化一次,所以不会初始化Parent,直接输出Parent.a的数据,Child同理,因此最终执行结果是:

?

这里也再次强调类初始化的实际为:

只有一下6种主动使用的情况会导致类的初始化,其他任何情况都被视为是被动使用,不会导致类的初始化!
1、创建类的实例
2、访问某个类或接口的静态变量,或者对该静态变量赋值
3、调用类的静态方法
4、反射(如Class.forName(“com.yhj.jvm.classloader.finalTest.TestCase”))
5、初始化一个类的子类
6、Java虚拟机启动时被标明为启动类的类(Java Test)
除了以上6种情况都属于被动使用,不会导致类的初始化。

再来看一段代码:

class StaticClassTest{

    public static int staticValue = 2 / 1;

    static{

       System.out.println("StaticClassTest"s static block!");

    }

}


public class TestCase {

    public static void main(String[] args) {

       System.out.println(StaticClassTest.staticValue);

    }

}

很显然属于调用类的静态成员变量,类会被初始化,初始化就会执行执行静态初始化语句块,就会执行System.out.println("StaticClassTest"s static block!");语句,因此运行结果如下:

但是如果这个类的静态成员是常量呢?
如下代码:

class StaticClassTest{

    public static int staticValue = 2 / 1;

    static{

       System.out.println("StaticClassTest"s static block!");

    }

}

//===================================================================



class StaticFinalTest1{

    public final static int staticValue = 2 / 1;

    static{

       System.out.println("StaticFinalTest1"s static block!");

    }

}

//===================================================================



class StaticFinalTest2{

    public final static int staticValue = new Random().nextInt(10);

    static{

       System.out.println("StaticFinalTest2"s static block!");

    }

}

//===================================================================


public class TestCase {

    public static void main(String[] args) {

       System.out.println(StaticClassTest.staticValue);

       System.out.println(StaticFinalTest1.staticValue);

       System.out.println(StaticFinalTest2.staticValue);

    }

}

第一个我们已经知道了,会执行静态代码块,那第二个和第三个呢?他们的区别是第二个是在第一个的基础之上将staticValue变为了final,第三个对于第二个的区别是第三个static不是一个计算的具体值,而是0-10之间的一个随机数!又有什么样的区别呢?我们先来看一下运行结果!

?

二、编译时常量

看了上面的结果,应该很好奇,为什么没有输出:StaticFinalTest1"s static block!

StaticFinalTest1的静态代码块没有执行,即StaticFinalTest1类并没有被初始化,而StaticFinalTest2被初始化了!

这是为什么呢?我们再来看一下他们的区别:StaticFinalTest1的静态常量值是2/1=2,对于这种值在编译的时候JVM就会把结果计算出来放进class文件中,相当于StaticFinalTest1=2,而StaticFinalTest2在编译的时候并不知道具体的数值是多少,需要运行的时候才会被赋值,所以我们可以看出,调用编译时的静态常量并不会初始化这个类(即不属于主动使用),而调用运行时的静态常量是会初始化该类的!

我们再来改动一下上面父子类的程序

class Parent{

    static int a=1;

    static{

       System.out.println("Parent"s static block");

    }

}



class Child extends Parent{

    static int b=2;

    static{

       System.out.println("Child"s static block");

    }

}



public class InitTestCase {

 

    public static void main(String[] args) {

       System.out.println(Child.a);

    }

}

这样执行结果有什么呢?我们直接调用子类中父类定义的a会怎样呢?
按照我们之前的理论,执行结果应该是调用子类,先初始化父类,我们看下执行结果:

我们看到子类好像没有被初始化,不对,是确实没有被初始化!所以我们一定要注意:JVM规范规定只有当程序访问的静态变量或静态方法确实在当前类或当前接口中定义时,才可以认为是对类或接口的主动使用,所以我们直接调用Child中没有定义的a不属于对Child的主动使用,因此没有初始化Child!

三、ClassLoader

调用ClassLoader类的loadClass方法加载一个类,并不是对类的主动使用,不会导致类的初始化。

我们来看一下loadClass的API文档:

根据二进制名称来加载类,返回该类的Class对象的实例!那什么是二进制名称?

二进制名称我们可以理解为类的全称,如上图中的类、内部类、匿名内部类等!以前我们写的类都是通过启动类或者Web容器帮我们加载的,这里我们可以看到ClassLoader可以直接加载载这个类,但是我们看到这个类ClassLoader是抽象类。

也就是说我们无法通过new的方法创建实例,那我们该怎么做呢?
我们知道如果这个类不能实例化,我们可以通过他的静态方法访问这个类,我们来看一下ClassLoader的一个静态方法getSysytemClassLoader()这个方法

?

看一段程序:

class Test{

    static{

       System.out.println("Test"s static block");

    }

}



public class ClassLoaderTestCase {

 

    public static void main(String[] args) throws ClassNotFoundException {

       ClassLoader classLoader=ClassLoader.getSystemClassLoader();

       System.out.println("ClassLoader:"+classLoader);

       Class testClass=classLoader.loadClass("com.yhj.jvm.classloader.finalTest.Test");

       System.out.println("using class.forName("com.yhj.jvm.classloader.finalTest.Test")");

       testClass=Class.forName("com.yhj.jvm.classloader.finalTest.Test");

    }

}

这段程序执行下来,我们来看下运行结果

?

很显然,这段程序当调用loadClass的时候没有对类Test进行初始化,而当调用Class.forName的时候才被初始化,因此调用ClassLoader类的loadClass方法加载一个类,并不是对类的主动使用,不会导致类的初始化!只有主动使用的6种情况才会初始化该类!

从加载到连接到初始化,我们看到loadClass只做加载和连接的操作,只有主动使用的6种才会对类进行初始化!

?

参考资料:

圣思园张龙老师深入Java虚拟机系列

当前文章:http://www.okgramy.com/xoudl6i/40133-746862-91036.html

发布时间:15:26:24

香港最快开奖现场直播??766766香港挂牌??今期跑狗玄机图??1861户民图库??香港曾道人??六合宝典210678??六合开奖结果现场直播??2017香港开奖现场直播??天天好彩免费资料大全??香港六开奖结果直播??

相关阅读 四川省委常委、统战部部长田向利当选省总工会主席为了疏远美国,杜特尔特做了一件菲元首60年来没做过的事情冯小刚退色了!陈道明、黄波经全体投票当选为电影协会主席、副主席。美国夏威夷群岛发生6.9级地震 震源深度10千米加密厂商Denuvo进军电竞,将推终端型反作弊技术王思聪首战LPL引争议,电竞来到上位的十字路重庆和广东两个部门发布的地质灾害和气象警报处于高风险状态。反复的微信订阅号

文章评论
发表评论

热门文章 阿里巴巴打假联盟成员破百:微软苹果LV等加入外媒:华为计划下半年推出游戏手机柏溪乡大力发展乡村旅游 带动村民增收致富中国大陆GRE考生愈加青睐商科和物理科学相关专业

最新文章 如何用英语表达激动之情中储粮:守住管好“大国粮仓” 苏宁8.8拼购日剧透:iPhone X仅6888元,红米S2卖799元KEY社打造美少女主题痛车:3万元起拍四川女子公招复试被拒录:自考法律专业不属法学类嘉利药业与苏州康宁杰瑞签署战略合作协议嘉利药业-新浪财经

人气排行 椰树集团危机刚刚开始:虚假宣传业绩下滑品牌老化新浪财经金牌亚洲谭天谦:由下而上的研、产、销发展战略史上最畅销女歌手MARIAH CAREY世界巡回演唱会[每日必领]最高1111元,天猫、京东11.11无门槛抵现金红包开抢传亚马逊将开售网络交换设备,思科股价应声下跌媒体展示英特尔Core i9-9900K处理器包装图天了噜!加拿大医生竟抗议涨工资?!苹果$1000000000000市值,换算成人民币能绕地球265圈