zoukankan      html  css  js  c++  java
  • 深入了解Java程序执行顺序

    Java中main方法,静态,非静态的执行顺序详解

      Java程序运行时,第一件事情就是试图访问main方法,因为main相等于程序的入口,如果没有main方法,程序将无法启动,main方法更是占一个独立的线程,找到main方法后,是不是就会执行mian方法块里的第一句话呢?答案是不一定

     看看下面两种最常见的情况:

      第一种情况:main方法在一个具有其他方法或属性的类中;

    package com.dfs.util;
    
    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方法在一个没有其他方法或属性的类中;

    package com.dfs.util;
    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初始化可能会依赖于父类成员能否被正确初始化),如果父类还有父类,依次类推,不管你是否打算产生一个该父类的对象,这都是自然发生的。

    下面是一道小程序:

      代码部分:

    package com.dfs.util.base;
    
    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         ---public static int j = print();   静态部分
    Test print      ---public static int k = print();   静态部分
    main start      ---System.out.println("main start");
    A的构造方法       ---A的构造方法
    Test1的构造方法   ---Test1的构造方法

    下面是一道阿里巴巴的面试题:非常经典,可以不断的改变程序的顺序,来找到执行顺序机制。

      代码部分:

    package com.dfs.util.base;
    
    public class Text {
        public static int k = 0;
        public static Text s1 = new Text("s1");
        public static Text s2 = new Text("s2");
        public static int i = print("静态一");
        public static int n = 99;
        public int j = print("成员一");
    
        {
            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:成员一   i=0    n=0    
    2:构造块   i=1    n=1
    3:构造s1   i=2    n=2
    4:成员一   i=3    n=3
    5:构造块   i=4    n=4
    6:构造s2   i=5    n=5
    7:静态一   i=6    n=6       //public static int i = print("静态一");
    8:静态块   i=7    n=99      //print("静态块");
    9:成员一   i=8    n=100
    10:构造块   i=9    n=101
    11:构造init i=10    n=102

    结果说明(可以通过注解某一行代码,对比结果):

    1、代码行:public static Text s1 = new Text("s1");

    1:成员一 i=0 n=0 
    2:构造块 i=1 n=1
    3:构造s1 i=2 n=2

    2、代码行:public static Text s2 = new Text("s2");

    4:成员一   i=3    n=3
    5:构造块   i=4    n=4
    6:构造s2   i=5    n=5

    3、代码行:Text t = new Text("init");  //创建一个实例

    9:成员一   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.

    转:https://www.cnblogs.com/greatfish/p/5771548.html

  • 相关阅读:
    匿名字段和内嵌结构体
    Go函数式编程的闭包和装饰器设计模式
    理解Golang中defer的使用
    Go匿名函数及闭包
    GO语言学习笔记-缓冲区Channels和线程池
    你需要知道的关于Neutron的一切事情
    []T 还是 []*T, 这是一个问题
    Golang 方法接收者为值与指针的区别
    第九章 python语法入门之与用户交互、运算符
    第八章 python语法入门之垃圾回收机制
  • 原文地址:https://www.cnblogs.com/duanxz/p/3012535.html
Copyright © 2011-2022 走看看