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);
}
}
原因分析:
- 陈皓博客
- Java Tutor - Visualize Java code execution to learn Java online (also visualize Python, Java, JavaScript, TypeScript, Ruby, C, and C++ code)
JAVA构造时成员初始化的陷阱的更多相关文章
- Java 构造时成员初始化的陷阱
1.首先列出代码 Base.java public class Base { Base() { preProcess(); } void preProcess() {} } Derived.java ...
- Java中的成员初始化顺序和内存分配过程
Java中的成员初始化顺序和内存分配过程 原帖是这样描述的: http://java.dzone.com/articles/java-object-initialization?utm_source= ...
- java类的成员初始化顺序和初始化块知识
java类的成员初始化顺序和初始化块知识 转自:http://blog.csdn.net/lgfeng218/article/details/7606735 属性.方法.构造方法和自由块都是类中的成员 ...
- Java类的成员初始化顺序
Java类的成员初始化顺序 2017-06-01 代码: public class InitializeSequence { public static void main(String[] args ...
- Java继承时的初始化顺序
Java程序在启动和运行时,需要首先完成初始化的工作.在涉及到继承.static成员变量等因素时,初始化的顺序就复杂起来.下面以一个例子说明继承时的Java初始化顺序. 例子: class Insec ...
- Java 类的成员初始化顺序
做个简单笔录,就当是重温下基础知识. 1.先看代码: package com.test; public class Test { public static void main(String[] ar ...
- java 编译时的初始化顺序
有的时候,java的初始化会对我的工作照成很大影响,所以简单介绍一下, 首先介绍简单的变量的初始化:在类的内部,变量定义的先后顺序决定了初始化的顺序,即使变量定义散布于方法定义之间,它也会先于构造器和 ...
- JAVA构造MAP并初始化MAP
第一种方法:static块初始化 public class Demo{ private static final Map<String, String> myMap; static { m ...
- Java类和对象初始化
类的生命周期: Java类的初始化: 本阶段负责为类变量赋正确的初始值.(类变量即静态变量) Java编译器把所有的类变量初始化语句和静态初始化器通通收集到<clinit>方法中,该方法只 ...
随机推荐
- 故障重现, JAVA进程内存不够时突然挂掉模拟
背景,服务器上的一个JAVA服务进程突然挂掉,查看产生了崩溃日志,如下: # Set larger code cache with -XX:ReservedCodeCacheSize= # This ...
- UE4新手之编程指南
虚幻引擎4为程序员提供了两套工具集,可共同使用来加速开发的工作流程. 新的游戏类.Slate和Canvas用户接口元素以及编辑器功能可以使用C++语言来编写,并且在使用Visual Studio 或 ...
- ASP.NET Core应用的错误处理[2]:DeveloperExceptionPageMiddleware中间件如何呈现“开发者异常页面”
在<ASP.NET Core应用的错误处理[1]:三种呈现错误页面的方式>中,我们通过几个简单的实例演示了如何呈现一个错误页面,这些错误页面的呈现分别由三个对应的中间件来完成,接下来我们将 ...
- Linux安装LAMP开发环境及配置文件管理
Linux主要分为两大系发行版,分别是RedHat和Debian,lamp环境的安装和配置也会有所不同,所以分别以CentOS 7.1和Ubuntu 14.04做为主机(L) Linux下安装软件,最 ...
- 简单有效的kmp算法
以前看过kmp算法,当时接触后总感觉好深奥啊,抱着数据结构的数啃了一中午,最终才大致看懂,后来提起kmp也只剩下“奥,它是做模式匹配的”这点干货.最近有空,翻出来算法导论看看,原来就是这么简单(先不说 ...
- 关于Visual Studio 未能加载各种Package包的解决方案
问题: 打开Visual Studio 的时候,总提示未能加载相应的Package包,有时候还无法打开项目,各种提示 解决方案: 进入用户目录 C:\Users\用户名\AppData\Local\M ...
- CSS知识总结(八)
CSS常用样式 8.变形样式 改变元素的大小,透明,旋转角度,扭曲度等. transform : none | <transform-function> <transform-fun ...
- Tomcat启动报错org.springframework.web.context.ContextLoaderListener类配置错误——SHH框架
SHH框架工程,Tomcat启动报错org.springframework.web.context.ContextLoaderListener类配置错误 1.查看配置文件web.xml中是否配置.or ...
- 解决Ubuntu Kylin 1610安装ANSYS17.2的NVIDIA显卡驱动问题
Ubuntu Kylin 1610在安装完毕后,会自动安装显卡驱动,对于一般的图形图像使用来说自然不会有太大的问题,但是对于ANSYS17.2的一些模块,还是会出现问题.一个比较常见的问题就是Open ...
- 一点公益商城开发系统模式Ring Buffer+
一个队列如果只生产不消费肯定不行的,那么如何及时消费Ring Buffer的数据呢?简单的方案就是当Ring Buffer"写满"的时候一次性将数据"消费"掉. ...