1.成員變數和局部變數 1.1成員變數和局部變數定義 成員變數指的是類裡面定義的變數(field),局部變數指的是在方法里定義的變數。 成員變數無須顯示初始化,系統會自動在準備階段或創建該類的實例時進行預設初始化。 與成員變數不同,局部變數除了形參之外,都必須顯示初始化。 命名規則: 一個類里不能定 ...
1.成員變數和局部變數
1.1成員變數和局部變數定義
成員變數指的是類裡面定義的變數(field),局部變數指的是在方法里定義的變數。
成員變數無須顯示初始化,系統會自動在準備階段或創建該類的實例時進行預設初始化。
與成員變數不同,局部變數除了形參之外,都必須顯示初始化。
命名規則:
- 一個類里不能定義兩個同名的成員變數,即使一個是類變數,一個是實例變數;
- 一個方法里不能定義兩個同名的方法局部變數,方法局部變數與形參也不能同名;
- 同一個方法中不同代碼塊的代碼塊局部變數可以同名;
- 如果先定義的代碼塊局部變數,後定義方法局部變數,前面定義的代碼塊局部變數與後面的方法局部變數可以同名。
- Java 允許局部變數和成員變數同名,如果方法里的局部變數和成員變數同名,局部變數會覆蓋成員變數,如果需要在這個方法里引用被覆蓋的成員變數,可使用 this. (對於實例變數)或類名(對於類變數)作為調用者來限定訪問成員變數。
1.2成員變數初始化和記憶體
當系統載入類或創建該類的實例時,系統自動為成員變數分配記憶體空間,併在分配空間後,自動為成員變數賦初值。
當程式第一次使用某個類時,系統會為類變數分配記憶體空間並初始化,這個類變數是屬於這個類的,並不屬於實例。雖然我們還是能通過實例去訪問這個類變數,但是通過任何實例訪問的類變數都是同一個值,因為實際我們還是通過實例所屬的類去訪問的,為了避免語義混淆,建議訪問類變數的時候通過類去訪問,而不是實例。
1.3局部變數
系統不會為局部變數執行初始化,這意味著定義局部變數後,直到程式為這個變數賦初值,系統才會為局部變數分配記憶體,並將處置保存到記憶體中。
局部變數不屬於任何類或實例,它總是保存在其所在方法的棧記憶體中。
2 構造器
2.1 初始化
- 當程式員調用構造器的時候,系統會先為對象分配記憶體空間,並完成預設初始化,這個對象已經產生了。接著構造器的執行體完成之後,這個對象作為構造器的返回值被返回。
- 因為構造器一般需要被其它方法調用,因而常常把構造器設置成 public 許可權。極端情況下,如設置為 protected,主要用於被其子類調用;把其設置為 private,阻止其他類創建該類的實例。
2.2 構造器重載
重載的規則和方法重載差不多。特殊的一點是,如果一個構造器的執行體完全包含另一個構造器的執行體,則可在方法 B 中調用 方法 A。為避免調用時重覆創建對象,需要使用 this 關鍵字,如下麵代碼:
1 public class Apple{ 2 public String name; 3 public String color; 4 public double weight; 5 public Apple(){} //無參數構造器 6 //兩個參數構造器 7 public Apple(String name , String color){ 8 this.name = name; 9 this .color = color; 10 } 11 //三個參數構造器 12 public Apple(String name , String color , double weight){ 13 //通過另一個重載的構造器初始化代碼,調用兩個參數的構造器 14 this(name , color); 15 this.weight = weight; 16 } 17 }
PS:使用this調用另一個構造器只能在構造器中使用,而且必須作為構造器執行體的第一條語句。
3 隱藏和封裝
3.1理解封裝
封裝就是將對象的狀態信息隱藏在對象內部,不允許外部程式直接訪問對象內部信息,而是通過該類所提供的方法來實現對內部信息的操作和訪問。
良好的封裝可以實現一下目的:
- 隱藏類的實現細節;
- 讓使用者只能通過事先預定的方法來訪問數據,從而可以在該方法裡加入邏輯控制,限制對成員變數的不合理訪問;
- 可進行數據檢查,從而有利於保證對象信息的完整性;
- 便於修改,提高代碼的可維護性;
為了實現良好的封裝,需要從兩個方面考慮:
- 將對象的成員變數和實現細節隱藏起來,不允許外部直接訪問和操作;
- 把方法暴露出來,讓方法來控制對這些成員變數進行安全的訪問和操作。
3.2使用訪問控制符
Java 的 4 個訪問控制級別順序由小到大:
private -> default(不加控制符) -> protected -> public
詳細說明:
- private(當前類訪問許可權):如果類里某一成員(變數、方法、構造器)使用 private 修飾,則這個成員只能在當前類的內部訪問。顯然這個控制符用於修飾成員變數最合適,可以把成員變數隱藏在類的內部。
- default(包訪問許可權):default 訪問控制的成員或外部類可以被相同包下的其他類訪問
- protected(子類訪問許可權):被 protected 控制符修飾的成員,既可以被同一個包中的其它類訪問,也可以被不同包中的子類訪問。通常情況下,如果用 protected 修飾一個方法,通常是希望其子類來重寫這個方法。
- public (公共訪問許可權):最寬鬆的訪問級別,被 public 修飾的成員,可以被所有類訪問,不管訪問的類是否在一個包內,是否具有父子繼承關係。
對於外部類而言,只能用 public 和預設,因為外部類沒有處於任何類內部,也就沒有其所在類的內部、所在類的子類這兩個範圍。public 修飾的外部類可以被所有類使用,預設控制許可權的外部類只能被同一個包中的其他類使用。
PS:如果一個Java源文件里定義的所有類都沒有 public 修飾,則這個 Java 源文件的文件名可以是一切合法的文件名;若有 public 類,必須和 public 類名一致。
一個良好封裝的Person類:
1 public class person{ 2 //使用private修飾成員變數,將其隱藏起來 3 private String name; 4 private int age; 5 //提供方法來操作name成員變數 6 public void setName(String name){ 7 //執行合理性校驗,用戶名必須在2~6位之間 8 if (name.length() > 6 || name.length() < 2){ 9 System.out.println("您設置的人們不符合要求"); 10 return; 11 }else{ 12 this.name = name; 13 } 14 } 15 public String getName(){ 16 return this.name; 17 } 18 //提供方法來操作age成員變數 19 public void setAge(int age){ 20 //執行合理性,要求用戶年齡在0-100之間 21 if (age > 100 || age < 0){ 22 System.out.println("您設置的年齡不合法"); 23 return; 24 }else{ 25 this.age = age; 26 } 27 } 28 public int getAge(){ 29 return this.age; 30 } 31 }
PS:如果一個 Java 類的每個實例變數都被使用 private 修飾,併為每個實例變數都提供了 setter 和getter方法,那麼這就是一個符合 JavaBean 規範的類。
控制符使用的一些基本原則:
- 類里的絕大部夫成員變數應該用 private 修飾,只有一些 static修飾的、類似全局變數的成員變數,才考慮使用 public 修飾。除此之外,有些方法只用於輔助實現該類的其他方法,這些方法稱為工具方法,也應該用private修飾。
- 如果某個類要做其它類的父類,該類里包含的大部分方法可能僅希望被其子類重寫,而不想被調用,則應該使用 protected 修飾這些方法。
- 希望暴露出來給其他類自由調用的方法應該使用 public 修飾。因此類的構造器通過用 public 修飾從而允許在其他地方創建該類的實例。因為外部類通常都希望被其他類自由使用,所以大部分外部類用 public 修飾。
4 刪除排序數組中的重覆項(26題)
給定一個排序數組,你需要在原地刪除重覆出現的元素,使得每個元素只出現一次,返回移除後數組的新長度。不要使用額外的數組空間,你必須在原地修改輸入數組併在使用 O(1) 額外空間的條件下完成。
示例 1:
給定數組 nums = [1,1,2],
函數應該返回新的長度 2, 並且原數組 nums 的前兩個元素被修改為 1, 2。
你不需要考慮數組中超出新長度後面的元素。
示例 2:
給定 nums = [0,0,1,1,1,2,2,3,3,4],
函數應該返回新的長度 5, 並且原數組 nums 的前五個元素被修改為 0, 1, 2, 3, 4。
你不需要考慮數組中超出新長度後面的元素。
說明:
為什麼返回數值是整數,但輸出的答案是數組呢?
請註意,輸入數組是以“引用”方式傳遞的,這意味著在函數里修改輸入數組對於調用者是可見的。
你可以想象內部操作如下:
// nums 是以“引用”方式傳遞的。也就是說,不對實參做任何拷貝
int len = removeDuplicates(nums);
// 在函數里修改輸入數組對於調用者是可見的。
// 根據你的函數返回的長度, 它會列印出數組中該長度範圍內的所有元素。
for (int i = 0; i < len; i++) {
print(nums[i]);
}
我第一遍提交的時候發現速度排名有點慢,看了下評論才發現題目說這個數組是有序的,所以我的第一個答案是針對任何數組都可以刪重。
1 class Solution { 2 public int removeDuplicates(int[] nums) { 3 HashMap<Integer , Integer> map = new HashMap<Integer , Integer>(); 4 for (int i = 0; i < nums.length; i++){ 5 if(map.get(nums[i]) == null) 6 map.put(nums[i] , 0); 7 } 8 int count = 0; 9 for(int i = 0; i < nums.length; i++){ 10 if(map.get(nums[i]) == 0){ 11 nums[count] = nums[i]; 12 count ++; 13 map.put(nums[i] , 1); 14 } 15 } 16 for(int i = count;i < nums.length; i++){ 17 nums[i] = 0; 18 } 19 return count; 20 } 21 }
第二個答案是針對題目所說的有序數組,速度超過99.2%的人。
1 class Solution { 2 public int removeDuplicates(int[] nums) { 3 int count = 1; 4 for(int i = 1; i < nums.length; i++){ 5 if(nums[i] > nums[count-1]){ 6 nums[count] = nums[i]; 7 count ++; 8 } 9 } 10 for(int i = count;i < nums.length; i++){ 11 nums[i] = 0; 12 } 13 return count; 14 } 15 }