继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

Java中初始化的相关问题

慕斯709654
关注TA
已关注
手记 274
粉丝 37
获赞 183

已经快半个月没写博客了,这周在看 Thinking in Java 这本书,准备将书中的第五章和第七章的内容整合一下,写出这篇博客,也算是自己对Java的一些基础知识的复习。

这篇博客要说的是Java中的初始化问题,说到初始化,自然想到的是成员变量局部变量的初始化了。那么如何对成员变量和局部变量进行初始化就是我们要探讨的问题。

首先是

局部变量的初始化

来看下这段代码:

void f(){    int i;
    i++;    // ERROR -- i没有被初始化}

这里的 i 是一个局部变量,当对 i 进行 i++ 操作的时候,编译器告诉我们这样是不行的,因为 i 没有被初始化。其实万能的编译器完全可以在这种情况下为 i 赋一个初值,但是它没有这样做,Thinking in Java 这本书告诉我们:未初始化的局部变量更有可能是程序员的疏忽,所以采用默认值反而会掩盖这种失误,因此强制程序员提供初值,往往更能帮助更快发现程序中的缺陷。

局部变量是初始化还是很好理解的,编译器不会自动初始化,只能由程序员自己手动提供初始化。而在成员变量上就完全不一样了,下面我们来看看成员变量的初始化是什么样的。

成员变量的初始化

成员变量的初始化又分为自动初始化手动初始化,并且手动初始化也有一些独特之处,下面就来看看这些不同的地方:

 

复制代码

public class InitialValues{    boolean v1;    char v2;    byte v3;    short v4;    int v5;    long v6;    float v7;    double v8;
    InitialValues ref;    void print(){
        System.out.println(v1);
        System.out.println(v2);
        System.out.println(v3);
        System.out.println(v4);
        System.out.println(v5);
        System.out.println(v6);
        System.out.println(v7);
        System.out.println(v8);
        System.out.println(ref);            
    }    public static void main(String[] args){        new InitialValues().print();
    }
}/* output:
false
[]
0
0
0
0
0.0
0.0
null*/

复制代码

 

可见当没有给成员变量赋初值的时候,Java的编译器会帮我们处理“ 未初始化变量 ”的风险。这种初始化是自动初始化,当然我们也可以手动的为成员变量进行初始化,就像下面这样:

复制代码

 1 public class InitialValues{ 2     boolean v1 = true; 3     char v2 = 'a'; 4     byte v3 = 47; 5     short v4 = 0xff; 6     int v5 = 3; 7     long v6 = 4; 8     float v7 = 3.14f; 9     double v8 = 3.14159;10     String str = new String("hello world"); 
11 }

复制代码

 

这时是在定义类的成员变量的地方为其赋值,看起来简单明了。可是这种做法有一定的局限性:由类 InitialValues 生成的每个对象都由相同的初始值。这对于程序设计而言是相当缺乏灵活性的一种做法,所以我们需要一种新的方式:在创建对象的时候才对每个对象的成员变量进行赋值,这样每个对象都可以有不同的成员变量数据。这种方式就是下面要说的:构造器初始化。

构造器初始化

通过构造器来对成员变量做初始化是相当灵活的,请看下面一段代码:

public class InitialValue{
    int i;
    InitialValue(int i){
        this.i = i;
    }
}

这样就可以在创建多个对象时,向构造器的形参中传入不同的值。

上面讲解了两种成员变量的初始化,可见第二种构造器初始化更具灵活性。但是有一点要牢记:即使手动进行初始化了,自动初始化过程一样会在手动初始化之前完成。也就是说如果执行 new InitialValue( 7 ) 那么 i 首先会被置0,然后变成7。这种特性对于所有的基本类型和对象引用都是成立的。

静态数据的初始化

这里说的静态数据指的是成员变量为静态的情况,因为 static 关键字不能应用于局部变量。静态数据有一个特点:无论创建多少个对象,静态数据都只占用一份内存。

通过下面的一段代码,可以了解到静态数据是如何初始化的:

复制代码

 1 class Bowl{ 2     Bowl(int i){ 
 3         System.out.println("Bowl(" + i + ")") 
 4     } 5     void f1(int i){ 
 6         System.out.println("f1(" + i + ")")  
 7     } 8 } 9 clsss Table{10     static Bowl b1 = new Bowl(1);11     Table(){ 
12         b2.f1(1); 
13     }14     void f2(int i){ 
15         System.out.println("f2(" + i + ")")  
16     }17     static Bowl b2 = new Bowl(2);18 }19 class Cupboard{20     Bowl b3 = new Bowl(3);21     static Bowl b4 = new Bowl(4);22     Cupboard(){23         b4.f1(2);24     }25     void f3(int i){ 
26         System.out.println("f3(" + i + ")")  
27     }28     static Bowl b5 = new Bowl(5);29 }30 public class Test{31     public static void main(String[] args){32         new Cupboard();33         new Cupboard();34     }35     static Table table = new Table();36     static Cupboard cupboard = new Cupboard();37 }38 /* output:39 Bowl(1)40 Bowl(2)41 f1(1)42 Bowl(4)43 Bowl(5)44 Bowl(3)45 f1(2)46 Bowl(3) // 这是在main中通过new Cupboar() 打印出来的 start47 f1(2)48 Bowl(3)49 f1(2) // 这是在main中通过new Cupboar() 打印出来的 end50 f2(1)51 f3(1)52 */

复制代码

 

可见静态初始化只在必要的时候进行,即第一次访问对象的构造器(或者第一次访问类的静态数据)时静态数据才会被初始化(其实构造器也是一个static方法)。

首先编译器会试图访问 Test.main() ,因为 main() 是静态方法,要执行静态方法必须加载 Test 类,然后静态成员变量 table 和 cupboad 被初始化,这间接的导致他们的类被加载,由于他们都包含静态的 Bowl 成员变量,所以 Bowl 类也被加载。这三个类之间通过 static 关键字联系起来。

下面总结一下对象的创建过程,假设有一个名为Student的类:

  1. 当首次调用构造器new Student() ,或者第一次访问Student类的静态方法/成员变量时,Java解释器找到Student.class文件

  2. 然后载入Student.class文件,接着进行所有静态数据的初始化(这时对象还没创建呢!从这也能看出,静态数据与单个对象无关)

  3. 接下来创建Student对象,为对象在堆上分配空间

  4. 将分配到的存储空间清零,即将所有的非静态成员变量赋上对应的初始值

  5. 接下来如果在成员变量的定义出有初始化动作,那就再次执行相应的初始化

  6. 最后执行构造器的方法体,如果构造器有初始化动作,再次覆盖之前的初始化(到这里初始化和对象的创建就完成了)

总结

相比于C++来说,Java的初始化机制能规避很多一开始的编程错误。特别是通过堆构造器的使用,能给初始化很大的灵活度,初始化在Java中占有至关重要的地位,通过Java中的构造器能保证正确的初始化,没有正确的构造器调用,Java编译器是不允许创建对象的,编译器有了更多的控制,我们的程序也就更加安全。

 

原文出处:https://www.cnblogs.com/KKSJS/p/9622808.html  

打开App,阅读手记
1人推荐
发表评论
随时随地看视频慕课网APP