`
gao_20022002
  • 浏览: 164843 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

类初始化问题(转载)

阅读更多
对于静态变量、静态初始化块、变量、初始化块、构造器,它们的初始化顺序以此是(静态变量、静态初始化块)>(变量、初始化块)>构造器。
我们也可以通过下面的测试代码来验证这一点:
Java代码
public class InitialOrderTest {   
  
    // 静态变量   
    public static String staticField = "静态变量";   
    // 变量   
    public String field = "变量";   
  
    // 静态初始化块   
    static {   
        System.out.println(staticField);   
        System.out.println("静态初始化块");   
    }   
  
    // 初始化块   
    {   
        System.out.println(field);   
        System.out.println("初始化块");   
    }   
  
    // 构造器   
    public InitialOrderTest() {   
        System.out.println("构造器");   
    }   
  
    public static void main(String[] args) {   
        new InitialOrderTest();   
    }   
}  


运行以上代码,我们会得到如下的输出结果:

静态变量
静态初始化块
变量
初始化块
构造器

这与上文中说的完全符合。那么对于继承情况下又会怎样呢?我们仍然以一段测试代码来获取最终结果:
Java代码
class Parent {
	// 静态变量
	public static String p_StaticField = "父类--静态变量";
	// 变量
	public String p_Field = "父类--变量";

	// 静态初始化块
	static {
		System.out.println(p_StaticField);
		System.out.println("父类--静态初始化块");
	}

	// 初始化块
	{
		System.out.println(p_Field);
		System.out.println("父类--初始化块");
	}

	// 构造器
	public Parent() {
		System.out.println("父类--构造器");
	}
}

public class SubClass extends Parent {
	// 静态变量
	public static String s_StaticField = "子类--静态变量";
	// 变量
	public String s_Field = "子类--变量";
	// 静态初始化块
	static {
		System.out.println(s_StaticField);
		System.out.println("子类--静态初始化块");
	}
	// 初始化块
	{
		System.out.println(s_Field);
		System.out.println("子类--初始化块");
	}

	// 构造器
	public SubClass() {
		System.out.println("子类--构造器");
	}

	// 程序入口
	public static void main(String[] args) {
		new SubClass();
	}
}


运行一下上面的代码,结果马上呈现在我们的眼前:

父类--静态变量
父类--静态初始化块
子类--静态变量
子类--静态初始化块
父类--变量
父类--初始化块
父类--构造器
子类--变量
子类--初始化块
子类--构造器

现在,结果已经不言自明了。大家可能会注意到一点,那就是,并不是父类完全初始化完毕后才进行子类的初始化,实际上子类的静态变量和静态初始化块的初始化是在父类的变量、初始化块和构造器初始化之前就完成了。

那么对于静态变量和静态初始化块之间、变量和初始化块之间的先后顺序又是怎样呢?是否静态变量总是先于静态初始化块,变量总是先于初始化块就被初始化了呢?实际上这取决于它们在类中出现的先后顺序。我们以静态变量和静态初始化块为例来进行说明。

同样,我们还是写一个类来进行测试:
Java代码
public class TestOrder {
	// 静态变量
	public static TestA a = new TestA();
	
	// 静态初始化块
	static {
		System.out.println("静态初始化块");
	}
	
	// 静态变量
	public static TestB b = new TestB();

	public static void main(String[] args) {
		new TestOrder();
	}
}

class TestA {
	public TestA() {
		System.out.println("Test--A");
	}
}

class TestB {
	public TestB() {
		System.out.println("Test--B");
	}
}


运行上面的代码,会得到如下的结果:

Test--A
静态初始化块
Test--B

大家可以随意改变变量a、变量b以及静态初始化块的前后位置,就会发现输出结果随着它们在类中出现的前后顺序而改变,这就说明静态变量和静态初始化块是依照他们在类中的定义顺序进行初始化的。同样,变量和初始化块也遵循这个规律。

了解了继承情况下类的初始化顺序之后,如何判断最终输出结果就迎刃而解了。



其实还可以增加内部类的相关初始化问题。
分享到:
评论
4 楼 mercyblitz 2008-06-27  
不错,看看这个吧!
关于类型的生命周期,相信对理解类初始化会有帮助的!
http://mercyblitz.blog.ccidnet.com/blog-htm-do-showone-uid-45914-type-blog-itemid-167315.html
3 楼 gao_20022002 2008-06-26  
public class TestA {

	class A {
		String string1 = "内部类--实例数据";
		{
			System.out.println(string1);
		}
		
		public A(){
			System.out.println("内部类--构造器");
		}

	}
	
	public TestA(){
		System.out.println("主类--构造器");
	}

	static String string2 = "主类--静态数据";
	String string3 = "主类--实例数据";
	static {
		System.out.println(string2);
	}
	{
		System.out.println(string3);
	}

	public static void main(String[] args) {
		new TestA().new  A();
	}
}


运行上面的代码,会得到如下的结果:
主类--静态数据
主类--实例数据
主类--构造器
内部类--实例数据
内部类--构造器

同样,还是先初始化主类,然后初始化内部类。

可以得出这样的结论:
平常的基础类(即没有继承关系,内部类),初始化顺序:(静态数据|静态块)-->(实例数据|实例块)-->构造器。


继承的类,初始化顺序:继承类首先初始化,如果再次继承以此类推,最顶层类执行(静态数据|静态块),次顶层类执行(静态数据|静态块),以此类推,直至最底层基类,然后最顶层类执行(实例数据|实例块)-->构造器,次顶层类执行(实例数据|实例块)-->构造器,以此类推,直至最底层基类。


public class UpParent {
	
	// 静态变量
	public static String p_StaticField = "父父类--静态变量";
	// 变量
	public String p_Field = "父父类--变量";

	// 静态初始化块
	static {
		System.out.println(p_StaticField);
		System.out.println("父父类--静态初始化块");
	}

	// 初始化块
	{
		System.out.println(p_Field);
		System.out.println("父父类--初始化块");
	}

	// 构造器
	public UpParent() {
		System.out.println("父父类--构造器");
	}

}

public class Parent extends UpParent{
	// 静态变量
	public static String p_StaticField = "父类--静态变量";
	// 变量
	public String p_Field = "父类--变量";

	// 静态初始化块
	static {
		System.out.println(p_StaticField);
		System.out.println("父类--静态初始化块");
	}

	// 初始化块
	{
		System.out.println(p_Field);
		System.out.println("父类--初始化块");
	}

	// 构造器
	public Parent() {
		System.out.println("父类--构造器");
	}

}

public class SubClass extends Parent {
	// 静态变量
	public static String s_StaticField = "子类--静态变量";
	// 变量
	public String s_Field = "子类--变量";
	// 静态初始化块
	static {
		System.out.println(s_StaticField);
		System.out.println("子类--静态初始化块");
	}
	// 初始化块
	{
		System.out.println(s_Field);
		System.out.println("子类--初始化块");
	}

	// 构造器
	public SubClass() {
		System.out.println("子类--构造器");
	}

	// 程序入口
	public static void main(String[] args) {
		new SubClass();
	}

}




以上补充继承,执行结果:
父父类--静态变量
父父类--静态初始化块
父类--静态变量
父类--静态初始化块
子类--静态变量
子类--静态初始化块
父父类--变量
父父类--初始化块
父父类--构造器
父类--变量
父类--初始化块
父类--构造器
子类--变量
子类--初始化块
子类--构造器


内部类(静态),
首先调用静态内部类,初始化顺序:主类(静态变量|静态块)的初始化,静态内部类的(静态数据|静态块)-->(实例数据|实例块)-->构造器,如果再次调用主类,才会初始化主类(实例数据|实例块)-->构造器。
如果首先调用主类,初始化顺序:主类(静态变量|静态块)-->(实例数据|实例块)-->构造器,如果再次调用内部类,初始化顺序:内部类(静态变量|静态块)-->(实例数据|实例块)-->构造器。两个完全是独立的。

内部类(非静态),初始化顺序:主类(静态变量|静态块)-->(实例数据|实例块)-->构造器,然后内部类(实例数据|实例块)-->构造器。内部类总是与主类的一个对象相绑定。

如果出现两个或者多个内部类,初始化顺序以此类推
2 楼 gao_20022002 2008-06-26  
public class TestA {

	static class A {
		static String string1 = "内部类--静态数据";
		String string2 = "内部类--实例数据";
		static {
			System.out.println(string1);
		}
		{
			System.out.println(string2);
		}

	}

	static String string3 = "主类--静态数据";
	String string4 = "主类--实例数据";
	static {
		System.out.println(string3);
	}
	{
		System.out.println(string4);
	}

	public static void main(String[] args) {
		new A();
		System.out.println("---------");
		new TestA();
	}
}


运行上面的代码,会得到如下的结果:
主类--静态数据
内部类--静态数据
内部类--实例数据
---------
主类--实例数据
说明:
静态内部类调用也会首先初始化主类的静态数据,然后初始化静态内部类数据,实例数据。当然,静态数据与静态块遵照先后顺序。
1 楼 minstrel 2008-06-26  
精彩!为什么代码都贴两遍?

相关推荐

    C++面试中的常见问题转载

    - 引用必须在声明时被初始化,不能留作未初始化状态。 - 不能定义引用类型的数组。 - 引用没有自己的存储空间,不占用内存。 - 引用不是新的变量,所以没有默认构造函数,也不能重新绑定到另一个对象。 - 当...

    zigbee组网分析 转载

    - `osal_init_system()`是操作系统初始化的关键步骤,包括内存分配系统`osal_mem_init()`、消息队列`osal_qHead`、计时器`osalTimerInit()`、电源管理系统`osal_pwrmgr_init()`的初始化,以及任务的初始化`...

    java面试资料(转载)

    了解类加载过程,特别是静态块和构造器的执行顺序,可以帮助解决一些复杂的初始化问题。 接下来是继承,这是面向对象编程的核心概念之一。Java支持单一继承,即一个类只能直接继承自一个父类,但可以通过接口实现...

    Java程序员面试的试题集(1_122)帮助初学者的技术问题(转载)

    1. **init(ServletConfig config)**:初始化方法,仅在Servlet实例创建时调用一次,用于加载资源、初始化配置参数等。 2. **service(ServletRequest request, ServletResponse response)**:处理客户端请求的核心...

    screenshot-www.rockstargames.com-2019.12-1.png

    加载:static在类加载时初始化(加载)完成 含义:Static意为静态的,但凡被static 修饰说明属于类,不属于类的对象。 可修饰:Static 可以修饰 内部类、方法、成员变量、代码块。 不可修饰:Static不可...

    u-boot_mips移植分析 (转载的)

    在嵌入式系统中,Bootloader扮演着硬件初始化和内核引导的重要角色。对于商用产品而言,Bootloader需要确保基本的功能和高效的引导速度;而对于开发用途,灵活性、可配置性以及丰富的调试工具则更为关键。u-boot的...

    NDK13_C++基础:构造函数、拷贝构造函数、浅拷贝与深拷贝.zip

    C++中 这个语句执行完毕,在当前的堆内存内 初始化并且赋值好该对象 在java中执行这个语句,只是开辟了一块内存空间,并没初始化和赋值对象, 必须用new关键字,来进行初始化和赋值 ——————————————...

    hibernate官方入门教程 (转载)

    2. **配置文件**: Hibernate的配置文件(hibernate.cfg.xml)包含了数据库连接信息、方言设置、缓存配置等,是初始化SessionFactory的关键。 3. **实体类**: 实体类是映射到数据库表的Java类,通常包含主键和业务...

    Keil编程环境下STM32内存管理研究[转载]

    7. **初始化变量对内存的影响**:初始化变量不仅会占用额外的ROM空间,还可能影响程序启动的效率。当增加初始化变量的数量时,RO-data会随之增加,但代码段Code不会再有变化。这说明增加的是数据而非代码执行指令,...

    VC 中使用MFC通过ADO连接数据库方法 转载.doc

    在你的项目中,包含这个类的头文件,并创建一个`ADOConn`对象,然后通过`OnInitADOConn`方法初始化连接字符串。例如: ```cpp ADOConn m_Ado; m_Ado OnInitADOConn(strConnect); ``` 若要执行查询,使用`...

    C++编程思想【转载】

    内容涉及对象的演化、数据抽象、隐藏实现、初始化与清除、函数重载与缺省参数、输入输出流介绍、常量、内联函数、命名控制、引用和拷贝构造函数、运算符重载、动态对象创建、继承和组合、多态和虚函数、模板和包容器...

    uCos-II学习汇总(转载)

    初始化Timer应在优先级最高的任务中进行,以确保在多任务启动后,Timer初始化程序最先执行。若资源有限,可以创建一个高优先级任务仅用于初始化Timer,但随后挂起该任务。 - `OSTaskCreate()` 用于创建任务,通常...

    [转载]深入理解JVM

    5. **初始化**:执行类构造器 `<clinit>` 方法,为静态变量赋初值。 #### 四、结束语 深入了解JVM对于Java开发者来说至关重要。通过掌握JVM的工作原理和技术细节,开发者不仅能够编写出更加高效、安全的代码,还能...

    C指针全面总结(转载)

    - **指针的值**:未初始化,因此值不确定。 - **指针本身所占据的内存区**:占据4个字节。 2. `char *ptr;` - **指针的类型**:`char *`。 - **指针所指向的类型**:`char`。 - **指针的值**:未初始化。 - ...

    《JVM从入门到入魔》笔记.pdf

    1:JVM内存模型:类加载机制【转载、验证、准备、解析、初始化】+类装载器【装载器分类、加载原则】+运行时数据区【方法区、堆、虚拟机栈、本地方法栈、程序计数器】。 2:垃圾回收:垃圾确定【引用计数法、可达性分析...

    解决微信小程序调用moveToLocation失效问题【超简单】

    上图所示,mapUpdated表示地图加载完成后,再初始化数据。 为什么moveToLocation失败? 第一:可能你的ID取错了; 第二:调用moveToLocation时,必须需要调用wx.getLocation,并且用户授权后,才能使用...

    转载 纯nasm实现中文操作系统.txt

    - **初始化**: 初始化内存段,并调用`ShowMessage`子程序显示初始化信息。 - **磁盘读取**: 使用`INT 0x13`中断读取磁盘数据,用于加载操作系统内核到内存中。 - **引导加载器**: 最后,使用`JMP DWORD 0x910:0`指令...

    轻松OBS录屏黑屏解决办法(原创文章请勿转载)NVENC Error:init_encoder:报错信息

    错误提示“NV_ENC_ERR_INVALID_VERSION”表明编码器版本不匹配或驱动程序出现问题,导致编码初始化失败。 解决OBS黑屏问题的方法如下: 1. 首先,用户需要进入Windows系统中的“设置”菜单。这通常可以通过点击...

    VC socket传送文件的例子(转载)

    1. 初始化Socket:调用`WSAStartup`函数启动Winsock服务。 2. 创建Socket:使用`socket`函数创建一个监听Socket。 3. 绑定地址:使用`bind`函数将Socket与特定的IP地址和端口号绑定。 4. 监听连接:调用`listen`函数...

Global site tag (gtag.js) - Google Analytics