【Java基础】Java类的加载和对象创建流程的详细分析
相信我们在面试Java的时候总会有一些公司要做笔试题目的,而Java类的加载和对象创建流程的知识点也是常见的题目之一。接下来通过实例详细的分析一下。
实例问题
实例代码
Parent类
package mytest.javaBase;
public class Parent {
int a = 10;
static int b = 11;
// 静态代码块
static {
System.out.println("Parent静态代码块:b=" + b);
b++;
}
// 代码块
{
System.out.println("Parent代码块: a=" + a);
System.out.println("Parent代码块: b=" + b);
b++;
a++;
}
// 无参构造函数
Parent() {
System.out.println("Parent无参构造函数: a=" + a);
System.out.println("Parent无参构造函数: b=" + b);
}
// 有参构造函数
Parent(int a) {
System.out.println("Parent有参构造函数: a=" + a);
System.out.println("Parent有参构造函数: b=" + b);
}
// 方法
void function() {
System.out.println("Parent function run ……");
}
}
Child类
package mytest.javaBase;
public class Child extends Parent {
int x = 10;
static int y = 11;
// 静态代码块
static {
System.out.println("Child静态代码块:y=" + y);
y++;
}
// 代码块
{
System.out.println("Child代码块: x=" + x);
System.out.println("Child代码块: y=" + y);
y++;
x++;
}
// 构造函数
Child() {
System.out.println("Child构造函数: x=" + x);
System.out.println("Child构造函数: y=" + y);
}
// 方法
void function() {
System.out.println("Child function run ……");
}
}
Test测试类
package mytest.javaBase;
public class Test {
public static void main(String[] args) {
Child demo = new Child();
demo.function();
System.out.println("…………………………………………………………………………………………………………………………");
Child child = new Child();
child.function();
}
}
我们可以先不看运行结果,自己思考下,运行结果会是什么,之后再比较下和自己思考的结果是否一样。
运行结果
Parent静态代码块:b=11
Child静态代码块:y=11
Parent代码块: a=10
Parent代码块: b=12
Parent无参构造函数: a=11
Parent无参构造函数: b=13
Child代码块: x=10
Child代码块: y=12
Child构造函数: x=11
Child构造函数: y=13
Child function run ……
…………………………………………………………………………………………………………………………
Parent代码块: a=10
Parent代码块: b=13
Parent无参构造函数: a=11
Parent无参构造函数: b=14
Child代码块: x=10
Child代码块: y=13
Child构造函数: x=11
Child构造函数: y=14
Child function run ……
结果详细分析
我们运行Test类的main方法
1、 启动JVM,开始分配内存空间;
2、 开始加载Test.class文件,加载到方法区中,在加载的过程中静态的内容要进入静态区中;
3、 在开始运行main方法,这时JVM就会把main调用到栈中运行,开始从方法的第一行往下运行;
4、 在main方法中new Child();这时JVM就会在方法区中查找有没有Child文件,如果没有就加载Child.class文件,并且Child继承Parent类,所以也要查找有没有Parent类,如果没有也要加载Parent.class文件。
5、 Child.class和Parent.class中的所有的非静态内容会加载到非静态的区域中,而静态的内容会加载到静态区中。静态内容(静态变量,静态代码块,静态方法)按照书写顺序加载。
说明:类的加载只会执行一次。下次再创建对象时,可以直接在方法区中获取class信息。
6、 开始给静态区中的所有静态的成员变量开始默认初始化。默认初始化完成之后,开始给所有的静态成员变量显示初始化。
7、 所有静态成员变量显示初始化完成之后,开始执行静态的代码块。先执行父类的静态代码块,再执行子类的静态代码块。
//这时输出
Parent静态代码块:b=11
Child静态代码块:y=11
说明:>>静态代码块是在类加载的时候执行的,类的加载只会执行一次所以静态代码块也只会执行一次;
>>非静态代码块和构造函数中的代码是在对象创建的时候执行的,因此对象创建(new)一次,它们就会执行一次。
8、 这时Parent.class文件 和 Child.class文件加载完成。
9、 开始在堆中创建Child对象。给Child对象分配内存空间,其实就是分配内存地址。
10、开始对类中的的非静态的成员变量开始默认初始化。
11、开始加载对应的构造方法,执行隐式三步
①有个隐式的super(); ②显示初始化(给所有的非静态的成员变量) ③执行构造代码块 之后才开始执行本类的构造方法中的代码
super()是调用父类的构造函数,此处即为Parent的构造函数,在Parent的构造函数中也有个隐式三步:首先super(),再执行Parent的显示初始化,然后执行Parent的非静态构造代码块,最后执行Parent的构造函数中的代码。
//这时输出
Parent代码块: a=10
Parent代码块: b=12
Parent无参构造函数: a=11
Parent无参构造函数: b=13
说明:虽然Parent没有明写extends,但是我们知道在Java中有个超类Object,它是所有类的父类,因此此处Parent类的super()是调用Object的构造函数
Parent的执行完之后,回来继续执行Child自己的隐式三步中的第二步:显示初始化,然后执行Child的非静态代码块的,最后执行Child的构造函数中的代码
//这时输出
Child代码块: x=10
Child代码块: y=12
Child构造函数: x=11
Child构造函数: y=13
12、对象创建完成,把内存的地址赋值给demo使用。
13、执行demo.function()方法。
//这时输出
Child function run ……
14、由于后面又创建(new)了一个新的Child对象,因此重复一下【9】之后的步骤,很容易明白它的输出结果为
Parent代码块: a=10
Parent代码块: b=13
Parent无参构造函数: a=11
Parent无参构造函数: b=14
Child代码块: x=10
Child代码块: y=13
Child构造函数: x=11
Child构造函数: y=14
Child function run ……
简单的画个内存运行示例图

总结
我们知道,我们在创建(new)一个对象的时候,先要去JVM的方法区里获取该对象所对应的类的信息,如果方法区里没有该类的信息,则需要去将它加载进来,加载进来之后,有了该类的信息,我们才能创建一个对象。
一般,Java类被编译后,会生成一个class文件,在运行的时候会将class文件加载到Java虚拟机JVM中,class文件由类装载器装载,在JVM中(准确的来说应该是在JVM的方法区里)将形成一份描述Class结构的元信息对象,通过该元信息对象可以获知Class的结构信息:如构造函数,属性和方法等。
一、类的加载过程
首先,Jvm在执行时,遇到一个新的类时,会到内存中的方法区去找class的信息,如果找到就直接拿来用,如果没有找到,就会去将类文件加载到方法区。在类加载时,静态成员变量加载到方法区的静态区域,非静态成员变量加载到方法区的非静态区域。
静态代码块是在类加载时自动执行的代码,非静态代码块是在创建对象时自动执行的代码,不创建对象不执行该类的非静态代码块。
加载过程:
1、JVM会先去方法区中找有没有相应类的.class存在。如果有,就直接使用;如果没有,则把相关类的.clss加载到方法区。
2、在.class加载到方法区时,先加载父类再加载子类;先加载静态内容,再加载非静态内容
3、加载静态内容:
- 把.class中的所有静态内容加载到方法区下的静态区域内
- 静态内容加载完成之后,对所有的静态变量进行默认初始化
- 所有的静态变量默认初始化完成之后,再进行显式初始化
- 当静态区域下的所有静态变量显式初始化完后,执行静态代码块
4、加载非静态内容:把.class中的所有非静态变量及非静态代码块加载到方法区下的非静态区域内。
5、执行完之后,整个类的加载就完成了。
对于静态方法和非静态方法都是被动调用,即系统不会自动调用执行,所以用户没有调用时都不执行,主要区别在于静态方法可以直接用类名直接调用(实例化对象也可以),而非静态方法只能先实例化对象后才能调用。
二、对象的创建过程
1、new一个对象时,在堆内存中开辟一块空间。
2、给开辟的空间分配一个地址。
3、把对象的所有非静态成员加载到所开辟的空间下。
4、所有的非静态成员加载完成之后,对所有非静态成员变量进行默认初始化。
5、所有非静态成员变量默认初始化完成之后,调用构造函数。
6、在构造函数入栈执行时,分为两部分:先执行构造函数中的隐式三步,再执行构造函数中书写的代码。
隐式三步:
①执行super()语句 ②显示初始化(对开辟空间下的所有非静态成员变量进行) ③执行构造代码块
7、在整个构造函数执行完并弹栈后,把空间分配的地址赋给引用对象。
三、其他
super语句,可能出现以下三种情况:
1)构造方法体的第一行是this()语句,则不会执行隐式三步,而是调用this()语句所对应的的构造方法,最终肯定会有第一行不是this语句的构造方法。
package mytest.javaBase;
public class Student {
private String name;
private String age;
Student() {
};
Student(String name) {
this.name = name;
};
Student(String name, String age) {
// 不会执行隐式三步
this(name);
this.age = age;
};
}
2)构造方法体的第一行是super()语句,则调用相应的父类的构造方法,
3)构造方法体的第一行既不是this()语句也不是super()语句,则隐式调用super(),即其父类的默认构造方法,这也是为什么一个父类通常要提供默认构造方法的原因;
更多内容:http://www.cnblogs.com/study-everyday/
【Java基础】Java类的加载和对象创建流程的详细分析的更多相关文章
- Java类的加载和对象创建流程的详细分析
相信我们在面试Java的时候总会有一些公司要做笔试题目的,而Java类的加载和对象创建流程的知识点也是常见的题目之一.接下来通过实例详细的分析一下: package com.test; public ...
- Java基础_类的加载机制和反射
类的使用分为三个步骤: 类的加载->类的连接->类的初始化 一.类的加载 当程序运行的时候,系统会首先把我们要使用的Java类加载到内存中.这里加载的是编译后的.class文件 每个类加载 ...
- 深入java虚拟机学习 -- 类的加载机制(续)
昨晚写 深入java虚拟机学习 -- 类的加载机制 都到1点半了,由于第二天还要工作,没有将上篇文章中的demo讲解写出来,今天抽时间补上昨晚的例子讲解. 这里我先把昨天的两份代码贴过来,重新看下: ...
- Java程序设计19——类的加载和反射-Part-B
接下来可以随意提供一个简单的主类,该主类无须编译就可使用上面的CompileClassLoader来运行它. package chapter18; public class Hello { publi ...
- Java程序设计19——类的加载和反射-Part-A
1 本文概要 本章介绍Java类的加载.连接和初始化的深入知识,并重点介绍Java反射相关的内容.本章知识偏底层点,这些运行原理有助于我们更好的把我java程序的运行.而且Java类加载器除了根加载器 ...
- 深入java虚拟机学习 -- 类的加载机制
当看到"类的加载机制",肯定很多人都在想我平时也不接触啊,工作中无非就是写代码,不会了可以百度,至于类,jvm是怎么加载的我一点也不需要关心.在我刚开始工作的时候也觉得这些底层的内 ...
- 深入java虚拟机学习 -- 类的加载机制(三)
类的初始化时机 在上篇文章中讲到了类的六种主动使用方式,反射是其中的一种(Class.forName("com.jack.test")),这里需要注意一点:当调用ClasLoade ...
- java 反射,类的加载过程以及Classloader类加载器
首先自定义一个类Person package reflection; public class Person { private String name; public int age; public ...
- JAVA初始化及类的加载
在许多传统语言中,程序是作为启动过程的一部分被加载的.然后是初始化,紧接着程序开始运行.这些语言的初始化过程必须小心控制,以确保定义为static的东西,其初始化顺序不会造成麻烦.例如C++期望一个s ...
随机推荐
- ajax删除数据(不跳转页面)
以前我们讲的删除是利用嵌入php代码,跳转到另一个页面,从而降低了删除速度,但我们今天讲的利用ajax不仅可以达到不跳页面快速删除,并且能添加特效来美化页面. AJAX = 异步 JavaScript ...
- 出现java.lang.reflect.UndeclaredThrowableException异常
解决方案:1.看导进来的项目是否有中文路径.2.看是否有get.set方法没写.3.和部署的环境有关.比如,是否写了构造函数.EJB需要.
- Codevs3278[NOIP2013]货车运输
3287 货车运输 2013年NOIP全国联赛提高组 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 钻石 Diamond 题目描述 Description A 国有 ...
- conky 1.10以后的新配置格式
包装 config conky.config = { ... } 包装 TEXT conky.text = [[ ... ]] 每个 config 选项的变量和取值之间插入 "=" ...
- AVFoundation之如何从摄像头获取图像
前言: 最近项目有个需求是对试图对手机密码进行强破解的人进行拍照(通过摄像头截图),因为之前没做过,所以一堆坑.现在就把我的经验都分享出来,希望后来人不用再踏上坑途中. 直接上代码: // 创建会话 ...
- PHP无锁内存nosql---Yac的实战
无锁内存nosql---Yac的实战 最近在工作使用了yac,所以比较了下Memcache和Yac的高并发读写性能测试,发现Yac要比Memcache快很多(这里没有比较Yac和Apc的性能情况, 不 ...
- 开源中文分词工具探析(五):FNLP
FNLP是由Fudan NLP实验室的邱锡鹏老师开源的一套Java写就的中文NLP工具包,提供诸如分词.词性标注.文本分类.依存句法分析等功能. [开源中文分词工具探析]系列: 中文分词工具探析(一) ...
- Java中的枚举的治理
版权声明:本文为博主原创文章,转载请注明出处,欢迎使劲喷 一.为啥用枚举&为啥要对枚举进行治理 1.先来说说为啥用枚举 表中某个字段标识了这条记录的状态,我们往往使用一些code值来标识,例如 ...
- 移动端高清、多屏适配方案——rem
背景: 开发移动端H5页面 一套设计图 不同尺寸的手机 不同分辨率的手机 方案:使用rem作为单位解决一套设计图适应不同分辨率,不同尺寸的手机. 概念: REM(font size of the ro ...
- Ubuntu 16.04系统下安装RapidSVN版本控制器及配置diff,editor,merge和exploer工具
在Window下我们使用TortoiseSVN(小乌龟),可以很方便地进行查看.比较.更新.提交.回滚等SVN版本控制操作. 在Linux下我们可以使用RapidSVN.RapidSVN是一款轻量级的 ...