《Java核心技術》閱讀筆記(一)-面向對象、內部類、Lambda基礎

基本程序設計結構

每個Java應用程序都必須有一個main方法

object.method(parameters)

註釋

  • 單行 //
  • 長篇 /* */
  • 自動生成文檔 /** */

基本數據類型(primitive type)

強類型:必須爲每個變量聲明一種類型

  1. 整型
    byte
    short
    int
    long

  2. 浮點型
    float
    double
    (正無窮大、負無窮大、NaN)

  3. char(2byte)
    Unicode字符用一個或兩個char類型表示。
    使用char描述所有Unicode字符:碼點(17個代碼級別)

  4. boolean
    整型與boolean不能轉換

變量及操作

  1. 變量
    聲明(命名)
    初始化

  2. 常量
    final:只能賦值一次
    類常量:在類的多個方法中使用(static final)

  3. 運算符
    加、減、乘、除(除零時的結果差異)、取模
    浮點運算,保持移植時的精確度,使用strictfp修飾類或方法(截斷操作,有可能溢出)

    • Math
      計算機浮點單元中的例程,得到多平臺相同結果,則使用StrictMath

    • 類型轉換
      基本類型轉換
      虛箭頭:有精度損失的轉換
      二元運算時,先將操作數轉換爲同一類型,然後再進行運算:

      1. 如果其中一個操作數爲double,則另一個轉爲double
      2. 。。。。。。。。。。float,。。。。。。。float
      3. 。。。。。。。。。。long,。。。。。。。。long
      4. 否則,兩個操作數都將轉爲int
    • 結合賦值和運算符
      += -= *= /= (當最後變量與計算結果類型不同時,觸發強制轉換)
      自增自減

    • 關係和boolean運算符

    • 位運算符
      與 或 異或 非

    • 括號和運算符規則

    • 枚舉

  4. 字符串
    subString
    format
    不可變:效率權衡-字符串常量共享

    CodePoint:一個或兩個代碼單元(char)構成

  5. 輸入輸出

    • 輸入
      Scanner:關聯標準輸入System.in、scanner.nextLine、next()、netInt()
      Console:從控制檯讀取密碼cons.readPassword(“Password:”)

    • 輸出
      System.out.printf("%8.2f", x);
      System.out.printf(“Hello, %s. Next year, you’ll be %d”, name, age);
      用於printf的轉換符
      格式說明符語法

    • 文件的IO
      讀取:Scanner in = new Scanner(Paths.get(“myfile.txt”), “UTF-8”);
      寫入:PrintWriter out = new PrintWriter('myfile.txt", “UTF-8”);
      確定啓動路徑:String dir = System.getProperty('user.dir"):

  6. 控制流程

    • 塊作用域

    • 條件語句

    • 循環
      while:爲真才執行
      dowhile: 先執行再判斷,至少執行一次
      for

    • 多重選擇switch語句
      case標籤可以爲:byte、short、int、char、枚舉變量、字符串字面量(7)注意case貫穿問題

    • 中斷控制流程

      • continue

      • 帶標籤的break——挑出多層嵌套的控制

        label:
        {
            // deal with bad situation
            // carry out normal processing
            if (condition) break label; // exits block
            ... 
        }
        
        // jumps here when the break statement executes
        

對象與類

類之間關係

  1. 依賴
  2. 關聯(知道、瞭解)
  3. 聚合(擁有)
  4. 繼承

static

  1. 靜態域
    也稱類域,屬於類,不屬於任何類的對象。而實例域,每一個對象都有其拷貝。
    初始化:聲明時指定初始化值、靜態初始化塊初始化

    // static initialization block
    static
    {
        Random generator = new Random();
        nextld = generator.nextlnt(1OOOO);
    }
    
  2. 靜態final域(靜態常量)
    示例:Math.PI、System.out(setOut native方法)

  3. 靜態方法

    特點:

    • 沒有隱式參數
    • 可以訪問靜態域
    • 可通過對象調用(易混淆)

    使用場景:

    • 只通過顯示參數完成執行的方法(不需訪問對象狀態的方法)
    • 只需訪問類靜態域的方法
    • factoryMethod構造對象
      解決構造器問題:無法命名、改變構造類型(返回對象爲子類對象)
    • main方法(可用於單元測試)

對象

對象的三個特性:行爲、狀態、標識

域(對象域或實例域)

聲明

  1. private(pulic破壞封裝性)
  2. 可選final修飾(大都應用於基本類型primitive、不可變類Immutable)

封裝的好處(提供域訪問器、域更改器方法mutator method、accessor method)

  1. 可以改變內部實現,隻影響該類的代碼
  2. 更改器方法可以執行錯誤檢查

訪問器

  1. 不要編寫返回引用可變對象的訪問器方法。若要返回,需先clone
  2. 類中的每個方法都不會改變其對象,這種類就是不可變類
  3. C++註釋:const修飾的表示訪問器方法

初始化

  1. 構造器中設置值(創建類時初始化默認值–不同於局部變量)
  2. 生命中賦值
  3. 初始化塊(initialization block,構造器執行前執行)

方法

  • 對內:訪問域
    類方法可以訪問所屬類任何對象的私有域

    public boolean equals(Employee other)
    {
        return name.equals(other.name);
    }
    
  • 對外:方法調用

    • 方法signature:方法名、參數類型;返回類型不屬於方法簽名
    • 重載:方法名稱相同、參數不同
    • 重載解析(編譯階段)

參數

  • 類型:

    • 隱式(implicit)、顯式(explicit)
      (this:調用對象域、構造方法)
    • 基本數據類型、對象引用
  • 傳值:無論是原始類型、還是引用類型變量,都會複製值

    • 方法無法修改基本數據類型參數
    • 可改變對象參數的狀態
    • 不能讓對象參數引用新對象

import 類、靜態域、靜態方法

繼承(Inheritance)

多態的含義
枚舉類的使用
反射是什麼?

Object基礎方法

equals

規則:

  1. 自反性
  2. 對稱性
  3. 傳遞性
  4. 一致性
  5. 對於任意非空引用,x.equals(null)返回false

實現:

  1. 參數命名爲otherObject,轉爲other變量
  2. 檢測this與otherObject是否引用同一對象
  3. otherObject是否爲null
  4. this與otherObject是否屬於同一類
    • 子類擁有自己的相等概念,用getClass
    • 由超類決定相等概念,用instanceOf
  5. 轉換otherObject爲相應類類型變量
  6. 對需要域進行比較,對象域用
    • Objects.equals比較,基本類型域用 ==
    • 若子類中重新定義equals,就要在其中包含super.equals(other)

hashCode

實現:

  1. Objects.hashCode() ——參數爲null時返回0
  2. Double.hashCode() ——避免創建Double對象
  3. Objects.hash(……) ——對參數調用hashCode方法,並組合

toString

  1. getClass().getName + “[attrName:value,……]”
  2. Arrays.toString 、Arrays.deepToString

反射

能夠分析類能力的程序稱爲反射(reflective)

作用:

  1. 運行時分析類的能力
  2. 運行時查看對象
  3. 實現通用的操作(示例:通用數組擴容)

Class

保存運行時的類型信息

  1. 獲取Class對象

    • Object.getClass()
    • Class.forName() --可用於程序啓動時提供加載頁面
    • Type.class --示例:Class cl = int.class;

    每個類型一個類對象:e.getClass() == Employee.class

  2. 創建類對象

    • clzz.newInstance() 調用默認的構造方法,若沒有默認構造器則拋出異常
    • constructor.newInstance()

分析類

獲取域、方法:

  1. getFields、getMethods、getConstructors 獲取類及其超類的public成員
  2. getDeclareFields、getDeclareMethods、getDeclareConstructors 獲取全部成員(不包括超類成員)

Field

  1. 獲取值
    f.get(obj) --安全管理器控制,私有域訪問需覆蓋訪問控制
    f.getDouble(obj)

  2. 設置值
    f.set(obj, value)
    AccessibleObject.setAccessible(true)

Method

clazz.getMethod(name, Class… parameterTypes)
m.invoke(obj, Object…) --沒有隱式參數,obj傳null

Constructor

公共方法:

  1. getName
  2. getModifiers(可通過Modifie靜態方法分析、toString方法可打印修飾符)
  3. setAccessible(true)

泛型數組擴容(應用)

Arrays.copyOf

問題:new Object[newLength] ——不能直接擴展數組,需要創建與原類型相同的新數組
解決:Array.newInstance()

Class.getComponentType()
Array.getLength(a)
System.arraycopy()

繼承設計技巧

  1. 公共操作和類放到超類
  2. 不要使用受保護的域(不要設置域爲protectd–子類集合無限制破壞封裝性,同一個包的類也可訪問)
  3. 使用繼承實現is-a關係
  4. 除非所有繼承的方法都有意義,否則不要使用繼承
  5. 覆蓋方法時,不改變預期行爲
  6. 使用多態,而非類型信息
  7. 不要過多使用反射。

接口

概念

接口不是類,是對類的一組需求描述,這些類要遵從接口描述的統一格式進行定義

類實現接口:實現方法指定爲public,否則爲包可見性

注:繼承情況下的反對稱規則
違反時拋出ClassCastException:

  1. 如果子類之間的比較含義不一樣,那就屬於不同類對象的非法比較。每個compareTo方法都應該在開始時進行下列檢測:
    if(getClass()!=other.getClass())thrownewClassCastException()
  2. 如果存在這樣一種通用算法,它能夠對兩個不同的子類對象進行比較,則應該在超類中提供一個compareTo方法,並將這個方法聲明爲final

特性

  • 不可實例化,但可定義接口類型變量,可被instanceOf檢測
  • 可被擴展
  • 可包含常量,默認push static final
  • 一個類可實現多個接口
  • 默認方法爲接口方法提供默認實現,可調用其他方法(用法:接口演化)

注:默認方法的衝突解決?

  1. 超類優先(與JavaSE7兼容)
  2. 接口衝突需要覆蓋

對比抽象類

可將接口看成沒有實例域的抽象類

存在了抽象類,還需要接口:每個類只能擴展於一個類。
接口可以提供多重繼承的大多數好處,同時還能避免多重繼承的複雜性和低效性。

接口示例

  1. callback機制:指出某個特定事件發生時應該採取的動作

  2. Comparator
    Arrays.sort

  3. Cloneable

    如果希望copy是一個新對象,它的初始狀態與original相同,但是之後它們各自會有自己不同的狀態,這種情況下就可以使用clone方法

    默認的clone操作是”淺拷貝“,克隆對象與原對象共享子對象爲可變對象時,調用更改器方法會篡改另一對象的狀態,造成不一致現象。

    1. 默認的clone方法是否滿足要求;
    2. 是否可以在可變的子對象上調用 clone 來修補默認的clone方法;
    3. 是否不該使用clone(默認)

    Cloneable接口只是作爲一個標記,指示類設計者瞭解克隆過程

標記接口不包含任何方法;它唯一的作用就是允許在類型查詢中使用 instanceof

Lambda表達式

概念

是一個可傳遞的代碼塊,可以在以後執行一次或多次。

之前:在Java中傳遞一個代碼段並不容易,不能直接傳遞代碼段。Java是一種面嚮對象語言,所以必須構造一個對象,這個對象的類需要有一個方法能包含所需的代碼。

語法

(參數) -> 表達式

  • 參數類型如果可推導,則可忽略
  • 參數只有一個,且類型可推導,小括號可忽略
  • 無需返回類型,根據上下文推導得出

應用

對於只有一個抽象方法的接口,當需要這個接口的對象時,就可以提供一個lambda表達式,這種接口被稱爲函數式接口

方法引用

可能已經有現成的方法可以完成你想要傳遞到其他代碼的某個動作

  • object::instanceMethod
  • Class::staticMethod
  • Class::instanceMethod (第一個參數稱爲方法目標)
    可以使用this、super參數
構造器引用

方法名爲new的方法引用

可以用數組類型建立構造器引用。如int[]::new

Java有一限制:無法構造泛型類型 T 的數組,可利用數組構造器解決:
Person[] people = stream.toArray(Person[]::new);

變量作用域

lambda表達式有3部分:

  1. 方法體
  2. 參數
  3. 自由變量的值
    指非參數而且不在代碼中定義的變量(被lambda捕獲)
    實現:lambda被轉換爲包含一個方法的對象,自由變量的值複製到實例變量中。
    (閉包(closure):一種引用了外部作用域變量的特殊函數)

規則:lambda表達式中捕獲的變量必須實際上是最終變量
(effectively final:這個變量初始化之後就不會再爲它賦新值)
在lambda中改變值,併發情況不安全。在外部改變也不允許。

this關鍵字,是指創建這個lambda表達式方法的this參數

處理lambda

使用lambda是爲了:延遲執行(deferred execution)

  • 在一個單獨的線程中運行代碼;
  • 多次運行代碼;
  • 在算法的適當位置運行代碼(例如,排序中的比較操作;)
  • 發生某種情況時執行代碼(如,點擊了一個按鈕,數據到達等等;)
  • 只在必要時才運行代碼。

選擇或提供一個函數式接口,常見如下表:
常用函數式接口
基本類型函數式接口

內部類

inner class:定義在一個類中的類
For:

  • 內部類方法可以訪問該類定義所在的作用域中的數據,包括私有的數據;
  • 內部類可以對同一個包中的其他類隱藏起來;
  • 當想要定義一個回調函數且不想編寫大量代碼時,使用匿名(anonymous)內部類比較便捷。

使用

  • 內部類的對象總有一個隱式引用, 它指向了創建它的外部類對象
  • 內部類既可以訪問自身的數據域,也可以訪問創建它的外圍類對象的數據域
  • 只有內部類可以聲明爲私有的,這樣只有外圍類能構造內部類對象。

特殊語法規則
引用外圍類表達式:OuterClass.this.beep
內部類構造器outerObject.new InnerClass(construction parameters)
外圍類作用域之外引用類:OuterClass.InnerClass

內部類中聲明的所有靜態域都必須是 final
內部類不能有static方法。Java語言規範對這個限制沒有做任何解釋。也可以允許有靜態方法,但只能訪問外圍類的靜態域和方法

原理

內部類是一種編譯器現象,虛擬機對此一無所知。編譯器爲內部類生成了帶有外部類參數的構造方法,並提供了訪問外部類對象狀態的靜態方法

反射反編譯解析字節碼
java reflection.ReflectionTest irmerClass.lil kingClock\STimePrinter

javap -private ClassName

可以創建同一個包下的類利用虛擬機指定獲取內部類訪問的私有變量,破壞安全性。

局部內部類

在方法中定義的局部類,不能用public或private訪問說明符進行聲明。它的作用域被限定在聲明這個局部類的塊中
優勢:

  1. 對外部世界可以完全地隱藏起來
  2. 不僅能夠訪問包含它們的外部類, 還可以訪問局部變量。不過,那些局部變量必須事實上爲 final(有時final限制不方便,可以使用一個元素的數組代替參數)

匿名內部類

如果只創建局部內部類的一個對象,就不必命名了,這種累被稱爲匿名內部類(anonymous innerclass)

new SuperType(construction parameters) {
inner class methods and data
}

匿名類不能有構造器,取而代之的是,將構造器參數傳遞給超類。
實現接口時不能有任何構造參數。

應用技巧:

  • “雙括號初始化invite” (new ArrayList() {{ add(“Harry”); add(“Tony”); }});
  • 靜態方法生成類名信息 new Object(){}.getCIass().getEndosingClass0 // gets class of static method

注意:實現equals方法時,對於匿名子類getClass測試會失敗;

靜態內部類

當使用內部類只是爲了把一個類隱藏在另外一個類的內部,並不需要內部類引用外圍類對象。爲此,可以將內部類聲明爲 static, 以便取消產生的引用。

  • 與常規內部類不同,靜態內部類可以有靜態域和方法
  • 聲明在接口中的內部類自動成爲 static 和 public 類
    只有內部類可以聲明爲static

代理

For:利用代理可以在運行時創建一個實現了一組給定接口的新類,用於:

  • 路由對遠程服務器的方法調用
  • 在程序運行期間,將用戶接口事件與動作關聯起來
  • 爲調試, 跟蹤方法調用

創建的類具有下列方法:

  • 指定接口所需要的全部方法;
  • Object類中的全部方法。

提供一個調用處理器( invocation handler)

使用

創建代理對象:Proxy的newProxyInstance方法,參數包含

  • class loader,null表示默認的類加載器;
  • 一個class對象數組,元素要實現的接口;
  • 一個調用處理器

特性

  • 代理類在程序運行中創建,所有代理類擴展於Proxy類,一個代理類只有一個實例域——調用處理器
  • 所有的代理類都覆蓋了 Object 類中的方法 toString、 equals 和 hashCode
  • 對於特定的類加載器和預設的一組接口來說,只能有一個代理類(可用getProxyClass方法獲得這個類)
  • 代理類一定是 public 和 final。
  • Proxy.isProxyClass檢測特定Class是否是代理類

小結

到此爲止,Java 程序設計語言的基礎概念整理完畢。包括Java編程中面向對象相關基礎、接口相關知識,以及內部類、Java8新增的Lambda表達式等。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章