第2章 萬事萬物皆對象
一.所有對象都必須由你建立
1. 存儲在哪里
1. 寄存器:我們在程序中無法控制
2. stack:存放基本類型的數據和對象的reference,但對象本身不存放在stack中,而是存放在Heap中
3. Heap:存放用new產生的數據
4. Static storage:存放在對象中用static定義的靜態成員
5. Constant storage:存放常量
6. NON-RAM:硬盤等永久存儲空間
2. 特例:基本型別
基本類型數據存放在Stack中,存放的是數據。而產生對象時,只把對象的reference存放在stack中,用于指向某個對象,對象本身存放在Heap中。
3. Java中的數組
當你產生某個存儲對象的數組時,真正產生的其實是存儲reference的數組。引數組建立后,其中的每一個reference都會被自動設為null,表示"不指向任何對象"。
二.建立新的數據型別:class
1. 數據成員和函數
1.1 基本成員的缺省值
1) 當class的某個成員屬于基本型別時,即使你沒有為它提供初值,Java仍保證它有一個缺省值。
2) 只有當變量身份是"class內的成員時,Java才保證為該變量提供初值。
三.函數(Mehtods),引數(arguments),返回值(return values)
1. 引數列
當引數傳遞的是對象時,傳遞的是對象的reference。
四.注解用內嵌式文檔
Java提供兩種注解風格:/*XXXX*/、//XXXX
第3章 控制程序流程
一.使用Java運算符
1.關系運算符
1.) 當對兩個對象運用關系運算符進行比較時,比較的是object reference,如:
Integer n1 = new Integer(3);
Integer n2 = new Integer(3);
System.out.println(n1==n2);
結果為false,因為兩個object reference(n1和n2)值是不同的
2) equals()的缺省行為也是拿referenct來比較。不過Java中的class覆寫了equals方法(覆寫時一般先做instanceof的比較,然后在做期望的值的比較,如Integer期望的比較值就是其數值),如:
Integer n1 = new Integer(3);
Integer n2 = new Integer(3);
System.out.println(n1.quals(n2));//值為true
2. 邏輯運算符
1) 只能將and、or、not施用于boolean值身上。如果邏輯運算符兩邊的值存在non-boolean值,將會出錯,如:
int test1 = 1;
System.out.println((test && 1<2);//編輯出錯,test是non-boolean值
3. 位移運算符
如果所操作的位移對象是char、byte、short,位移動作發生之前,其值會先被晉升為int,運算結果會是int。
二.流程控制
1. 迭代(iteration)
1.1 逗號運算符
逗號運算符只能用于for循環的控制表達式中的initialization和step兩部分中,如:for(int i=0, j=I+1; I<5; i++, j=I*2)
1.2 break和continue
break表示退出循環;continue表示退出本次循環,回來循環起始位置。
1.3 label
label只有放在迭代語句之前才起作用,在label和迭代語句之間插入任何語句都不會起作用。
2. Switch
switch中的選擇器必須是int或char型,而且每個case段都應該break,否則不管是否匹配,將按代碼順序執行每個case段的代碼。如:
char c='c';
switch (c){
case 'a':System.out.println("a");break;//如果不break將繼續執行下面的case 'b',case 'c',default等段
case 'b':System.out.println("b");break;
default:System.out.println("default");break;
case 'c':System.out.println("c");break;
}
3. 計算細節
1) 從float或double轉為整數值,總是以完全舍棄小數的方式進行。
2) 默認情況帶小數的為double型,如:1.23應該是double型,如果你想要float型的話得寫為 1.23f 或 1.23F。
4. Math.random()的輸出范圍是[0, 1)。
第4章 初始化和清理
一.以構造函數(constructor)確保初始化的進行
如果某個class具備構造函數,Java便會在對象生成之際,使用者有能力加以操作之前,自動調用其構造函數,于是便能名確保初始化動作一定被執行。
二.函數重載(Method overloading)
1. 區分重載函數
由于只能從函數名和函數的引數列來區分兩個函數,而重載函數具有相同的函數名稱,所以每個重載函數都必須具備獨一無二的引數列。
2. Default構造函數
1) default構造函數是一種不帶任何引數的構造函數。如果你所開發的class不具任何構造函數,編譯器會自動為你生成一個default構造函數。
2) 如果你自行定義了任何一個構造函數(不論有無引數),編譯器就不會為你生成default構造函數。
3) 如果定義了一個class,如
class Bush{
Bush(int I){}
}
當想用new Bush();來產生class的實例時,會產生錯誤。因為在定義class時已定義了構造函數,所以編譯器就不會為class生成default構造函數。當我們用new Bush()來產生實例時,會嘗試調用default構造函數,但在class中沒有default構造函數,所以會出錯。如:
class Sundae
{
Sundae(int i) {}
}
public class IceCream
{
public static void main(String[] args)
{
//Sundae x = new Sundae();會編譯出錯,無構造函數Sundae()
Sundae y = new Sundae(1);
}
}
*:在定義一個class時,如果定義了自己的構造函數,最好同時定義一個default構造函數
3. 關鍵字this
1) this僅用于函數之內,能取得"喚起此一函數"的那個object reference。
2) 在構造函數中,通過this可以調用同一class中別的構造函數,如
public class Flower{
Flower (int petals){}
Flower(String ss){}
Flower(int petals, Sting ss){
//petals++;調用另一個構造函數的語句必須在最起始的位置
this(petals);
//this(ss);會產生錯誤,因為在一個構造函數中只能調用一個構造函數
}
}
**:1)在構造調用另一個構造函數,調用動作必須置于最起始的位置
2)不能在構造函數以外的任何函數內調用構造函數
3)在一個構造函數內只能調用一個構造函數
4. Static的意義
無法在static函數中調用non-static函數(反向可行)。為什么不能呢,我們看下面的例子。
例4.2.4.1
假設能在static函數中調用non-static函數,那么(a)處就將出錯。因為在沒有產生Movie class實例之前,在就不存在Movie class內的name實例,而在getName()中卻要使用name實例,顯然的錯誤的。
class Movie{
String name = "";
Movie(){}
public Movie(String name) { this.name = name; }
public static String getName() { return name; }
}
public class Test{
public static void main(String[] args){
//下面兩名先產生實例后再調用getName()沒有問題
//Movie movie1 = new Movie("movie1");
//String name1 = movie1.getName();
//下面一名將出錯
//String name2 = Movie.getname(); (a)
}
}
三.清理(cleanup):終結(finalization)與垃圾回收(garbage collection)
1)你的對象可能不會被回收
只有當程序不夠內存時,垃圾回收器才會啟動去回收不再被使用的對象的內存空間。某個對象所占用的空間可能永遠不會被釋放掉,因為你的程序可能永遠不會逼近內存用完的那一刻,而垃圾回收器完全沒有被啟動以釋放你的對象所占據的內存,那些空間便會在程序終止時才一次歸還給操作系統
3) 只有在采用原生函數(native methods)時,才使用finalize()。
四.成員初始化(member initialization)
1) 函數中的變量不會被自動初始化,如
void f(){
int i;
i++;
}
將發生編譯錯誤,因為i沒有被初始化。
2) class的數據成員會被自動初始化,具體情況如下(見P220例子):
基本型別:boolean:false、char:null(\u0000)、byte:0、short:0、int:0、
long:0 、float:0、double:0
對象(reference):null
五. 初始化次序:
1) 所有變量一定會在任何一個函數(甚至是構造函數)被調用之前完成初始化(見P233例子)
2) 在產生一個class的對象(包含static成員的class的代碼被裝載)時,首先自動按代碼排列順序初始化class中的static成員變量或static block,所有這些初始化操作只在第一次生成該對象時進行。
3) 自動初始化class中的其它成員變量或non-static block。
4) 調用構造函數。
例:
class Cup{
Cup(int marker){
System.out.println("Cup(" + marker + ")");
}
void f(int marker){
System.out.println("f(" + marker + ")");
}
}
class Cups{
static Cup c1 = new Cup(11);
static Cup c2;
Cup c3 = new Cup(33);
Cup c4;
{
c3 = new Cup(3);
c4 = new Cup(4);
}
static{
c1 = new Cup(1);
c2 = new Cup(2);
}
Cups(){
System.out.println("Cups()");
}
}
public class ExplicitStatic{
public static void main(String[] args){
System.out.println("Inside main()");
Cups.c1.f(99);
}
static Cups x = new Cups();
static Cups y = new Cups();
}
結果為:
Cup(11)
Cup(1)
Cup(2)
Cup(33)
Cup(3)
Cup(4)
Cups()
Cup(33)
Cup(3)
Cup(4)
Cups()
Inside main()
f(99)
2. Array的初始化
1) 定義數組時不能指定大小。如int[4] iArr = {0, 1, 2, 3};,由于指定了數組的大小,會編譯出錯。
2) 數組只是存放reference的數組。Array與non-array的結構圖如下:
a)對于基本型別數據,存放的是數據。如
int i = 5;
b)對于class變量,存放的是reference,這個reference指向一個存有class實例的內存空間。如
String s = "hello";
變量s存放的是一個reference,這個reference指向一個存有String實例的內存空間。
c)對于基本型別數組,存放的是reference數組,數組中的每一個reference都指向一個class實例的內存空間。如
int[] ia = {10, 11, 12};
數組ia存放的是一個reference數組,數組中的每一個reference都指向一個的int實例的內存空間。
d)對于class數組,存放的是reference數組,數組中的每一個reference都指向一個的class實例的內存空間。如
String[] sa = {"hello1", "hello2", "hello3"};
數組sa存放的是一個reference數組,數組中的每一個reference都指向一個的String實例的內存空間。
3) 任何數組都要進行初始化,使用沒有進行初始化的數組會產生運行時錯誤,如:
int[] iArr;
System.out.pritnln(iArr[0]);//產生錯誤,因為iArr還未初始化
數組初始化可在任何地方,可用以下方法來對數組進行初始化:
a) int[] iArr = {1,1,1,1};//數組的長度為{}元素的個數
b) int i = 10;
int[] iArr = new int[i];//數組的長度可為變量(這在C/C++中不行)
System.out.println(iArr[0]);//iArr[0]是一個int,自動初始化值為0
Integer[] iArr2 = new Integer[i];
System.out.println(iArr2[0]);//iArr[0]是一個reference,自動初始為null
I) 對于基本型別數組,new產生的是用于存放數據的數組;否則,產生的只是存放reference的數組。
II) new可用來初始化基本型別的數組,但不能產生non-array的 基本型別數據。
c) int[] iArr = new int[]{1,1,1,1};
Integer[] iArr2 = new Integer[]{new Integer(1), new Integer(2)};
3. 多維數組(Multidimensional)arrays
多維數組每一維的大小可以不一樣,如:
Integer[][][] a5;
a5 = new Integer[3];
for(int i=0; i<a5.length; i++)
a5[i] = new Integer[i+1];
for(int j=0; j<a5[i].length
a5[i][j] = new Integer[i+j+1];