• Java构造时成员初始化的陷阱


    让我们先来看两个类:Base和Derived类。注意其中的whenAmISet成员变量,和方法preProcess()。

    情景1:(子类无构造方法)

    class Base {
        Base() {
            preProcess();
        }
    
        void preProcess() {
        }
    }
    
    class Derived extends Base {
        public String whenAmISet = "set when declared";
    
        void preProcess() {
            whenAmISet = "set in preProcess()";
        }
    }
    
    public class StaticTest {
        public static void main(String[] args) {
            Derived d = new Derived();
            System.out.println(d.whenAmISet);
        }
    }

    当.java源代码转换成一个.class文件后,其转换成类似下面的等价代码:

    class Base {
        Base() {
            preProcess();
        }
    
        void preProcess() {
        }
    }
    
    class Derived extends Base {
        public String whenAmISet;
    
        {whenAmISet = "set when declared";}
    
        void preProcess() {
            whenAmISet = "set in preProcess()";
        }
    }
    
    public class StaticTest {
        public static void main(String[] args) {
            Derived d = new Derived();
            System.out.println(d.whenAmISet);
        }
    }

    输出结果是: set when declared

    情景2:(子类添加了构造方法)

    class Base {
        Base() {
            preProcess();
        }
    
        void preProcess() {
        }
    }
    
    class Derived extends Base {
        public String whenAmISet = "set when declared";
    
        public Derived() {
            whenAmISet = "set in constructor";
        }
    
        void preProcess() {
            whenAmISet = "set in preProcess()";
        }
    }
    
    public class StaticTest {
        public static void main(String[] args) {
            Derived d = new Derived();
            System.out.println(d.whenAmISet);
        }
    }

    当.java源代码转换成一个.class文件后,其转换成类似下面的等价代码:

    class Base {
        Base() {
            preProcess();
        }
    
        void preProcess() {
        }
    }
    
    class Derived extends Base {
        public String whenAmISet;
    
        public Derived() {
            whenAmISet = "set when declared";
            whenAmISet = "set in constructor";
        }
    
        void preProcess() {
            whenAmISet = "set in preProcess()";
        }
    }
    
    public class StaticTest {
        public static void main(String[] args) {
            Derived d = new Derived();
            System.out.println(d.whenAmISet);
        }
    }

    输出结果为:set in constructor

    情景3:(赋值的细节)

    public class Singleton {
    
        private static Singleton mInstance = new Singleton();  // 位置1
        public static int counter1;
        public static int counter2 = 0;
    
        private Singleton() {
            counter1++;
            counter2++;
        }
    
        public static Singleton getInstantce() {
            return mInstance;
        }
    
        public static void main(String[] args) {
            Singleton singleton = Singleton.getInstantce();
            System.out.println("counter1: " + singleton.counter1);
            System.out.println("counter2: " + singleton.counter2);
        }
    }

    当.java源代码转换成一个.class文件后,其转换成类似下面的等价代码:

    public class Singleton {
    
        private static Singleton mInstance;
        public static int counter1;
        public static int counter2;
    
        static {
            mInstance = new Singleton();
            counter2 = 0;
        }
    
        private Singleton() {
            counter1++;
            counter2++;
        }
    
        public static Singleton getInstantce() {
            return mInstance;
        }
    
        public static void main(String[] args) {
            Singleton singleton = Singleton.getInstantce();
            System.out.println("counter1: " + singleton.counter1);
            System.out.println("counter2: " + singleton.counter2);
        }
    }
    • 在Prepare阶段,mInstance、counter1、counter2的初始值为(null,0,0);
    • 执行至 mInstance = new Singleton()时,进行实例创建并调用构造方法,使counter1、counter2变量的值改变为(1,1);
    • 执行counter2 = 0时,counter2的值再次置为0,最终程序的输出结果为:counter1: 1   counter2: 0

    同理,以下代码的最终输出结果为:counter1: 1  counter2: 1

    public class Singleton {
    
        public static int counter1;
        public static int counter2 = 0;
        private static Singleton mInstance = new Singleton(); // 位置2
    
        private Singleton() {
            counter1++;
            counter2++;
        }
    
        public static Singleton getInstantce() {
            return mInstance;
        }
    
        public static void main(String[] args) {
    
            Singleton singleton = Singleton.getInstantce();
            System.out.println("counter1: " + singleton.counter1);
            System.out.println("counter2: " + singleton.counter2);
        }
    }

    原因分析:

    1. 陈皓博客
    2. Java Tutor - Visualize Java code execution to learn Java online (also visualize PythonJavaJavaScriptTypeScriptRubyC, and C++ code)
  • 相关阅读:
    WinRT下GridView/ListView随滚动条自动加载
    C# 获取属性名
    Xaml System Resources 系统资源
    WinRT常见而又困扰的问题
    .net 解压 zip 包完全不用第三方 library (.net unzip zip file without third party library)
    团队.Net技术手册,现代.Net技术栈摸索梳理
    C# 构建可扩展的应用程序(插件)
    分别用MVC,MCP,MVVM实现一个井字棋游戏
    Android系统属性
    ThreadLocal 和 Countdaowlatch
  • 原文地址:https://www.cnblogs.com/echo1937/p/6243271.html
Copyright © 2020-2023  润新知