美文网首页
java初始化过程

java初始化过程

作者: jiahzhon | 来源:发表于2020-04-20 11:14 被阅读0次
20180705100250350.png 20180705100306225.png

初始化顺序:父类的静态变量-->父类的静态代码块-->子类的静态变量-->子类的静态代码快-->父类的非静态变量(父类的非静态代码块)-->父类的构造函数-->子类的非静态变量(子类的非静态代码块)-->子类的构造函数

public class Test {
    public static void main(String[] args) {
        Child c=new Child();
    }
}
class Parent {
    public static PrintMessage a=new PrintMessage("父类静态成员被初始化");
    private PrintMessage b=new PrintMessage("父类非静态成员被初始化"); 
    static{
        System.out.println("父类的静态代码块被执行");
    }
    {
        System.out.println("父类的非静态代码块被执行");
    }
    public Parent(){
        System.out.println("父类的构造方法被执行");
    }   
}

class Child extends Parent{
    public static PrintMessage a1=new PrintMessage("子类静态成员被初始化");
    private PrintMessage b1=new PrintMessage("子类非静态成员被初始化");    
    
    static {
        System.out.println("子类的静态代码块被执行");
    }
    {
        System.out.println("子类的非静态代码块被执行");
    }
    public Child(){
        System.out.println("子类的构造函数被执行");
    }
}

class PrintMessage{
    public PrintMessage(String mes){
        System.out.println(mes);
    }
}

986140-20170728164542149-1587220315.png
  • 如果父类构造器调用了被子类重写的方法,且通过子类构造函数创建子类对象,调用了这个父类构造器(无论显示还是隐式),就会导致父类在构造时实际上调用的是子类覆盖的方法
public abstract class Father {
    public Father() {
        display();
    }
 
    public void display() {
        System.out.println("Father's display");
    }    
}
public class Son extends Father {
 
    public Son() {
    }
 
    public void display() {
        System.out.println("Son's display");
    }
    
    public static void main(String[] args) {
        new Son();
    }
 
}

输出为:Son's display
优点:通过继承相同的父类,初始化子类时,父类会调用不同子类的不同复写方法,从而实现多态性。
缺点:如果在父类构造函数中调用被子类重写的方法,会导致子类重写的方法在子类构造器的所有代码之前执行,从而导致子类重写的方法访问不到子类实例变量的值,因为此时这些变量还没有被初始化。

成员变量的初始化过程:默认初始化-》显示初始化(就是赋值,是调用)-》在构造函数的特定初始化(如果有)

关于final

  • final在jvm中
    • 不加final关键字:
public class demo02 {

    public static void main(String[] args) {
        System.out.println(demo.test);
    }

}
class demo{
    public static String test="我是demo类的一个测试字符串";
    static {
        System.out.println("我是demo的静态代码块!!!");
    }
}
1747974-20191229192652819-1589162209.png
  • 加final关键字:
public class demo02 {

    public static void main(String[] args) {
        System.out.println(demo.test);
    }

}
class demo{
    public static final String test="我是demo类的一个测试字符串";
    static {
        System.out.println("我是demo的静态代码块!!!");
    }
}
1747974-20191229192739854-330549726.png
  • 可以看到这两个的运行结果的不同,加了final关键字的java程序并不会去主动加载demo这个class类。
  • 分析结果:如果加入了常量关键字,也就是final关键字,JVM会把这个常量放到demo02这个类里面的常量池当中,因此并不会主动加载demo这个类(当然这里是因为采用的final赋值是直接赋值)
  • final的四种初始化
public class Main {
 
    private final static int i = 0;//声明时直接初始化
    private final int j;//构造函数赋值(在构造代码块之后执行)
    private final static int k;//静态代码块中赋值(先于构造代码块执行)
    private final int m;//构造代码块中赋值(先于构造函数执行)
 
    public Main(int j) { //正确
        this.j = j;
    }
 
    public Main() {//正确
        this.j = 1;
    }
 
    static {//静态代码块
        k = 2;
    }
 
    {//构造代码块
        m = 3;
    }
 
    public static void main(String[] args) {
    }
}
class Person {
 
    int age;
 
}
 
 
public class LearnHeap {
 
 
    public static void main(String args[]){
 
        int a=10;
        Person person = new Person();
        person.age =20;
 
        change(a,person);
        System.out.println("a="+ a+",and person.age = "+person.age);
 
    }
 
    static void change(int a1, Person person){
 
        a1 = 11;
        person.age= 21;
        System.out.println("a1="+ a1+",and age1 = "+person.age);
 
    }
  1. main()函数是程序入口,JVM先执行,首先将main方法压入栈中,在栈内存中开辟一个空间,存放int类型变量a,同时附值10。在堆中分配一片区域,用来存放和创建Person对象,这片内存区域会有属于自己的内存地址,假设是1001,然后给成员变量赋值,age=20。执行结束后,构造防范弾栈,Person创建完成,将Person的内存地址1001赋值给person(此处person小写,是引用变量类型)。
  2. JVM执行change()函数,在栈内存中又开辟一个新的空间,存放int类型变量a1和类型为Person的person。此时main空间与change空间并存,同时运行,互不影响。
  3. change()方法执行,将a1赋值为11,person对象的堆中age赋值为21。
  4. change()执行完毕,变量a1立即释放,空间消失。

相关文章

网友评论

      本文标题:java初始化过程

      本文链接:https://www.haomeiwen.com/subject/idlrihtx.html