Chapter5 初始化(Initialization)
Chapter5 初始化(Initialization)
- 1. 構(gòu)造器(constructor)
- 2. 方法重載(method overloading)
- 3. 原型的重載(overloading with primitive type)
- 4. 默認(rèn)構(gòu)造器(Default constructor)
- 5. “this” 關(guān)鍵詞
- 5.1 返回當(dāng)前對象的reference
- 5.2 method之間傳遞reference
- 5.3 從一個構(gòu)造器調(diào)用另一個構(gòu)造器
- 6. 成員變量初始化
- 7.構(gòu)造器初始化
- 7.1 初始化順序
- 8. static數(shù)據(jù)初始化
- 8.1 明確的static初始化(explicit)
- 9. Non-static 實例初始化
- 10. 數(shù)組初始化
- 11. 變量參數(shù)列表(Variable argument list)
- 12. 枚舉類型(enumerated types)
1. 構(gòu)造器(constructor)
-
構(gòu)造器在java中的相當(dāng)于一個特殊的method,目的是為了在創(chuàng)建類的對象時,對其進行初始化。構(gòu)造器的名稱和類名必須相同,沒有返回值,可以添加參數(shù)(用來接收給定信息),即特殊的method。當(dāng)構(gòu)造器沒有參數(shù)時,即比如 Classname A = new Classname(),那么會調(diào)用默認(rèn)構(gòu)造器。默認(rèn)構(gòu)造器系統(tǒng)會在編譯時自動添加,但當(dāng)存在其他自定義構(gòu)造器時,如果想要用默認(rèn)方式方式創(chuàng)建對象,那么必須加入默認(rèn)構(gòu)造器聲明。
示例如下:
public class StringInitialization {StringInitialization(){//do something}String s;public static void main(String[] argv){StringInitialization si = new StringInitialization();System.out.println(si.s);} }Output:
null
-
因為String類s只是一個String的引用,所以默認(rèn)值是null。這里的構(gòu)造器可以省略。
2. 方法重載(method overloading)
-
程序設(shè)計過程中往往會涉及命名,好的命名可以表示某method的作用。但有的method之間可能只存在細(xì)微的差異,我們可能想用相同的名字來命名但又不至于混亂。舉個例子,在日常生活中,我們會說“去英國旅游”和“去法國旅游”,而不是說“英國去英國旅游”,“法國去法國旅游”,這顯然很怪。這里的“英國”,“法國”就相當(dāng)于重載方法所需要的參數(shù),而“去…旅游”可以理解為我們的方法名。那么,即使method名字相同,只要我們能對它們的參數(shù)進行區(qū)分,那么就不會造成混亂。(這在c中是不被允許的,因為c要求每一個函數(shù)名有一個獨有的id)
-
另一個為什么我們需要重載的原因是java中構(gòu)造器的存在。有時候?qū)τ谕粋€類的不同對象,我們想要的初始化并不相同。比如對于某一個對象,希望在創(chuàng)建它的時候取一個系統(tǒng)隨機給的名字,另一個對象希望可以在初始化的時候自定義一個名字,除了名字之外其他初始屬性都相同。那么如果我可以根據(jù)初始化時是否給定名字來自動選擇不同的構(gòu)造器,顯然是完美的。
-
總的來說,雖然是相同的方法名(包括構(gòu)造器),只要我們給定的參數(shù)不同,那么并不會造成混亂。換句話說,即使是相同的參數(shù),如果順序不同,也不會造成混亂,但這并不推薦,當(dāng)然也許特定情況下也是一種不錯的選擇。
示例如下:
public class Test1 {public int height;Test1(){System.out.println("Planting a new seedling!");height= 0;}Test1(int Height){height = Height;System.out.println("Transplant a "+height+" feet tall tree!");}void h(String s) {System.out.println(s+": This tree is "+height+" feet tall now.");}void h() {System.out.println("This tree is "+height+" feet tall now.");}public static void main(String[] argv){Test1 tree = new Test1(10);tree.h("allen");} }Output:
Transplant a 10 feet tall tree!
allen: This tree is 10 feet tall now.
3. 原型的重載(overloading with primitive type)
-
Primitive type在Chapter3中說過存在promotion現(xiàn)象(即向上造型,upcasting),這和重載可以互相結(jié)合。需要注意的是當(dāng)向上造型發(fā)生時會自動匹配與其最接近的參數(shù)類型,但向下造型則必須指定(即強制類型轉(zhuǎn)換)。但char類在沒有char類型的情況下會首先匹配int型,常數(shù)型也是如此。
示例如下:
public class Test1 {void f1(char x) {System.out.print("f1(char) ");}void f1(byte x) {System.out.print("f1(byte) ");}void f1(short x) {System.out.print("f1(short) ");}void f1(int x) {System.out.print("f1(int) ");}void f1(long x) {System.out.print("f1(long) ");}void f1(float x) {System.out.print("f1(float) ");}void f1(double x) {System.out.print("f1(double) ");}void f2(byte x) {System.out.print("f2(byte) ");}void f2(short x) {System.out.print("f2(short) ");}void f2(int x) {System.out.print("f2(int) ");}void f2(long x) {System.out.print("f2(long) ");}void f2(float x) {System.out.print("f2(float) ");}void f2(double x) {System.out.print("f2(double) ");}void f3(short x) {System.out.print("f3(short) ");}void f3(int x) {System.out.print("f3(int) ");}void f3(long x) {System.out.print("f3(long) ");}void f3(float x) {System.out.print("f3(float) ");}void f3(double x) {System.out.print("f3(double) ");}void f4(int x) {System.out.print("f4(int) ");}void f4(long x) {System.out.print("f4(long) ");}void f4(float x) {System.out.print("f4(float) ");}void f4(double x) {System.out.print("f4(double) ");}void f5(long x) {System.out.print("f5(long) ");}void f5(float x) {System.out.print("f5(float) ");}void f5(double x) {System.out.print("f5(double) ");}void f6(float x) {System.out.print("f6(float) ");}void f6(double x) {System.out.print("f6(double) ");}void f7(float x) {System.out.println("f7(float) ");}void testConstVal() {System.out.print("5:");f1(5);f2(5);f3(5);f4(5);f5(5);f6(5);f7(5);}void testChar() {System.out.print("char:");f1('x');f2('x');f3('x');f4('x');f5('x');f6('x');f7('x');}void testByte() {byte b = 5;System.out.print("byte:");f1(b);f2(b);f3(b);f4(b);f5(b);f6(b);f7(b);}void testShort() {short b = 5;System.out.print("short:");f1(b);f2(b);f3(b);f4(b);f5(b);f6(b);f7(b);}void testInt() {int b =5;System.out.print("int:");f1(b);f2(b);f3(b);f4(b);f5(b);f6(b);f7(b);}void testFloat() {float b = 5;System.out.print("float:");f1(b);f2(b);f3(b);f4(b);f5(b);f6(b);f7(b);}void testLong() {long b =5;System.out.print("long:");f1(b);f2(b);f3(b);f4(b);f5(b);f6(b);f7(b);}void testDouble() {double b=5;System.out.print("double:");f1(b);f2(b);f3(b);f4(b);f5(b);f6(b);f7((float)b);}public static void main(String[] argv) {Test1 ts = new Test1();ts.testConstVal();ts.testChar();ts.testByte();ts.testShort();ts.testInt();ts.testFloat();ts.testLong();ts.testDouble();} }Output:
5:f1(int) f2(int) f3(int) f4(int) f5(long) f6(float) f7(float)
char:f1(char) f2(int) f3(int) f4(int) f5(long) f6(float) f7(float)
byte:f1(byte) f2(byte) f3(short) f4(int) f5(long) f6(float) f7(float)
short:f1(short) f2(short) f3(short) f4(int) f5(long) f6(float) f7(float)
int:f1(int) f2(int) f3(int) f4(int) f5(long) f6(float) f7(float)
float:f1(float) f2(float) f3(float) f4(float) f5(float) f6(float) f7(float)
long:f1(long) f2(long) f3(long) f4(long) f5(long) f6(float) f7(float)
double:f1(double) f2(double) f3(double) f4(double) f5(double) f6(double) f7(float)
4. 默認(rèn)構(gòu)造器(Default constructor)
-
之前已經(jīng)提到過,如果把構(gòu)造器看成一個特殊的method,而每創(chuàng)建一個對象,都會調(diào)用構(gòu)造器。如果在類的定義里面沒有自定義構(gòu)造器,那么java會默認(rèn)幫我們添加一個默認(rèn)的,沒有參數(shù)的構(gòu)造器。如果想要在創(chuàng)建對象時傳遞參數(shù),那么我們需要在類中自定義相應(yīng)參數(shù)的構(gòu)造器。此時同時需要自定義原先的默認(rèn)構(gòu)造器(如果不創(chuàng)建不含參數(shù)的對象則不定義也沒關(guān)系),因為java認(rèn)為我們已經(jīng)知道如何使用構(gòu)造器,它不會再幫我們自動添加默認(rèn)的構(gòu)造器。
示例如下:
class Bird{Bird(int i){}Bird(double x){} } public class DefaultConstructorTest1 {public static void main(String[] argv){Bird bird1 = new Bird(3);Bird bird2 = new Bird(3.0);//! Bird bird3 = new Bird(); this would not be allowed} }
5. “this” 關(guān)鍵詞
- this 是對象的引用,是指調(diào)用當(dāng)前method的對象的reference,也就是說this只能出現(xiàn)在non-static method中(因為首先需要有對象,this才有意義)。this的主要用法大致可以分為以下三種:
5.1 返回當(dāng)前對象的reference
-
調(diào)用一個method之后返回當(dāng)前對象的引用,可以一定程度上簡化代碼。
示例如下:
class Bird{int num=0;Bird numAdd() {num++;System.out.println(num);return this;}} public class Test1 {public static void main(String[] argv){Bird bird1 = new Bird();bird1.numAdd().numAdd().numAdd();} }Output:
1
2
3
5.2 method之間傳遞reference
-
顧名思義,就是指在不同的method 之間傳遞reference,自然也可以把一個對象傳遞給另一個對象的method,相當(dāng)于不同對象之間的信息傳遞。
示例如下:
class Person{public void eat(Apple apple) {Apple peeled = apple.getPeel();System.out.println("my apple is yummy!");} }class Apple{public Apple getPeel(){Peeler peeler = new Peeler();return peeler.peell(this);} }class Peeler{public static Apple peel(Apple apple) {// do something to remove the peelreturn apple; //peeled}public Apple peell(Apple apple) {// do something to remove the peelreturn apple; //peeled} }public class Test1 {public static void main(String[] argv){Person p1 = new Person();p1.eat(new Apple());} }Output:
my apple is yummy!
5.3 從一個構(gòu)造器調(diào)用另一個構(gòu)造器
-
當(dāng)把構(gòu)造器看成特殊的method之后,其實這就是傳遞當(dāng)前對象自己的reference。同時,如果method中的變量名和類field名相同的話,也可以用this.x = x的形式來區(qū)分field和局部變量。同時需要注意,構(gòu)造器調(diào)用構(gòu)造器的形式必須出現(xiàn)在構(gòu)造器內(nèi)部第一行,且每個構(gòu)造器只能調(diào)用一次其它構(gòu)造器。
示例如下:
class Bird{String name;int weight;Bird(){}Bird(String name){this(10);this.name = name;}Bird(int weight){this.weight = weight;}void printName() {System.out.println(name);} } public class Test1 {public static void main(String[] argv){Bird bird1 = new Bird("angry");bird1.printName();} }Output:
angry -
再回顧下static關(guān)鍵字,當(dāng)method有static定義時,顯然此時內(nèi)部不能出現(xiàn)this,即this只能出現(xiàn)在non-static方法中,因為static是獨立于對象的存在,而this是對象的引用。static方法從某種程度上可以看作是全局方法,它只能調(diào)用同樣是static的method或者field。顯然,如果static方法過多,那么OOP的特點相應(yīng)減弱,因此過多的static程序可能可以進行優(yōu)化。
6. 成員變量初始化
-
類中的field變量如果是primitive type,那么即使沒有給定確定值,出于安全性考慮,java會自動保證每一個field有初始化的值。如果field變量是沒有實例化的對象引用的話,那么會自動給該field賦值null(如之前是String s),即此時的reference為null。需要注意,這里java會默認(rèn)賦初始值的僅僅是field,不包括method成員變量,也就是說在method中的變量必須給定初始化的值,否則會報錯。
示例如下:
public class Test1 {boolean bool;byte b;char c;short s;int i;float f;long l;double d;Test1 ts;public void print() {System.out.println("boolean "+bool);System.out.println("byte "+b);System.out.println("char "+"["+c+"]");System.out.println("short "+s);System.out.println("int "+i);System.out.println("float "+f);System.out.println("long "+l);System.out.println("double "+d);System.out.println("objetc "+ts);}public static void main(String[] argv){Test1 ts = new Test1();ts.print();} }Output:
boolean false
byte 0
char []
short 0
int 0
float 0.0
long 0
double 0.0
objetc null
7.構(gòu)造器初始化
-
之前提到過,構(gòu)造器用于對象的初始化,但這里需要注意的是,構(gòu)造器的初始化在成員變量(field)的初始化之后。如下程序,i首先被初始化為0,再被構(gòu)造器更改為7。
public class Counter{ int i; Counter(){i = 7;} }
7.1 初始化順序
-
如上面的例程所示,i 首先被初始化為0,即使“int i”這一條語句在構(gòu)造器的后面才出現(xiàn)。換句話說,所有成員變量(field)的初始化都在構(gòu)造器被初始化之前。
示例如下:
class Bird{Bird(int i){System.out.println("im bird "+i); }} class Animal{int i = 2;Bird bird0 = new Bird(0);Animal() {System.out.println("im_an_animal");}Bird bird1 = new Bird(1);Bird birdi = new Bird(i);int j = 3;Bird birdj = new Bird(j); } public class Test1 {public static void main(String[] argv){Animal animal = new Animal();} }Output:
im bird 0
im bird 1
im bird 2
im bird 3
im_an_animal
8. static數(shù)據(jù)初始化
-
static變量只能出現(xiàn)在field中,而且即使多個相同類的對象被創(chuàng)建出來,static只會被初始化一次,并且static的優(yōu)先級在其它普通成員變量之前。可以理解為當(dāng)一個對象被創(chuàng)建之后,static是最先被初始化的,然后是普通field,最后是構(gòu)造器。
示例如下:
class Bird{Bird(int i){System.out.println("im bird "+i); }}class Animal{int i = 2;Bird bird0 = new Bird(0);Animal() {System.out.println("im_an_animal");}Bird bird1 = new Bird(1);Bird birdi = new Bird(i); // int j = 3;Bird birdj = new Bird(j);static int j = 3; }public class Test1 {public static void main(String[] argv){Animal animal = new Animal();} }Output:
im bird 0
im bird 1
im bird 2
im bird 3
im_an_animal
-
可以看到即使j的定義在birdj的下面,但因為這是static變量,因此在birdj之前就已經(jīng)被初始化,所以可以被調(diào)用。
8.1 明確的static初始化(explicit)
-
java中存在一種結(jié)構(gòu)叫做static block,允許我們把static變量放在一個部分進行初始化,構(gòu)成一種特殊的static分句。
示例如下:
public class Explicit{static int i;static int j;static int k;static{i = 1;j = 2;k = 3;} }
9. Non-static 實例初始化
-
實例初始化,用來對非靜態(tài)變量對象進行初始化。它的結(jié)構(gòu)和static block幾乎相同,只是沒有static,用花括號直接擴起來。同樣的,實例初始化也發(fā)生在構(gòu)造器之前。
示例如下:
class Bird{Bird(int i){System.out.println("im bird "+i); } }class Animal{Bird bird0 ;Bird bird1 ;Animal() {System.out.println("im_an_animal");}{bird0 = new Bird(0);bird1 = new Bird(1);} }public class Test1 {public static void main(String[] argv){Animal animal = new Animal();} }Output:
im bird 0
im bird 1
im_an_animal
10. 數(shù)組初始化
-
數(shù)組的初始化在C中可以寫成:“int a[]; ” 的形式,java中也允許這種形式。但更常用的形式如下:“int[] a;”,這其實比較好理解,“[]”即表示數(shù)組,整句話表示int型數(shù)組a,更符合邏輯。同樣,這里的數(shù)組名也只是一個引用,如果數(shù)組元素是對象的話,那么每一個元素也只是一個引用。
示例如下:
public class ArrayTest1 {public static void main(String[] argv){int[] a1 = {1,2,3,4,5}; // int[] a1 = {1,2,3,4,5,};int[] a2 = a1;for(int i=0;i<a2.length;i++) {a2[i]++;}for(int i:a1) {System.out.print(i+" ");}} }Output:
2 3 4 5 6
-
length是數(shù)組類中的一個成員變量,表示數(shù)組的長度。數(shù)組的初始化可以直接像上面的例程一樣用花括號括起來,末尾逗號可省略。也可以用關(guān)鍵字new進行初始化,可以指定長度但不具體賦值,系統(tǒng)會給每一個數(shù)組元素賦初始值;也可以不指定長度但后面跟具體賦值。Arrays.toString()方法是數(shù)組輸出的一種特殊形式,在java.util library中。
示例如下:
public class Test2 {public static void main(String[] argv){int[] a1 = new int[5];String[] s1 = new String[] {"one","two", "three", "four", "five"};for(int i:a1) {System.out.print(i+" ");}System.out.println("");System.out.println(Arrays.toString(s1));} }Output:
0 0 0 0 0
[one, two, three, four, five]
11. 變量參數(shù)列表(Variable argument list)
-
用來處理method中未知數(shù)量或未知類型的參數(shù)。因為java中所有對象都繼承自一個Object對象,我們可以用object數(shù)組來作為method的參數(shù)列表。需要注意的是method調(diào)用時參數(shù)也要選擇object數(shù)組類型。
public class VararsTest1 {static void printArray(Object[] args) {for(Object obj:args) {System.out.print(obj+" ");}System.out.println("");}public static void main(String[] argv){printArray(new Object[] {1,2,3,4});printArray(new Object[] {new Integer(1),new Float(1.2),new Double(2.2)});printArray(new Object[] {new String("one"),new String("two"),new String("three")});printArray(new Object[] {new A(),new A(),new A()});} } class A{}Output:
1 2 3 4
1 1.2 2.2
one two three
thinkingjava.A@7852e922 thinkingjava.A@4e25154f thinkingjava.A@70dea4e
-
由上面結(jié)果可以看到,primitive type的wrapper型和String類型在輸出時都是直接輸出內(nèi)容,但是自定義的類輸出的是類名和各個元素的地址。這是因為在String等類中含有toString method,系統(tǒng)在進行輸出時會自動調(diào)用。而Object中有一個默認(rèn)的toString方法,因此如果我們需要自定義輸出的內(nèi)容,不妨在類A中增加toString方法。
示例如下:
public class VararsTest2 {static void printArray(Object[] args) {for(Object obj:args) {System.out.print(obj+" ");}System.out.println("");}public static void main(String[] argv){printArray(new Object[] {1,2,3,4});printArray(new Object[] {new Integer(1),new Float(1.2),new Double(2.2)});printArray(new Object[] {new String("one"),new String("two"),new String("three")});printArray(new Object[] {new A(),new A(),new A()}); // printArray();} } class A{static int i;public String toString() {return "" + (++i);} }Output:
1 2 3 4
1 1.2 2.2
one two three
1 2 3
-
但上面的例子還是存在一個問題,不能傳入空參數(shù)。為了解決這個問題,java有一種特殊的方式,即把“[]”改為"…"。
示例如下:
public class VararsTest3 {static void printArray(Object... args) {for(Object obj:args) {System.out.print(obj+" ");}System.out.println("");}public static void main(String[] argv){printArray(new Object[] {new A(),new A(),new A()});printArray(); } }class A{static int i;public String toString() {return "" + (++i);} } -
但即使用“…”來作為接受無參數(shù)的調(diào)用,仍然存在問題。如果多個method中使用這種形式,那么有可能造成歧義,即系統(tǒng)無法識別我們究竟想要調(diào)用哪個method。因此在使用時需要格外注意。
示例如下:
public class Test2 {static void printArray(Character... args) {}static void printArray(Integer... args) {}static void printArray(Float... args) {}public static void main(String[] argv){//printArray(); this is not correct!} }
12. 枚舉類型(enumerated types)
-
java中可以用enum關(guān)鍵字把常數(shù)數(shù)組放在同一個集合中,增加代碼安全性和可讀性。
示例如下:
public class EnumTest1 {public enum Spiciness{NOT, MILD, MEDIUM, HOT, FLAMING}public static void main(String[] argv){Spiciness howHot = Spiciness.MILD;System.out.println(howHot);} }Output:
MILD
-
enum的另一個很有用的用法是可以作為swtich的selector
public class Burrito {Spiciness degree;Burrito(Spiciness degree){this.degree = degree;}public enum Spiciness{NOT, MILD, MEDIUM, HOT, FLAMING}public void describe() {switch(degree) {case NOT:System.out.println("This is not hot!");break;case MILD:case MEDIUM:System.out.println("a little hot!");break;case HOT:case FLAMING:default: System.out.println("This may be too hot!");}}public static void main(String[] argv){Spiciness howHot = Spiciness.MILD;new Burrito(howHot).describe();} }Output:
a little hot!
總結(jié)
以上是生活随笔為你收集整理的Chapter5 初始化(Initialization)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: PLSQL Developer 登录报错
- 下一篇: [W pthreadpool-cpp.c