Java初始化理解与总结 转载
Java的初始化可以分为两个部分:   
(a)类的初始化    
(b)对象的创建
一、类的初始化
1.1 概念介绍:
一个类(class)要被使用必须经过装载,连接,初始化这样的过程。
- 在装载阶段,类装载器会把编译形成的class文件载入内存,创建类相关的Class对象,这个Class对象封装了我们要使用的类的类型信息。
- 连接阶段又可以分为三个子步骤:验证、准备和解析。
- 验证就是要确保java类型数据格式 的正确性,并适于JVM使用。
- 准备阶段,JVM为静态变量分配内存空间,并设置默认值,注意,这里是设置默认值,比如说int型的变量会被赋予默认值0 。在这个阶段,JVM可能还会为一些数据结构分配内存,目的是提高运行程序的性能,比如说方法表。
- 解析过程就是在类型的常量池中寻找类、接口、字段和方法的符号引用,把这些符号引用替换成直接引用。这个阶段可以被推迟到初始化之后,当程序运行的过程中真正使用某个符号引用的时候 再去解析它。
 
- 类的初始化:
- 初始化类,是指初始化static静态变量和执行static静态代码块。
- 初始化接口,是指初始化定义在该接口中的filed。
 
1.2 类的初始化条件
类会在首次被“主动使用”时执行初始化,以下情况第一次发生之前类会被初始化:
- 创建类的实例;
- 调用类的静态方法;
- 调用类的静态变量,并且该变量不是一个常变量;
- 为类的静态字段赋值;
- 在顶层类中执行assert语句;(?)
- 调用类Class及包java.lang.reflect中的某些反射方法;
JLS严格的说明:在任何其他的情况下,都不会对类或接口进行初始化。
1.3 类的初始化规则
1、类初始化时,该类的父类将首先被初始化,此过程一直递归到 java.lang.Object为止。但是父类实现的接口并不会被初始化。
class Parent implements J{
 { System.out.println("父类初始化块"); }  
 static{
     System.out.println("父类静态初始化块");  
 }
}
class Child extends Parent implements I{
 { System.out.println("子类初始化块"); }  
 static{
     System.out.println("子类静态初始化块");  
 }
}
interface I {  
 int i = Test.out("interface : i", 1);  
}  
interface J {  
 int j = Test.out("interface : j", 2);  
}  
public class Test {    
 static int out(String s, int i) {  
     System.out.println(s + "=" + i);  
     return i;  
 }  
 public static void main(String [] args){
     new Child();
 }
}
接口只有被用到时才会被初始化
输出:
父类静态初始化块
子类静态初始化块
父类初始化块
子类初始化块
interface I {  
  int i = Test.out("interface : i", 1);  
  int ii = Test.out("interface : ii", 11);  
}  
interface J extends I{  
  int j = Test.out("interface : j", 2);  
  int jj = Test.out("interface : jj", 22);  
}  
public class Test {    
  static int out(String s, int i) {  
      System.out.println(s + "=" + i);  
      return i;  
  }  
  public static void main(String [] args){
      System.out.println(J.j);  
  }
}
输出:
interface : j=2
interface : jj=22
2
示例1:(如上所述)
class Parent{
  static int p = 10;
  static{
      System.out.println("父类静态初始化块");  
  }
}
class Child extends Parent{ 
  static int c = 20; 
  static{
      System.out.println("子类静态初始化块");  
  }
} 
public class Test {    
  public static void main(String [] args){
      System.out.println(Child.p);    //静态域p被子类引用
  }
}
父类静态初始化块
10
此规则暂时还有点疑惑。
public class Test
{
public static void main(String[] args)
{
System.out.println(Child.c); //满足类的初始化条件,父类也会被初始化,与示例1不同
}
}
父类静态初始化块
子类静态初始化块
20
示例1:
class Parent{
  static{
      System.out.println("父类静态初始化块");  
  }
}
class Child extends Parent{
  static final int x = 2005;  
  static{
      System.out.println("子类静态初始化块");  
  }
} 
public class Test {    
  public static void main(String [] args){
      System.out.println(Child.x); 
  }
}
输出:
2005
interface I {  
  int i = 1;  
  int ii = Test.out("ii", 2);  
}  
public class Test {    
  static int out(String s, int i) {  
      System.out.println(s + "=" + i);  
      return i;  
  }  
  public static void main(String [] args){
      System.out.println(I.i);  
  }
}
输出:
1
初始化接口也只会进行一次。
class Child extends Parent{ 
  static int c = 20; 
  static{
      System.out.println("子类静态初始化块");  
  }
} 
public class Test {    
  public static void main(String [] args){
      System.out.println(Child.c); 
      System.out.println(Child.c); 
  }
}
父类静态初始化块
子类静态初始化块
20
20
小结
二、对象创建过程中的Java初始化
2.1 对象的创建过程总结
假设有个名为Dog的类:
- 当首次创建类型为Dog的对象时,或者Dog类的静态方法/静态域首次被访问时,java解释器必须查找类路径,以定位Dog.class文件。
- 然后载入Dog.class(这将创建一个Class对象),有关静态初始化的所有动作都会执行。因此,静态初始化只在class对象首次加载的时候进行一次。
- 当用new Dog()创建对象的时候,首先将在堆上为Dog对象分配足够的存储空间。
- 这块存储空间会被清零,这就自动地将Dog对象中的所有基本类型数据都设置成了默认值(对数字来说就是0,对布尔型与字符型也相同),而引用则被设置成了null。
- 执行所有出现于字段定义处的初始化动作。
- 执行构造器。
---《Thinking in java》
2.2 对象创建过程中初始化顺序
父静态成员>子静态成员>父普通成员初始化>父构造>子普通成员初始化>子构造.
  
( 静态初始化块以静态变量对待)
public class Test {    
  public static void main(String [] args){
      new Child();
  }
}
class Parent{
  { 
      System.out.println("父类普通成员初始化块"); 
  }  
  static{
      System.out.println("父类静态成员及初始化块");
  }
  public Parent(){
      System.out.println("父类构造函数"); 
  }
}
class Child extends Parent{
  { 
      System.out.println("子类普通成员初始化块"); 
  }  
  static{
      System.out.println("子类静态成员及初始化块");
  }
  public Child(){
      super();
      System.out.println("子类构造函数"); 
  }
}
父类静态成员及初始化块
子类静态成员及初始化块
父类普通成员初始化块
父类构造函数
子类普通成员初始化块
子类构造函数
public class Test {    
  public static void main(String [] args){
      new Child();
  }
}
class Parent{
  public Parent(){
      System.out.println("父类构造函数"); 
      System.out.println("子类成员变量 height:" + ((Child)this).height);
  }
}
class Child extends Parent{
  public int height= 20;
  { 
      System.out.println("子类非静态成员初始化块"); 
      System.out.println("子类成员变量 height:" + this.height);
  }  
  public Child(){
      super();
      System.out.println("子类构造函数"); 
  }
}
父类构造函数
子类成员变量 height:0
子类非静态成员初始化块
子类成员变量 height:20
子类构造函数
2.3 对象创建过程的说明
- 1、静态域的初始化是在类的初始化期间,非静态域的初始化时在类的实例创建期间。这意味这静态域初始化在非静态域之前。
- 2、非静态域通过构造器初始化,子类在做任何初始化之前构造器会隐含地调用父类的构造器,这保证了父类非静态实例变量初始化早于子类。
- 3、调用Class的类成员变量时,构造函数和成员变量不会执行
public class Test {    
  public static void main(String [] args){
      System.out.println(Parent.a); 
  }
}
class Parent{
  public static int a = 10;
  {
      System.out.println("父类普通成员初始化块");
  }
  public Parent(){
      System.out.println("父类构造函数"); 
  }
}
输出:10
- 4、在类的内部,变量定义的先后顺序决定了初始化的顺序;
 
 即使变量定义散布于方法定义之间,它们仍会在任何方法(包括构造器)被调用之前得到初始化。
public class Test {    
  public static void main(String [] args){
      new Parent(); 
  }
}
class Parent{
  {
      System.out.println("普通成员初始化块1");
  }
  public Parent(){
      System.out.println("构造函数"); 
  }
  {
      System.out.println("普通成员初始化块2");
  }
}
普通成员初始化块1
普通成员初始化块2
构造函数
- 5、多态情况下的对象初始化
public class Test {    
public static void main(String [] args){
    Parent parent = new Child();
}
}
class Parent{
{ 
    System.out.println("父类普通成员初始化块"); 
}  
static{
    System.out.println("父类静态成员及初始化块");
}
public Parent(){
    System.out.println("父类构造函数"); 
}
}
class Child extends Parent{
{ 
    System.out.println("子类普通成员初始化块"); 
}  
static{
    System.out.println("子类静态成员及初始化块");
}
public Child(){
    super();
    System.out.println("子类构造函数"); 
}
}
父类静态成员及初始化块
子类静态成员及初始化块
父类普通成员初始化块
父类构造函数
子类普通成员初始化块
子类构造函数
- 6、程序的执行过程,即Java虚拟机执行Test 的静态方法main,这也会引起Test 类的初始化。(理解面向对象而非面向过程)
public class Test {    
  public static void main(String [] args){
    System.out.println("Test- main");
  }
  static{
    System.out.println("静态成员及初始化块");
  }
}
静态成员及初始化块
Test- main
三、练习
下面以阿里巴巴2014校招笔试题来练手,看看大家是否真正理解对象创建过程:
public class Alibaba
{
public static int k = 0;
public static Alibaba t1 = new Alibaba("t1");
public static Alibaba t2 = new Alibaba("t2");
public static int i = print("i");
public static int n = 99;
private int a = 0;
public int j = print("j");
{
print("构造块");
}
static
{
print("静态块");
} public Alibaba(String str)
{
System.out.println((++k) + ":" + str + " i=" + i + " n=" + n);
++i;
++n;
} public static int print(String str)
{
System.out.println((++k) + ":" + str + " i=" + i + " n=" + n);
++n;
return ++i;
} public static void main(String args[])
{
Alibaba t = new Alibaba("init");
}
}
输出:
1:j i=0 n=0
2:构造块 i=1 n=1
3:t1 i=2 n=2
4:j i=3 n=3
5:构造块 i=4 n=4
6:t2 i=5 n=5
7:i i=6 n=6
8:静态块 i=7 n=99
9:j i=8 n=100
10:构造块 i=9 n=101
11:init i=10 n=102
Java初始化理解与总结 转载的更多相关文章
- Java深入理解文章(转载)
		引用自:http://droidyue.com/ninki/ JVM运行时的数据区 http://droidyue.com/blog/2014/12/21/java-runtime-data-area ... 
- Java 初始化的个人理解
		先说明一下由来吧,下面是同学的一道笔试题,以前感觉对java初始化也是了解一二的,结果,看到这题泪奔了,不会...上网查,自己添加println,总算是能把自己讲明吧了,不知理解的对不对,先记录下吧, ... 
- openerp经典收藏 深入理解对象(转载)
		深入理解对象(转载) 原文地址:http://shine-it.net/index.php/topic,2159.0.htmlhttp://blog.sina.com.cn/s/blog_57ded9 ... 
- [java] 深入理解内部类: inner-classes
		[java] 深入理解内部类: inner-classes // */ // ]]> [java] 深入理解内部类: inner-classes Table of Contents 1 简介 ... 
- Java 序列化 序列化与单例模式 [ 转载 ]
		Java 序列化 序列化与单例模式 [ 转载 ] @author Hollis 本文将通过实例+阅读Java源码的方式介绍序列化是如何破坏单例模式的,以及如何避免序列化对单例的破坏. 单例模式,是设计 ... 
- Effective Java通俗理解(持续更新)
		这篇博客是Java经典书籍<Effective Java(第二版)>的读书笔记,此书共有78条关于编写高质量Java代码的建议,我会试着逐一对其进行更为通俗易懂地讲解,故此篇博客的更新大约 ... 
- Effective Java通俗理解(下)
		Effective Java通俗理解(上) 第31条:用实例域代替序数 枚举类型有一个ordinal方法,它范围该常量的序数从0开始,不建议使用这个方法,因为这不能很好地对枚举进行维护,正确应该是利用 ... 
- Effective Java通俗理解(上)
		这篇博客是Java经典书籍<Effective Java(第二版)>的读书笔记,此书共有78条关于编写高质量Java代码的建议,我会试着逐一对其进行更为通俗易懂地讲解,故此篇博客的更新大约 ... 
- java初始化与清理
		初始化与清理 欢迎转载,转载烦请注明出处,谢谢. https://www.cnblogs.com/sx-wuyj/p/11177257.html 1.用构造器确保初始化 java中通过提供构造器,可以 ... 
随机推荐
- 【19】设计class犹如设计type
			设计class 的时候,需要好好考虑下面的问题: 1.新type的对象应该如何被创建和销毁? 2.对象的初始化和对象的赋值该有什么样的差别? 3.新type的对象如果pass by value,意味着 ... 
- 采用Reflector的VS.net插件断点调试无源码DLL 分类:
			.Net的编程利器Reflector可以反编译基于.net开发的应用程序和DLL,其功能强大不用多说.今天想试验一把利用VS.net的插件断点调试外部无源码的DLL(只要是程序集都可以,所以exe也行 ... 
- ThinkPHP函数详解:C方法
			C方法是ThinkPHP用于设置.获取,以及保存配置参数的方法,使用频率较高.了解C方法需要首先了解下ThinkPHP的配置,因为C方法的所有操作都是围绕配置相关的.ThinkPHP的配置文件采用PH ... 
- ORM之一:适合我的ORM
			一.常见开源ORM框架 比喻:Kerosene ORM,DbLinq,Dapper,DynamicQuery,elinq,glinq,NPoco,Relinq,EF,ServiceStack.OrmL ... 
- Get Files from Directory
			http://www.csharp-examples.net/get-files-from-directory/ Get Files from Directory [C#] This example ... 
- iOS开发——UI篇&提示效果
			提示效果 关于iOS开发提示效果是一个很常见的技术,比如我们平时点击一个按钮,实现回馈,或者发送网络请求的时候! 技术点: 一:View UIAlertView UIActionSheet 二:控制器 ... 
- careercup-C和C++
			13.1 用C++写个方法,打印输出文件的最后K行. 解答: 一种方法是打开文件两次,第一次计算文件的行数N,第二次打开文件,跳过N-K行, 然后开始输出.如果文件很大,这种方法的时间开销会非常大. ... 
- 10个android开源项目
			http://www.51testing.com/?uid-116228-action-viewspace-itemid-244285 1.Android团队提供的示例项目 如果不是从学习Androi ... 
- 分享一个字数限制和统计的UITextView分类方法
			- (NSUInteger)letterCountWithLimits:(NSInteger)limits { NSString *toBeString = self.text; NSUInteger ... 
- iOS 中使用.9图
			背景 .9图来源于Android.为了设计出一套图,兼容Android和iOS,使用.9图的方式来对图片进行拉伸以适应不同的屏幕.在iOS中没有.9图的概念,只能先了解Android的.9图再进行模拟 ... 
