Java 學習筆記

參考資料

Eclipse

  • 在preference裏面, 搜索keys, 能夠查看和修改快捷鍵
  • Content Assist, 自動補全
  • Format, 代碼格式化


1. Java簡介

1.1 歷史

  • Java 與 Internet 一塊兒發展

    www, 萬維網, 全部信息用連接鏈接起來html

    Java, 靜態網頁 -> 動態網頁java

  • Java的出現, 1995年, SUN, Stanford University Network, JDK 1.0
  • JDK, Java Development Kit, Java開發工具包git

    1995, JDK 1.0
    1998, JDK 1.2, Java2
    2000, JDK 1.3
    2002, JDK 1.4, assert, logging, re
    2004, JDK 1.5, 語法增長
    2006, JDK 1.6, 普遍, Compiler API(動態編譯), 腳本語言支持, WebService支持
    2010, Oracle併購SUN
    2011, JDK 1.7, 帶資源的try, 重拋異常
    2014, JDK 1.8, 大改進, lambda表達式
    
    注: 從 JDK 1.5 以後, JDK 1.x 也被稱爲 JDK x, 如 JDK 1.8 也被叫作 Java 8
  • Java的推進力

    JCP, Java Community Process, 社區程序員

    JSR, Java Specification Requests, 規範segmentfault

1.2 三大平臺

  • Java SE, J2SE, Java 2 Platform Standard Edition, 標準版, 桌面引用
  • Jave EE, J2EE, Java 2 Platform Enterprise Edition, 企業版, Web應用
  • Java ME, J2ME, Micro Edition, 微型版, 嵌入式設備


1.3 特色

  • 跨平臺, 安全穩定(不易內存溢出), 支持多線程, 豐富的類庫
  • 純的面向對象, 變量和方法都在對象裏面
  • 與 C++ 的區別api

    • 無直接指針, 自動內存管理
    • 基本數據類型長度固定
    • 不使用頭文件
    • 不支持宏
    • 無多重繼承(使用接口)
    • 沒有(放在類外面的)全局變量
    • 沒有 GOTO

1.4.1 Java的編譯與運行(IDE, 以 Eclipse 爲例)

  • 打開 Eclipse, 而後左上角 File --- New --- Java Project, 輸入工程名 javanote --- Finish
  • 在 Package Explorer 中展開 javanote --- 右鍵 src --- New --- Class, 在 Name 中輸入 Main (首字母大寫), 這以後咱們會在 Package Explorer 中看到新增的 Main.java
  • 編寫 Main.java數組

    package javanote;
    
    public class Main{
        public static void main(String args[]) {
            System.out.println("hello world");
        }
    }
  • 運行

1.4.2 Java的編譯與運行(命令行)

  • 進入新建文件夾 ./javanote, 而後新建源程序文件 Main.java, 注意文件名和 public class 後面的類名一致安全

    public class Main {
        // 注意 String[] args 不能省略
        public static void main(String[] args){
            System.out.println("hello world");
        }
    }
  • 編譯, 將會獲得 Main.class 目標文件(obj), 字節碼 bytecode, 擴展名 class .它不是實際機器的最終執行代碼bash

    # c 表明 compiler
    $ javac Main.java
    $ ls
    Main.class  Main.java
  • 運行網絡

    # 注意不是 java Main.class
    java Main

    經過 JVM 讀取並處理 class 文件, 最終轉化成 CPU 的指令. JVM for Win/Unix/ 模擬了一個操做系統/接口

1.5 三種核心機制

  • Java Virtual Machine, Java虛擬機

    源程序(.java 後綴) ---javac--- 字節碼(bytecode, .class 後綴) ---java--- 在 JVM 上運行

    JVM 規定了虛擬的CPU和內存, 包含如下內容: 寄存器集, 類文件結構, 堆棧, 垃圾收集堆, 內存區域

  • Code Security, 代碼安全性檢測
  • Garbage Collection, 垃圾回收, 自動管理內存

    Java 程序不能依賴於垃圾回收的時間或者順序

    GC 是徹底自動的, 不能被強制執行, 程序員最多隻能用 System.gc()來建議執行垃圾回收器回收內存, 可是具體的回收時間, 是不可知的。當對象的引用變量被賦值爲 null, 可能被當成垃圾

    GC 自動回收內存, 程序員不須要要沒法精確控制回收過程, 也就是說只有 new, 沒有 delete

    系統級線程會跟蹤存儲空間的分配狀況

    JVM 空閒時, 檢查和釋放那些能夠釋放的空間

1.6 Java 運行環境, JRE, Java Runtime Environment

  • 在具體運行的時候, Java須要一個運行環境, 即 JRE
  • JRE = JVM + API(Lib)
  • JRE 運行程序時的三項主要功能

    加載代碼(class loader), 校驗代碼(bytecode verifier), 執行代碼(runtime interpreter, 所以虛擬機有時候也簡單稱爲一個解釋器)

    小結: Java運行環境(JRE) 首先由虛擬機 JVM 來裝載程序, 而後調用相應的指令來執行

  • 平臺無關: 把 class 文件放到不一樣的系統, 虛擬機能夠執行, 不須要從新編譯

1.7 Java 層次結構

  • JDK = JRE + Tools (編譯工具 javac, 打包工具 jar, 調試工具 jdb, 執行器 java, 文檔生成器 javadoc)

    JRE = JVM + API

    開發程序須要 JDK, 若是隻是運行程序則 JRE 夠了

1.8 面向對象簡介

  • 類, class, 是抽象的具備共同屬性和行爲的集合

    類 = 屬性(變量, 字段, field) + 行爲(函數, 方法, method)

  • 對象, object, 對象實例, instance, 一個實體, 一塊能夠標識的存儲區域
  • 三大特徵: 封裝, 繼承, 多態

1.9 簡單的 Java 程序

  • 類型: Application(應用程序), Applet(小應用, 嵌入到HTML)


  • Application 的基本結構

    package javanote;
    
    publicc class Main{
        public static void main(String args[]){
            System.out.println("Hello");
        }
    }
    
    // 一個文件能夠有多個 class, 可是隻能有一個 public class, 且與文件同名
    // main 方法必須是 public static
    // main 方法是一個特殊的方法, 它是程序運行的入口
    // main 還可用於測試, 直接運行該類的時候會調用 main, 若是是其餘類調用這個類的方法, 則不會運行 main
    // package, 包, 文件路徑
    // import, 導入其餘的類

1.10 輸入輸出

  • 輸入 Scanner, 輸出 System.out.print()

    package javanote;
    
    import java.util.InputMismatchException;
    import java.util.Scanner;
    
    public class Main{
        public static void main(String args[]) {
            try {
                // 注意, 若是輸入的不是整數, 而是小數/字符串, 則會報錯. 所以須要try...catch...
                Scanner in = new Scanner(System.in);
                int a = in.nextInt();
                System.out.println(a);
            }
            catch (InputMismatchException e) {
                // TODO: handle exception
            }
        }
    }

1.11 基本數據類型

  • 整數型

    byte, 1 字節

    short, 2 字節

    int, 4 字節

    long, 8 字節

    Java 中沒有 無符號數

  • 實數, 浮點數

    float, 4 字節

    double, 8 字節, 浮點數默認是 double

  • 邏輯型

    boolean, 1 bit, true/false

    Java 中不能夠用 if(1)if(0), 也不能 if( a = 5 )

    boolean 不能比較大小, 不能進行算術運算

    2 + true           // error
    true + true        // error, 就算換成 boolean a, b; 而後 a + b; 仍然是error
    true > false       // error
    1 > false          // error
    true == 6;         // error
    a == b > false     // error, ==, != 優先級低, 先作 b > false, 這裏也會出錯
    ( a == b ) > false // error, 相似於 true > false 的error

    邏輯運算的 !, &&, || 只能用於 boolean, 做用到其餘類型上面會出錯, 這點也和 C 不同

  • 字符型

    char, 2 字節, 統一使用 Unicode 編碼, 跨平臺

  • 強制類型轉換

    double a = 10.3;
    int b = (int)a;
    int c = (int) (10/3.0);
    // (10/3.0)要加括號, 由於(int)是單目運算, 優先級高

1.12 對象存儲區域

  • 基本數據類型, 變量在棧. 複製變量的時候,複製的是值.
  • 非基本數據類型, 引用類型, 在堆, 變量只是引用, 相似於指針, 只能指向特定對象, 不能亂指, 複製變量的時候,複製的是引用

1.13 數組

  • 數組是一種容器, 全部元素有相同的數據類型, 一旦建立, 則不能改變大小. 數組必須用 new 來分配空間, 數組元素默認初始化.

    // 注意方括號的位置
    
    // a和b都是數組, 其中 a 未分配空間, b 分配了 4 個 int 的空間
    int[] a, b = new int[4];
    // c 是數組, d 是 int 變量
    int c[], d;
  • 數組是引用類型, 元素個數能夠用變量定義,

    // 數組是引用類型
    int[] a1 = new int[3];
    int b = 10;
    // 元素個數能夠用變量定義, 和c99相似
    int[] a2 = new int[b];
    
    // error, 數組是引用類型, 理解爲指針, 不能直接給它分配空間, 分配的空間在堆, 必須 new 一塊空間而後指向那裏
    int a3[5];
  • 若是沒有對數組進行顯式初始化, 則會隱式初始化爲 0 或 null, 比 C 安全

    // 若是沒有對數組進行顯式初始化, 則會隱式初始化爲 0 或 null
    int[] a4 = {3, 1, 2};
    int[] a5 = new int[]{3, 1, 2};
    
    // 注意, 數組元素會默認初始化, 可是基本數據類型聲明後不會默認初始化
    int a;
    a++;  // error
  • 與指針的相似之處, 數組變量只是數組的管理者, 而不是擁有者

    int[] a = new int[10];
    int[] b = a;
    b[0] = 5;
    
    // 此時 a[0] 也變成了 5
    // a == b 的結果是 true
  • 複製數組, 不能用 b = a, 而須要遍歷, 除此之外還有 a.clone()System.arraycopy(src, int srcPos, dst, int dstPos, int length) 方法

    int[] a = {1, 2, 3, 4};
    // 方法1, a.clone()
    int[] b = a.clone();
    
    int[] c = new int[4];
    // 方法2, System.arraycopy()
    System.arraycopy(a, 0, c, 0, a.length);
  • 每一個數組, 都有一個 .length 屬性, 比 C 安全

    int[] a = new int[10];
    for( int i = 0; i < a.length; i++ ) {  }
    
    // 也能夠這樣遍歷, 由於數組是個 Iterable 對象
    for( int n : a ) {  }
  • 二維數組

    int[][] a = new int[3][5];
    for( int i = 0; i < a.length; i++ ) {
        for( int j = 0; j < a[0].length; j++ ) {
            a[i][j] = i * 5 + j; 
        }
    }
    
    // 初始化
    int[][] a = {
        {1, 2, 3, 4},
        {1, 2, 3},
    }
    
    // 注意最後能夠多一個逗號

1.14 字符, char, 字符串, String

  • 字符類型 char, 能被單引號包圍, 如 'a', '+', '你', unicode16編碼, 2個字節, 在全部機器上是一致和統一的
  • 字符串, String, 第一個字母大寫, 說明是一個類, 是管理者

    String s1 = new String("123");
    String s2 = "abc";
    
    // 自動轉換
    String s3 = s1 + s2 + 12 + 24;    // "123abc1224"
    String s4 = s1 + s2 + (12 + 24);  // "123abc36"
    
    // 注意
    String s5 = null;
    System.out.println( s5 + "a" ); // nulla
  • 讀取輸入

    next --- 至關於 C++ 中的 cin
    nextLine --- 至關於 C++ 中的 getline
  • 字符串的操做. 字符串是對象, 對它的全部操做都要經過 . 這個運算符

    • 長度, s.length()

      注意要加括號 () , 是字符串的 length() 方法, 和數組的 length 屬性不同

      並且在調用 .length() 時, s 必須指向一塊字符串, 不能是未初始化區域(null)

    • 訪問字符, s.charAt(index), 注意這是隻讀操做, read-only
    • 遍歷, 注意字符串沒法使用 for(char ch : s), 由於字符串不是 Iterable 對象

      for( int i = 0; i < s.length(); i++ ){
          s.charAt(i);
      }
    • 子串, .substring()

      // [n:-1]
      s.substring(n);
      // [n, n + len)
      s.substring(n, len);
    • 內容是否相同 .equals()

      if( s.equals("Hello") ) {  }
    • 比較大小, .compareTo(), unicode 編碼相減

      s1.compareTo(s2);        // unicode 編碼相減
    • 其餘操做

      int loc = s.indexOf('a');
      s.indexOf('a', loc + 1);
      s.indexOf("abc");
      
      s.lastIndexOf('a');
      
      s.startsWith(ch);
      s.endsWith(ch);
      s.trim();            // 去掉兩端空格
      s.replace(c1, c2);
      s.toLowerCase();
      s.toUpperCase();
    • 注意, 字符串自己是不可變的. 以上操做若是返回了字符串, 那這些返回的字符串是新生成的, 而不是修改了原來的字符串.

1.15 包裹類型, Wrapper

  • 基本數據類型對應的包裹類型

    boolean --- Boolean
    byte --- Byte
    short --- Short
    char --- Character
    int --- Integer
    long --- Long
    float --- Float
    double --- Double
  • 基本數據類型 + 更多方法和字段

    int a = Integer.MAX_VALUE;        // 2^31 - 1
    boolean b = Character.isDigit('a');    // false
    char c = Character.toLowerCase('A');    // a
  • 數學類, Math

    Math.abs()
    Math.round()
    Math.random()
    Math.pow()

1.16 方法(函數)

  • 實參類型的寬度(尺寸大小)必須 <= 形參類型的寬度(大小)

    好比形參 double 實參 int 是能夠的, 可是反過來就不行

  • true 和 int 沒法轉換
  • 方法的每一次運行, 都會產生獨立的本地變量空間, 參數也是本地變量

    因爲沒有 C 的指針, 也沒有 C++ 的引用 &, 因此通常方法沒法實現 swap, 須要使用數組或者對象

1.17 命名習慣

  • 類名首字母大寫. 其他的, 首字母小寫
  • 少用下劃線
  • 隨寫隨用,而不是上方統一聲明、分開使用


1.18 修飾符

  • 訪問控制修飾符

    access control

  • 其餘修飾符

    其餘修飾符

  • static

    不屬於某個實例, 而是屬於整個類的(屬性). static 變量, 至關於該類一個全局變量, 有點像 C 中的 extern 全局變量, 是全部該類的對象實例所共享的. 這樣看來, Java 的類是一個樹的結構, 根結點中存儲着公有信息, 而對象實例是衍生出來的子結點. 也就是說, static 是一個公共的路燈, 只有一盞, 每一個人能夠去開關路燈. 可是若是你要去關掉某一戶人家裏的燈, 就要明確指明他家的門牌號

    類就至關於計網中的協議, 好比網絡層協議, 規定了每一個數據包應該有什麼樣的格式. 而對象則是一個個具體的實際的數據包.

    類變量, 至關於協議頭部的一個字段, 全部這個類的對象實例都有相同的頭部信息. 相對的, 成員變量則是數據部分.

    static 變量單獨劃分一塊存儲空間, 不與具體的對象綁定在一塊兒, 該存儲空間被類的各個對象所共享.

    static 變量值在方法區加載一次, 而 非 static 變量在建立對象時會加載不少次, 每次建立都會拷貝一份

    static 方法再內存中有專用的代碼段

  • static 的訪問

    static 方法能夠訪問 static 變量 (類變量)

    static 方法不能訪問無 static 前綴的 普通成員變量, 不能訪問實例變量, 也就是不能使用 thissuper, 調用類方法時, 能夠經過 `類名.

    普通成員函數 能夠訪問 static 變量

    能夠經過 類名.類變量 訪問類變量, 也能夠經過 對象名.類變量 訪問類變量. 同理, 調用類方法時能夠用 類名.類方法, 也能夠用 類名.類方法

    class A{
        static int var = 1;
        public static void main(String args[]){
            A.var++;            // OK, 類名.類變量
            A a = new A();
            a.var++;            // OK, 對象名.類變量
            var++;                // OK, 直接訪問
        }
    }
  • final, 不可改變的, 最終的

    類前面加上 final, 表示這個類不能被繼承, 有利於 Java 的優化

    方法前面加上 final, 則這個方法不能被子類覆蓋 Override

    字段前面加上 final, 能且只能被賦值一次, 而後就不能被改變了. 一般用 static final 表示常量

2. 面向對象, OOP

2.1 類和對象

  • 類 = 字段(成員變量) + 方法(成員函數)

    成員變量的生存期 ---> 該對象的生存期, new 開始, GC 收集

    成員變量的做用域 ---> 類的內部

  • 構造方法, 構造函數, new 一個新的對象會調用構造方法, 能夠重載 overload (參數表不一樣)

    初始化順序

    1.new
    2.先跳到構造函數(但不進入)
    3.調到父類的構造函數(若是有父類)
    4.重複3, 一直到最高的父類
    5.跳到構造函數外面的 定義初始化
        int price = 80, balance = 0;
    6.進入構造函數, 完成構造函數裏面的語句
    7.回到子類, 進行子類的定義初始化, 再完成子類的構造函數
    8.把構造出來的對象交給對象實例 vm 管理

    若是有構造方法的重載, 並且若是其中一個要調用另外一個構造方法, 那麼要藉助 this

    class Person{
        private int age, id;
        public Person(){
            this(1);// 必須放在最開始, 調用另一個構造方法
            age = 0;
        }
        public Person(int pid){
            id = pid;
        }
    }

    析構函數使用的是 finalize() 方法, 可是通常不用去定義, 交給 JVM 就行了

  • 對象是實體, 能夠像理解 C++ 的指針同樣理解對象實例

    必須 new, 纔會分配具體的空間, 每一個對象的 id 不一樣

  • 若是是用另外一個對象對其賦值, 則至關於兩我的管理同一個對象實例, 和指針同樣

    Student s1 = new Student();
    Student s2 = s1;
    s1.setAge(5);   // 5
    s1.getAge();    // 5
    s2.setAge(10);  // s2 更新 age
    s1.getAge();    // 10, s1也會修改, 由於二者指向同一塊區域
  • 安全性

    一個對象實例是內存中的一塊區域, 只能經過同類型的引用去訪問, 不能隨便拿一個指針亂指到一個實例(內存區域)

2.2 訪問控制

  • private

    是對類的限制, 而不是對對象的限制

    同一個類的內部, 該類的不一樣對象實例之間, 能夠互相訪問private.

    可是出了這個類,就沒法訪問private了。舉例以下

    class Lower{
        private int val = 0;
    
        public void test(){
            Lower a = new Lower();
            Lower b = new Lower();
            // 類的內部, 該類的不一樣對象實例之間, 能夠互相訪問private
            a.val = b.val;
        }
    }
    
    class Higher{
        public void test2(){
            Lower l1 = new Lower();
            Lower l2 = new Lower();
            // error, not visible. 超出了Lower類的內部, private字段就變成了不可見
            l1.val = l2.val;
        }
    }

    private 方法和字段不能繼承

  • public
  • default (friendly)

    既沒有前綴 public, 也沒有前綴 private, 那麼這個成員是 default

    那麼和它位於同一個包(同一個文件夾)的其餘類能夠訪問.

    注意, 不須要顯式聲明爲 default

  • protected

    同一包內的類可見

    全部子類可見

  • 繼承關係

    父類 public, 子類也必須爲 public

    父類 protected, 子類 protected, public, private

    父類 private 不可以被繼承

2.3 重載

  • 方法重載: 多個方法有着相同的名字, 這些方法的簽名 signature 不一樣 (參數不一樣, 或者參數類型不一樣), 編譯的時候可以被識別出來
  • 經過方法重載, 能夠實現多態

2.4 封裝

  • 模塊化, 把相關的屬性和方法封裝成爲一個類
  • 信息隱蔽, 隱藏類的細節(private), 用戶只能經過受保護的接口訪問某個類

    class Person{
        private int age;
        public int getAge(){
            return age;
        }
    }

2.5 繼承

  • 父類(super)和子類(sub, 也做派生類)之間若是有共同的屬性和方法, 就不用再寫第二遍了。能夠更好地抽象和分類, 提升代碼的可重用性, 提升可維護性
  • 單繼承, 一個類只能有一個直接父類, 子類自動繼承父類的狀態和行爲
  • 繼承的例子 (extends)

    class Person{
        int age;
        String name;
    }
    
    // 注意關鍵字 extends 表示繼承(擴展)
    class Student extends Person{
        String school;
    }
    
    // 若是沒有 extends xxx, 那麼默認爲 extends java.lang.Object 的子類
    // 所以全部的類都是直接或間接地繼承了 Object
  • 覆蓋

    JDK 1.5 以後引入了 @Override, 表示覆蓋 (修改) 了父類的方法.

    能夠當作註釋, 同時編譯器會幫你檢查, 若是父類中沒有相同的 方法名 + 參數表, 就會報錯

    父類方法 func() 被覆蓋後, 仍然能夠用 super.func() 調用父類的方法

  • 重載

    方法名相同, 參數表不一樣. 重載是新的方法.

  • 構造方法是不能繼承的, 由於構造方法是和類名同名的

    子類構造的過程當中須要用 super 來調用父類的構造方法

    例題, 問如下代碼的結果是什麼

    class Person {
        String name = "";
        public Person(String n) {
            name = n;
        }
    }
    class Employee extends Person {
        String empID = "";
        public Employee(String id) {
            empID = id;
        }
    }
    public class Test {
        public static void main(String args[]) {
            Employee e = new Employee("123");
            System.out.println(e.empID);
        }
    }

    結果是編譯報錯, 由於父類 Person 沒有無參數的構造函數, 解決方法

    // 方法1, 給父類 Person 增長無參數的構造函數(子類會默認調用)
    class Person {
        public Person() {}
    }
    
    // 方法2, 子類構造函數中用 super 顯式調用父類已有的構造函數
    class Employee extends Person {
        public Employee(String id) {
            super("Bob");// 注意 super 必須放在第一句
            empID = id;
        }
    }
  • 子類對象和父類對象的轉換

    Student 是一個 Person (子類對象能夠被視爲父類對象)

    Person 不必定是 Student, 具體狀況看下面的例子

    // 假設 Student extends Person
    Person p1 = new Student(); // 編譯正常, 運行正常
    Student s1 = (Student) p1; // 編譯正常, 運行正常
    
    Person p2 = new Person();
    Student s2 = (Student) p2;
    s2.xxx(); // 【編譯正常, 運行錯誤】

2.6 包

  • 與類的繼承沒有關係, 子類和父類能夠位於不一樣的包中
  • 至關於名字空間, 解決同名衝突. 同時包也相同於文件夾(存儲路徑)
  • 另外就是可訪問性, 同一個包中的各個類, 默認是能夠互相訪問的


2.7 抽象(abstract) 和接口(interface)

  • abstract, 抽象

    abstract class Person{
        // 含有抽象方法的類必須聲明爲抽象類
        // 注意抽象方法是分號;結尾, 而不是花括號
        // 也就是說抽象方法只有聲明, 沒有實現
        abstract void speak();
    
        void eat() {
            // 抽象類能夠有非抽象方法
        }
    }
    
    class Student extends Person{
        @Override
        void speak() {
            // 子類 extends 父類後
            // 要麼仍然保持 abstract 方法
            // 要麼 Override 覆蓋(這裏也能夠稱爲實現)父類的 abstract 方法
        }
    }
    
    public class Main{
        public static void main(String args[]) {
            // 錯誤, 抽象類不能被實例化 `instantiate`
            // Person p1 = new Person();
    
            // 可是抽象類能夠用於定義變量
            Person p2 = new Student();
        }
    }
  • interface, 接口, 約定某種特徵

    interface 是純抽象類, 全部方法都是 abstract, 也就是 C++ 中的純虛類

    全部的成員變量都是 public static final (至關於 const 常量)

    static 代表它屬於這個類而不是某個具體對象, final 代表它一旦初始化就不會被改變, 是一個編譯時刻已經肯定的常量

    // 注意直接用 interface 修飾, 不須要 class
    interface Flyable{
        // 接口類的全部方法都是 `public abstract`, 不須要顯式寫出
        // public abstract void fly();
    
        void fly();
    }
    
    interface Runnable{
        // 若是使用 static 修飾符, 則代表這是默認實現, 不須要每次 implements 都去實現 run
        // java8 以上的新特性, 可是這就又回到父子繼承了
        static void run() {
        }
    }
    
    // implements 多個接口用逗號分隔
    class Superman extends Person implements Flyable, Runnable{
        @Override
        public void fly() {
        }
    }
    
    class Airplane implements Flyable{
        @Override
        public void fly() {
        }
    }

    接口能夠繼承接口,可是不能繼承(extends)類

    接口不能實現接口,只有類可以實現(implements)接口. 一個類能夠實現(implements)多個接口(多繼承), 也就是說, 經過接口能夠實現不相關類的相同行爲(fly()), 而不須要考慮層次關係(``)

  • 引用類型一共有三大類: 1.類 class, 2.接口 interface, 3.數組

2.8 多態, polymorphism

  • 相同的名字表示不一樣的含義
  • case1, 編譯時多態, 靜態

    重載 Overload, 多個方法同名但參數表不一樣

    p.say();
    p.say("Hi");
  • case2, 運行時多態, 動態

    覆蓋 Override, 子類方法覆蓋父類同名方法. 動態綁定 dynamic binding, 也稱爲虛方法調用, 運行的時候根據實例對象來調用方法, 舉例以下

    class Person{
        void say(){
            System.out.println("hi");
        }
    }
    
    class Student extends Person{
        @Override
        void say(){
            System.out.println("hi, I'm a student");
        }
    }
    
    public class Main{
        static void func(Person p){
            p.say();
        }
    
        public static void main(String args[]) {
            Person p = new Person();
            Student s = new Student();
            Person ps = new Student();
            // func() 的參數表說明了咱們須要一個 Person 對象
            func(p);
            // 可是咱們也能夠把 Student 傳進去
            // 這時候不會報錯, 並且會正確調用子類的 say()
            func(s);
            // 更進一步, 若是咱們用向上造型, func() 能接受 ps, 且會正確調用子類的 say()
            func(ps);
        }
    }

    能夠被 Override 的方法均可以稱做虛方法, 虛方法不須要特殊的聲明

    static、final、private 修飾的全部方法都是虛方法

  • 向上造型 upcasting

    父類 obj = new 子類();

    子類的對象, 能夠被當成父類的對象來使用, 能夠賦值給父類的變量, 能夠傳遞給須要父類對象的函數, 若是一個容器存放的是父類對象, 那麼子類對象也能夠放進去

    // 爲何? 由於繼承了父類, 對外的接口是同樣的

    子類的對象能夠賦值給父類的變量. 注意, Java 不存在 對象對象 的賦值, 而是讓兩個管理員共同管理一個對象. 類比指針, 不是複製內容, 而是給地址.

2.9 OOP 設計思想

  • 有哪些類, 類裏面有什麼屬性和方法, 類之間的關係(繼承, 關聯), 對象之間發送消息(調用方法)

3. 容器

3.1 什麼是容器

  • 容器 container, 是放東西的東西, 數組能夠看作一種容器, 可是數組的長度一旦肯定就沒法改變, 而容器通常能夠改變其容量


  • 基本數據類型數組對象數組

    // 基本數據類型數組, 每個元素都是基本數據類型
    int[] ai = new int[32]; // 每一個元素被初始化爲0
    
    // String[] 數組, 對象數組, 每個元素只是一個管理者, 一個指針, 而不是保存實際內容
    // 元素初始化爲 null, 實際內容還不存在, 須要 for 循環去建立和賦值(指向)
    String[] as = new String[10];

3.2 順序容器 ArrayList

  • 容器類

    // import java.util.ArrayList;
    ArrayList<String> noteList = new ArrayList<String>();
    
    // 用法參考 https://www.w3schools.com/java/java_arraylist.asp
    noteList.add( Integer.toString(0) );
    noteList.add( "1" );
    noteList.get(0);
    noteList.set(0, "00");
    
    // import java.util.Collections;
    // 排序
    Collections.sort(noteList);
    
    noteList.remove(0);
    noteList.clear();
    
    // 若是要構建一個數組, 能夠調用 toArray() 方法
    int size = noteList.size();
    String[] mylist = new String[size];
    noteList.toArray(mylist);
  • 關係圖

    container1

    container2

    container3

    container4

3.3 Set

3.4 Map

  • 鍵值映射關係

    /*
    映射關係
    1        penny
    5        nickel
    10        dime
    25        quarter
    50        half-dollar
    */
    HashMap<Integer, String> dollarMap = new HashMap<Integer, String>();
    dollarMap.put(1, "penny"); // 注意其餘容器都是 add, map 是 put
    dollarMap.put(5, "nickel");
    dollarMap.put(10, "dime");
    dollarMap.put(25, "quarter");
    dollarMap.put(50, "half");
    dollarMap.replace(50, "half-dollar");
    dollarMap.containsKey(30); // false
    dollarMap.get(30); // null
    dollarMap.get(50);
相關文章
相關標籤/搜索