Java中值的初始化,static变量初始化,继承构造器初始化

  1. Java尽力保证,所有变量在使用前都会得到初始化。对于方法的局部变量,Java会以编译错误来保证。如下:

    public class InitTest {
    public int a;
    public void  f(){
        int i;
        i++;//这里会编译错误 没有初始化
        System.out.println(i);
    }
    public static void main(String[] args) {
        InitTest initTest=new InitTest();
        System.out.println(initTest.a);// 输出 0
    }
    }
    

    对于局部变量,未初始化的局部变量可能是程序员的疏忽,而采用默认值会掩盖这种锁雾,因此强制程序员提供初始值往往能够帮助找出程序中的错误。对于类的数据成员,若是基本类型,情况就不同,类的每个基本数据成员都会保证有一个初始值。这里不罗列基本类型对应的初始值了。如果类里定义一个对象的引用而不将其初始化,那么改引用值为null。
    如何去为变量赋初值?定义地方赋值/构造器赋值初始化。

  2. 构造器初始化
    我们着重看一下构造器初始化。在构造器中可以调用方法或者某些动作来确定初值。但是这种方法无法阻止自动初始化的进行,它会被构造器调用之前发生。如下代码:

       public InitTest() {
        int z;
        z=7;
    }
    

对于z首先会变成0,然后为7。对于所有的基本类型与对象引用,包括在定义时已经指定初值的变量,这种情况都会成立的。因此,编译器不会强制一定要在构造器某个地方或者在使用他们之前对元素进行初始化,因为初始化已经得到了保证。

  • 初始化顺序
    在类的内部,变量定义的先后顺序决定了初始化的顺序。即时变量定义散步与方法定义之间,它们仍旧会在任何方法(包括构造器)被调用之前得到初始化。 如下:
public class OrderInit {
    public static void main(String[] args) {
        House h=new House();
    }
}

class Window{
    Window(int marker){
        System.out.println("Window("+marker+")");
    }
}

class House{
    Window w1=new Window(1);//构造器之前调用
    House(){
        System.out.println("House()");
        w3=new Window(33);
    }
    Window w2=new Window(2);
    Window w3=new Window(3);
}
//打印结果
Window(1)
Window(2)
Window(3)
House()
Window(33)

仔细看上面的w3 , 在构造器之后定义,但仍然可以在构造器中使用,且初始化顺序是先3后33。因此证明它们全不会在调用构造器或者其他方法之前得到初始化。

  • 静态数据的初始化
    无论创建多少个对象,静态数据只占用一份存储区域。如果不对它进行初始化,那么它就获得基本类型的初值或者为null。
package init;

public class StaticInitTest {
    public static void main(String[] args) {
        new CupBoard();
        System.out.println();
        new CupBoard();
    }
    static Table table=new Table();
    static CupBoard cupBoard=new CupBoard();
    /**
     * 根据输出可见,静态初始化智慧在必要时刻进行。如果不创建Table对象也不引用Table.b1或b2,那么静态的Bowl b1,b2则永远不会被创建,只有在
     * 第一个Table对象被创建(或者第一次访问静态数据)的时候,他们才会被初始化。此后,静态对象不会再被初始化。
     * 显然最先初始化的是static table , cupBoard 因此打印:
     * Bowl(1)  //static
     * Bowl(2)  //static
     * Table()  //静态变量初始化完开始进入构造函数
     * f1(1)    // static table初始化 调用完毕
     * Bowl(4)  //进入cupboard中 同样优先static变量
     * Bowl(5)
     * Bowl(3)
     * Cupboard()  //进入构造函数
     * f1(2)    //cupboard初始化 调用完毕
     * Bowl(3)  //进入main函数中 执行new Cupboard()  @注意 但是static变量已经被初始化过了 而且初始化内容是相同的 所以直接打印bowl(3)
     * Cupboard()
     * f1(2)    //main中第一行执行完毕
     */
    /**
     * Bowl(3) //开始执行main中第3行
     * Cupboard()
     * f1(2)
     */
}
class Bowl{
    Bowl(int marker){
        System.out.println("Bowl("+marker+")");
    }
    void f1(int marker){
        System.out.println("f1("+marker+")");
    }
}
class Table{
    static Bowl bowl1=new Bowl(1);
    Table(){
        System.out.println("Table()");
        bowl2.f1(1);
    }
    void f2(int marker){
        System.out.println("f2("+marker+")");
    }
    static Bowl bowl2=new Bowl(2);
}
class CupBoard{
    Bowl bowl3=new Bowl(3);
    static Bowl bowl4=new Bowl(4);
    CupBoard(){
        System.out.println("Cupboard()");
        bowl4.f1(2);
    }
    void f3(int marker){
        System.out.println("f3("+marker+")");
    }
    static Bowl bowl5=new Bowl(5);
}

这里可能存在不好理解的地方就算在static变量为什么后面不实例化了?上述例子中的静态bowl变量是属于一个类所共有的。可以根据以下示例理解:

public class StaticTTest3 {
    static People people1=new People();
    static People people2=new People();
    //这里打印两次1
    public static void main(String[] args) {

    }
}
class People{
    public People() {
        System.out.println(1);
    }
}
public class StaticTTets2 {
    static Person p1=new Person();
    static Person p2=new Person();
    public static void main(String[] args) {

    }
}
class Person{
    static STest sTest=new STest();
    public Person() {
//        sTest=new STest();//这一行注释掉 打印一个0 不注释掉 打印3个0
    }
}
class STest{
    int a;
    public STest() {
        System.out.println(a);
    }
}

初始化的顺序是先静态对象(如果他们尚未因前面的对象创建过程而被初始化),而后是非静态对象。

  • 总结一下对象创建的过程,假设有个名为Dog的类:
  1. 即时没有显示使用static关键字,构造器实际上也是静态方法。因此,首次创建Dog对象时,或者Dog的静态对象或域首次被访问,Java解释器必须查找类路径,以定位Dog.class文件
  2. 然后载入Dog.class,有关静态初始化的所有动作都会执行。因此,静态初始化只在Class对象首次加载的时候进行一次。
  3. 当用new Dog()创建对象时,首先在堆上为Dog分配足够的存储空间。
  4. 这块存储空间清零,这就自动将Dog对象中所有的类型设置为默认值或者null
  5. 执行所有出现在字段定义处的初始化动作
  6. 执行构造器。

关于数组,普通变量的初始化等,这里就不写了。详见 Java编程思想 第四版 第五章
参考资料
Java编程思想 第四版 第五章

补充:Java中基类,子类的构造器初始化关系

  1. 无参
/**
 * 继承:子类会自动获得基类中所有的域和方法
 */
public class ExtendInit {
    public static void main(String[] args) {
        Cartoon x=new Cartoon();
        /**
         * 打印结果:
         * Art constructor
         * Drawing constructor
         * Cartoon constructor
         *
         * 所以构建过程是从基类“向外扩散的”,所以基类在子类构造器可以访问他之前
         * 就已经完成了初始化。
         * 即时你不为Cartoon()创建构造器,编译器也会为你合成一个默认的构造器,该构造器将调用基类构造器
         * 这里的调用基类构造器是如果没有写构造器或者没有带参数构造器的情况下,默认是调用无参的
         */

    }
}
class Art{
    Art(){
        System.out.println("Art constructor");
    }
}
class Drawing extends Art{
    Drawing(){
        System.out.println("Drawing constructor");
    }
}
class Cartoon extends Drawing{

    public Cartoon() {
        System.out.println("Cartoon constructor");
    }
}

  1. 带参
package extend;

/**
 * 针对带参数的,如果没有默认的基类构造器,
 * 或者想调用一个带参数的基类构造器,
 * 就必须用关键字super显示的编写调用基类构造器语句
 */
public class ArgsExtendInit {
    public static void main(String[] args) {
        Chess chess=new Chess(1);
        /**
         * 打印结果
         * Game constructor
         * BoardGame constructor
         * Chess constructor
         */
    }
}
class Game{
    Game(int i){
        System.out.println("Game constructor");
    }
}
class BoardGame extends Game{
    //这里必须创建一个带参的构造器
    //无参构造器是没有用的
    BoardGame(int i) {
        //显示调用
        super(i);
        System.out.println("BoardGame constructor");
    }
}
class Chess extends BoardGame{

    Chess(int i) {
        super(i);
        System.out.println("Chess constructor");
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值