关于JAVA中初始化及默认值的细节问题

在JAVA中,若某个主数据类型属于一个类成员,那么即使不显示初始化,也可以获得一个默认值。

如下表格:

主类型 默认值
Boolean false
Char '\u0000' (null)
byte (byte) 0
short (short) 0
int  0
long 0L
float 0.0f
double 0.0d

一旦将变量作为类成员使用,就要注意由Java分配的默认值,这样做的目的是为了保证主类型成员变量得到初始化,有效遏制编程错误。但是在“局部”变量中,则并不会按照上表所示进行分配默认值。例如:

import java.util.*;
public class Project1 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		class Dateonly{
			int x;
			float a;
			void func(){
				int x;  //此处编译报错
				System.out.println(x);
			}
		}
		Dateonly d = new Dateonly();
		d.func();
	}

}
对于func函数中的“局部”变量x来说,此时x会得到一些随机值,不会自动初始化为0,我们必须在正式使用x前分配一个适当的值,如果忘记,编译器会报错,所以,在func函数中的x必须初始化。

对于主数据类型来说,类Dateonly中的x和a均会初始化为上表所示内容。

在C++中并不会得到编译器发出的警告,但JAVA中明确了这一错误。
在一个类中,初始化的顺序由变量在类中的顺序有关,即使变量定义分布在方法定义的中间,或者说在构建器内部或之间或外部或结尾,编译器都会在调用任何方法及构建器之前去初始化一遍每个变量。接下来看一段代码:

import java.util.*;
class Tag{
	Tag(int marker){
		System.out.println("Tag("+marker+")");
	}
}
class Card{
	Tag t1 = new Tag(1);
	Card(){
		System.out.println("Card()");
		t3 = new Tag(33);
	}
	Tag t2 = new Tag(2);
	void f(){
		System.out.println("f()");
	}
	Tag t3 = new Tag(3);
}
public class Project9 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Card t =new Card();
		t.f();
	}

}
程序运行结果为:

关于JAVA中初始化及默认值的细节问题_第1张图片
根据结果可以看到,主函数在未调用构建器之前,变量仍会进行一次编译器默认的初始化,进行创建Card对象t时首先对变量进行一次初始化,再进行对象的创建,其中t3句柄初始化了两次,从表面上看这样的初始化效率低下,但却能够保证足够的安全性。

那么对于static静态数据的初始化问题来说,同样的事情仍然会发生,若是主数据类型,则初始化一个上述表格值;若指向的是一个对象的句柄,一般会得到“NULL”。若想在定义的同时进行初始化操作,由于static只有一个存储区域,无论创建多少个对象,都会遇到何时对其初始化的问题。下面给出一段代码:

import java.util.*;
class Bowl{
	Bowl(int marker){
		System.out.println("Bowl("+marker+")");
	}
	void f(int marker){
		System.out.println("f("+marker+")");
	}
}
class Table{
	static Bowl b1 = new Bowl(1);
	Table(){
		System.out.println("Table()");
		b2.f(1);
	}
	void f2(int marker){
		System.out.println("f2("+marker+")");
	}
	static Bowl b2 = new Bowl(2);
}
class Cupboard{
	Bowl b3 = new Bowl(3);
	static Bowl b4 = new Bowl(4);
	Cupboard(){
		System.out.println("Cupboard()");
		b4.f(2);
	}
	void f3(int marker){
		System.out.println("f3("+marker+")");
	}
	static Bowl b5 = new Bowl(5);
}
public class Project10 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		System.out.println("Creating new Cupboard() in main");
		new Cupboard();
		t2.f2(1);
		t3.f3(1);
	}
	static Table t2 = new Table();
	static Cupboard t3 = new Cupboard();
}
此程序运行结果为:

关于JAVA中初始化及默认值的细节问题_第2张图片
可以看到的是,在主函数运行时期,首先进行static的初始化操作,创建Table对象t2时,先对Table中的两个static变量进行初始化,之后调用Table的构建器创建t2,值得注意的是在Cupboard对象t3创建过程中,先创建了一个非static的Bowl b3。static的初始化只有在必要的时候才会进行,如果不创建一个Table对象,而且永远不引用Table.b1或Table.b2,那么static Bowl b1和b2永远不会创建,然而在创建了第一个Table对象之后(或者发生第一次static访问的时候),它们才会创建,初始化的顺序是static对象,接着是非static对象。(这一点在运行结果中可以看出)

有的时候,static初始化工作还可以放到一个特殊的“static构建从句”中,也可以叫做静态块,类似于:

class Project{

    static int i;

    static {

        i=4;

    }

}

与其他static一样,此段代码只执行一次,下面给出一段程序:

import java.util.*;
class Cup{
	Cup(int marker){
		System.out.println("Cup("+marker+")");
	}
	void f(int marker){
		System.out.println("f("+marker+")");
	}
}
class Cups{
	static Cup c1;
	static Cup c2;
	static{
		c1 = new Cup(1);
		c2 = new Cup(2);
	}
	Cups(){
		System.out.println("Cups()");
	}
}
public class ExplicitStatic {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		System.out.println("Inside main()");
		Cups.c1.f(99);            //1
	}
	//static Cups x = new Cups();     //2
	//static Cups y = new Cups();     //2

}
此段程序运行结果为:


若注释掉1,不注释掉2,程序运行结果为:

关于JAVA中初始化及默认值的细节问题_第3张图片

若1,2两处均注释掉,static初始化进程永不发生,程序运行结果为:


进行如上操作的目的,就是为了验证之前提到的static初始化只会在必要的时候进行。。。

那么当内含继承关系时,对基类子对象的正确初始化也至关重要,JAVA会在构建器中调用基类的构建器来进行初始化,而基类构建器具有执行基类初始化所需的所有知识和能力。JAVA会在导出类的构建器中插入对基类构建器的调用,看如下代码段:

package access;
import java.util.*;
class Art{
	Art(){
		System.out.println("Art constructor");
	}
}
class Drawing extends Art{
	Drawing (){
		System.out.println("Drawing constructor");
	}
}
public class Cartoon extends Drawing{
	public Cartoon(){
		System.out.println("Cartoon constructor");
	}
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Cartoon x = new Cartoon();
		System.out.println();
		Drawing y = new Drawing();
		System.out.println();
		Art z = new Art();
	}

}
此程序运行结果为:

关于JAVA中初始化及默认值的细节问题_第4张图片
从结果可以证明构建过程是从基类“向外”扩散的,基类在导出类构建器可以访问它之前就已经完成了初始化。

上例中的每个类均有默认的构建器,编译器无需考虑传递参数的问题,但是如果没有默认的构建器,或者想调用一个带有参数的基类构建器,就必须用“super”关键字显示调用,并配备相应的参数列表,看如下代码:

package access;
class Game {
	Game(int i){
		System.out.println("Game constructor");
	}
}
class BoardGame extends Game{
	BoardGame(int i){
		super(i);
		System.out.println("BoardGame constructor");
	}
}
public class Chess extends BoardGame{
	Chess(){
		super(11);
		System.out.println("Chess constructor");
	}
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Chess x = new Chess();
	}

}
此程序运行结果为:




























你可能感兴趣的:(JAVA)