美文网首页
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