20189200餘超 2018-2019-2 移動平臺應用開發實踐第四做業

2018-2019-2 移動平臺應用開發實踐第四周做業

第12章枚舉

  1. 什麼狀況下使用枚舉類?
    有的時候一個類的對象是有限且固定的,這種狀況下咱們使用枚舉類就比較方便。java

  2. 爲何不用靜態常量來替代枚舉類呢?
public static final int SEASON_SPRING = 1;
    public static final int SEASON_SUMMER = 2;
    public static final int SEASON_FALL = 3;
    public static final int SEASON_WINTER = 4;

枚舉類更加直觀,類型安全。使用常量會有如下幾個缺陷:算法

  1. 類型不安全。若一個方法中要求傳入季節這個參數,用常量的話,形參就是int類型,開發者傳入任意類型的int類型值就行,可是若是是枚舉類型的話,就只能傳入枚舉類中包含的對象。
  2. 沒有命名空間。開發者要在命名的時候以SEASON_開頭,這樣另一個開發者再看這段代碼的時候,才知道這四個常量分別表明季節。

3.枚舉類入門
先看一個簡單的枚舉類數組

package enumcase;

public enum SeasonEnum {
    SPRING,SUMMER,FALL,WINTER;
}

1.enum和class、interface的地位同樣
2.使用enum定義的枚舉類默認繼承了java.lang.Enum,而不是繼承Object類。枚舉類能夠實現一個或多個接口。
3.枚舉類的全部實例都必須放在第一行展現,不需使用new 關鍵字,不需顯式調用構造器。自動添加public static final修飾。
4.使用enum定義、非抽象的枚舉類默認使用final修飾,不能夠被繼承。
5.枚舉類的構造器只能是私有的。安全

4.枚舉類介紹
枚舉類內也能夠定義屬性和方法,但是是靜態的和非靜態的。數據結構

package enumcase;

public enum SeasonEnum {
    SPRING("春天"),SUMMER("夏天"),FALL("秋天"),WINTER("冬天");
    
    private final String name;
    
    private SeasonEnum(String name)
    {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

實際上在第一行寫枚舉類實例的時候,默認是調用了構造器的,因此此處須要傳入參數,由於沒有顯式申明無參構造器,只能調用有參數的構造器。
構造器需定義成私有的,這樣就不能在別處申明此類的對象了。枚舉類一般應該設計成不可變類,它的Field不該該被改變,這樣會更安全,並且代碼更加簡潔。因此咱們將Field用private final修飾。框架

5.switch語句裏的表達式能夠是枚舉值dom

package enumcase;

public class SeasonTest {
    public void judge(SeasonEnum s)
    {
        switch(s)
        {
        case SPRING:
            System.out.println("春天適合踏青。");
            break;
        case SUMMER:
            System.out.println("夏天要去游泳啦。");
            break;
        case FALL:
            System.out.println("秋天必定要去旅遊哦。");
            break;
        case WINTER:
            System.out.println("冬天要是下雪就好啦。");
            break;
        }
    }
    
    public static void main(String[] args) {
        SeasonEnum s = SeasonEnum.SPRING;
        SeasonTest test = new SeasonTest();
        test.judge(s);
    }
}

第十三章 操做時間和日期

1、Java中的日期概述
 
日期在Java中是一塊很是複雜的內容,對於一個日期在不一樣的語言國別環境中,日期的國際化,日期和時間之間的轉換,日期的加減運算,日期的展現格式都是很是複雜的問題。
 
在Java中,操做日期主要涉及到一下幾個類:
 
一、java.util.Date
        類 Date 表示特定的瞬間,精確到毫秒。從 JDK 1.1 開始,應該使用 Calendar 類實現日期和時間字段之間轉換,使用 DateFormat 類來格式化和分析日期字符串。Date 中的把日期解釋爲年、月、日、小時、分鐘和秒值的方法已廢棄。
 
二、java.text.DateFormat(抽象類)
        DateFormat 是日期/時間格式化子類的抽象類,它以與語言無關的方式格式化並分析日期或時間。日期/時間格式化子類(如 SimpleDateFormat)容許進行格式化(也就是日期 -> 文本)、分析(文本-> 日期)和標準化。將日期表示爲 Date 對象,或者表示爲從 GMT(格林尼治標準時間)1970 年,1 月 1 日 00:00:00 這一刻開始的毫秒數。
 
三、java.text.SimpleDateFormat(DateFormat的直接子類)
        SimpleDateFormat 是一個以與語言環境相關的方式來格式化和分析日期的具體類。它容許進行格式化(日期 -> 文本)、分析(文本 -> 日期)和規範化。
        SimpleDateFormat 使得能夠選擇任何用戶定義的日期-時間格式的模式。可是,仍然建議經過 DateFormat 中的 getTimeInstance、getDateInstance 或 getDateTimeInstance 來新的建立日期-時間格式化程序。
 
四、java.util.Calendar(抽象類)
        Calendar 類是一個抽象類,它爲特定瞬間與一組諸如 YEAR、MONTH、DAY_OF_MONTH、HOUR 等 日曆字段之間的轉換提供了一些方法,併爲操做日曆字段(例如得到下星期的日期)提供了一些方法。瞬間可用毫秒值來表示,它是距曆元(即格林威治標準時間 1970 年 1 月 1 日的 00:00:00.000,格里高利曆)的偏移量。
        與其餘語言環境敏感類同樣,Calendar 提供了一個類方法 getInstance,以得到此類型的一個通用的對象。Calendar 的 getInstance 方法返回一個 Calendar 對象,其日曆字段已由當前日期和時間初始化。
 
五、java.util.GregorianCalendar(Calendar的直接子類)
        GregorianCalendar 是 Calendar 的一個具體子類,提供了世界上大多數國家使用的標準日曆系統。
        GregorianCalendar 是一種混合日曆,在單一間斷性的支持下同時支持儒略曆和格里高利曆系統,在默認狀況下,它對應格里高利日曆創立時的格里高利曆日期(某些國家是在 1582 年 10 月 15 日創立,在其餘國家要晚一些)。可由調用方經過調用 setGregorianChange() 來更改起始日期。
代碼清單13.1 使用Instant來計時一項操做函數

import java.time.Duration;
import java.time.Instant;

public class InstantDemo1{
    public static void main(String[] args){
        Instant start=Instant.now();
        System.out.println("Hello World");
        Instant end=Instant.now();
        System.out.println(Duration.between(start,end).toMillis());
    }
}

LocalDate類建模了沒有時間部分的日期。
代碼清單13.2 LocalDate示例性能

import java.time.LocalDate;
import java.time.temporal.ChronoField;
import java.time.temporal.ChronoUnit;

public class LocalDateDemo1{
    public static void main(String[] args){
        LocalDate today=LocalDate.now();
        LocalDate tomorrow=today.plusDays(1);
        LocalDate oneDecadeAgo=today.minus(1,
            ChronoUnit.DECADES);
        System.out.println("Day of month:"
            +today.getDayOfMonth());
        System.out.println("Today is"+today);
        System.out.println("Tomorrow is"+tomorrow);
        System.out.println("A decade ago was"+oneDecadeAgo);
        System.out.println("Year:"
            +today.get(ChronoField.YEAR));
        System.out.println("Day of year:"+today.getDayOfYear());
    }
}

一、java.util.Date的API簡介
 
類 java.util.Date 表示特定的瞬間,精確到毫秒。提供了不少的方法,可是不少已通過時,不推薦使用,下面僅僅列出沒有過期的方法:
 
構造方法摘要
-------------
Date()
          分配 Date 對象並用當前時間初始化此對象,以表示分配它的時間(精確到毫秒)。
Date(long date)
          分配 Date 對象並初始化此對象,以表示自從標準基準時間(稱爲「曆元(epoch)」,即 1970 年 1 月 1 日 00:00:00 GMT)以來的指定毫秒數。
 
方法摘要
-------------
 boolean after(Date when)
          測試此日期是否在指定日期以後。
 
 boolean before(Date when)
          測試此日期是否在指定日期以前。
 
 Object clone()
          返回此對象的副本。
 
 int compareTo(Date anotherDate)
          比較兩個日期的順序。
 
 boolean equals(Object obj)
          比較兩個日期的相等性。
 
 long getTime()
          返回自 1970 年 1 月 1 日 00:00:00 GMT 以來此 Date 對象表示的毫秒數。
 
 int hashCode()
          返回此對象的哈希碼值。
 
 void setTime(long time)
          設置此 Date 對象,以表示 1970 年 1 月 1 日 00:00:00 GMT 之後 time 毫秒的時間點。
 
 String toString()
          把此 Date 對象轉換爲如下形式的 String: dow mon dd hh:mm:ss zzz yyyy 其中:
          dow 是一週中的某一天 (Sun, Mon, Tue, Wed, Thu, Fri, Sat)。
          mon 是月份 (Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec)。
          dd 是一月中的某一天(01 至 31),顯示爲兩位十進制數。
          hh 是一天中的小時(00 至 23),顯示爲兩位十進制數。
          mm 是小時中的分鐘(00 至 59),顯示爲兩位十進制數。
          ss 是分鐘中的秒數(00 至 61),顯示爲兩位十進制數。
          zzz 是時區(並能夠反映夏令時)。標準時區縮寫包括方法 parse 識別的時區縮寫。若是不提供時區信息,則 zzz 爲空,即根本不包括任何字符。
          yyyy 是年份,顯示爲 4 位十進制數。測試

import java.util.Date; 

/** 
* Created by IntelliJ IDEA. 
* User: leizhimin 
* Date: 2007-11-30 
* Time: 8:45:44 
* 日期測試 
*/ 
public class TestDate { 
    public static void main(String args[]) { 
        TestDate nowDate = new TestDate(); 
        nowDate.getSystemCurrentTime(); 
        nowDate.getCurrentDate(); 
    } 

    /** 
     * 獲取系統當前時間 
     * System.currentTimeMillis()返回系統當前時間,結果爲1970年1月1日0時0分0秒開始,到程序執行取得系統時間爲止所通過的毫秒數 
     * 1秒=1000毫秒 
     */ 
    public void getSystemCurrentTime() { 
        System.out.println("----獲取系統當前時間----"); 
        System.out.println("系統當前時間 = " + System.currentTimeMillis()); 
    } 

    /** 
     * 經過Date類獲取當前日期和當前時間 
     * date.toString()把日期轉換爲dow mon dd hh:mm:ss zzz yyyy 
     */ 
    public void getCurrentDate() { 
        System.out.println("----獲取系統當前日期----"); 
        //建立並初始化一個日期(初始值爲當前日期) 
        Date date = new Date(); 
        System.out.println("如今的日期是 = " + date.toString()); 
        System.out.println("自1970年1月1日0時0分0秒開始至今所經歷的毫秒數 = " + date.getTime()); 
    } 
}
 
運行結果:
----獲取系統當前時間---- 
系統當前時間 = 1196413077278 
----獲取系統當前日期---- 
如今的日期是 = Fri Nov 30 16:57:57 CST 2007 
自1970年1月1日0時0分0秒開始至今所經歷的毫秒數 = 1196413077278

二、java.text.DateFormat抽象類的使用
 
DateFormat 是日期/時間格式化子類的抽象類,它以與語言無關的方式格式化並分析日期或時間。日期/時間格式化子類(如 SimpleDateFormat)容許進行格式化(也就是日期 -> 文本)、分析(文本-> 日期)和標準化。將日期表示爲 Date 對象,或者表示爲從 GMT(格林尼治標準時間)1970 年,1 月 1 日 00:00:00 這一刻開始的毫秒數。
 
DateFormat 提供了不少類方法,以得到基於默認或給定語言環境和多種格式化風格的默認日期/時間 Formatter。格式化風格包括 FULL、LONG、MEDIUM 和 SHORT。方法描述中提供了使用這些風格的更多細節和示例。
 
DateFormat 可幫助進行格式化並分析任何語言環境的日期。對於月、星期,甚至日曆格式(陰曆和陽曆),其代碼可徹底與語言環境的約定無關。
 
要格式化一個當前語言環境下的日期,可以使用某個靜態工廠方法:
      myString = DateFormat.getDateInstance().format(myDate);
 
 
 
若是格式化多個日期,那麼得到該格式並屢次使用它是更爲高效的作法,這樣系統就沒必要屢次獲取有關環境語言和國家約定的信息了。
 DateFormat df = DateFormat.getDateInstance();
 for (int i = 0; i < myDate.length; ++i) {
  output.println(df.format(myDate[i]) + "; ");
 }
 
要格式化不一樣語言環境的日期,可在 getDateInstance() 的調用中指定它。
 DateFormat df = DateFormat.getDateInstance(DateFormat.LONG, Locale.FRANCE);
 
還可以使用 DateFormat 進行分析。
 myDate = df.parse(myString);
 
使用 getDateInstance 來得到該國家的標準日期格式。另外還提供了一些其餘靜態工廠方法。使用 getTimeInstance 可得到該國家的時間格式。使用 getDateTimeInstance 可得到日期和時間格式。能夠將不一樣選項傳入這些工廠方法,以控制結果的長度(從 SHORT 到 MEDIUM 到 LONG 再到 FULL)。確切的結果取決於語言環境,可是一般:
 SHORT 徹底爲數字,如 12.13.52 或 3:30pm
 MEDIUM 較長,如 Jan 12, 1952
 LONG 更長,如 January 12, 1952 或 3:30:32pm
 FULL 是徹底指定,如 Tuesday, April 12, 1952 AD 或 3:30:42pm PST。
 
 若是願意,還能夠在格式上設置時區。若是想對格式化或分析施加更多的控制(或者給予用戶更多的控制),能夠嘗試將從工廠方法所得到的 DateFormat 強制轉換爲 SimpleDateFormat。這適用於大多數國家;只是要記住將其放入一個 try 代碼塊中,以防遇到特殊的格式。
 
還可使用藉助 ParsePosition 和 FieldPosition 的分析和格式化方法形式來:逐步地分析字符串的各部分。 對齊任意特定的字段,或者找出字符串在屏幕上的選擇位置。
 
DateFormat 不是同步的。建議爲每一個線程建立獨立的格式實例。若是多個線程同時訪問一個格式,則它必須保持外部同步
三、java.util.Calendar(抽象類)
 
java.util.Calendar是個抽象類,是系統時間的抽象表示,它爲特定瞬間與一組諸如 YEAR、MONTH、DAY_OF_MONTH、HOUR 等 日曆字段之間的轉換提供了一些方法,併爲操做日曆字段(例如得到下星期的日期)提供了一些方法。瞬間可用毫秒值來表示,它是距曆元(即格林威治標準時間 1970 年 1 月 1 日的 00:00:00.000,格里高利曆)的偏移量。
 
與其餘語言環境敏感類同樣,Calendar 提供了一個類方法 getInstance,以得到此類型的一個通用的對象。Calendar 的 getInstance 方法返回一個 Calendar 對象,其日曆字段已由當前日期和時間初始化。
 
一個Calendar的實例是系統時間的抽象表示,從Calendar的實例能夠知道年月日星期月份時區等信息。Calendar類中有一個靜態方法get(int x),經過這個方法能夠獲取到相關實例的一些值(年月日星期月份等)信息。參數x是一個產量值,在Calendar中有定義。
 
Calendar中些陷阱,很容易掉下去:
一、Calendar的星期是從週日開始的,常量值爲0。
二、Calendar的月份是從一月開始的,常量值爲0。
三、Calendar的每月的第一天值爲1

第十四章 集合框架


說明:對於以上的框架圖有以下幾點說明

1.全部集合類都位於java.util包下。Java的集合類主要由兩個接口派生而出:Collection和Map,Collection和Map是Java集合框架的根接口,這兩個接口又包含了一些子接口或實現類。

  1. 集合接口:6個接口(短虛線表示),表示不一樣集合類型,是集合框架的基礎。
  2. 抽象類:5個抽象類(長虛線表示),對集合接口的部分實現。可擴展爲自定義集合類。
  3. 實現類:8個實現類(實線表示),對接口的具體實現。
  4. Collection 接口是一組容許重複的對象。
  5. Set 接口繼承 Collection,集合元素不重複。
  6. List 接口繼承 Collection,容許重複,維護元素插入順序。
  7. Map接口是鍵-值對象,與Collection接口沒有什麼關係。
    9.Set、List和Map能夠看作集合的三大類:
    List集合是有序集合,集合中的元素能夠重複,訪問集合中的元素能夠根據元素的索引來訪問。
    Set集合是無序集合,集合中的元素不能夠重複,訪問集合中的元素只能根據元素自己來訪問(也是集合裏元素不容許重複的緣由)。
    Map集合中保存Key-value對形式的元素,訪問時只能根據每項元素的key來訪問其value。

Collection接口是處理對象集合的根接口,其中定義了不少對元素進行操做的方法。Collection接口有兩個主要的子接口List和Set,注意Map不是Collection的子接口,這個要牢記

**1.List接口*

List集合表明一個有序集合,集合中每一個元素都有其對應的順序索引。List集合容許使用重複元素,能夠經過索引來訪問指定位置的集合元素。

List接口繼承於Collection接口,它能夠定義一個容許重複的有序集合。由於List中的元素是有序的,因此咱們能夠經過使用索引(元素在List中的位置,相似於數組下標)來訪問List中的元素,這相似於Java的數組。

List接口爲Collection直接接口。List所表明的是有序的Collection,即它用某種特定的插入順序來維護元素順序。用戶能夠對列表中每一個元素的插入位置進行精確地控制,同時能夠根據元素的整數索引(在列表中的位置)訪問元素,並搜索列表中的元素。實現List接口的集合主要有:ArrayList、LinkedList、Vector、Stack。

(1)ArrayList

ArrayList是一個動態數組,也是咱們最經常使用的集合。它容許任何符合規則的元素插入甚至包括null。每個ArrayList都有一個初始容量(10),該容量表明瞭數組的大小。隨着容器中的元素不斷增長,容器的大小也會隨着增長。在每次向容器中增長元素的同時都會進行容量檢查,當快溢出時,就會進行擴容操做。因此若是咱們明確所插入元素的多少,最好指定一個初始容量值,避免過多的進行擴容操做而浪費時間、效率。

  size、isEmpty、get、set、iterator 和 listIterator 操做都以固定時間運行。add 操做以分攤的固定時間運行,也就是說,添加 n 個元素須要 O(n) 時間(因爲要考慮到擴容,因此這不僅是添加元素會帶來分攤固定時間開銷那樣簡單)。

  ArrayList擅長於隨機訪問。同時ArrayList是非同步的。

(2)LinkedList

一樣實現List接口的LinkedList與ArrayList不一樣,ArrayList是一個動態數組,而LinkedList是一個雙向鏈表。因此它除了有ArrayList的基本操做方法外還額外提供了get,remove,insert方法在LinkedList的首部或尾部。

  因爲實現的方式不一樣,LinkedList不能隨機訪問,它全部的操做都是要按照雙重鏈表的須要執行。在列表中索引的操做將從開頭或結尾遍歷列表(從靠近指定索引的一端)。這樣作的好處就是能夠經過較低的代價在List中進行插入和刪除操做。

  與ArrayList同樣,LinkedList也是非同步的。若是多個線程同時訪問一個List,則必須本身實現訪問同步。一種解決方法是在建立List時構造一個同步的List:

List list = Collections.synchronizedList(new LinkedList(...))
2.Set接口
Set是一種不包括重複元素的Collection。它維持它本身的內部排序,因此隨機訪問沒有任何意義。與List同樣,它一樣容許null的存在可是僅有一個。因爲Set接口的特殊性,全部傳入Set集合中的元素都必須不一樣,同時要注意任何可變對象,若是在對集合中元素進行操做時,致使e1.equals(e2)==true,則一定會產生某些問題。Set接口有三個具體實現類,分別是散列集HashSet、鏈式散列集LinkedHashSet和樹形集TreeSet。
Set是一種不包含重複的元素的Collection,無序,即任意的兩個元素e1和e2都有e1.equals(e2)=false,Set最多有一個null元素。須要注意的是:雖然Set中元素沒有順序,可是元素在set中的位置是由該元素的HashCode決定的,其具體位置實際上是固定的。

3.Hash Set
HashSet 是一個沒有重複元素的集合。它是由HashMap實現的,不保證元素的順序(這裏所說的沒有順序是指:元素插入的順序與輸出的順序不一致),並且HashSet容許使用null 元素。HashSet是非同步的,若是多個線程同時訪問一個哈希set,而其中至少一個線程修改了該set,那麼它必須保持外部同步。 HashSet按Hash算法來存儲集合的元素,所以具備很好的存取和查找性能。

HashSet的實現方式大體以下,經過一個HashMap存儲元素,元素是存放在HashMap的Key中,而Value統一使用一個Object對象。

HashSet使用和理解中容易出現的誤區:

a.HashSet中存放null值
HashSet中是容許存入null值的,可是在HashSet中僅僅可以存入一個null值。

b.HashSet中存儲元素的位置是固定的
HashSet中存儲的元素的是無序的,這個沒什麼好說的,可是因爲HashSet底層是基於Hash算法實現的,使用了hashcode,因此HashSet中相應的元素的位置是固定的。

c.必須當心操做可變對象(Mutable Object)。若是一個Set中的可變元素改變了自身狀態致使Object.equals(Object)=true將致使一些問題。

4.Map接口

Map與List、Set接口不一樣,它是由一系列鍵值對組成的集合,提供了key到Value的映射。同時它也沒有繼承Collection。在Map中它保證了key與value之間的一一對應關係。也就是說一個key對應一個value,因此它不能存在相同的key值,固然value值能夠相同。

1.HashMap

以哈希表數據結構實現,查找對象時經過哈希函數計算其位置,它是爲快速查詢而設計的,其內部定義了一個hash表數組(Entry[] table),元素會經過哈希轉換函數將元素的哈希地址轉換成數組中存放的索引,若是有衝突,則使用散列鏈表的形式將全部相同哈希地址的元素串起來,可能經過查看HashMap.Entry的源碼它是一個單鏈表結構。

5.Iterator
1.Iterator

Iterator的定義以下:

public interface Iterator {}
Iterator是一個接口,它是集合的迭代器。集合能夠經過Iterator去遍歷集合中的元素。Iterator提供的API接口以下:

boolean hasNext():判斷集合裏是否存在下一個元素。若是有,hasNext()方法返回 true。
Object next():返回集合裏下一個元素。
void remove():刪除集合裏上一次next方法返回的元素。

使用示例:

public class IteratorExample {
    public static void main(String[] args) {
        ArrayList<String> a = new ArrayList<String>();
        a.add("aaa");
        a.add("bbb");
        a.add("ccc");
        System.out.println("Before iterate : " + a);
        Iterator<String> it = a.iterator();
        while (it.hasNext()) {
            String t = it.next();
            if ("bbb".equals(t)) {
                it.remove();
            }
        }
        System.out.println("After iterate : " + a);
    }
} 

輸出結果以下:
Before iterate : [aaa, bbb, ccc]
After iterate : [aaa, ccc]

第十五章 泛型

1.什麼是泛型
泛型,即「參數化類型」。一提到參數,最熟悉的就是定義方法時有形參,而後調用此方法時傳遞實參。那麼參數化類型怎麼理解呢?顧名思義,就是將類型由原來的具體的類型參數化,相似於方法中的變量參數,此時類型也定義成參數形式(能夠稱之爲類型形參),而後在使用/調用時傳入具體的類型(類型實參)。看着好像有點複雜,首先咱們看下上面那個例子採用泛型的寫法。

1 public class GenericTest {
 2 
 3     public static void main(String[] args) {
 4         /*
 5         List list = new ArrayList();
 6         list.add("qqyumidi");
 7         list.add("corn");
 8         list.add(100);
 9         */
10 
11         List<String> list = new ArrayList<String>();
12         list.add("qqyumidi");
13         list.add("corn");
14         //list.add(100);   // 1  提示編譯錯誤
15 
16         for (int i = 0; i < list.size(); i++) {
17             String name = list.get(i); // 2
18             System.out.println("name:" + name);
19         }
20     }
21 }

採用泛型寫法後,在//1處想加入一個Integer類型的對象時會出現編譯錯誤,經過List ,直接限定了list集合中只能含有String類型的元素,從而在//2處無須進行強制類型轉換,由於此時,集合可以記住元素的類型信息,編譯器已經可以確認它是String類型了。
結合上面的泛型定義,咱們知道在List 中,String是類型實參,也就是說,相應的List接口中確定含有類型形參。且get()方法的返回結果也直接是此形參類型(也就是對應的傳入的類型實參)。下面就來看看List接口的的具體定義:

1 public interface List<E> extends Collection<E> {
 2 
 3     int size();
 4 
 5     boolean isEmpty();
 6 
 7     boolean contains(Object o);
 8 
 9     Iterator<E> iterator();
10 
11     Object[] toArray();
12 
13     <T> T[] toArray(T[] a);
14 
15     boolean add(E e);
16 
17     boolean remove(Object o);
18 
19     boolean containsAll(Collection<?> c);
20 
21     boolean addAll(Collection<? extends E> c);
22 
23     boolean addAll(int index, Collection<? extends E> c);
24 
25     boolean removeAll(Collection<?> c);
26 
27     boolean retainAll(Collection<?> c);
28 
29     void clear();
30 
31     boolean equals(Object o);
32 
33     int hashCode();
34 
35     E get(int index);
36 
37     E set(int index, E element);
38 
39     void add(int index, E element);
40 
41     E remove(int index);
42 
43     int indexOf(Object o);
44 
45     int lastIndexOf(Object o);
46 
47     ListIterator<E> listIterator();
48 
49     ListIterator<E> listIterator(int index);
50 
51     List<E> subList(int fromIndex, int toIndex);
52 }

咱們能夠看到,在List接口中採用泛型化定義以後, 中的E表示類型形參,能夠接收具體的類型實參,而且此接口定義中,凡是出現E的地方均表示相同的接受自外部的類型實參。

天然的,ArrayList做爲List接口的實現類,其定義形式是:

1 public class ArrayList<E> extends AbstractList<E> 
 2         implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
 3     
 4     public boolean add(E e) {
 5         ensureCapacityInternal(size + 1);  // Increments modCount!!
 6         elementData[size++] = e;
 7         return true;
 8     }
 9     
10     public E get(int index) {
11         rangeCheck(index);
12         checkForComodification();
13         return ArrayList.this.elementData(offset + index);
14     }
15     
16     //...省略掉其餘具體的定義過程
17 
18 }

由此,咱們從源代碼角度明白了爲何//1處加入Integer類型對象編譯錯誤,且//2處get()到的類型直接就是String類型了。

自定義泛型接口、泛型類和泛型方法
從上面的內容中,你們已經明白了泛型的具體運做過程。也知道了接口、類和方法也均可以使用泛型去定義,以及相應的使用。是的,在具體使用時,能夠分爲泛型接口、泛型類和泛型方法。

自定義泛型接口、泛型類和泛型方法與上述Java源碼中的List、ArrayList相似。以下,咱們看一個最簡單的泛型類和方法定義:

1 public class GenericTest {
 2 
 3     public static void main(String[] args) {
 4 
 5         Box<String> name = new Box<String>("corn");
 6         System.out.println("name:" + name.getData());
 7     }
 8 
 9 }
10 
11 class Box<T> {
12 
13     private T data;
14 
15     public Box() {
16 
17     }
18 
19     public Box(T data) {
20         this.data = data;
21     }
22 
23     public T getData() {
24         return data;
25     }
26 
27 }

在泛型接口、泛型類和泛型方法的定義過程當中,咱們常見的如T、E、K、V等形式的參數經常使用於表示泛型形參,因爲接收來自外部使用時候傳入的類型實參。那麼對於不一樣傳入的類型實參,生成的相應對象實例的類型是否是同樣的呢?

1 public class GenericTest {
 2 
 3     public static void main(String[] args) {
 4 
 5         Box<String> name = new Box<String>("corn");
 6         Box<Integer> age = new Box<Integer>(712);
 7 
 8         System.out.println("name class:" + name.getClass());      // com.qqyumidi.Box
 9         System.out.println("age class:" + age.getClass());        // com.qqyumidi.Box
10         System.out.println(name.getClass() == age.getClass());    // true
11 
12     }
13 
14 }

由此,咱們發現,在使用泛型類時,雖然傳入了不一樣的泛型實參,但並無真正意義上生成不一樣的類型,傳入不一樣泛型實參的泛型類在內存上只有一個,即仍是原來的最基本的類型(本實例中爲Box),固然,在邏輯上咱們能夠理解成多個不一樣的泛型類型。
究其緣由,在於Java中的泛型這一律念提出的目的,致使其只是做用於代碼編譯階段,在編譯過程當中,對於正確檢驗泛型結果後,會將泛型的相關信息擦出,也就是說,成功編譯事後的class文件中是不包含任何泛型信息的。泛型信息不會進入到運行時階段。
對此總結成一句話:泛型類型在邏輯上看以當作是多個不一樣的類型,實際上都是相同的基本類型。
statistics.sh腳本運行結果的截圖

相關文章
相關標籤/搜索