一个例子搞清楚Java程序执行顺序
当我们new一个GirlFriend时,我们都做了什么?
一个例子搞懂Java程序运行顺序
public class Girl {
Person person = new Person("Girl");
/*构造代码块忘记加了,不过构造代码块和成员变量一样,每次构造方法时都会执行一次
{
System.out.println("构造代码块");
}
*/
static{
System.out.println("Girl static");
}
static Person staticPerson = new Person("GirlStaticPerson");
public Girl() {
System.out.println("Girl constructor");
}
public static void main(String[] args) {
new MyGirlFriend();
}
}
class Person{
static{
System.out.println("person static");
}
static Person staticPerson = new Person("PersonStaticPerson");
public Person(String str) {
System.out.println("person "+str);
}
}
class MyGirlFriend extends Girl {
Person person = new Person("MyGirlFriend");
static Person myStaticPerson = new Person("MyStaticPerson");
static{
System.out.println("MyGirlFriend static");
}
public MyGirlFriend() {
System.out.println("MyGirlFriend constructor");
}
}
这段代码同时包含了继承和静态,下面我来仔细分析这段代码的运行结果会是什么。
首先,在执行java Girl之后,jvm会寻找Girl.class,找到之后加载,加载的时候会首先给成员变量开辟空间并隐式赋值(默认值),然后执行静态代码块和静态成员变量显式赋值(按顺序执行),因此首先执行static{ System.out.println("Girl static"); }
然后静态成员变量,执行这一句:static Person staticPerson = new Person("GirlStaticPerson");,接着会寻找Person类并加载(因为赋值的时候使用到了Person类),因此开始执行Person的静态代码块和静态成员变量,即执行这一句:static{ System.out.println("person static"); },接着是静态成员变量:static Person staticPerson = new Person("PersonStaticPerson");此时已经加载了Person类,即开始执行public Person(String str) { System.out.println("person "+str); }方法
接着回到Girl类,Person类已经加载成功,执行person的构造方法并打印person GirlStaticPerson
此时Girl类的静态区域已经执行完,开始执行main方法,即程序主体:new MyGirlFriend();然后寻找MyGirlFriend类并加载,加载后执行静态成员变量:static Person myStaticPerson = new Person("MyStaticPerson");执行Person构造方法打印person MyStaticPerson,然后是静态代码块:static{ System.out.println("MyGirlFriend static"); }
此时MyGirlFriend已经加载完毕,准备执行构造方法,然后发现MyGirlFriend 继承 Girl,需要先执行父类构造方法,执行父类构造方法前又需要初始化父类成员变量和执行父类构造代码块,即执行Girl类的Person person = new Person("Girl");该行调用Person的构造方法,打印person Girl,接着才会执行父类构造方法:public Girl() { System.out.println("Girl constructor"); }
父类构造方法执行完之后回到MyGirlFriend类,准备执行子类构造方法,在执行构造方法前需要对其成员变量初始化,也就是执行:Person person = new Person("MyGirlFriend");,最后才是子类构造方法:public MyGirlFriend() { System.out.println("MyGirlFriend constructor"); }
至此,回到main方法,该行代码执行完毕。运行结果是:
Girl static//Girl类静态代码块
person static//Person类静态代码块
person PersonStaticPerson//Person类中的静态Person成员变量
person GirlStaticPerson//Girl类中的静态Person成员变量
person MyStaticPerson//MyGirlFriend类中的静态成员变量
MyGirlFriend static//MyGirlFriend类中的静态代码块
person Girl//Girl类的成员变量
Girl constructor//Girl类的构造方法
person MyGirlFriend//MyGirlFriend类的成员变量
MyGirlFriend constructor//MyGirlFriend构造方法
但是,在当在IDEA里把断点打到这一行时:

偶尔会出现下面的奇怪运行结果:

个人猜测是idea的debug模式会提前加载类,从而使静态成员变量和静态代码块都提前运行了
总结: 没有女朋友就new一个呀o((>ω< ))o
先静后非,先父后子,先块后器
更新:例子中缺少了构造代码块(也就是不带static的代码块),构造代码块和成员变量是同等位置,每次执行构造器前都会执行构造代码块,初始化成员变量。
再次更新:再来分析一个例子看看有没有掌握:
public class Demo2 {
public static int k = 0;
public static Demo2 t1 = new Demo2("t1");
public static Demo2 t2 = new Demo2("t2");
public static int i = print("i");
public static int j = print("j");
public static int n = 99;
//int a = print("a");
{
print("constructor code");
}
static {
print("static code");
}
public static int print(String s) {
System.out.println("i=" + i + " " + s + " k=" + k + " n=" + n + " j=" + j);
++i;
++k;
++n;
return i;
}
public Demo2(String string) {
print(string);
}
public static void main(String[] args) {
Demo2 d = new Demo2("T");
}
}
问打印了多少次,每次打印的值分别是多少?如果例子中注释的int a = print("a");取消注释呢?
答案
当前执行次数为9次,去掉注释会加3次,也就是12次。需要注意的是每次调用new Demo2("")会执行两次print,去掉注释执行三次。
i=0 constructor code k=0 n=0 j=0
i=1 t1 k=1 n=1 j=0
i=2 constructor code k=2 n=2 j=0
i=3 t2 k=3 n=3 j=0
i=4 i k=4 n=4 j=0
i=5 j k=5 n=5 j=0
i=6 static code k=6 n=99 j=6
i=7 constructor code k=7 n=100 j=6
i=8 T k=8 n=101 j=6
i=0 a k=0 n=0 j=0
i=1 constructor code k=1 n=1 j=0
i=2 t1 k=2 n=2 j=0
i=3 a k=3 n=3 j=0
i=4 constructor code k=4 n=4 j=0
i=5 t2 k=5 n=5 j=0
i=6 i k=6 n=6 j=0
i=7 j k=7 n=7 j=0
i=8 static code k=8 n=99 j=8
i=9 a k=9 n=100 j=8
i=10 constructor code k=10 n=101 j=8
i=11 T k=11 n=102 j=8
答出来了吗( •̀ ω •́ )✧
再来一道:
public class Father {
private String s = "father";
public Father(){
m();
}
public void m(){
System.out.println(s);
}
}
public class Son extends Father {
String s2 = "son";
@Override
public void m() {
System.out.println(s2);
}
public static void main(String[] args) {
Father f1 = new Son();//第一句
f1.m();//第二句
Father f2 = new Father();//第三句
}
}
答案:
null
son
father
第一句,我本来以为执行父类构造方法的时候,m方法是在父类调用的,因此会执行父类的m方法,此时s已经赋值,便会打印“Father”,但实际上却并非如此。需要注意的是,此时是多态形式实例化Son,并且m()已经被重写,因此即使在父类中调用,调用的仍然是子类的m()方法!,此时子类中s2并未赋值,因此打印null。
假如不使用多态,直接构造父类对象,此时则调用父类的m方法。
直接实例化子类或者将f1向下转型,调用m()时均为子类。
这里解释下为什么“多态形式实例化Son,并且m()已经被重写,因此即使在父类中调用,调用的仍然是子类的m()方法”:需要知道的是我们在方法中调用其他成员方法,比如上例中父类的构造方法调用m()方法,其实都是this.m(),只不过this是可以省略的,并且在上例中,this只有一个,那就是子类对象,因此父类构造方法public Father(){ m(); }其实就是public Father(){ this.m(); },this是子类对象,调用的当然是子类重写的方法,只有子类没有找到该方法的情况下才会执行父类的方法。
参考此博客
一个例子搞清楚Java程序执行顺序的更多相关文章
- [转]JAVA程序执行顺序,你了解了吗:JAVA中执行顺序,JAVA中赋值顺序
本文主要介绍以下两块内容的执行顺序,熟悉的大虾可以直接飘过. 一.JAVA中执行顺序 静态块 块 构造器 父类构造器 二.JAVA中赋值顺序 静态块直接赋值 块直接赋值 父类继承的属性已赋值 静态变量 ...
- 深入了解Java程序执行顺序
Java中main方法,静态,非静态的执行顺序详解 Java程序运行时,第一件事情就是试图访问main方法,因为main相等于程序的入口,如果没有main方法,程序将无法启动,main方法更是占一个独 ...
- java程序执行顺序
原来自己一直都没弄明白Java程序的执行顺序问题,今天,自己写了个测试,果然与自己考虑的有差距 测试代码: 一个父类Animal 一个子类Dog 测试类Test 运行结果: 所以执行顺序是: 父类An ...
- 深入了解类加载过程及Java程序执行顺序
前言 在Java中,静态 Static关键字使用十分常见 本文全面 & 详细解析静态 Static关键字,希望你们会喜欢 目录 1. 定义 一种 表示静态属性的 关键字 / 修饰符 2. 作用 ...
- JAVA程序执行顺序(静态代码块》非静态代码块》静态方法》构造函数)
总结:静态代码块总是最先执行. 非静态代码块跟非静态方法一样,跟对象有关.只不过非静态代码块在构造函数之前执行. 父类非静态代码块.构造函数执行完毕后(相当于父类对象初始化完成), 才开始执行子类的非 ...
- java 程序执行顺序之继承
1.首先会初始化父类,因为没有父类子类也无从谈起.第一步初始化static 变量 或者 静态初始化话块 2.初始化子类的static 变量 或者 静态初始化块 3.顺序初始化父类普通变量 或者 父类普 ...
- Java程序执行过程及内存机制
本讲将介绍Java代码是如何一步步运行起来的,其中涉及的编译器,类加载器,字节码校验器,解释器和JIT编译器在整个过程中是发挥着怎样的作用.此外还会介绍Java程序所占用的内存是被如何管理的:堆.栈和 ...
- java中子类继承父类程序执行顺序
java中子类继承父类程序执行顺序 FatherTest.java public class FatherTest { private String name; public FatherTest() ...
- PHPWind 8.7中代码结构与程序执行顺序
pw9在此不谈,他是完全重构的作品,是完全MVC下的体系.当然,其中很多东西在PW8.7下已经可见端倪. 主要代码结构 1. 以现代的观点,PW是多入口应用模式,程序根目录下的文件几乎都是入口: 2. ...
随机推荐
- java 垒骰子
垒骰子 赌圣atm晚年迷恋上了垒骰子,就是把骰子一个垒在另一个上边,不能歪歪扭扭,要垒成方柱体. 经过长期观察,atm 发现了稳定骰子的奥秘:有些数字的面贴着会互相排斥! 我们先来规范一下骰子:1 的 ...
- Kubernetes 集群日志管理【转】
Kubernetes 开发了一个 Elasticsearch 附加组件来实现集群的日志管理.这是一个 Elasticsearch.Fluentd 和 Kibana 的组合.Elasticsearch ...
- SQL statement ignored
存储过程语句错误,字段或变量名可能拼错,导致存储过程无法执行. 解决办法:仔细检查存储过程里的变量,字段,语句等是否正确.
- java核心-多线程(9)- ThreadLocal类
1.背景 ThreadLocal类我想一般的码农或初级程序员在平时开发中基本上接触不到,但是面试老师会问.往高级点走会遇到这个类.这个类不是为了解决资源的竞争问题,而是为每个线程提供同一个容器 ...
- 你从未见过的Case Study写作指南
Case Study,意为案例分析,Case Study与其它的留学论文作业最大的的差别就在于Case Study在论文开始就需要明确给出论,然后再阐述这个结论的论证依据和理由.留学生们需要知道的是C ...
- 编译安装常用包+阿里镜像源-常用资源-系统-下载-科莱软件下载-docker仓库包-安全圈-杏雨梨云-图形界面安装-docker私有双仓库-阿里源报错处理-centos7目录大小
yum install apr-util apr-util-devel apr apr-devel pcre pcre-devel zlib zlib-devel openssl openssl-de ...
- Python中eval与exec用法的区别
Python中eval,exec这两个函数有着相似的输入参数类型和执行功能,因此在用法上经常出现混淆,以至经常用错,程序易抛出错误.下面主要通过这两个函数的语法来阐述区别,并用例子来进一步说明. 首先 ...
- 吴裕雄 Bootstrap 前端框架开发——Bootstrap 字体图标(Glyphicons):glyphicon glyphicon-minus
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name ...
- NO8 find结合sed查找替换企业案例多方法精讲&命令总结!
·sed #替换 eg: sed 'sed 's#已有的内容#更改的内容#g' oldboy.txt s 代表替换,g代表全局,sg就是全局替换 ...
- POJ 1565:Skew Binary
Skew Binary Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 10676 Accepted: 6807 Desc ...