1. 程式人生 > 其它 >方法、類和物件

方法、類和物件

方法、類和物件

  • 方法:類似於其它語言的函式。
  • 類:類是一個模板,它描述一類物件的行為和狀態。
  • 物件:物件是類的一個例項,有狀態和行為。

方法

方法的基礎用法

方法宣告格式:

[修飾符1  修飾符2  …]   返回值型別    方法名(形式引數列表){
    Java語句;… … …
 }

方法的呼叫:

物件名.方法名(實參列表)

方法的命名:

  • 方法的名字的第一個單詞應以小寫字母作為開頭,後面的單詞則用大寫字母開頭寫,不使用連線符。
  • 下劃線可能出現在 JUnit 測試方法名稱中用以分隔名稱的邏輯元件。

方法的過載

方法的過載是指一個類中可以定義多個方法名相同,但引數不同的方法。 呼叫時,會根據不同的引數自動匹配對應的方法。 構成方法過載的條件:

  • 形參型別、形參個數、形參順序不同
  • 只有返回值不同不構成方法的過載
  • 只有形參的名稱不同,不構成方法的過載

引數傳值機制

Java方法中所有引數都是“值傳遞”,也就是“傳遞的是值的副本”。 也就是說,我們得到的是“原引數的影印件,而不是原件”。因此,影印件改變不會影響原件。

  • 基本資料型別引數的傳值:傳遞的是值的副本。 副本改變不會影響原件。
  • 引用型別引數的傳值:引用型別指的是“物件的地址”。因此,副本和原引數都指向了同一個“地址”,改變“副本指向地址物件的值,也意味著原引數指向物件的值也發生了改變”。

類的定義方法

// 每一個原始檔必須有且只有一個public class,並且類名和檔名保持一致!
public class Car { 
}
class Tyre { // 一個Java檔案可以同時定義多個class
}
class Engine {
}
class Seat {
}

一個類可以包含以下型別變數:

  • 區域性變數:在方法、構造方法或者語句塊中定義的變數被稱為區域性變數。變數宣告和初始化都是在方法中,方法結束後,變數就會自動銷燬。
  • 成員變數:(屬性,field)成員變數是定義在類中,方法體之外的變數。這種變數在建立物件的時候例項化。成員變數可以被類中方法、構造方法和特定類的語句塊訪問。
  • 類變數:類變數也宣告在類中,方法體之外,但必須宣告為 static 型別。

一個類可以擁有多個方法。

構造方法

構造器也叫構造方法(constructor),用於物件的初始化。

  • 構造器是一個建立物件時被自動呼叫的特殊方法,目的是物件的初始化。
  • 構造器的名稱應與類的名稱一致。Java通過new關鍵字來呼叫構造器,從而返回該類的例項,是一種特殊的方法。
  • 每個類都有構造方法。如果沒有顯式地為類定義構造方法,Java 編譯器將會為該類提供一個預設構造方法。
  • 構造器雖然有返回值,但是不能定義返回值型別(返回值的型別肯定是本類),不能在構造器裡使用return返回某個值。
  • 構造方法也可以像普通類一樣過載。
public class Puppy{
    public Puppy(){
    }
    public Puppy(String name){
        // 這個構造器僅有一個引數:name
    }
}

static關鍵字

在類中,用static宣告的成員變數為靜態成員變數,也稱為類變數。 類變數的生命週期和類相同,在整個應用程式執行期間都有效。它有如下特點:

  • 為該類的公用變數,屬於類,被該類的所有例項共享,在類被載入時被顯式初始化。
  • 對於該類的所有物件來說,static成員變數只有一份,被該類的所有物件共享。
  • 一般用“類名.類屬性/方法”來呼叫。(也可以通過物件引用或類名(不需要例項化)訪問靜態成員。)
  • 在static方法中不可直接訪問非static的成員。
  • static修飾的成員變數和方法,從屬於類。普通變數和方法從屬於物件的。

物件

建立物件

物件是根據類建立的。在Java中,使用關鍵字 new 來建立一個新的物件。建立物件需要以下三步:

  1. 宣告:宣告一個物件,包括物件名稱和物件型別。
  2. 例項化:使用關鍵字 new 來建立一個物件。
  3. 初始化:使用 new 建立物件時,會呼叫構造方法初始化物件。

示例:

public class Puppy{
   public Puppy(String name){
      //這個構造器僅有一個引數:name
      System.out.println("小狗的名字是 : " + name ); 
   }
   public static void main(String[] args){
      // 下面的語句將建立一個Puppy物件
      Puppy myPuppy = new Puppy( "tommy" );
   }
}//小狗的名字是 : tommy

訪問例項變數和方法

//例項化物件
Object referenceVariable = new Constructor();
//訪問類中的變數
referenceVariable.variableName;
//訪問類中的方法
referenceVariable.methodName();

this關鍵字

this的本質就是建立好的物件的地址, 由於在構造方法呼叫前,物件已經建立。因此,在構造方法中也可以使用this代表“當前物件”。

this最常的用法:

  • 在程式中產生二義性之處,應使用this來指明當前物件。普通方法中,this總是指向呼叫該方法的物件。構造方法中,this總是指向正要初始化的物件。
  • 使用this關鍵字呼叫過載的構造方法,避免相同的初始化程式碼。但只能在構造方法中用,並且必須位於構造方法的第一句。
  • this不能用於static方法中。

原始檔宣告規則

  • 一個原始檔中只能有一個 public 類
  • 一個原始檔可以有多個非 public 類
  • 原始檔的名稱應該和 public 類的類名保持一致。例如:原始檔中 public 類的類名是 Employee,那麼原始檔應該命名為Employee.java。
  • 如果一個類定義在某個包中,那麼 package 語句應該在原始檔的首行。
  • 如果原始檔包含 import 語句,那麼應該放在 package 語句和類定義之間。如果沒有 package 語句,那麼 import 語句應該在原始檔中最前面。
  • import 語句和 package 語句對原始檔中定義的所有類都有效。在同一原始檔中,不能給不同的類不同的包宣告。

面向物件的記憶體分析

Java虛擬機器的記憶體可以分為三個區域:

  • 棧stack
    • 棧描述的是方法執行的記憶體模型。每個方法被呼叫都會建立一個棧幀(儲存區域性變數、運算元、方法出口等)。
    • JVM為每個執行緒建立一個棧,用於存放該執行緒執行方法的資訊(實際引數、區域性變數等)。
    • 棧屬於執行緒私有,不能實現執行緒間的共享。
    • 棧的儲存特性是“先進後出,後進先出”。
    • 棧是由系統自動分配,速度快,棧是一個連續的記憶體空間。
  • 堆heap
    • 堆用於儲存建立好的物件和陣列(陣列也是物件)。
    • JVM只有一個堆,被所有執行緒共享。
    • 堆是一個不連續的記憶體空間,分配靈活,速度慢。
  • 方法區method area(又叫靜態區)
    • JVM只有一個方法區,被所有執行緒共享。
    • 方法區實際也是堆,只是用於儲存類、常量相關的資訊。
    • 用來存放程式中永遠是不變或唯一的內容。(類資訊、靜態變數、字串常量等)

內部類

一般情況,我們把類定義成獨立的單元。有些情況下,我們把一個類放在另一個類的內部定義,稱為內部類(innerclasses)。

內部類可以使用public、default、protected 、private以及static修飾。而外部頂級類(我們以前接觸的類)只能使用public和default修飾。

內部類只是一個編譯時概念,一旦我們編譯成功,就會成為完全不同的兩個類。對於一個名為Outer的外部類和其內部定義的名為Inner的內部類。編譯完成後會出現Outer.class和Outer$Inner.class兩個類的位元組碼檔案。所以內部類是相對獨立的一種存在,其成員變數/方法名可以和外部類的相同。

內部類的分類

  • 成員內部類

    • 非靜態內部類

      可以使用private、default、protected、public任意進行修飾。 類檔案:外部類$內部類.class

      • 非靜態內部類必須寄存在一個外部類物件裡。因此,如果有一個非靜態內部類物件那麼一定存在對應的外部類物件。非靜態內部類物件單獨屬於外部類的某個物件。

      • 非靜態內部類可以直接訪問外部類的成員,但是外部類不能直接訪問非靜態內部類成員。

      • 非靜態內部類不能有靜態方法、靜態屬性和靜態初始化塊。

      • 外部類的靜態方法、靜態程式碼塊不能訪問非靜態內部類,包括不能使用非靜態內部類定義變數、建立例項。

      • 成員變數訪問要點:

        • 內部類裡方法的區域性變數:變數名。
        • 內部類屬性:this.變數名。
        • 外部類屬性:外部類名.this.變數名。
      • 內部類的訪問:

        • 外部類中定義內部類:

          new Inner()
          
        • 外部類以外的地方使用非靜態內部類:

          Outer.Inner  varname = new Outer().new Inner()
          
    • 靜態內部類

      • 當一個靜態內部類物件存在,並不一定存在對應的外部物件。 因此,靜態內部類的例項方法不能直接訪問外部類的例項方法。
      • 靜態內部類看做外部類的一個靜態成員。 因此,外部類的方法中可以通過:“靜態內部類.名字”的方式訪問靜態內部類的靜態成員,通過 new 靜態內部類()訪問靜態內部類的例項。
  • 匿名內部類:

    • 適合那種只需要使用一次的類。比如:鍵盤監聽操作等等。

    • 匿名內部類沒有訪問修飾符。

    • 匿名內部類沒有構造方法。因為它連名字都沒有那又何來構造方法呢。

      new  父類構造器(實參類表) \實現介面 () {
                 //匿名內部類類體!
      }
      
    • 匿名內部類的使用

      this.addWindowListener(new WindowAdapter(){
              @Override
              public void windowClosing(WindowEvent e) {
                  System.exit(0);
              }
          }
      );
      this.addKeyListener(new KeyAdapter(){
              @Override
              public void keyPressed(KeyEvent e) {
                  myTank.keyPressed(e);
              }      
              @Override
              public void keyReleased(KeyEvent e) {
                  myTank.keyReleased(e);
              }
          }
      );
      
  • 區域性內部類

    • 作用域只限於本方法

    • 對於比較複雜的情況,需要建立一個非公用的類輔助,此即為區域性內部類

    • 示例

      public class Test2 {
          public void show() {
              //作用域僅限於該方法
              class Inner {
                  public void fun() {
                      System.out.println("helloworld");
                  }
              }
              new Inner().fun();
          }
          public static void main(String[] args) {
              new Test2().show();
          }
      }
      

Lambda表示式

Lambda表示式相當於對匿名內部類又一次簡化,但僅適用於只有一個待實現方法的情況。

示例:

new Thread(()->{//只有run()方法待實現,故可以使用
    for(int i=0;i<20;i++){
        System.out.println("helloworld");
    }
}).start();