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

java类在jvm中经历的几个阶段以及对象中的属性赋值和方法的执行顺序

2017-07-31 12:08 288 查看
本文基于个人的一些理解做的整理,如果有什么位置有问题,欢迎留言指教。



jvm加载资源的几个阶段

  jvm加载一个类需要经过加载、连接、初始、使用和卸载几个阶段。我们介绍下前3个阶段

加载

加载是jvm加载二进制字节流转换成运行时数据结构的过程

连接

连接又分为三个小阶段,分别是验证、准备和解析

验证 

验证解决要的事就是验证当前执行代码是否可以被当前jvm正常执行。比如jvm版本导致的问题就出现在这个阶段

准备

jvm在准备阶段开始一些内存的分配,并且对一些静态资源进行赋值操作,静态变量在这个阶段会赋给默认值,而静态常量在这个阶段会赋给期望值。

解析

解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程

初始化

在初始化阶段jvm会做以下几件事

a) 加载类的实例

b) 加载类的静态变量

c) 加载类的静态方法

d) 实例化一个对象

e) 初始化时先初始化父类

f) 类初始化是类加载过程的最后一步,前面的类加载过程,除了在加载阶段用户应用程序可以通过自定义类加载器参与之外,其余动作完全由虚拟机主导和控制。到了初始化阶段,才真正开始执行类中定义的Java程序代码。初始化阶段是执行类构造器()方法的过程。()方法是由编译器自动收集类中的所有类变量的赋值动作和静态语句块(static{}块)中的语句合并产生的。

java类中的方法和属性在jvm中的执行顺序

  java属性分为静态属性、和非静态属性。

  静态属性的访问是基于类直接访问的。不会依附于某个实例,这也是静态代码块在实例化对象的时候只会执行一次的原因,而非静态属性是依附于当前的java对象实例的。

  在jvm执行过程中静态资源是在对象初始化之前就完成了内存的分配,但是不同的是静态常量(final)的是在内存分配的同时完成了赋值操作,而静态变量在分配内存的时候并没有赋值。而在是初始化阶段的类初始化时进行赋值操作的。

  

  在java类中属性的代码的执行顺序是从上往下的。并且是静态代码优先于非静态,另外就是子类实例化之前必须要实例化父类。

实例一
package com.example.classes;

public class SingleTon {
private static SingleTon singleTon = new SingleTon();
public static int count1;
public static int count2 = 0;

private SingleTon() {
count1++;
count2++;
}

public static SingleTon getInstance() {
return singleTon;
}

public static void main(String[] args) {
SingleTon singleTon = SingleTon.getInstance();
System.out.println("count1=" + singleTon.count1);
System.out.println("count2=" + singleTon.count2);
}

}

count1=1
count2=0


实例二
package com.example.classes;

public class SingleTon {
public static int count1;
public static int count2 = 0;
private static SingleTon singleTon = new SingleTon();

private SingleTon() {
count1++;
count2++;
}

public static SingleTon getInstance() {
return singleTon;
}

public static void main(String[] args) {
SingleTon singleTon = SingleTon.getInstance();
System.out.println("count1=" + singleTon.count1);
System.out.println("count2=" + singleTon.count2);
}

}

count1=1
count2=1


我们简单的看下上面的两段代码,曾经我一直对这个问题不是很理解,但是现在我感觉自己的思想还是比较清晰了。

介绍下我的理解

出现这种情况的原因是java代码是顺序执行的,在实例一中是先实例化了一个singleTon实例,那这个时候两个值都是默认值也就是0,然后在进行++操作之后都变成了1,但是顺序执行的过程中,count2又被重新赋值了,这个时候就把原有值覆盖掉了。

但是实例二的执行顺序是先进行赋值操作,然后在实例化singleTon对象,那这个时候已经完成了赋值操作了,后续的++是在赋值以后的基础上做的,这个时候也就看到的结果就很容易接受了。

:想想这种问题很有可能在我们不经意间写错了代码的顺序,然后当代码执行的过程中就会出现一些很意外的现象,这时候我们就会很费劲,现在了解了这个执行顺序以后,就感觉思维清晰了很多了



public class SimpleField {

private final BlockingQueue<Runnable> workQueue;

public SimpleField(BlockingQueue<Runnable> workQueue) {
this.workQueue = workQueue;
}

public void set(BlockingQueue<Runnable> workQueue) {
//        this.workQueue = workQueue;
}
}


记录一段代码。上面的代码块中被注释的代码是编译不通过的,再贴一段代码对比。下面代码编译都不通过

import java.util.concurrent.BlockingQueue;

public class SimpleField {

private static final BlockingQueue<Runnable> workQueue;

public SimpleField(BlockingQueue<Runnable> workQueue) {
this.workQueue = workQueue;
}

public void set(BlockingQueue<Runnable> workQueue) {
this.workQueue = workQueue;
}
}


我们会发现final修饰的静态常量在声明的时候就必须给定初始值。但是final修饰的类变量并不用,不过类变量只支持构造赋值。这正好符号了静态属性是直接挂到类上的,而非静态属性是挂在实例对象上的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  jvm
相关文章推荐