一個新對象的初始化的最終步驟是去調(diào)用對象的構(gòu)造方法。
構(gòu)造方法必須滿足以下條件:
l. 方法名必須與類名稱完全相匹配;
2. 不要聲明返回類型;
3. 不能被static、final、synchronized、abstract、native修飾。
下列代碼中的構(gòu)造方法都是合法的。
public class Xyz {
public Xyz() { // No-arg constructor
// set up the object.
}
public Xyz(int x) { //int-arg constructor
// set up the object using the parameter x.
}
}
5.2.2 重載構(gòu)造方法
如果有一個類帶有幾個構(gòu)造方法,那么也許會想復(fù)制其中一個構(gòu)造方法的某些操作到另一個構(gòu)造方法中??梢酝ㄟ^使用關(guān)鍵字this作為一個方法調(diào)用來達到這個目的。不能通過方法名直接調(diào)用構(gòu)造方法。
public class Employee {
private String name;
private int salary;
public Employee(String n, int s) {
name = n;
salary = s;
}
public Employee(String n) {
this(n, 0);
}
public Employee() {
this( " Unknown " );
}
}
在第二個構(gòu)造方法中,有一個字符串參數(shù),調(diào)用this(n,0)將控制權(quán)傳遞到構(gòu)造方法的另一個版本,即采用了一個String參數(shù)和一個int參數(shù)的構(gòu)造方法。
在第三個構(gòu)造方法中,它沒有參數(shù),調(diào)用this("Unknownn")將控制權(quán)傳遞到構(gòu)造方法的另一個版本,即采用了一個String參數(shù)的構(gòu)造方法。
對于this的任何調(diào)用,如果出現(xiàn)在任何構(gòu)造方法中,必須作為第一個語句。
5.2.3 缺省構(gòu)造方法
缺省構(gòu)造方法是沒有參數(shù)的構(gòu)造方法,你可以顯式定義類的缺省構(gòu)造方法。
為了保證每個類至少有一個構(gòu)造方法,如果定義的類中一個構(gòu)造方法也沒有寫,Java將自動提供一個缺省構(gòu)造方法。該構(gòu)造方法沒有參數(shù),用public 修飾,而且方法體為空。格式如下:
public ClassName(){}
只要類中顯式定義了一個或多個構(gòu)造方法,而且所有顯式定義的構(gòu)造方法都帶參數(shù),那么將失去缺省構(gòu)造方法。
舉例如下:
public class Sample1{}
public class Sample2
{
public Sample2(int a){System.out.println("My Constructor");}
}
public class Sample3
{
public Sample3(){System.out.println("My Default Constructor");}
}
可以調(diào)用Sample1的缺省構(gòu)造方法來創(chuàng)建Sample1對象。
Sample1 s=new Sample1();
Sample2類的缺省構(gòu)造方法失效,因此以下的創(chuàng)建Sample2對象的方法編譯會出錯。
Sample2 s=new Sample2();
正確的創(chuàng)建方法是:
Sample2 s=new Sample2(0);
Sample3類定義了自己的缺省構(gòu)造方法,因此以下語句是合法的。
Sample3 s=new Sample3();
5.2.4 子類調(diào)用父類的構(gòu)造方法
父類的構(gòu)造方法不能被子類繼承。子類不能直接通過方法名調(diào)用父類的一個構(gòu)造方法,而是要通過super關(guān)鍵字調(diào)用父類的一個構(gòu)造方法,super語句必須位于子類構(gòu)造方法的第一行。
例如:
/**
* Son.java
*/
class GrandPa
{
protected GrandPa()
{
System.out.println("default GrandPa");
}
public GrandPa(String name)
{
System.out.println(name);
}
}
class Father extends GrandPa
{
protected Father()
{
System.out.println("default Father");
}
public Father(String grandPaName,String fatherName)
{
super(grandPaName);
System.out.println(fatherName);
}
}
public class Son extends Father
{
public Son()
{
System.out.println("default Son");
}
public Son(String grandPaName,String fatherName,String sonName)
{
super(grandPaName,fatherName);
System.out.println(sonName);
}
public static void main(String args[])
{
Son s1=new Son();
Son s2= new Son("My GrandPa", "My Father", "My Son");
}
}
執(zhí)行語句:
Son s2=new Son("My GrandPa", "My Father", "My Son");
將會輸出如下結(jié)果:
My GrandPa
My Father
My Son
當(dāng)子類的某個構(gòu)造方法沒有調(diào)用父類的一個構(gòu)造方法,通過這個構(gòu)造方法創(chuàng)建子類對象時,會自動先調(diào)用父類的缺省構(gòu)造方法。對于多級繼承,執(zhí)行缺省構(gòu)造方法的順序是從最上層的類開始。
對于以上的代碼執(zhí)行語句Son s1=new Son();
將會輸出如下結(jié)果:
default GrandPa
default Father
default Son
當(dāng)子類的某個構(gòu)造方法沒有調(diào)用父類的一個構(gòu)造方法,而父類又沒有提供缺省構(gòu)造方法時,將會出現(xiàn)編譯錯誤。
例如:修改一下Son.java文件。去掉GrandPa類中顯式定義的缺省構(gòu)造方法:
protected GrandPa()
{
System.out.println("default GrandPa");
}
這樣,GrandPa類中就失去了缺省構(gòu)造方法,這時,在編譯Father類的缺省構(gòu)造方法時,因為找不到GrandPa類的缺省構(gòu)造方法而出錯。
子類的某個構(gòu)造方法沒有通過super語句調(diào)用父類的構(gòu)造方法,而是通過this語句調(diào)用了另一個構(gòu)造方法,而在另一個構(gòu)造方法中通過super語句調(diào)用了父類的構(gòu)造方法,這種情況下,父類的缺省構(gòu)造方法不會被調(diào)用。
例如:
class A
{
A(int i){}
}
class B extends A
{
B(){this(0);}
B(int i){super(i);}
}
對于new B()語句將依次執(zhí)行如下方法:
1:A(int) 2:B(int) 3:B()
5.2.5 構(gòu)造方法的作用域
構(gòu)造方法只能被所在類的其他構(gòu)造方法或子類的構(gòu)造方法調(diào)用,或在用new語句創(chuàng)建類的實例時被調(diào)用。引用類型的變量不能訪問對象的構(gòu)造方法。
對于以下代碼,請讀者自己分析某些語句編譯出錯的原因。
class Base
{
public Base(int i,int j){}
public Base(int i){
this(i,0); //CORRECT
Base(i,0); //ERROR
}
}
class Sub extends Base
{
public Sub(int i,int j)
{
super(i,0); //CORRECT
}
void method1(int i,int j)
{
this(i,j); //ERROR
Sub(i,j); //ERROR
}
void method2(int i,int j)
{
super(i,j); //ERROR
}
void method3(int i,int j)
{
Base s=new Base(0,0); //CORRECT
s.Base(0,0); //ERROR
}
}
5.2.6 構(gòu)造方法不能被繼承
構(gòu)造方法可以被子類的構(gòu)造方法調(diào)用,但不能被子類繼承。
例如:
class A
{
A(int i){};
A(){}
}
class B extends A{}
class C extends B
{
C(){
super.super(0); //COMPILE ERROR
A(0); //COMPILE ERROR
}
}
由于類B并沒有繼承類A的構(gòu)造方法A(int i),所以在構(gòu)造方法C()中調(diào)用類A的構(gòu)造方法是非法的。
5.2.7 考察要點
l 識別合法的構(gòu)造方法;
2 構(gòu)造方法可以被重載,一個構(gòu)造方法可以通過this關(guān)鍵字調(diào)用另一個構(gòu)造方法,this語句必須位于構(gòu)造方法的第一行;
3 當(dāng)一個類中沒有定義任何構(gòu)造方法,Java將自動提供一個缺省構(gòu)造方法;
4 子類通過super關(guān)鍵字調(diào)用父類的一個構(gòu)造方法;
5 當(dāng)子類的某個構(gòu)造方法沒有通過super關(guān)鍵字調(diào)用父類的構(gòu)造方法,通過這個構(gòu)造方法創(chuàng)建子類對象時,會自動先調(diào)用父類的缺省構(gòu)造方法
6 構(gòu)造方法不能被static、final、synchronized、abstract、native修飾,但可以被public、private、protected修飾;
7 構(gòu)造方法不是類的成員方法;
8 構(gòu)造方法不能被繼承。
5.2.8 試題解析
1.Which line contains a constructor in this class definition?
public class Counter { // (a)
int current, step;
public Counter(int startValue, int stepValue) { // (b)
set(startValue);
setStepValue(stepValue);
}
public int get() { return current; } // (c)
public void set(int value) { current = value; } // (d)
public void setStepValue(int stepValue) { step = stepValue; } // (e)
}
a) Code marked with (a) is a constructor
b) Code marked with (b) is a constructor
c) Code marked with (c) is a constructor
d) Code marked with (d) is a constructor
e) Code marked with (e) is a Constructor
答案:b
解析:類的構(gòu)造方法必須遵循以下規(guī)則:方法名和類同名,無返回值。一個方法同時滿足這兩個條件,Java編譯器就會將其當(dāng)作類的構(gòu)造方法。一個方法如果只滿足其中的一個條件,將會編譯出錯,被認為是不合法的方法聲明。
2.Which of the following can be applied to constructors:
a) final
b) static
c) synchronized
d) native
e) None of these.
答案:e
解析:構(gòu)造方法不能被子類繼承,所以用final修飾沒有意義。構(gòu)造方法用于創(chuàng)建一個新的對象,不能作為類的靜態(tài)方法,所以用static修飾沒有意義。此外,Java語言不支持native或synchronized的構(gòu)造方法。
3.What will happen when you attempt to compile and run the following code .
public class Hope{
public static void main(String argv[]){
Hope h = new Hope();
}
protected Hope(){
for(int i =0; i <10; i ++){
System.out.println(i);
}
}
}
a) Compilation error: Constructors cannot be declared protected
b) Run time error: Constructors cannot be declared protected
c) Compilation and running with output 0 to 10
d) Compilation and running with output 0 to 9
答案:d
解析:構(gòu)造方法可以被public、protected、private修飾。
4.What will happen if you try to compile and execute B´s main() method?
class A {
int i;
A(int i) {
this.i = i * 2;
}
}
class B extends A {
public static void main(String[] args) {
B b = new B(2);
}
B(int i) {
System.out.println(i);
}
}
Select the one right answer.
a) The instance variable i is set to 4
b) The instance variable i is set to 2
c) The instance variable i is set to 0
d) This code will not compile
答案:d
解析:由于類B的構(gòu)造方法B(int i)中沒有調(diào)用父類的構(gòu)造方法,而父類中又沒有缺省構(gòu)造方法,導(dǎo)致編譯錯誤。
5.What happens when you try to compile and run the following program?
class Mystery {
String s;
public static void main(String[] args) {
Mystery m = new Mystery();
m.go();
}
void Mystery() {
s = "constructor";
}
void go() {
System.out.println(s);
}
}
Select the one right answer.
a) this code will not compile
b) this code compiles but throws an exception at runtime
c) this code runs but nothing appears in the standard output
d) this code runs and "constructor" in the standard output
e) this code runs and writes "null" in the standard output
答案:e
解析:這道題中故意設(shè)置了一個陷阱,定義了一個貌似構(gòu)造方法的Mystery()方法。由于它被聲明了返回類型,所以其實不是構(gòu)造方法。new Mystery()語句其實調(diào)用的是類Mystery的缺省構(gòu)造方法。
6.Yes or No?
Statement1) If you create a non-default derived constructor and don´t call the base class constructor ,the compiler will call the default base class constructor automatically. (Assume that the default constructor is defined for the base class).
Statement2) What about if it is not defined?
Statement3) What about the case of a default derived constructor, does the compiler
call the default base class constructor
a) Yes ,No,Yes
b) Yes,No,No
答案:a
解析:子類的構(gòu)造方法中如果沒有調(diào)用父類的構(gòu)造方法,程序運行時會自動先調(diào)用父類的缺省構(gòu)造方法(包括自動提供的缺省構(gòu)造方法或顯式定義的缺省構(gòu)造方法)。如果父類沒有缺省構(gòu)造方法,將會編譯出錯。
7.Given the following code how could you invoke the Base constructor that will print out the string "base constructor";
class Base{
Base(int i){
System.out.println("base constructor");
}
Base(){
}
}
public class Sup extends Base{
public static void main(String argv[]){
Sup s= new Sup();
//One
}
Sup()
{
//Two
}
public void derived()
{
//Three
}
}
a) On the line After //One put Base(10);
b) On the line After //One put super(10);
c) On the line After //Two put super(10);
d) On the line After //Three put super(10);
答案:c
解析:構(gòu)造方法只能被所在類的其他構(gòu)造方法或子類的構(gòu)造方法調(diào)用。所以在//one、//three處調(diào)用構(gòu)造方法都是非法的。
8.True or False.
The Class class has no public constructor.
a) True
b) False
答案:a
解析:Class類沒有公共構(gòu)造方法,所以Class類的對象不能在程序中通過構(gòu)造方法創(chuàng)建。當(dāng)Java虛擬機裝載某個類(假如類名是ClassSample)的時候,會自動調(diào)用JVM的類裝載器的defineClass()方法,這個方法會創(chuàng)建和某個類ClassSample相關(guān)的Class類的對象。Object類有個方法getClass(),它返回一個Class類的對象。所以,所有的Java對象都可以通過方法getClass()獲得它的Class類的對象。例如:
ClassSample s=new ClassSample();
System.out.println(s.getClass().getName());
將會打印ClassSample。
9.Which statements concerning the following code are true?
class A {
public A() {}
public A(int i) { this(); }
}
class B extends A {
public boolean B(String msg) { return false; }
}
class C extends B {
private C() { super(); }
public C(String msg) { this(); }
public C(int i) {}
}
a) The code will fail to compile.
b) The constructor in A that takes an int as an argument will never be called as a result of constructing an object of class B or C.
c) Class C has three constructors.
d) Objects of class B cannot be constructed.
e) At most one of the constructors of each class is called as a result of constructing an object of class C.
答案:b,c
解析:在類B中定義的方法B()有返回值,所以它不是構(gòu)造方法。類B只有一個缺省構(gòu)造方法,所以創(chuàng)建類B或類C的對象時,會先調(diào)用類B的缺省構(gòu)造方法,而類B的缺省構(gòu)造方法會先調(diào)用類C的缺省構(gòu)造方法,類A的構(gòu)造方法A(int i)永遠不會被調(diào)用。選項e是不正確的,new C("hello")語句將調(diào)用類C的兩個構(gòu)造方法C()和C(String msg)。
10.Which of the following code fragments are legal?
a) class MusicWork {
MusicWork(String s) {
System.out.println("The name of this work is" + s);
}
}
class ClassicalWork extends MusicWork {
ClassicalWork(String s, String composer) {
System.out.println("The composer is " + composer);
}
}
b) class MusicWork {
MusicWork(String s) {
System.out.println("The name of this work is" + s);
}
}
class ClassicalWork extends MusicWork {
ClassicalWork(String s, String composer) {
super(s);
System.out.println("The composer is " + composer);
}
}
c) class MusicWork {
MusicWork(String s) {
System.out.println("The name of this work is" + s);
}
}
class ClassicalWork extends MusicWork {
ClassicalWork(String s, String composer) {
System.out.println("This is a work of classical music");
System.out.println("The composer is " + composer);
super(s);
}
}
d) class MusicWork {
MusicWork() {
System.out.println("This is a work of music");
}
MusicWork(String name) {
this();
System.out.println("The name of this work is" + name);
}
}
e) class MusicWork {
MusicWork() {
System.out.println("This is a work of music");
}
MusicWork(String name) {
this();
System.out.println("The name of this work is" + name);
}
MusicWork(String composer) {
this();
System.out.println("The composer of this work is" + composer);
}
}
f) class MusicWork {
MusicWork() {
System.out.println("This is a work of music");
}
MusicWork(String name) {
System.out.println("The name of this work is" + name);
this();
}
}
答案:b,d
解析:選項a中必須為父類提供缺省構(gòu)造方法。選項c中沒有把super語句放在構(gòu)造方法的第一行。選項e中有兩個Music(String)構(gòu)造方法,不合法。選項f中沒有把this語句放在構(gòu)造方法的第一行。
11.Given the following class definition which of the following can be legally placed after the comment line//Here ?
class Base{
public Base(int i){}
}
public class MyOver extends Base{
public static void main(String arg[]){
MyOver m = new MyOver(10);
}
MyOver(int i){
super(i);
}
MyOver(String s, int i){
this(i);
//Here
}
}
a) MyOver m = new MyOver();
b) super();
c) this("Hello",10);
d) Base b = new Base(10);
答案:d
解析:類MyOver沒有缺省構(gòu)造方法,所以選項a不正確。