---
# System prepended metadata

title: CH09-使用封裝與建構子(2020/06/17)

---

<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|Ｖ|Ｖ|Ｖ|Ｖ|
    |protected|Ｖ|Ｖ|Ｖ||
    |default|Ｖ|Ｖ|||
    |private|Ｖ||||

+ 存取修飾子適用場合：
    + 類別只有 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|Ｖ|Ｖ|Ｖ|Ｖ|
        |protected|<font id="sp">Ｘ</font>|Ｖ|Ｖ|Ｖ|
        |default|Ｖ|Ｖ|Ｖ|Ｖ|
        |private|<font id="sp">Ｘ</font>|Ｖ|Ｖ|Ｖ|




## 建構子(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>