1. 程式人生 > >學習筆記之《Java核心技術卷I》---- 第四章 物件與類

學習筆記之《Java核心技術卷I》---- 第四章 物件與類

  • 識別類的簡單規則是在分析問題的過程中尋找名次,而方法對應著動詞
  • 類之間最常見的三種關係:
  1. 依賴關係:即"uses-a"的關係。如果一個類的方法操作另一個類的物件(方法引數),也就是說這個類依賴於另一個類。應該儘可能地將相互依賴的類減至最少,即讓類之間的耦合度更小
  2. 聚合關係:即"has-a"的關係。意為著一個類中包含另一個類的物件(例項域)
  3. 繼承關係:即"is-a"的關係。
  • 表達類關係的UML符號:

  • new 操作符的返回值是一個引用(物件的地址),new String()除外???
  • 區域性變數不會初始化為空(區域性變數若不初始化就試圖引用,則會報錯)。只有是類的例項域(物件變數)才會被初始化為null
private static int[] a;
public static void main(String[] args){
	System.out.println(a == null);//true
}
  • 日曆類:LocalDate
System.out.println(LocalDate.now());//2018-11-29
LocalDate ld = LocalDate.of(2018, 1, 1);//構造一個指定的日期
LocalDate ld2 = ld.plusDays(1000);//在日期ld的基礎上增加1000天
System.out.println(ld2);//2020-09-27
  • 更改器方法和訪問器方法:只訪問物件而不修改物件的方法稱為訪問器方法
  • 不要編寫返回可變引用物件的訪問器方法,因為這樣在類外部也可以完成對類內部私有例項域的改變。而應該返回該可變資料域的拷貝,應使用clone方法(該類需事先Cloneable介面並重寫clone())
public class Test {
	public static void main(String[] args){
		Employee e = new Employee();
		int[] res = e.getArray();
		System.out.println(Arrays.toString(res));//[1,2,3]
		res[0] = 4;
		System.out.println(Arrays.toString(e.getArray()));//[4,2,3]
	}
}
class Employee{
	private int[] array = {1,2,3};
	public int[] getArray() {
		return array;
	}
}
public class Test {
	public static void main(String[] args){
		Employee e = new Employee();
		int[] res = e.getArray();
		System.out.println(Arrays.toString(res));//[1,2,3]
		res[0] = 4;
		System.out.println(Arrays.toString(e.getArray()));//[1,2,3]
	}
}
class Employee{
	private int[] array = {1,2,3};
	public int[] getArray() {
	    return array.clone();//返回array的一個副本(在堆中又新建了一個與array中元素一樣的陣列並返回)
	}
}
  • final例項域必須在定義時初始化或者在構造器中初始化後才能被使用
  • 而其他型別例項域都是在定義的時候,類已經給了初始值
  • 靜態常量(static final)一般被設定為public。因為final限制了它不能被Java方法修改,所以設定為public更方便在類外使用類名.變數名直接呼叫變數
  • 一個典型的靜態常量System.out:
public final static PrintStream out = null;

看到這,為什麼null還能呼叫println方法呢?因為在System.class中有一個setOut方法,它呼叫了一個本地方法setOut0(),而本地方法是不受Java語法的限制的

public static void setOut(PrintStream out) {
    checkIO();
    setOut0(out);
}
private static native void setOut0(PrintStream out);

那麼setOut方法又是什麼時候被呼叫改的呢?我猜測是被虛擬機器內部呼叫的(這裡還是不太理解,只知道有這麼個流程)

System.out.println(System.out);//[email protected]

 將靜態常量System.out打印出來,我們發現它並不是null,由此可以肯定out已經被修改了

  • 靜態方法不能訪問例項域,只能訪問靜態域(因為靜態方法不能操作物件);例項方法既可以訪問例項域也可以訪問靜態域
  • 工廠方法:使用類的某個static final方法返回類的例項或者其子類的例項
NumberFormat cf = NumberFormat.getCurrencyInstance();
NumberFormat pf = NumberFormat.getPercentInstance();
double x = 0.1;
System.out.println(cf.format(x));//¥0.10
System.out.println(pf.format(x));//10%
  • 一個Java檔案中可以有多個類,但只能有一個public類;每一個類中都可以有一個static void main(),方便用於單元測試
  • Java中總是採用按值(物件變數傳遞的是其指向物件所在的地址)傳遞,也就是說方法得到的是所有引數值的一個拷貝,而不是直接引用。程式碼佐證如下
public class Test {
	public static void main(String[] args){
		Employee e1 = new Employee("e1");
		Employee e2 = new Employee("e2");
		Employee.swap(e1, e2);
		System.out.println(e1.name);
		System.out.println(e2.name);
	}
}
class Employee{
	public String name;
	public Employee(String n) {
		name = n;
	}
	public static void swap(Employee x,Employee y) {
		Employee tmp = x;
		x = y;
		y = tmp;
	}
}

如果是引用(x與e1就是同一個物件變數,y與e2也是同一個物件變數)的話,因此使x重新指向另一個物件也就是使e1重新指向另一個物件,y與e2同理。輸出的應該就是e2,e1

如果是拷貝的話(x與e1是兩個不同的物件變數,但都指向同一個物件e1;y與e2也是兩個不同的物件變數,也都指向同一個物件e2),使x重新指向另一個物件並不會對e1有任何影響,y與e2同理。因此輸出的應該是e1,e2

而最後結果輸出確實是e1,e2.因此可以證明Java中引數傳遞是拷貝傳遞(按值傳遞)而不是引用傳遞

以上建議畫圖理解

  • 方法簽名:方法名 + 引數型別列表
  • 類會自動對域進行初始化(無論是基本資料型別還是非基本資料型別),不會對區域性變數程序初始化(無論是基本資料型別還是非基本資料型別)
  • 僅當類沒有提供任何構造器的時候,系統才會提供一個預設的構造器
  • Java如果有final例項域,必須在類外對其完成初始化!!!
  • Java對例項域的初始化順序:static例項域 ---> 按從上至下對非static例項域初始化(如果初始化例項域時需要呼叫類的某個方法,那麼就直接呼叫)  --->呼叫類的構造器方法。程式碼佐證如下:
public class Test {
	public static void main(String[] args){
		Employee e1 = new Employee();
		System.out.println(e1.name);
	}
}
class Employee{
	public int t = 10;
	public final int b = t;
	public String name = setName();
	public static int a = 5;
	public Employee() {
		System.out.println("hello");
	}
	public String setName() {
		System.out.println(a);
		System.out.println(b);
		return "s";
	}
}

輸出結果為:
5
10
hello
s

static例項域會被最先初始化,但是static函式並不會被自動呼叫,只有當被類呼叫的時候才起作用。程式碼佐證如下:

public class Test {
	public static void main(String[] args){
		Employee e1 = new Employee();
		System.out.println(e1.name);
	}
}
class Employee{
	public int t = 10;
	public final int b = t;
	public String name = "sc";
	public static int a = 5;
	public Employee() {
		System.out.println("hello");
	}
	public static String setName() {
		System.out.println(a);
//		System.out.println(b);
		return "s";
	}
}

輸出結果:
hello
sc
  •  Java可以通過this關鍵字在構造器中呼叫另一個構造器
  • 靜態初始化塊: static{}     物件初始化塊:{}
  • 可以為任何一個類新增finalize方法。finalize方法將在垃圾回收器清除物件之前呼叫。在實際應用中,不要依賴於使用finalize方法回收任何短缺的資源,這是因為很難知道這個方法什麼時候被呼叫
  • 使用包的主要原因是確保類名的唯一性
  • 標記為public的部分可以被任意的類使用;標記為private的部分只能被定義它們的類使用。如果沒有指定public或private,這個部分(類、方法或變數)可以被同一個包中的所有方法訪問
  •