1. 程式人生 > >Java程式設計思想重點筆記

Java程式設計思想重點筆記

2. is-a關係和is-like-a關係

  • is-a關係屬於純繼承,即只有在基類中已經建立的方法才可以在子類中被覆蓋,如下圖所示:

    基類和子類有著完全相同的介面,這樣向上轉型時永遠不需要知道正在處理的物件的確切型別,這通過多型來實現。

  • is-like-a關係:子類擴充套件了基類介面。它有著相同的基本介面,但是他還具有由額外方法實現的其他特性。

    缺點就是子類中介面的擴充套件部分不能被基類訪問,因此一旦向上轉型,就不能呼叫那些新方法。

3. 執行時型別資訊(RTTI + 反射)

  • 概念
    RTTI:執行時型別資訊使得你可以在程式執行時發現和使用型別資訊。
  • 使用方式
    Java是如何讓我們在執行時識別物件和類的資訊的,主要有兩種方式(還有輔助的第三種方式,見下描述):

    • 一種是“傳統的”RTTI,它假定我們在編譯時已經知道了所有的型別,比如Shape s = (Shape)s1;
    • 另一種是“反射”機制,它執行我們在執行時發現和使用類的資訊,即使用Class.forName()
    • 其實還有第三種形式,就是關鍵字instanceof,它返回一個bool值,它保持了型別的概念,它指的是“你是這個類嗎?或者你是這個類的派生類嗎?”。而如果用==或equals比較實際的Class物件,就沒有考慮繼承—它或者是這個確切的型別,或者不是。
  • 工作原理
    要理解RTTI在Java中的工作原理,首先必須知道型別資訊在執行時是如何表示的

    ,這項工作是由稱為Class物件的特殊物件完成的,它包含了與類有關的資訊。Java送Class物件來執行其RTTI,使用類載入器的子系統實現

無論何時,只要你想在執行時使用型別資訊,就必須首先獲得對恰當的Class物件的引用,獲取方式有三種:
(1)如果你沒有持有該型別的物件,則Class.forName()就是實現此功能的便捷途,因為它不需要物件資訊;
(2)如果你已經擁有了一個感興趣的型別的物件,那就可以通過呼叫getClass()方法來獲取Class引用了,它將返回表示該物件的實際型別的Class引用。Class包含很有有用的方法,比如:

package rtti;
interface HasBatteries{} interface WaterProof{} interface Shoots{} class Toy { Toy() {} Toy(int i) {} } class FancyToy extends Toy implements HasBatteries, WaterProof, Shoots { FancyToy() { super(1); } } public class RTTITest { static void printInfo(Class cc) { System.out.println("Class name: " + cc.getName() + ", is interface? [" + cc.isInterface() + "]"); System.out.println("Simple name: " + cc.getSimpleName()); System.out.println("Canonical name: " + cc.getCanonicalName()); } public static void main(String[] args) { Class c = null; try { c = Class.forName("rtti.FancyToy"); // 必須是全限定名(包名+類名) } catch(ClassNotFoundException e) { System.out.println("Can't find FancyToy"); System.exit(1); } printInfo(c); for(Class face : c.getInterfaces()) { printInfo(face); } Class up = c.getSuperclass(); Object obj = null; try { // Requires default constructor. obj = up.newInstance(); } catch (InstantiationException e) { System.out.println("Can't Instantiate"); System.exit(1); } catch (IllegalAccessException e) { System.out.println("Can't access"); System.exit(1); } printInfo(obj.getClass()); } }


輸出:

Class name: rtti.FancyToy, is interface? [false]
Simple name: FancyToy
Canonical name: rtti.FancyToy
Class name: rtti.HasBatteries, is interface? [true]
Simple name: HasBatteries
Canonical name: rtti.HasBatteries
Class name: rtti.WaterProof, is interface? [true]
Simple name: WaterProof
Canonical name: rtti.WaterProof
Class name: rtti.Shoots, is interface? [true]
Simple name: Shoots
Canonical name: rtti.Shoots
Class name: rtti.Toy, is interface? [false]
Simple name: Toy
Canonical name: rtti.Toy

(3)Java還提供了另一種方法來生成對Class物件的引用,即使用類字面常量。比如上面的就像這樣:FancyToy.class;來引用。
這樣做不僅更簡單,而且更安全,因為它在編譯時就會受到檢查(因此不需要置於try語句塊中),並且它根除了對forName方法的引用,所以也更高效。類字面常量不僅可以應用於普通的類,也可以應用於介面、陣列以及基本資料型別。

注意:當使用“.class”來建立對Class物件的引用時,不會自動地初始化該Class物件,初始化被延遲到了對靜態方法(構造器隱式的是靜態的)或者非final靜態域(注意final靜態域不會觸發初始化操作)進行首次引用時才執行:。而使用Class.forName時會自動的初始化。

為了使用類而做的準備工作實際包含三個步驟:
- 載入:由類載入器執行。查詢位元組碼,並從這些位元組碼中建立一個Class物件
- 連結:驗證類中的位元組碼,為靜態域分配儲存空間,並且如果必需的話,將解析這個類建立的對其他類的所有引用。
- 初始化:如果該類具有超類,則對其初始化,執行靜態初始化器和靜態初始化塊。

這一點非常重要,下面通過一個例項來說明這兩者的區別:

package rtti;
import java.util.Random;
class Initable {
        static final int staticFinal = 47;
        static final int staticFinal2 = ClassInitialization.rand.nextInt(1000);

        static {
            System.out.println("Initializing Initable");
        }
}
class Initable2 {
        static int staticNonFinal = 147;

        static {
            System.out.println("Initializing Initable2");
        }
}
class Initable3 {
        static int staticNonFinal = 74;

        static {
            System.out.println("Initializing Initable3");
        }
}
public class ClassInitialization {

        public static Random rand = new Random(47);

        public static void main(String[] args) {
            // Does not trigger initialization
            Class initable = Initable.class;
            System.out.println("After creating Initable ref");
            // Does not trigger initialization
            System.out.println(Initable.staticFinal);
            // Does trigger initialization(rand() is static method)
            System.out.println(Initable.staticFinal2);

            // Does trigger initialization(not final)
            System.out.println(Initable2.staticNonFinal);

            try {
                Class initable3 = Class.forName("rtti.Initable3");
            } catch (ClassNotFoundException e) {
                System.out.println("Can't find Initable3");
                System.exit(1);
            }
            System.out.println("After creating Initable3 ref");
            System.out.println(Initable3.staticNonFinal);
        }
}


輸出:

After creating Initable ref
47
Initializing Initable
258
Initializing Initable2
147
Initializing Initable3
After creating Initable3 ref
74

  • RTTI的限制?如何突破? — 反射機制
    如果不知道某個物件的確切型別,RTTI可以告訴你,但是有一個限制:這個型別在編譯時必須已知,這樣才能使用RTTI識別它,也就是在編譯時,編譯器必須知道所有要通過RTTI來處理的類。

可以突破這個限制嗎?是的,突破它的就是反射機制
Class類與java.lang.reflect類庫一起對反射的概念進行了支援,該類庫包含了FieldMethod以及Constructor類(每個類都實現了Member介面)。這些型別的物件是由JVM在執行時建立的,用以表示未知類裡對應的成員。這樣你就可以使用Constructor建立新的物件,用get()/set()方法讀取和修改與Field物件關聯的欄位,用invoke()方法呼叫與Method物件關聯的方法。另外,還可以呼叫getFields()、getMethods()和getConstructors()等很便利的方法,以返回表示欄位、方法以及構造器的物件的陣列。這樣,匿名物件的類資訊就能在執行時被完全確定下來,而在編譯時不需要知道任何事情。

####反射與RTTI的區別
當通過反射與一個未知型別的物件打交道時,JVM只是簡單地檢查這個物件,看它屬於哪個特定的類(就像RTTI那樣),在用它做其他事情之前必須先載入那個類的Class物件,因此,那個類的.class檔案對於JVM來說必須是可獲取的:要麼在本地機器上,要麼可以通過網路取得。所以RTTI與反射之間真正的區別只在於:對RTTI來說,編譯器在編譯時開啟和檢查.class檔案(也就是可以用普通方法呼叫物件的所有方法);而對於反射機制來說,.class檔案在編譯時是不可獲取的,所以是在執行時開啟和檢查.class檔案。

下面的例子是用反射機制打印出一個類的所有方法(包括在基類中定義的方法):

package typeinfo;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.regex.Pattern;

// Using reflection to show all the methods of a class.
// even if the methods are defined in the base class.
public class ShowMethods {
    private static String usage = 
        "usage: \n" + 
        "ShowMethods qualified.class.name\n" +
        "To show all methods in class or: \n" +
        "ShowMethods qualified.class.name word\n" +
        "To search for methods involving 'word'";

    private static Pattern p = Pattern.compile("\\w+\\.");

    public static void main(String[] args) {
        if(args.length < 1) {
            System.out.println(usage);
            System.exit(0);
        }
        int lines = 0;
        try {
            Class<?> c = Class.forName(args[0]);
            Method[] methods = c.getMethods();
            Constructor[] ctors = c.getConstructors();
            if(args.length == 1) {
                for(Method method : methods) {
                    System.out.println(p.matcher(method.toString()).replaceAll(""));
                }
                for(Constructor ctor : ctors) {
                    System.out.println(p.matcher(ctor.toString()).replaceAll(""));
                }
                lines = methods.length + ctors.length;
            } else {
                
            
           

相關推薦

java程式設計思想重點筆記java程式設計師必看)

Java中的多型性理解(注意與C++區分) Java中除了static方法和final方法(private方法本質上屬於final方法,因為不能被子類訪問)之外,其它所有的方法都是動態繫結,這意味著通常情況下,我們不必判定是否應該進行動態繫結—它會自動發生。 fin

Java程式設計思想重點筆記Java開發必看)

Java程式設計思想,Java學習必讀經典,不管是初學者還是大牛都值得一讀,這裡總結書中的重點知識,這些知識不僅經常出現在各大知名公司的筆試面試過程中,而且在大型專案開發中也是常用的知識,既有簡單的概念理解題(比如is-a關係和has-a關係的區別),也有深入的涉及RTTI和JVM底層反編譯知識。

Java程式設計思想重點筆記Java開發必看)

1. Java中的多型性理解(注意與C++區分) Java中除了static方法和final方法(private方法本質上屬於final方法,因為不能被子類訪問)之外,其它所有的方法都是動態繫結,這意味著通常情況下,我們不必判定是否應該進行動態繫結—它會自動發生。 fi

Java程式設計思想重點筆記

2. is-a關係和is-like-a關係 is-a關係屬於純繼承,即只有在基類中已經建立的方法才可以在子類中被覆蓋,如下圖所示: 基類和子類有著完全相同的介面,這樣向上轉型時永遠不需要知道正在處理的物件的確切型別,這通過多型來實現。 is-like-a關係:子類擴充套件了基類介

Java程式設計思想學習筆記-第11章

.title { text-align: center; margin-bottom: .2em } .subtitle { text-align: center; font-size: medium; font-weight: bold; margin-top: 0 } .todo { font-famil

JAVA程式設計思想學習筆記(一)

物件導論 1.1 抽象過程 Smalltalk的五個基本特性: 萬物皆為物件。 程式是物件的集合,它通過傳送訊息來告知彼此所要做的。 每個物件都有自己的由其他物件所構成的儲存。 每個物件都有其型別。 某一特定型別的所有物件都可以接受同樣的訊息。

java程式設計思想學習筆記——第1章 物件導論

1.1 抽象過程 面向物件思想的實質:程式可以通過新增新型別的物件使自身適用於某個特定問題。 面向物件思想的五個基本特徵: 1)萬物皆物件 2)程式是物件的集合 3)每個物件都有自己的由其他物件所構成的儲存 4)每個物件都有其型別 5)某一特定型別的所有物件都可以接收同樣的訊息 物件具有行為、

Java程式設計思想筆記14.型別資訊

執行時型別資訊使得你可以在執行時發現和使用型別資訊,主要有兩種方式: “傳統的”RTTI,它假定我們在編譯時已經知道了所有的型別; “反射”機制,它允許我們在執行時發現和使用類的資訊。 14.1 為什麼需要RTTI RTTI維護型別型別的資訊,為多型機制的實現提供基礎。 14.2 Cla

java程式設計思想學習筆記——第2章 一切都是物件

儘管java是基於C的,但是相比之下,java是一種更“純粹”的面向物件程式設計語言。 2.1 用引用操縱物件 一切都視為物件,因此可採用單一固定的語法。儘管這一切都看作物件,但操縱的識別符號實際上是物件的一個“引用(reference)”。 java語言的一個特性:字串可以用帶引號的文字初始化。通常,

java程式設計思想讀書筆記二(物件的建立)

java物件 物件的建立 java的物件是在執行時建立的,建立物件的的觸發條件有以下幾種: 用new語句建立物件,這是最常用的建立物件方法。 運用反射手段,呼叫java.lang.reflect.Constructor類的newInstance()例項方法。

java程式設計思想讀書筆記一(面向物件)

面向物件 我們常見的程式設計正規化有指令式程式設計,函數語言程式設計,邏輯式程式設計,而面向物件程式設計也是一種指令式程式設計。 指令式程式設計式面向計算機硬體的一種抽象,有變數(儲存單元),賦值語句(獲取儲存指令),表示式(記憶體引用和算術運算)和控制語句(跳轉指令),命令式程

java程式設計思想讀書筆記三(HashMap詳解)

Map Map介面規定了一系列的操作,作為一個總規範它所定義的方法也是最基礎,最通用的。 AbstractMap AbstractMap是HashMap、TreeMap,、ConcurrentHashMap 等類的父類。當我們巨集觀去理解Map時會發現,其實Map就是一

Java程式設計思想讀書筆記(一)第1~13、16章

目錄: 第1章 物件導論 1.1 伴隨多型的可互換物件   面向物件程式設計語言使用了後期繫結的概念。當向物件傳送訊息時,被呼叫的程式碼直到執行時才能確定。也叫動態繫結。   編譯器確保被呼叫方法的存在,並對呼叫引數和返回值執行型別檢查(Java是強型別的語言,無法

java程式設計思想學習筆記:初始&過載&this&static

構造器初始化 構造器 ,採用與類相同的名稱,建立物件時,自動呼叫構造器來初始化物件 預設構造器(無參構造器):不接受任何引數的構造器 構造器也可帶引數,指定如何建立物件 當類中沒有構造器,編譯器會自動建立一個預設構造器; 當類中已經定義了一個構造

JAVA程式設計思想學習筆記(三)操作符

操作符 別名問題 先來看段程式碼,猜猜最後輸出的是什麼: class Test{ int t; } public class A { public static void main(String[] args) { // TODO Auto-gener

Java程式設計思想讀書筆記_8_多型

如果一種語言想實現後期繫結,就必須具有某種機制以便在執行時能判斷物件的型別,從而呼叫恰當的方法,也就是說,編譯器不知道物件的型別,但是方法呼叫機制能找到並呼叫正確的方法體; Java中除了static方法和final方法(private方法也屬於final方法)以外,其它的方法都是後期繫結的;(因此以前還可

Java】《Java程式設計思想筆記(含練習題答案程式碼)-第二章 一切都是物件

2.1 用引用操縱物件【String】 遙控器(引用)- 電視機(資料)建立String引用,如果向無物件的引用傳送資訊會返回一個執行時錯誤,所以安全的做法是:建立一個引用的同時便進行初始化Stri

java程式設計思想讀書筆記 第十五章 泛型 (匿名內部類和擦除)

1.匿名內部類 泛型還可以應用於內部類以及匿名內部類。下面的例子使用匿名內部類實現了Generator介面: public class Customer { private static long counter = 1; private f

JAVA程式設計思想學習筆記(八)介面

介面 抽象類和抽象方法 抽象方法:這種方法不完整,僅有宣告而沒有方法體。所採用的語法如下: abstract void f(); 抽象類:包含抽象方法的類叫做抽象類,如果一個類包含一個或多個抽象方法,該類必須被限定為抽象的。 介面 關鍵字:interface 介面定

JAVA程式設計思想學習筆記(七)多型

多型 繫結 繫結: 將一個方法呼叫同一個方法主體關聯起來被稱作繫結。 前期繫結: 若在程式執行前進行繫結,叫做前期繫結,它是面嚮物件語言不需要選擇就預設的繫結方式。 後期繫結: 它的含義就是在執行時根據物件的型別進行繫結,也叫做動態繫結或執行時繫結。java中除了static和fin