<style> img{border:1px solid gray;} #sp{color:#CC0000;} #imp{color:#CC0000; font-style:bold; background-color:#FFFFBB;} #imp2{color:#5500FF; font-style:bold; background-color:#FFFFBB;} #tit{color:white; font-style:bold; </style> # CH09-使用封裝與建構子(2020/06/17) ## 存取修飾子(Access Modifier) + public + 所有類別皆能存取 + protected + 同套件底下類別或所有其子類別都可以存取 + default + 同套件底下的類別皆可存取 + private + 只有該類別內部才可存取 + 存取修飾子開放等級由大至小: public → protected → default → private | 修飾子 | the Same Class | the Same Package |Subclass |Universe | | :--------: | :--------: | :--------: | :--------:| :--------: | |public|V|V|V|V| |protected|V|V|V|| |default|V|V||| |private|V|||| + 存取修飾子適用場合: + 類別只有 public 與 default 兩種修飾子可使用 + [http://net-informations.com/java/cjava/private.htm](https://) + A top-level class as private would be completely useless because nothing would have access to it. + Protected class member is just like package-private , except that it also can be accessed from subclasses. + 方法變數(區域變數)不能使用存取修飾子,因為沒有意義 + 區域變數的存取已經由本身所屬的方法決定了。 + 一般而言,設計類別時,建議實體變數(instance variable)設定為 private權限,再透過方法來存取資料 (如getter/setter方法) + 各存取修飾子可使用的時機 | 修飾子 | 類別| 實體變數 |方法 |建構子 | | :--------: | :--------: | :--------: | :--------:| :--------: | |public|V|V|V|V| |protected|<font id="sp">X</font>|V|V|V| |default|V|V|V|V| |private|<font id="sp">X</font>|V|V|V| ## 建構子(Constructor) + 宣告方式:[modifier] constructor_name ([arguments]) {…} + 建構子名稱需與類別名稱相同(大小寫也需一致) + 建構子<font id="sp">沒有回傳值</font>,加了回傳值即成為一般方法(連void都不能加) + 必須用 new 呼叫建構子而產生物件,並同時初始化物件的成員變數 (使用 new 關鍵字創建新物件時,將會分配記憶體空間給此物件) + Java 會自動給一個不帶參數的建構子,一旦宣告其他建構子,則 Java 會自動將此預設建構子移除(建議保留不帶參數的建構子以增加彈性) + 可以用<font id="sp">this(...)</font>呼叫其他建構子 ```JAVA=1 public class PenConstructor { public PenConstructor(String brandXXX, double priceXXX) { brand = brandXXX; price = priceXXX; } } ``` ## static修飾子 + 實體變數和方法若是宣告為 static,則此變數和方法即成為<font id="sp">類別變數(或稱靜態變數)</font>和<font id="sp">類別方法(或稱靜態方法)</font> + 宣告為static後,不再是此類別的物件個別擁有,而是此類別的物件<font id="sp">共同擁有</font> + 使用static變數和static方法的方式有兩種: + 經由類別的任何實體來呼叫(不好也不鼓勵使用) + 經由類別的名稱來呼叫(較好的方式) + static方法內可叫用static方法、變數。 但不能用this呼叫非static方法與變數,除非先new出物件實體。(this代表的是物件,但static方法會在物件產生前就先產生,而物件卻是new後才產生)<font id="sp">(用存在記憶體的先後順序去思考)</font> ```‵‵‵java=1 public class Count{ private int serialNumber; public static int getSerialNumber(){ //不能使用this叫用實體變數(物件尚未產生) return this.serialNumber; //除非先產生物件,並透過物件來叫用實體變數 //serialNumber會因為產生了count而存在 Count count = new Count(); return count.serialNumber; } } ``` + 如下方程式碼,會印出: SKB的售價為 = 10.0 Pentel的售價為 = 999.0 SKB在Pentel設定售價後,新價格也受影響變為 = 999.0 + brand是實體變數,所以物件各自擁有自己的brand + price是類別變數,所以同一個類別new出來的物件都會共用同一個的price + 儲存類別變數和方法的記憶體空間為global,與儲存物件的記憶體空間(heap)是分開的 ```java=1 public class PenStatic { private String brand; //實體變數 private static double price; //類別變數 public void setBrand(String brand) { this.brand = brand; } public String getBrand() { return brand; } public void setPrice(double price) { this.price = price; } public double getPrice() { return price; } } public class PenTest { public static void main(String[] args) { //第1支筆--SKB PenStatic skb = new PenStatic(); skb.setBrand("SKB"); skb.setPrice(10); System.out.println( skb.getBrand() + "的售價為 = " + skb.getPrice()); //第2隻筆--Pentel PenStatic pentel = new PenStatic(); pentel.setBrand("Pentel"); pentel.setPrice(999); System.out.println( pentel.getBrand() + "的售價為 = " + pentel.getPrice()); //查看SKB是否受影響 System.out.println( skb.getBrand() + "在Pentel設定售價後,新價格也受影響變為 = " + skb.getPrice()); } } ``` + 當類別(.class)第一次被載入JVM時,在<font id="sp">任何實體被建構之前</font>,靜態的變數與方法就會先被載入。 如下方案例,雖然沒有透過new先產生物件,但因為getTotalCount()是static method,已在Count.class第一次被載入JVM時就跟著一起載入,所以可透過類別來叫用該方法,而不用先建立物件再用物件來呼叫。 編譯→載入class→static出現→程式開始跑,誕生物件 ```java=1 public class Count { // 產品序號 private int serialNumber; public int getSerialNumber() { return serialNumber; } // 產品數量 private static int counter; public static int getTotalCount() { return counter; } // 建構式 public Count() { counter++; serialNumber = 1000 + counter; } } public class TestCount { public static void main(String[] args) { //透過類別來呼叫類別方法 System.out.println(Count.getTotalCount() + "\n"); //透過物件來呼叫類別方法(不建議,會容易搞混;用物件呼叫的方法,直覺上會是成員方法) Count count01 = new Count(); System.out.println(Count01.getTotalCount() + "\n"); } } ``` + static程式區塊 + static程式區塊裡的資料在載入類別時會先執行一次 + 如下方程式碼,會印出【起始數量:0】;0是因為counter沒有初始值,JAVA會給予type=int時的預設初始值0 ```java=1 public class Count2 { // 產品序號 private int serialNumber; public int getSerialNumber() { return serialNumber; } // 產品數量 private static int counter; //static區塊 static { counter = 0; System.out.println("起始數量:" + counter + "\n"); } public static int getTotalCount() { return counter; } // 建構式 public Count2() { counter++; serialNumber = 1000 + counter; } } public class TestCount2 { public static void main(String[] args) { Count2 count1 = new Count2(); } } ``` + 如下方程式碼,會印出: ===================== 起始數量:0 + JAVA的載入順序是<font id="sp">依照程式碼的先後順序</font>。 在31行時還不會載入Count這個class,直到32行時被呼叫才會載入JVM,並於此時將static區塊一起載入。 ```java=1 public class Count2 { // 產品序號 private int serialNumber; public int getSerialNumber() { return serialNumber; } // 產品數量 private static int counter; //static區塊 static { counter = 0; System.out.println("起始數量:" + counter + "\n"); } public static int getTotalCount() { return counter; } // 建構式 public Count2() { counter++; serialNumber = 1000 + counter; } } public class TestCount2 { public static void main(String[] args) { System.out.println("====================="); Count2 count1 = new Count2(); } } ``` <img src="https://i.imgur.com/IZxaCEo.png"><br>