深入了解Java程序执行顺序
Java中main方法,静态,非静态的执行顺序详解
Java程序运行时,第一件事情就是试图访问main方法,因为main相等于程序的入口,如果没有main方法,程序将无法启动,main方法更是占一个独立的线程,找到main方法后,是不是就会执行mian方法块里的第一句话呢?答案是不一定
看看下面两种最常见的情况:
第一种情况:
main方法在一个具有其他方法或属性的类中;
public class Test1 {
public static String name;
public Test1() {
}
public static void get() {
System.out.println("Test start");
}
public static void main(String[] args) {
System.out.println("main start");
Test1 bb = new Test1();
}
}
第二种情况:
main方法在一个没有其他方法或属性的类中;
public class Test {
public static void main(String[] args) {
Student stu =new Student();
}
}
分析:
因为静态部分是依赖于类,而不是依赖于对象存在的,所以静态部分的加载优先于对象存在。
当找到main方法后,因为main方法虽然是一个特殊的静态方法,但是还是静态方法,此时JVM会加载main方法所在的类,试图找到类中其他静态部分,即首先会找main方法所在的类。
执行顺序大致分类:
1.静态属性,静态方法声明,静态块。
2.动态属性,普通方法声明,构造块。
3.构造方法。
1.1 静态:
当加载一个类时,JVM会根据属性的数据类型第一时间赋默认值(一举生成的)。然后再进行静态属性初始化,并为静态属性分配内存空间,静态方法的声明,静态块的加载,没有优先级之分,按出现顺序执行,静态部分仅仅加载一次。至此为止,必要的类都已经加载完毕,对象就可以被创建了。
1.2 普通:
当new一个对象时,此时会调用构造方法,但是在调用构造方法之前,(此刻1.1已经完成,除非被打断而暂停)执行动态属性定义并设置默认值(一举生成的)。然后动态属性初始化,分配内存,构造块,普通方法声明(只是加载,它不需要初始化,只有调用它时才分配内存,当方法执行完毕后内存立即释放),没有优先级之分,按出现顺序执行。最后进行构造方法中赋值。当再次创建一个对象,不再执行静态部分,仅仅重复执行普通部分。
注意:如果存在继承关系,创建对象时,依然会首先进行动态属性进行定义并设默认值,然后父类的构造器才会被调用,其他一切都是先父类再子类(因为子类的static初始化可能会依赖于父类成员能否被正确初始化),如果父类还有父类,依次类推,不管你是否打算产生一个该父类的对象,这都是自然发生的。
下面是一道小程序:
代码部分:
class A {
public A() {
System.out.println("A的构造方法");
}
public static int j = print();
public static int print() {
System.out.println("A print");
return 521;
}
}
public class Test1 extends A {
public Test1() {
System.out.println("Test1的构造方法");
}
public static int k = print();
public static int print() {
System.out.println("Test print");
return 522;
}
public static void main(String[] args) {
System.out.println("main start");
Test1 t1 = new Test1();
}
}
运行结果:
A print Test print main start A的构造方法 Test1的构造方法
下面是一道阿里巴巴的面试题:非常经典,可以不断的改变程序的顺序,来找到执行顺序机制。
代码部分:
public class Text {
public static int k = 0;
public static Text t1 = new Text("t1");
public static Text t2 = new Text("t2");
public static int i = print("i");
public static int n = 99;
public int j = print("j");
{
print("构造块");
}
static {
print("静态块");
}
public Text(String str) {
System.out.println((++k) + ":" + str + " i=" + i + " n=" + n);
++i;
++n;
}
public static int print(String str) {
System.out.println((++k) + ":" + str + " i=" + i + " n=" + n);
++n;
return ++i;
}
public static void main(String args[]) {
Text t = new Text("init");
}
}
运行结果:
1:j i=0 n=0 2:构造块 i=1 n=1 3:t1 i=2 n=2 4:j i=3 n=3 5:构造块 i=4 n=4 6:t2 i=5 n=5 7:i i=6 n=6 8:静态块 i=7 n=99 9:j i=8 n=100 10:构造块 i=9 n=101 11:init i=10 n=102
总结:只要按照这个步骤,遇到这一类问题就可以解决了。
1-3:类加载过程,不涉及构造方法
1-5: 实例化过程,涉及构造方法
1.类中所有属性的默认值(一举而成)
2. 父类静态属性初始化,静态块,静态方法的声明(按出现顺序执行)
3. 子类静态属性初始化,静态块,静态方法的声明 (按出现顺序执行)
4. 调用父类的构造方法,
首先父类的非静态成员初始化,构造块,普通方法的声明(按出现顺序执行)
然后父类构造方法
5. 调用子类的构造方法,
首先子类的非静态成员初始化,构造块,普通方法的声明(按出现顺序执行)
然后子类构造方法
(注意:类加载过程中,可能调用了实例化过程(因为static可以修饰方法,属性,代码块,内部类),此时则会暂停类加载过程而先执行实例化过程(被打断),执行结束再进行类加载过程,上面阿里那道面试题就是典型的暂停类加载。
下面附加一个小程序来显示新建对象时属性的四个过程:
public class Test {
public static void main(String[] args) {
Student stu = new Student("zhangan", 21);
}
}
class Student {
public String name = "李寻欢";
public int age = 20;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
}
1.

2.

3.

4.

深入了解Java程序执行顺序的更多相关文章
- [转]JAVA程序执行顺序,你了解了吗:JAVA中执行顺序,JAVA中赋值顺序
本文主要介绍以下两块内容的执行顺序,熟悉的大虾可以直接飘过. 一.JAVA中执行顺序 静态块 块 构造器 父类构造器 二.JAVA中赋值顺序 静态块直接赋值 块直接赋值 父类继承的属性已赋值 静态变量 ...
- 一个例子搞清楚Java程序执行顺序
当我们new一个GirlFriend时,我们都做了什么? 一个例子搞懂Java程序运行顺序 public class Girl { Person person = new Person("G ...
- java程序执行顺序
原来自己一直都没弄明白Java程序的执行顺序问题,今天,自己写了个测试,果然与自己考虑的有差距 测试代码: 一个父类Animal 一个子类Dog 测试类Test 运行结果: 所以执行顺序是: 父类An ...
- 深入了解类加载过程及Java程序执行顺序
前言 在Java中,静态 Static关键字使用十分常见 本文全面 & 详细解析静态 Static关键字,希望你们会喜欢 目录 1. 定义 一种 表示静态属性的 关键字 / 修饰符 2. 作用 ...
- JAVA程序执行顺序(静态代码块》非静态代码块》静态方法》构造函数)
总结:静态代码块总是最先执行. 非静态代码块跟非静态方法一样,跟对象有关.只不过非静态代码块在构造函数之前执行. 父类非静态代码块.构造函数执行完毕后(相当于父类对象初始化完成), 才开始执行子类的非 ...
- java 程序执行顺序之继承
1.首先会初始化父类,因为没有父类子类也无从谈起.第一步初始化static 变量 或者 静态初始化话块 2.初始化子类的static 变量 或者 静态初始化块 3.顺序初始化父类普通变量 或者 父类普 ...
- java中子类继承父类程序执行顺序
java中子类继承父类程序执行顺序 FatherTest.java public class FatherTest { private String name; public FatherTest() ...
- PHPWind 8.7中代码结构与程序执行顺序
pw9在此不谈,他是完全重构的作品,是完全MVC下的体系.当然,其中很多东西在PW8.7下已经可见端倪. 主要代码结构 1. 以现代的观点,PW是多入口应用模式,程序根目录下的文件几乎都是入口: 2. ...
- Java基础知识强化之网络编程笔记25:Android网络通信之 Future接口介绍(Java程序执行超时)
1. Future接口简介 在Java中,如果需要设定代码执行的最长时间,即超时,可以用Java线程池ExecutorService类配合Future接口来实现. Future接口是Java标准API ...
随机推荐
- C#内存释放
看微软件的宣传说NET会自动回收内存.以前一直以为NET会自动回收也没有去细看. 近来发现NET下的winForm程序,默认情况下不会自动释放内存.如果是循执行的程序内存会不断增大.具体会大到多少没有 ...
- 20151012 C# 第一篇 字符与字符串
20151012 字符与字符串: Char.String等类来表示 字符类Char 1. 字符类Char 表示一个 Unicode 字符,(Unicode字符是计算机通用的字符编码,对不同语言中的每个 ...
- Windows server 修改mysql端口
[此方法对mysql免安装版本适用] (最好先停止mysql服务) 1.解压MySQL后,在MySQL根目录下有一个my-default.ini,将该文件复制粘贴一份,重命名为:my.ini,还是放在 ...
- 21)pom 中的缺省值(default properties)
1 引言 项目中build 时用到了maven-jar-plugin ,其中有一个 ${project.build.directory} <plugin> <artifactId&g ...
- catalina.properties
追踪 startup.bat set "EXECUTABLE=%CATALINA_HOME%\bin\catalina.bat" call "%EXECUTABLE%&q ...
- IIS 部署 node.js ---- 基础安装部署
一些可能有用的相关文章: https://blogs.msdn.microsoft.com/scott_hanselman/2011/11/28/window-iisnode-js/ http://b ...
- PERL/LEX/YACC技术实现文本解析--XML解析
继周六的p_enum.pl后,再来一篇说说我用perl做的lex,yacc工具.之前说了,我学习lex和yacc的最初动机是为了做个C语言解释器的SHELL:但后来工作中的实际需要也是制作perl版l ...
- 前端学习之回调函数、call方法、apply方法
今天学习的内容比较少,大部分时间是自己在写qq音乐和京东移动端的页面.现在说说今天学到的内容: 首先,回调函数,就是在函数内部中调用另外一个函数, 将一个函数当作参数传给另一个函数,被传的函数叫做回调 ...
- Android 百度云媒体 等播放器播放4:3等多种比例的视频 大小配置的问题
视频播放宽度大小各不一样,就需要根据视频的尺寸去适应屏幕的宽度和高度进行缩放. 思路是在onPrepared方法中,或者切换屏幕后,获取video的高度和宽度.以及屏幕展示区域的高度的宽度.并对比二者 ...
- 为Apache动态增加模块
Apache已经安装完毕并投入运行,但是后来却发现部分模块没有加载,当然有两个方法: 1. 一是完全重新编译Apache, 再安装 2. 编译模块为SO文件,使用LoadModule指令加载扩展模块. ...