当前位置: 首页 > news >正文

深入了解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.

相关文章:

  • 4.ASCII码排序
  • 如何对system.img和userdata.img解包,再重新打包
  • UML在软件开发中各个阶段的作用和意义
  • elasticsearch的索引自动清理及自定义清理
  • ACdream 1069 无耻的出题人
  • puppert部署一
  • 永久修改主机名-Linux
  • 微信分享屏蔽跳转appstore解决方法
  • CISCO 交换设备IOS 备份/恢复操作
  • BZOJ1701 : [Usaco2007 Jan]Cow School牛学校
  • 【git】Intellij IDEA中Git插件提交内容到远程仓库
  • 本地项目上传到Github的示例方法
  • cenos下ActiveMQ关闭时出现异常
  • NOIP 2002过河卒 Label:dp
  • MongoDB概述
  • 自己简单写的 事件订阅机制
  • (十五)java多线程之并发集合ArrayBlockingQueue
  • 【mysql】环境安装、服务启动、密码设置
  • Brief introduction of how to 'Call, Apply and Bind'
  • Docker 笔记(2):Dockerfile
  • JavaScript/HTML5图表开发工具JavaScript Charts v3.19.6发布【附下载】
  • Odoo domain写法及运用
  • vue的全局变量和全局拦截请求器
  • 电商搜索引擎的架构设计和性能优化
  • 翻译 | 老司机带你秒懂内存管理 - 第一部(共三部)
  • 服务器从安装到部署全过程(二)
  • 基于webpack 的 vue 多页架构
  • 记一次和乔布斯合作最难忘的经历
  • 猫头鹰的深夜翻译:Java 2D Graphics, 简单的仿射变换
  • 山寨一个 Promise
  • 微信公众号开发小记——5.python微信红包
  • 微信开放平台全网发布【失败】的几点排查方法
  • 小程序button引导用户授权
  • 一文看透浏览器架构
  • 原生Ajax
  • - 转 Ext2.0 form使用实例
  • 7行Python代码的人脸识别
  • 如何在 Intellij IDEA 更高效地将应用部署到容器服务 Kubernetes ...
  • 昨天1024程序员节,我故意写了个死循环~
  • #在线报价接单​再坚持一下 明天是真的周六.出现货 实单来谈
  • (C语言)深入理解指针2之野指针与传值与传址与assert断言
  • (二)斐波那契Fabonacci函数
  • (附源码)spring boot公选课在线选课系统 毕业设计 142011
  • (附源码)ssm基于微信小程序的疫苗管理系统 毕业设计 092354
  • (附源码)ssm考生评分系统 毕业设计 071114
  • (个人笔记质量不佳)SQL 左连接、右连接、内连接的区别
  • (算法)前K大的和
  • (淘宝无限适配)手机端rem布局详解(转载非原创)
  • (五)Python 垃圾回收机制
  • (一)搭建springboot+vue前后端分离项目--前端vue搭建
  • (转)Linux NTP配置详解 (Network Time Protocol)
  • (转)拼包函数及网络封包的异常处理(含代码)
  • .NET 8 编写 LiteDB vs SQLite 数据库 CRUD 接口性能测试(准备篇)
  • .NET/C# 使窗口永不激活(No Activate 永不获得焦点)
  • .Net接口调试与案例