Java基础:JDK1.5新特性
1. ENUM枚舉
1.1 枚舉概述
枚舉是指將變量的值一一列出來,變量的值只限于列舉出來的值的范圍內。舉例:一周只有7天,一年只有12個月等。
回想單例設計模式:單例類是一個類只有一個實例
那么多例類就是一個類有多個實例,但不是無限個數的實例,而是有限個數的實例。這才能是枚舉類。
格式是:只有枚舉項的枚舉類
public enum 枚舉類名 {枚舉項1,枚舉項2,枚舉項3…;}1.2 注意事項
- 定義枚舉類要用關鍵字enum
- 所有枚舉類都是Enum的子類
- 枚舉類的第一行上必須是枚舉項,最后一個枚舉項后的分號是可以省略的,但是如果枚舉類有其他的東西,這個分號就不能省略。建議不要省略
- 枚舉類可以有構造器,但必須是private的,它默認的也是private的。枚舉項的用法比較特殊:枚舉(“”);
- 枚舉類也可以有抽象方法,但是枚舉項必須重寫該方法
- 枚舉在switch語句中的使用
1.3 枚舉類中的幾個常見方法
1.4 枚舉的應用
用法一:常量
在JDK1.5 之前,我們定義常量都是: publicstaticfianl…. 。現在好了,有了枚舉,可以把相關的常量分組到一個枚舉類型里,而且枚舉提供了比常量更多的方法。
public enum Color {RED, GREEN, BLANK, YELLOW }用法二:SWITCH
JDK1.6之前的switch語句只支持int,char,enum類型,使用枚舉,能讓我們的代碼可讀性更強。
enum Signal {GREEN, YELLOW, RED } public class TrafficLight {Signal color = Signal.RED;public void change() {switch (color) {case RED:color = Signal.GREEN;break;case YELLOW:color = Signal.RED;break;case GREEN:color = Signal.YELLOW;break;}} }用法三:向枚舉中添加新方法
如果打算自定義自己的方法,那么必須在enum實例序列的最后添加一個分號。而且 Java 要求必須先定義 enum 實例。
public enum Color {RED("紅色", 1), GREEN("綠色", 2), BLANK("白色", 3), YELLO("黃色", 4);// 成員變量private String name;private int index;// 構造方法private Color(String name, int index) {this.name = name;this.index = index;}// 普通方法public static String getName(int index) {for (Color c : Color.values()) {if (c.getIndex() == index) {return c.name;}}return null;}// get set 方法public String getName() {return name;}public void setName(String name) {this.name = name;}public int getIndex() {return index;}public void setIndex(int index) {this.index = index;} }用法四:覆蓋枚舉的方法
下面給出一個toString()方法覆蓋的例子。
public enum Color {RED("紅色", 1), GREEN("綠色", 2), BLANK("白色", 3), YELLO("黃色", 4);// 成員變量private String name;private int index;// 構造方法private Color(String name, int index) {this.name = name;this.index = index;}//覆蓋方法@Overridepublic String toString() {return this.index+"_"+this.name;} }用法五:實現接口
所有的枚舉都繼承自java.lang.Enum類。由于Java 不支持多繼承,所以枚舉對象不能再繼承其他類。
public interface Behaviour {void print();String getInfo(); } public enum Color implements Behaviour{RED("紅色", 1), GREEN("綠色", 2), BLANK("白色", 3), YELLO("黃色", 4);// 成員變量private String name;private int index;// 構造方法private Color(String name, int index) {this.name = name;this.index = index;} //接口方法@Overridepublic String getInfo() {return this.name;}//接口方法@Overridepublic void print() {System.out.println(this.index+":"+this.name);} }用法六:使用接口組織枚舉
public interface Food {enum Coffee implements Food{BLACK_COFFEE,DECAF_COFFEE,LATTE,CAPPUCCINO}enum Dessert implements Food{FRUIT, CAKE, GELATO} }把常量定義在接口里與類里都能通過編譯,那2者到底有什么區別呢?那個更合理?
1.5 常量接口
public interface ConstInterfaceA { public static final String CONST_A = "aa"; public static final String CONST_C = "ac"; }存在問題:
無法限制開發員繼承/實現接口.
開發員能夠在子接口里繼續添加常量.而這些常量可能得不到祖先層的支持.
常量作為參數時,是String,int等弱類型,開發員可以傳入沒有在常量接口里定義的值,這個問題無法通過編譯器發現.
由于開發員可以直接寫常量值, 所以不能用==對比,只能用equals對比,不能優化性能
開發員在沒有參考資料時,不可能知道某個int型的參數到底應該賦什么內容.
編譯時,是直接把常量的值編譯到類的二進制代碼里,常量的值在升級中變化后,需要重新編譯所有引用常量的類,因為里面存的是舊值.
常量類
public class ConstClassA { public static final String CONST_A = "aa"; public static final String CONST_C = "ac"; private ConstClassA() { } }常量類可以設置構造函數為private,從而限制繼承,也就沒有繼續添加常量的問題了.
但是其他問題與常量接口一樣無法解決
枚舉常量類
public class EnumClassA { private String name; private EnumClassA(String name) { this.name = name; } public static final EnumClassA CONST_A = new EnumClassA("aa"); public static final EnumClassA CONST_C = new EnumClassA("ac"); }解決了以上所有問題,主要體現在:
私有構造函數,避免被繼承和擴展.
定義方法的參數時,必須用枚舉常量類類型,如上面的EnumClassA類型,這樣就轉變成了強類型,不會出現弱類型引起的問題.
常量值地址唯一,可以用==直接對比,性能會有提高.
開發員可以根據該參數類型打開對應的類,從而找到定義的常量.
編譯時,沒有把常量值編譯到代碼里,即使常量的值發生變化也不會影響引用常量的類.
enum類型
public static enum Grade { A(4), B(3), C(2), D(1), F(0); private int points; Grade(int points) { this.points = points; } int getPoints() { return points; } };這是JDK1.5引入的,其實就是枚舉常量類的代碼封裝簡化而已。查看enum反編譯后的代碼與枚舉常量類的結構非常相似。這可能是因為java的設計者一開始覺得enum與OO思想不符,所以沒有提供支持,但是隨著常量接口的濫用和枚舉常量類方案的出現,才在JDK1.5里增加了enum
2. 靜態導入
1、要使用用靜態成員(方法和變量)我們必須給出提供這個方法的類。使用靜態導入可以使被導入類的所有靜態變量和靜態方法在當前類直接可見,使用這些靜態成員無需再給出他們的類名。
2、不過,過度使用這個特性也會一定程度上降低代碼地可讀性。
3、格式:import static 包名….類名.方法名;
4、靜態導入的注意事項:
- 方法必須是靜態的
- 如果有多個同名的靜態方法,容易不知道使用誰?這個時候要使用,必須加前綴。由此可見,意義不大,所以一般不用,但是要能看懂。
3. 增強for循環
1、增強for:是for循環的一種。
2、格式:
3、好處:簡化了數組和集合的遍歷。
4、弊端: 增強for的目標不能為null。
5、如何解決呢?對增強for的目標先進行不為null的判斷,然后在使用。
4. 可變參數
1、可變參數概述:定義方法的時候不知道該定義多少個參數
2、格式
注意:
- 這里的變量其實是一個數組
- 如果一個方法有可變參數,并且有多個參數,那么,可變參數肯定是最后一個
代碼示例1:
package cn.itcast_03;/** 可變參數:定義方法的時候不知道該定義多少個參數* 格式:* 修飾符 返回值類型 方法名(數據類型… 變量名){* * }* * 注意:* 這里的變量其實是一個數組* 如果一個方法有可變參數,并且有多個參數,那么,可變參數肯定是最后一個*/ public class ArgsDemo {public static void main(String[] args) {// 2個數據求和int a = 10;int b = 20;int result = sum(a, b);System.out.println("result:" + result);// 3個數據的求和int c = 30;result = sum(a, b, c);System.out.println("result:" + result);// 4個數據的求和int d = 30;result = sum(a, b, c, d);System.out.println("result:" + result);// 需求:我要寫一個求和的功能,到底是幾個數據求和呢,我不太清楚,但是我知道在調用的時候我肯定就知道了// 為了解決這個問題,Java就提供了一個東西:可變參數result = sum(a, b, c, d, 40);System.out.println("result:" + result);result = sum(a, b, c, d, 40, 50);System.out.println("result:" + result);}public static int sum(int... a) {// System.out.println(a);//return 0;int s = 0;for(int x : a){s +=x;}return s;}// public static int sum(int a, int b, int c, int d) {// return a + b + c + d;// }//// public static int sum(int a, int b, int c) {// return a + b + c;// }//// public static int sum(int a, int b) {// return a + b;// } }運行結果:
代碼示例2:把數組轉成集合
package cn.itcast_03;import java.util.Arrays; import java.util.List;/** public static <T> List<T> asList(T... a):把數組轉成集合* * 注意事項:* 雖然可以把數組轉成集合,但是集合的長度不能改變。*/ public class ArraysDemo {public static void main(String[] args) {// 定義一個數組// String[] strArray = { "hello", "world", "java" };// List<String> list = Arrays.asList(strArray);List<String> list = Arrays.asList("hello", "world", "java");// UnsupportedOperationException// list.add("javaee");// UnsupportedOperationException// list.remove(1);list.set(1, "javaee");for (String s : list) {System.out.println(s);}} }運行結果:
5. 基本數據類型的自動拆箱與裝箱
1、自動裝箱:把基本類型轉換為包裝類類型
2、自動拆箱:把包裝類類型轉換為基本類型
1. 泛型
6.1 泛型概述
泛型(Generic type 或者 generics)是對 Java 語言的類型系統的一種擴展,以支持創建可以按類型進行參數化的類。可以把類型參數看作是使用參數化類型時指定的類型的一個占位符,就像方法的形式參數是運行時傳遞的值的占位符一樣。
泛型是Java SE 1.5的新特性,泛型的本質是參數化類型,也就是說所操作的數據類型被指定為一個參數。這種參數類型可以用在類、接口和方法的創建中,分別稱為泛型類、泛型接口、泛型方法。 Java語言引入泛型的好處是安全簡單。
在Java SE 1.5之前,沒有泛型的情況的下,通過對類型Object的引用來實現參數的“任意化”,“任意化”帶來的缺點是要做顯式的強制類型轉換,而這種轉換是要求開發者對實際參數類型可以預知的情況下進行的。對于強制類型轉換錯誤的情況,編譯器可能不提示錯誤,在運行的時候才出現異常,這是一個安全隱患。
泛型的好處是在編譯的時候檢查類型安全,并且所有的強制轉換都是自動和隱式的,以提高代碼的重用率。
可以在集合框架(Collection framework)中看到泛型的動機。例如,Map 類允許您向一個 Map添加任意類的對象,即使最常見的情況是在給定映射(map)中保存某個特定類型(比如 String)的對象。
因為 Map.get() 被定義為返回 Object,所以一般必須將 Map.get() 的結果強制類型轉換為期望的類型,如下面的代碼所示:
Map m = new HashMap();m.put("key", "blarg");String s = (String) m.get("key");要讓程序通過編譯,必須將 get() 的結果強制類型轉換為 String,并且希望結果真的是一個 String。但是有可能某人已經在該映射中保存了不是 String 的東西,這樣的話,上面的代碼將會拋出 ClassCastException。
理想情況下,您可能會得出這樣一個觀點,即 m 是一個 Map,它將 String 鍵映射到 String 值。這可以讓您消除代碼中的強制類型轉換,同時獲得一個附加的類型檢查層,該檢查層可以防止有人將錯誤類型的鍵或值保存在集合中。這就是泛型所做的工作。
package cn.itcast_01;import java.util.ArrayList; import java.util.Iterator;/** ArrayList存儲字符串并遍歷* * 我們按照正常的寫法來寫這個程序, 結果確出錯了。* 為什么呢?* 因為我們開始存儲的時候,存儲了String和Integer兩種類型的數據。* 而在遍歷的時候,我們把它們都當作String類型處理的,做了轉換,所以就報錯了。* 但是呢,它在編譯期間卻沒有告訴我們。* 所以,我就覺得這個設計的不好。* 回想一下,我們的數組* String[] strArray = new String[3];* strArray[0] = "hello";* strArray[1] = "world";* strArray[2] = 10;* 集合也模仿著數組的這種做法,在創建對象的時候明確元素的數據類型。這樣就不會在有問題了。* 而這種技術被稱為:泛型。* * 泛型:是一種把類型明確的工作推遲到創建對象或者調用方法的時候才去明確的特殊的類型。參數化類型,把類型當作參數一樣的傳遞。* 格式:* <數據類型>* 此處的數據類型只能是引用類型。* 好處:* A:把運行時期的問題提前到了編譯期間* B:避免了強制類型轉換* C:優化了程序設計,解決了黃色警告線*/ public class GenericDemo {public static void main(String[] args) {// 創建ArrayList<String> array = new ArrayList<String>();// 添加元素array.add("hello");array.add("world");array.add("java");// array.add(new Integer(100));//array.add(10); // JDK5以后的自動裝箱// 等價于:array.add(Integer.valueOf(10));// 遍歷Iterator<String> it = array.iterator();while (it.hasNext()) {// ClassCastException// String s = (String) it.next();String s = it.next();System.out.println(s);}// 看下面這個代碼// String[] strArray = new String[3];// strArray[0] = "hello";// strArray[1] = "world";// strArray[2] = 10;} }6.2 泛型的好處
Java 語言中引入泛型是一個較大的功能增強。不僅語言、類型系統和編譯器有了較大的變化,以支持泛型,而且類庫也進行了大翻修,所以許多重要的類,比如集合框架,都已經成為泛型化的了。這帶來了很多好處:
6.2.1 類型安全。
泛型的主要目標是提高 Java 程序的類型安全。通過知道使用泛型定義的變量的類型限制,編譯器可以在一個高得多的程度上驗證類型假設。沒有泛型,這些假設就只存在于程序員的頭腦中(或者如果幸運的話,還存在于代碼注釋中)。
Java 程序中的一種流行技術是定義這樣的集合,即它的元素或鍵是公共類型的,比如“String 列表”或者“String 到 String 的映射”。通過在變量聲明中捕獲這一附加的類型信息,泛型允許編譯器實施這些附加的類型約束。類型錯誤現在就可以在編譯時被捕獲了,而不是在運行時當作 ClassCastException 展示出來。將類型檢查從運行時挪到編譯時有助于您更容易找到錯誤,并可提高程序的可靠性。
6.2.2 消除強制類型轉換。
泛型的一個附帶好處是,消除源代碼中的許多強制類型轉換。這使得代碼更加可讀,并且減少了出錯機會。
盡管減少強制類型轉換可以降低使用泛型類的代碼的羅嗦程度,但是聲明泛型變量會帶來相應的羅嗦。
3、優化了程序設計,解決了黃色警告線
6.3 泛型的應用
6.3.1 泛型的內部原理
泛型是提供給javac編譯器使用的,可以限定集合中的輸入類型,讓編譯器擋住源程序中的非法輸入。但是,編譯器編譯帶類型說明的集合時會去除掉“類型”信息,目的就是使程序運行效率不受影響。因此,對于參數化的泛型類型,getClass()方法的返回值和原始類型完全一樣。
package com.itheima.day2;import java.util.ArrayList;public class GenericTest {public static void main(String[] args) {ArrayList<String> collection1 = new ArrayList<String>();ArrayList collection2 = new ArrayList();System. out.println(collection1.getClass() == collection2.getClass());//結果:true} }由于編譯生成的字節碼會去掉泛型的類型信息,只要能跳過編譯器,就可以往某個泛型集合中加入其它類型的數據,例如,用反射得到集合,再調用其add方法即可。
package com.itheima.day2;import java.util.ArrayList;public class GenericTest {public static void main(String[] args) throws Exception {ArrayList<Integer> collection1 = new ArrayList<Integer>();collection1.getClass().getMethod( "add",Object.class).invoke(collection1, "abc");System. out.println(collection1.get(0));} }ArrayList類定義和ArrayList類引用中涉及如下術語:
- 整個稱為ArrayList泛型類型
- ArrayList<E>中的E稱為類型變量或類型參數
- 整個ArrayList<Integer>稱為參數化的類型
- ArrayList<Integer>中的Integer稱為類型參數的實例或實際類型參數
- ArrayList<Integer>中的<>念著typeof
- ArrayList稱為原始類型
參數化類型與原始類型的兼容性:參數化類型可以引用一個原始類型的對象,編譯報告警告,例如
Collection<String> c = new Vector();//考慮到對以前代碼的兼容性,編譯器是可以通過的原始類型可以引用一個參數化類型的對象,編譯報告警告,例如
Collection c = new Vector<String>();//原來的方法接受一個集合參數,新的類型也要能傳進去參數化類型不考慮類型參數的繼承關系:
Vector<String> v = new Vector<Object>(); //錯誤!不寫<Object>沒錯,寫了就是明知故犯 Vector<Object> v = new Vector<String>(); //也錯誤!注意:
假設Vector<String> v = new Vector<Object>();可以的話,那么以后從v中取出的對象當作String用,而v實際指向的對象中可以加入任意的類型對象;
假設Vector<Object> v = new Vector<String>();可以的話,那么以后可以向v中加入任意的類型對象,而v實際指向的集合中只能裝String類型的對象。
編譯器不允許創建泛型變量的數組。即在創建數組實例時,數組的元素不能使用參數化的類型。
例如,下面語句有錯誤:
Vector<Integer> vectorList[] = new Vector<Integer>[10];思考題:
下面的代碼會報錯誤嗎?
Vector v1 = new Vector<String>(); Vector<Object> v = v1;答案:編譯的時候是不會報錯的,因為編譯器是一行一行按照語法檢查代碼的,因此不會出錯。
6.4 泛型類
把泛型定義在類上,格式:public class 類名<泛型類型1,…>,注意:泛型類型必須是引用類型
package cn.itcast_04;/** 泛型類的測試*/ public class ObjectToolDemo {public static void main(String[] args) {// ObjectTool ot = new ObjectTool();//// ot.setObj(new String("風清揚"));// String s = (String) ot.getObj();// System.out.println("姓名是:" + s);//// ot.setObj(new Integer(30));// Integer i = (Integer) ot.getObj();// System.out.println("年齡是:" + i);// ot.setObj(new String("林青霞"));// // ClassCastException// Integer ii = (Integer) ot.getObj();// System.out.println("姓名是:" + ii);System.out.println("-------------");ObjectTool<String> ot = new ObjectTool<String>();// ot.setObj(new Integer(27)); //這個時候編譯期間就過不去ot.setObj(new String("林青霞"));String s = ot.getObj();System.out.println("姓名是:" + s);ObjectTool<Integer> ot2 = new ObjectTool<Integer>();// ot2.setObj(new String("風清揚"));//這個時候編譯期間就過不去ot2.setObj(new Integer(27));Integer i = ot2.getObj();System.out.println("年齡是:" + i);} } //泛型類:把泛型定義在類上 class ObjectTool<T> {private T obj;public T getObj() {return obj;}public void setObj(T obj) {this.obj = obj;} }6.5 泛型方法
把泛型定義在方法上,格式:public <泛型類型> 返回類型 方法名(泛型類型 .)
package cn.itcast_05;public class ObjectToolDemo {public static void main(String[] args) {// ObjectTool ot = new ObjectTool();// ot.show("hello");// ot.show(100);// ot.show(true);// ObjectTool<String> ot = new ObjectTool<String>();// ot.show("hello");//// ObjectTool<Integer> ot2 = new ObjectTool<Integer>();// ot2.show(100);//// ObjectTool<Boolean> ot3 = new ObjectTool<Boolean>();// ot3.show(true);// 定義泛型方法后ObjectTool ot = new ObjectTool();ot.show("hello");ot.show(100);ot.show(true);} } //泛型方法:把泛型定義在方法上 class ObjectTool {public <T> void show(T t) {System.out.println(t);} }6.6 泛型接口
把泛型定義在接口上,格式:public interface 接口名<泛型類型1…>
package cn.itcast_06;public class InterDemo {public static void main(String[] args) {// 第一種情況的測試// Inter<String> i = new InterImpl();// i.show("hello");// // 第二種情況的測試Inter<String> i = new InterImpl<String>();i.show("hello");Inter<Integer> ii = new InterImpl<Integer>();ii.show(100);} } //泛型接口:把泛型定義在接口上 interface Inter<T> {public abstract void show(T t); } /實現類在實現接口的時候 //第一種情況:已經知道該是什么類型的了//public class InterImpl implements Inter<String> { // // @Override // public void show(String t) { // System.out.println(t); // } // }//第二種情況:還不知道是什么類型的 class InterImpl<T> implements Inter<T> {@Overridepublic void show(T t) {System.out.println(t);} }6.7 泛型高級(通配符)
為了解決類型被限制死了不能動態根據實例來確定的缺點,引入了“通配符泛型”,針對上面的例子,使用通配泛型格式為
package cn.itcast_07;import java.util.ArrayList; import java.util.Collection;/** 泛型高級(通配符)* ?:任意類型,如果沒有明確,那么就是Object以及任意的Java類了* ? extends E:向下限定,E及其子類* ? super E:向上限定,E極其父類*/ public class GenericDemo {public static void main(String[] args) {// 泛型如果明確的寫的時候,前后必須一致Collection<Object> c1 = new ArrayList<Object>();// Collection<Object> c2 = new ArrayList<Animal>();// Collection<Object> c3 = new ArrayList<Dog>();// Collection<Object> c4 = new ArrayList<Cat>();// ?表示任意的類型都是可以的Collection<?> c5 = new ArrayList<Object>();Collection<?> c6 = new ArrayList<Animal>();Collection<?> c7 = new ArrayList<Dog>();Collection<?> c8 = new ArrayList<Cat>();// ? extends E:向下限定,E及其子類// Collection<? extends Animal> c9 = new ArrayList<Object>();Collection<? extends Animal> c10 = new ArrayList<Animal>();Collection<? extends Animal> c11 = new ArrayList<Dog>();Collection<? extends Animal> c12 = new ArrayList<Cat>();// ? super E:向上限定,E極其父類Collection<? super Animal> c13 = new ArrayList<Object>();Collection<? super Animal> c14 = new ArrayList<Animal>();// Collection<? super Animal> c15 = new ArrayList<Dog>();// Collection<? super Animal> c16 = new ArrayList<Cat>();} }class Animal { }class Dog extends Animal { }class Cat extends Animal { }總結
以上是生活随笔為你收集整理的Java基础:JDK1.5新特性的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java基础:网络编程
- 下一篇: Android提醒:Dialog,Toa