Java 編程思想 Chapter_14 類型信息

来源:http://www.cnblogs.com/wislie/archive/2017/09/27/7599705.html
-Advertisement-
Play Games

本章內容繞不開一個名詞:RTTI(Run-time Type Identification) 運行時期的類型識別 知乎上有人推斷作者是從C++中引入這個概念的,反正也無所謂,理解並能串聯本章知識才是最重要的 本章的內容其實都是為類型信息服務的,主要內容有 一.Class對象 問題: 1.Class對 ...


本章內容繞不開一個名詞:RTTI(Run-time Type Identification) 運行時期的類型識別

知乎上有人推斷作者是從C++中引入這個概念的,反正也無所謂,理解並能串聯本章知識才是最重要的

本章的內容其實都是為類型信息服務的,主要內容有

圖一

一.Class對象

問題:

1.Class對象的創建過程是怎麼樣的

2.Class對象有哪幾種創建方式,之間有什麼差異

3.使用泛型

在瞭解類型信息之前,需要瞭解class對象

創建class對象,需要先查找這個這個類的.class文件, 查找到的class文件會以位元組碼的形式載入到記憶體, 這時便可以通過記憶體中的Class對象 創建這個類的所有對象

本章中創建對象的方式有三種

第一種: 通過new 構造器 的方式

第二種: Class cls = Class.forName(“全限定名”); cls.newInstance();

第三種 Class cls = 類:.class; cls.newInstance();

說3種比較牽強,第二種和第三種通過虛擬構造器newInstance()的方式創建了對象, newInstance()有2點需要註意: ①介面不能newInstance;②類必須有的空構造器

這三種方式有什麼差別呢,在這裡會牽涉到其他的知識點

1.調用一個類的靜態方法,有沒有創建了一個對象

答案是沒有的,因為沒有涉及到以上3種創建對象的方式,只是將.class文件載入到了記憶體,對類進行了初始化, 並沒有去創建對象

public class Test {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        A.print();
    }
}
class A{
    static{
        System.out.println("static 靜態塊");
    }
    
    public A(){
        System.out.println("構造方法");
    }
    
    public static void print(){
        System.out.println("列印class A");
    }
}

列印的結果如下:

static 靜態塊
列印class A

2. Class.forName(“全限定名”) 和 .class有沒有差別

有差別, Class.forName會主動去在載入靜態方法塊, 而.class不會, .class對靜態方法或非常數靜態域首次引用後才進行初始化

public static void main(String[] args) {
        // TODO Auto-generated method stub
        try {
            Class.forName("chapter_14.A");
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

列印: static 靜態塊

public static void main(String[] args) {
        // TODO Auto-generated method stub
        Class cls = A.class;
}

沒列印

提一點題外話, 為什麼我們平時看到的被訪問的靜態常量通常是這樣的

public static final int CONSTANT = 5;

而不是

public static int CONSTANT = 5;

原因在於static final 是編譯期常量, 類.CONSTANT 不需要對類進行初始化就可以被讀取

 

引入泛型的意義僅僅是為了提供編譯期檢查,第15章會著重講講泛型,在這裡提供了幾個概念

和平凡.class不一樣, 泛型newInstance()返回該對象的確切類型

class A{}

class B extends A{}

Class<A> clsA = A.class;
A objA = clsA.newInstance();

那麼超類呢?

Class<? super B> clsA = B.class.getSuperclass();
Object objA = clsA.newInstance();

超類得到不是精確類型,而只是Object

那繼承類呢?

Class<? extends A> clsB = B.class;
A objA = clsB.newInstance();

此時得到的是父類的類型,在接下來的學習中,我們會經常用這種方式來創建對象,說白了這屬於多態,屬於類型信息

二.類型轉換前先做檢查

問題:

1.類型轉換前先做的檢查的意義和檢查的方式

通過顯式的向下轉型,避免出現ClassCastException

常見的檢查方式有3種,instanceof, isInstance, isAssignableFrom

舉個例子:

class A implements Iface{}

class B extends A{}

interface Iface{}

第一種 instanceof

//類檢查 本身類實例化對象的類型
Class<? super B> clsA = B.class.getSuperclass();
Object objA = clsA.newInstance();
System.out.println(objA instanceof A);//true

//類檢查 子類實例化對象的類型
Class clsB = B.class;
Object objB = clsB.newInstance();
System.out.println(objB instanceof A);//true

//介面檢查 實現類實例化對象的類型
System.out.println(objA instanceof Iface);//true
System.out.println(objB instanceof Iface);//true

第二種:isInstance

Class<? super B> clsA = B.class.getSuperclass();
Object objA = clsA.newInstance();
            
Class clsB = B.class;
Object objB = clsB.newInstance();
//類的Class對象檢查 類本身的實例化對象
System.out.println(clsA.isInstance(objA));//true
System.out.println(clsB.isInstance(objB));//true
//類的Class對象檢查 子類的實例化對象
System.out.println(clsA.isInstance(objB));//true
            
            
//介面的Class對象檢查 實現類的實例化對象
Class clsIface = Iface.class;
System.out.println(clsIface.isInstance(objA));//true
System.out.println(clsIface.isInstance(objB));//true

第三種 isAssignableFrom

Class<? super B> clsA = B.class.getSuperclass();
Object objA = clsA.newInstance();
            
Class clsB = B.class;
Object objB = clsB.newInstance();
//類的Class對象檢查 類本身的Class對象
System.out.println(clsA.isAssignableFrom(clsA));//true
            
//類的Class對象檢查 子類的Class對象
System.out.println(clsA.isAssignableFrom(clsB));//true
            
//介面的Class對象檢查 實現類的Class對象
Class clsIface = Iface.class;
System.out.println(clsIface.isAssignableFrom(clsA));//true
System.out.println(clsIface.isAssignableFrom(clsB));//true

對以上3種方式進行總結,便是以下這張圖了

8

類型檢查在另一方面也說明瞭 類與類的關係,類與介面的關係。

三.註冊工廠

問題:

1.註冊工廠有什麼用

註冊工廠是將工廠方法設計模式和添加融合在一起,在本章中,還是和類型信息有關係,在基類中添加實現類的對象,不過都是根據工廠設計模式去實現的,這樣做的好處在於“避免新添加的數據對結構產生破壞”。本章中的例子很形象,也非常好,如果我有好的例子,也一定會放上鏈接

四.空對象

問題:

1.什麼是空對象

2.使用空對象的意義

通常,空對象是一個單例,它具有無法修改的特性

假設一個類的 某個變數預設情況下是空對象,那麼想要改變這個變數的屬性,就需要重新創建一個對象來代替這個空對象,感覺我說的是廢話,不過這是空對象的本質了,結合下以下代碼,好好考慮下

interface Null{}

class Person{
    
    private final String first;
    private final String last;
    private final String address;
    public Person(String first, String last, String address) {
        super();
        this.first = first;
        this.last = last;
        this.address = address;
    }
    @Override
    public String toString() {
        return "Person [first=" + first + ", last=" + last + ", address=" + address + "]";
    }
    
    static class NullPerson extends Person implements Null{

        private NullPerson(){
            super("None", "None", "None");
        }
        
        @Override
        public String toString() {
            return "NullPerson";
        }
    
    }
    
    public static final Person NULL = new NullPerson();
}

這段代碼抄自Java編程思想的空對象一節,另外有個知識點:不是每個類都會有預設的空構造器,像上面的Person類其實是沒有空構造器的,問題在於構造器的參數用final修飾,可以去探究下。

五.反射

1.反射機制是怎麼樣的

2.如何通過動態代理的方式使用反射機制

反射是程式在運行時打開和檢查.class文件,因此反射是動態的,JDK中使用Class類和java.lang.reflect類庫對反射的概念進行了支持

使用反射是由於 某些類的屬性,方法對外 沒有包訪問許可權,而我們不得不進行訪問,才能完成一些事情

可想而知,包訪問許可權對反射而言起不了作用,這裡包括了private修飾, 私有內部類和匿名內部方法

應用到反射的例子有android中組件通信的EventBus,可以下載下來看源碼

反射也可以用於動態代理(多說一句,動態代理本質還是類型信息)主要代碼抄自書上

public class Test {

    public static void main(String[] args) {
        A objA = new A();
        
        Iface iface = (Iface) Proxy.newProxyInstance(Iface.class.getClassLoader(), 
                new Class[]{Iface.class}, new DynamicProxyHandler(objA));
        doSomething(iface);
    }
    
    public static void doSomething(Iface iface){
        iface.doSomething();
    }
}

class A implements Iface{
    @Override
    public void doSomething() {
        System.out.println("A doSomething");
    }
}

interface Iface{
    
    public void doSomething();
    
}

//所有的調用都會重定向到這個單一的處理器上
class DynamicProxyHandler implements InvocationHandler{

    private Object proxied;
    
    public DynamicProxyHandler(Object proxy){
        proxied = proxy;
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // TODO Auto-generated method stub
        return method.invoke(proxied, args);
    }
}

這裡有3點需要註意的:1..重定向單一的處理器,調用的對象是什麼; 2.創建的iface必須是介面對象,創建介面對象要傳遞的第二個參數是Class數組, 它包含了所有proxied的介面名稱.class ; 3.動態代理與類型信息之間的關係

總結: 類型信息本質上還是關於 向上轉型或者向下轉型


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 要求:1.列印市、區、街道三級菜單 2.按b可隨時返回上一級 3.按q可隨時退出程式 1 dict={'北京':{'海澱區':['中關村','北太平莊','西三旗'], '昌平區':['回龍觀','霍營','沙河'],'朝陽區':['酒仙橋','望京','將台']}, 2 '上海':{'浦東新區': ...
  • test ...
  • 1.當我們使用IE內核的瀏覽器下在PHPExcel報表時(谷歌、火狐瀏覽器正常, IE瀏覽器,360瀏覽器的相容模式報錯),會出現如下錯誤: 2.解決辦法: 在下載文件時,對當前的瀏覽器進行判斷, 如果是IE內核的瀏覽器的話,進行文件名的轉碼, 若不是IE內核的瀏覽器,則不用。 關鍵代碼如下: EN ...
  • 我是一名c#老鳥,雖然編程多年,但只會使用c#通過Visual Studio工具開發Windows環境下的桌面應用和網站。這是我自學.net core的經歷,如果你也和我一樣,也是剛剛接觸.net core,並對此有新區,或許能對你有所幫助。眾所周知,.net也是跨平臺的,但是,都是Windows平 ...
  • 前一段時間做過一個 "郵件發送的服務" ,以前大體都測試過,文本、圖片、附件都是沒有問題的,可有同事反應發送的附件名稱有中文亂碼,類似如下截圖展示: 咋一看不像亂碼,抱著試試看的態度,為MimeMessageHelper硬性加了編碼: 並且對文件名稱加了轉碼: 但是,如果你跟進源碼會發現spring ...
  • 在以前的博文中我們介紹了Slick,它是一種FRM(Functional Relation Mapper)。有別於ORM,FRM的特點是函數式的語法可以支持靈活的對象組合(Query Composition)實現大規模的代碼重覆利用,但同時這些特點又影響了編程人員群體對FRM的接受程度,阻礙了FRM ...
  • jps:JVM Process StatusTool,顯示指定系統內所有的HotSpot虛擬機進程 jstat:JVM Statistics Monitoring Tool,用於手機HotSpot虛擬機各方面的運行數據 jinfo: Configuration Info for Java 顯示虛擬機 ...
  • 在瞭解了 "AQS獨占鎖模式" 以後,接下來再來看看共用鎖的實現原理。 原文地址:http://www.jianshu.com/p/1161d33fc1d0 搞清楚AQS獨占鎖的實現原理之後,再看共用鎖的實現原理就會輕鬆很多。兩種鎖模式之間很多通用的地方本文只會簡單說明一下,就不在贅述了,具體細節可 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...