Java數(shù)據(jù)類型有兩大種:引用數(shù)據(jù)類型和原始數(shù)據(jù)類型。 引用數(shù)據(jù)類型:interface、class、Array、enum 原始數(shù)據(jù)類型:byte(字節(jié))、short(短整型)、int(整數(shù)型)、long(長(zhǎng)整型)、float(單精度浮點(diǎn)數(shù)類型)、double(雙精度浮點(diǎn)數(shù)類型)、char(字符類型)、boolean(布爾類型)總共8種。
在JDK5中,switch可以接受五種數(shù)據(jù)類型:byte、short、int、char、enum。 在JDK7中,switch可以接受六種數(shù)據(jù)類型:多了一種String。
將做什么(interface)和怎么做分開(實(shí)現(xiàn))。
JDK5之前,對(duì)象保存到集合中就會(huì)失去其特性,取出的時(shí)候通常需要程序猿手工進(jìn)行類型的強(qiáng)制轉(zhuǎn)化,這樣不可避免的就會(huì)引發(fā)程序的一些安全性問題。 眾所周知,Java的開發(fā)者是一批C++工程師。在C++的語法中,有一門模板技術(shù)。事實(shí)上,Java5.0的泛型本質(zhì)上就是C++的模板。
從集合取出對(duì)象的時(shí)候,數(shù)據(jù)類型丟失。取出的時(shí)候,需要使用與類型相關(guān)方法,進(jìn)行強(qiáng)制類型轉(zhuǎn)換操作。
程序是存在安全隱患。比如下面的例子:
/** * 泛型測(cè)試 * */public class GenericTest { @Test public void demo1() { // JDK5之前集合對(duì)象 List list = new ArrayList(); // 因?yàn)镴DK5之前沒有泛型的類型檢查,添加的對(duì)象可以是任意類型 list.add("abc"); list.add(123); // 操作集合中對(duì)象,遍歷集合將數(shù)據(jù)取出來通過size()方法和get(index)方法 // 進(jìn)行類型的判斷處理 for (int i = 0; i < list.size(); i++) { // 提取對(duì)象的時(shí)候,數(shù)據(jù)類型丟失了 Object obj = list.get(i); // 操作String方法 -- 強(qiáng)制將數(shù)據(jù)轉(zhuǎn)換成響應(yīng)類型 String s = (String) obj; System.out.PRintln(s.toUpperCase()); } }}Java會(huì)報(bào)錯(cuò):
java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String(Integer 類型不能轉(zhuǎn)化為String類型)在這里,我們將上述代碼放在main函數(shù)中進(jìn)行javac編譯
public class GenericTest { public static void main(String[] args) { // JDK5之前集合對(duì)象 List list = new ArrayList(); // 因?yàn)镴DK5之前沒有泛型的類型檢查,添加的對(duì)象可以是任意類型 list.add("abc"); list.add(123); // 操作集合中對(duì)象,遍歷集合將數(shù)據(jù)取出來通過size()方法和get(index)方法 // 進(jìn)行類型的判斷處理 for (int i = 0; i < list.size(); i++) { // 提取對(duì)象的時(shí)候,數(shù)據(jù)類型丟失了 Object obj = list.get(i); // 操作String方法 -- 強(qiáng)制將數(shù)據(jù)轉(zhuǎn)換成響應(yīng)類型 String s = (String) obj; System.out.println(s.toUpperCase()); } }}在cmd中javac編譯會(huì)出現(xiàn)以下錯(cuò)誤提示:
D:/Java/Workspaces/MyEclipse 2015/day03/src/cn/idcast/jdk5>javac -Xlint:unchecked GenericTest.javaGenericTest.java:53: 警告: [unchecked] 對(duì)作為原始類型List的成員的add(E)的調(diào)用未經(jīng)過檢查 list.add("abc"); ^ 其中, E是類型變量: E擴(kuò)展已在接口 List中聲明的ObjectGenericTest.java:54: 警告: [unchecked] 對(duì)作為原始類型List的成員的add(E)的調(diào)用未經(jīng)過檢查 list.add(123); ^ 其中, E是類型變量: E擴(kuò)展已在接口 List中聲明的Object2 個(gè)警告JDK5中的泛型允許程序猿使用泛型技術(shù)限制集合的處理類型。
List list = new ArrayList<String>();注意:泛型是提供給javac編譯器使用的,他用于限定集合的輸入類型,讓編譯器在源代碼級(jí)別上,即擋住喜愛那個(gè)集合中插入非法數(shù)據(jù)。但編譯器編譯完帶有泛型的java程序之后,生成的class文件中將不在帶有泛型信息,以此使程序運(yùn)行效率不受到影響,這個(gè)過程稱之為“擦除”。 泛型技術(shù),其實(shí)只是編譯器階段技術(shù),為javac命令起到了類型安全檢查的作用,生成.class文件后,泛型信息將會(huì)被擦除。 泛型的基本術(shù)語,以ArrayList< E >為例:<> 念著typeof
ArrayList< E >中的E稱為類型參數(shù)變量ArrayList< Interger >中的Integer稱為實(shí)際類型參數(shù)整個(gè)ArrayList< Interger >稱為參數(shù)化類型ParameterizedTypeList< String >稱為參數(shù)化類型List < 泛型類型> 規(guī)定了List集合中的元素類型,取出集合中的元素的時(shí)候,獲得具體數(shù)據(jù)類型元素(不需要進(jìn)行類型強(qiáng)制轉(zhuǎn)換)。
使用類型安全的List并將List中的元素用三種不同的方式輸出。
@Testpublic void demo3() { // 使用類型的List List<String> list = new LinkedList<String>(); list.add("abc"); list.add("qwe"); list.add("asd"); // 因?yàn)槭褂梅盒?,只能將list添加String類型元素 // 遍歷List --三種方案 // 第一種是因?yàn)長(zhǎng)ist是有序的(存入順序和取出順序是一樣的),通過size和get方法進(jìn)行遍歷 for (int i = 0; i < list.size(); i++) { String str = list.get(i); System.out.println(str); } System.out.println("--------------------------"); // 第二種因?yàn)長(zhǎng)ist繼承了Collection接口通過Collection的iterator進(jìn)行遍歷 Iterator<String> iterator = list.iterator(); while (iterator.hasNext()) { String str = iterator.next(); System.out.println(str); } System.out.println("--------------------------"); // 第三種 JDK5引入了foreach循環(huán)結(jié)構(gòu),通過foreach結(jié)構(gòu)遍歷list for (String str : list) { System.out.println(str); } System.out.println("--------------------------");}輸出結(jié)果:
abcqweasd--------------------------abcqweasd--------------------------abcqweasd--------------------------使用類型安全的Set并將Set中的元素用兩種不同的方式輸出。
@Testpublic void demo4() { // 使用類型安全的Set Set<String> set = new TreeSet<String>(); set.add("qwe"); set.add("asd"); set.add("zxc"); // 因?yàn)槭褂梅盒?,只能添加String類型元素 // 取出Set元素 -- 因?yàn)镾et是無序的,所以比List少一種遍歷方法 // 第一種,繼承Collection 所以使用Iterator遍歷 Iterator<String> iterator = set.iterator(); while (iterator.hasNext()) { String str = iterator.next(); System.out.println(str); } System.out.println("--------------------------"); // 第二種,由于JDK5引入了foreach 可以使用foreach遍歷set for (String str : set) { System.out.println(str); }}輸出結(jié)果:
asdqwezxc--------------------------asdqwezxc使用類型安全的Map并將Map中的元素用兩種不同的方式輸出。
@Testpublic void demo5() { // 使用類型安全的Map,因?yàn)镸ap是一個(gè)鍵值對(duì)結(jié)構(gòu),執(zhí)行兩個(gè)類型泛型 Map<String,String> map = new HashMap<String, String>(); map.put("aaa","111"); map.put("sss", "222"); map.put("ddd", "333"); // 因?yàn)槭褂昧朔盒?,所以key和value類型必須是String // 取出map元素 -- 兩種方法 // 第一種 通過map中的keySet進(jìn)行遍歷 Set<String> keys = map.keySet(); for (String key : keys) { System.out.println(key + ":" + map.get(key)); } System.out.println("--------------------------"); // 第二種 通過map的entrySet ---- 獲得每一個(gè)鍵值對(duì) Set<Map.Entry<String, String>> entrySet = map.entrySet(); // 就是一個(gè)鍵值對(duì) for (Entry<String, String> entry : entrySet) { // 通過entry的getkey和getValue獲得每一個(gè)鍵和值 System.out.println(entry.getKey() + ":" + entry.getValue()); }}輸出結(jié)果:
aaa:111ddd:333sss:222--------------------------aaa:111ddd:333sss:2221.定義泛型方法,必須在方法的返回值之前進(jìn)行泛型類型聲明 < 泛型類型 >
@Testpublic void demo6() { Integer[] arr1 = new Integer[] { 1, 2, 3, 4, 5 }; changePostion(arr1, 0, 2); System.out.println(Arrays.toString(arr1));}// 使用泛型技術(shù)編寫通用的數(shù)組位置交換代碼public <T> void changePostion(T[] arr, int index1, int index2) { T temp = arr[index1]; arr[index1] = arr[index2]; arr[index2] = temp;}2.自定義泛型類,如果一個(gè)類多處要用到同一個(gè)泛型,這時(shí)可以把泛型定義在類上(即類級(jí)別的泛型),語法格式如下:
public class GenericDao<T> { private T field1; public void save (T obj) {} public T getId(int id) {}}當(dāng)類的泛型使用后,該類中所有方法都可以直接使用泛型。 對(duì)應(yīng)泛型類型參數(shù)的起名可以是任何大寫字母,但是建議使用有意義的字母來命名。 注意:類的泛型不對(duì)static起作用。
泛型:代表了任意類型; ?:任意泛型類型。
通過上下邊界,限制通配符類型范圍:
? extends Number :表示Number(包含Number)的任意子類型。 ?super String :表示String(包含String)的任意父類型。
范例一 比如在Set中的addAll(Collection< ? extends E > c) 方法。 Set中addAll(Collection< ? extends E > c) 表示將目標(biāo)集合c的內(nèi)容添加到當(dāng)前set中,? extends E表示目標(biāo)集合是E的子類型。
Set< Number > set = new HashSet< Number >();List< Integer > list = new ArrayList< Integer >();set.addAll(list); // list 中 Integer 自動(dòng)轉(zhuǎn)換為 Number范例二
比如在TreeSet中的構(gòu)造方法:TreeSet(Comparator< ? super E > comparator)
// 默認(rèn)需要蘋果比較器排序,但是我們?nèi)鄙貯pple比較器,所以我們需要自定義一個(gè)Set< Apple > set = new TreeSet< Apple >(); class FruitComparator implements Comparator< Fruit > { ......}// 需要Apple比較器 ,傳入 Fruit比較器。// 因?yàn)槟鼙容^水果的比較器,那么也能比較AppleSet< Apple > set = new TreeSet< Apple >(new FruitComparator());因?yàn)橐恍┓椒ㄔ谶\(yùn)行的時(shí)候,他需要的數(shù)據(jù)不能是任意的,而必須是一定范圍內(nèi)的值,此類問題在JDK5以前采用自定義帶有枚舉功能的類解決,Java5之后可以直接使用枚舉予以解決。 JDK5新增的enum關(guān)鍵字用于定義一個(gè)枚舉類。 創(chuàng)建枚舉格式: enum 枚舉類型名稱 { 枚舉對(duì)象名稱1, 枚舉對(duì)象名稱2, …… 枚舉對(duì)象名稱n; ?。? 作用:可讀性良好,阻止非法數(shù)據(jù)。
最初Java是沒有枚舉的。 1.第一個(gè)版本之String類型
employee.role1 = "BOSS";employee.role1 = "MANAGE";// 因?yàn)榻巧且粋€(gè)String// 如果經(jīng)理字符拼錯(cuò)了,程序就會(huì)出現(xiàn)問題employee.role1 = "WORKER";這個(gè)版本的確定就是字符可能輸錯(cuò),到時(shí)程序出錯(cuò)。當(dāng)變量過于大的時(shí)候,管理難度大,出錯(cuò)率高。 2.第一個(gè)版本之int類型
employee.role2 = 1;// 可讀性太差了employee.role2 = 4;// 非法數(shù)據(jù)比如上述形式的變量,這種方法具有可讀性差,無法阻止非法數(shù)據(jù)的缺點(diǎn)。 3.第二個(gè)版本之自定義類實(shí)現(xiàn)枚舉
class Role3 { public static final Role3 BOSS = new Role3(); public static final Role3 MANAGER = new Role3(); public static final Role3 WORKER = new Role3(); // 私有構(gòu)造方法,組織非法數(shù)據(jù) private Role3() { }}提高了可讀性,阻止了非法的初始化。 4.JDK5引入了enum
enum Role4 { // JDK5 以后引用枚舉技術(shù) 簡(jiǎn)化對(duì)象創(chuàng)建 ---- 功能等價(jià)于 Role3 BOSS, MANAGER, WORKER;}事實(shí)上,enum類是于構(gòu)造Role3。Enum主要的作用是簡(jiǎn)化了對(duì)象的創(chuàng)建…… 注意:枚舉類構(gòu)造器,必須是私有的(private)。
package cn.idcast.jdk5;public class EnumTest {} 全部代碼:/** * 假如我們需要定義一個(gè)角色屬性(公司內(nèi)角色只有三種:BOSS,MANAGER,WORKER) 定義員工角色, * 在三個(gè)值中取一個(gè) * 1 BOSS 2 MANAGE 3 WORKER * */class Employee { private String role1; private int role2; // 在JDK5之前,沒有枚舉,只能通過自定義類,實(shí)現(xiàn)枚舉功能 private Role3 role3; // 在JDK5之后,引入了枚舉,使用枚舉表示多個(gè)角色 private Role4 role4; public static void main(String[] args) { Employee employee = new Employee(); employee.role1 = "BOSS"; employee.role1 = "MANAGE"; // 因?yàn)榻巧且粋€(gè)String // 如果經(jīng)理字符拼錯(cuò)了,程序就會(huì)出現(xiàn)問題 employee.role1 = "WORKER"; // --------------------------- employee.role2 = 1;// 可讀性太差了 employee.role2 = 4;// 非法數(shù)據(jù) // --------------------------- // 定義Role2類,定義一組int常量,用來表示角色 employee.role2 = Role2.BOSS; // 可讀性大大提高了 employee.role2 = -1; // 非法數(shù)據(jù) // --------------------------- // 通過自定義Role3實(shí)現(xiàn)枚舉功能 employee.role3 = Role3.BOSS; // employee.role3 = new Role3(); // 數(shù)據(jù) 非法 // 使用枚舉之后 employee.role4 = Role4.BOSS;// 可讀性良好,阻止非法數(shù)據(jù)、 // employee.role4 = new Role4(); // 私有構(gòu)造方法,從這里可以看出在enum中的構(gòu)造方法也是private的 }}class Role2 { public static final int BOSS = 1; public static final int MANAGER = 2; public static final int WORKER = 3;}class Role3 { public static final Role3 BOSS = new Role3(); public static final Role3 MANAGER = new Role3(); public static final Role3 WORKER = new Role3(); // 私有構(gòu)造方法,組織非法數(shù)據(jù) private Role3() { }}enum Role4 { // JDK5 以后引用枚舉技術(shù) 簡(jiǎn)化對(duì)象創(chuàng)建 ---- 功能等價(jià)于 Role3 BOSS, MANAGER, WORKER;}單例設(shè)計(jì)模式須包含以下內(nèi)容: 1、私有構(gòu)造器 2、private static成員對(duì)象 3、public static獲得成員對(duì)象的方法
/** * 單例設(shè)計(jì)模式,強(qiáng)調(diào)一個(gè)類只能有一個(gè)示例 * */public class SingletonTest {}enum A { Test; // 該枚舉中只有TRST實(shí)例,相當(dāng)于**單例**。}class B { // 1. 私有構(gòu)造器 private B() { } // 2.private static 成員對(duì)象 private static B b = new B(); // 3.提供public static 獲得成員方法,獲得唯一的成員對(duì)象 public static B getInstance() { return b; }}定義: 在創(chuàng)建對(duì)象的時(shí)候,直接進(jìn)行初始化。 如上面的單例設(shè)計(jì)模式的例子,就是餓漢式。
class B { // 1. 私有構(gòu)造器 private B() { } // 2.private static 成員對(duì)象 private static B b = new B(); // 3.提供public static 獲得成員方法,獲得唯一的成員對(duì)象 public static B getInstance() { return b; }}定義: 在獲取對(duì)象的時(shí)候,進(jìn)行初始化。
// 懶漢式class C { // 1. 私有構(gòu)造器 private C() { } // 2.private static 成員對(duì)象 private static C c; // 3.提供public static 獲得成員方法,獲得唯一的成員對(duì)象 public static C getInstance() { if (c == null) { c = new C();// 標(biāo)準(zhǔn)的懶漢式 } return c; }}在枚舉實(shí)例定義過程中,向枚舉構(gòu)造器傳入?yún)?shù),通過匿名內(nèi)部類實(shí)現(xiàn)枚舉中抽象方法。
/** * 定義特殊結(jié)構(gòu)的枚舉 * */public enum EnumConstructorTest { // 創(chuàng)建枚舉值的時(shí)候,傳入構(gòu)造方法參數(shù) A(20) { @Override public void show() { // TODO Auto-generated method stub } }, B(30) { @Override public void show() { // TODO Auto-generated method stub } }; // 構(gòu)造方法,帶有參數(shù) private EnumConstructorTest(int a) { } public String toSting() { return super.toString(); } public void print() { System.out.println("Test"); } // 抽象方法 public abstract void show();}應(yīng)用舉例(包含兩種寫法):
public class WeekDayTest { public static void main(String[] args) { Weekday day = Weekday.Wed; day.show(); Weekdayabs dayabs = Weekdayabs.Fri; dayabs.show(); }}enum Weekday { Mon, Tue, Wed, Thu, Fri, Sat, Sun; // 編寫方法show public void show() { // 根據(jù)枚舉對(duì)象名字返回中文的的星期數(shù) if (this.name().equals("Mon")) { System.out.println("星期一"); } else if (this.name().equals("Tue")) { System.out.println("星期二"); } else if (this.name().equals("Wed")) { System.out.println("星期三"); } else if (this.name().equals("Thu")) { System.out.println("星期四"); } else if (this.name().equals("Fri")) { System.out.println("星期五"); } else if (this.name().equals("Sat")) { System.out.println("星期六"); } else if (this.name().equals("Sun")) { System.out.println("星期日"); } }}enum Weekdayabs { Mon{ @Override public void show() { // TODO Auto-generated method stub System.out.println("星期一"); } }, Tue { @Override public void show() { // TODO Auto-generated method stub System.out.println("星期二"); } }, Wed { @Override public void show() { // TODO Auto-generated method stub System.out.println("星期三"); } }, Thu { @Override public void show() { // TODO Auto-generated method stub System.out.println("星期四"); } }, Fri { @Override public void show() { // TODO Auto-generated method stub System.out.println("星期五"); } }, Sat { @Override public void show() { // TODO Auto-generated method stub System.out.println("星期六"); } }, Sun { @Override public void show() { // TODO Auto-generated method stub System.out.println("星期日"); } }; // 編寫方法show public abstract void show();}JDK1.5增加的靜態(tài)導(dǎo)入語法用于導(dǎo)入類的某個(gè)靜態(tài)屬性或方法。 使用靜態(tài)導(dǎo)入(staitc import)可以簡(jiǎn)化程序?qū)︻愳o態(tài)屬性和方法的調(diào)用。 語法: Import staic 包名,類名,靜態(tài)屬性|靜態(tài)方法|* 例如: Import static java.lang.System.out; Import static java.util.Arrays.sort; Import static java.lang.Math.*; 缺點(diǎn): 雖然靜態(tài)導(dǎo)入可以簡(jiǎn)化編程,但是靜態(tài)導(dǎo)入后的代碼的可讀性差。并且當(dāng)導(dǎo)入了沖突方法的時(shí)候,靜態(tài)導(dǎo)入將不可用(報(bào)錯(cuò))。
import java.util.Arrays;import static java.util.Arrays.sort;/** * 靜態(tài)導(dǎo)入 * */public class StaticImportTest { public static void main(String[] args) { int[] arr = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 }; // 對(duì)數(shù)據(jù)進(jìn)行排序 /** * 因?yàn)樵陬惿戏酵ㄟ^靜態(tài)導(dǎo)入引用Arrays.sort,所以可以直接sort * */ sort(arr);// 這里的sort是Arrays的靜態(tài)方法 // 注意:靜態(tài)導(dǎo)入Arrays.toString是會(huì)報(bào)錯(cuò)的,因?yàn)楹捅镜氐膖oString發(fā)生了沖突了 System.out.println(Arrays.toString(arr)); } @Override public String toString() { // TODO Auto-generated method stub return super.toString(); }}JDK5.0的語法允許開發(fā)人員把一個(gè)基本數(shù)據(jù)類型直接賦給對(duì)應(yīng)的包裝類變量,或者賦給Object類型的變量,這個(gè)過程稱之為自動(dòng)裝箱。 自動(dòng)拆箱和自動(dòng)裝箱與之相法,即把包裝類對(duì)象直接賦給一個(gè)對(duì)應(yīng)的基本類型變量。 典型應(yīng)用:
import org.junit.Test;/** * 自動(dòng)裝箱和拆箱 * */public class AutoBoxingTest { @Test public void demo1() { // 在JDK5之后將原始數(shù)據(jù)類型轉(zhuǎn)換為包裝類類型稱之為自動(dòng)裝箱。 Integer i = 10;// 編譯器將int轉(zhuǎn)換成Integer // 在JDK5之后將封裝類類型轉(zhuǎn)換為原始數(shù)據(jù)類型稱之為自動(dòng)拆箱。 int n = i;// 編譯器自動(dòng)將Integer轉(zhuǎn)換為int。 // 測(cè)試方法,將編譯的JDK版本調(diào)為JDK1.5以下版本即可。將會(huì)報(bào)錯(cuò): // Type mismatch:cannot convert from Integer to int } @Test public void demo2() { // JDK5之前如何進(jìn)行原始數(shù)據(jù)類型和包裝類類型轉(zhuǎn)換? int m = 10; // 如何將int轉(zhuǎn)換為Integer Integer i = new Integer(m); // 如何將Integer轉(zhuǎn)換為int int n = i.intValue(); }}典型案例 看下列代碼,思考代碼結(jié)果:
@Testpublic void demo3() { int i = 10; doSomething(i);}public void doSomething(double d) { System.out.println("double 參數(shù)");}public void doSomething(Integer d) { System.out.println("Integer 參數(shù)");}說明:
/*** 注意:這個(gè)方法將會(huì)執(zhí)行double參數(shù)的方法* 原因:因?yàn)橐嫒軯DK1.4的執(zhí)行效果,所以將會(huì)調(diào)用double的方法* */替換Iterator的復(fù)雜寫法,本質(zhì)就是Iterator。
如果一個(gè)對(duì)象,使用for/in語句中,該對(duì)象必須滿足兩個(gè)條件: 1、這個(gè)類必須實(shí)現(xiàn)Iterator接口 2、這個(gè)類必須重寫Iterator方法
import java.util.Iterator;import org.junit.Test;/** * for in語句 * */public class ForInTest { @Test public void demo2() { // Car用于for/in語句 Dota dota = new Dota(); for (String str : dota) { System.out.println(str); } }}// 實(shí)現(xiàn)讓自定義了類Car可以用于for/in語句class Dota implements Iterable<String> { String[] names = {"敵法師","龍騎士","黑暗游俠","圣堂武士"}; @Override public Iterator<String> iterator() { // TODO Auto-generated method stub // 自定義迭代器 return new MyIterator(); } class MyIterator implements Iterator<String> { int index = 0; // 當(dāng)前遍歷數(shù)組下標(biāo) @Override public boolean hasNext() { if (index >= names.length) { // 證明下標(biāo)無法獲取 return false; } return true; } @Override public String next() { String name = names[index]; index++; return name; } @Override public void remove() { // TODO Auto-generated method stub } }}在使用迭代器和for/in語句進(jìn)行l(wèi)ist循環(huán)的時(shí)候,刪除元素就會(huì)出現(xiàn)java.util.ConcurrentModificationException(并發(fā)異常)。 解決方案: 1、 使用Iterator自帶的remove方法(以下的demo3)。 2、如果只是刪除一個(gè)元素的時(shí)候,可以使用for/in語句刪除元素后,直接break跳出循環(huán)(以下的demo5)。 3、使用解決這類異常線程安全集合CopyOnWriteArrayList< E >。(以下的demo6)
import java.util.Iterator;import java.util.List;import java.util.ArrayList;import org.junit.Test;/** * for in語句 * */public class ForInTest { @Test public void demo6() { // 使用線程安全的集合對(duì)象,在for/in循環(huán)中刪除 List<String> list = new CopyOnWriteArrayList<String>(); list.add("qwer"); list.add("asd"); list.add("zxc"); list.add("123"); list.add("apple"); // 刪除apple // 如果只是刪除一個(gè)的話可以試以下方法 for (String str : list) { if (str.equals("apple")) { list.remove(str); } } System.out.println(list); } /** * 如果for/in語句中只是刪除一個(gè)元素,還可以使用break在刪除后跳出循環(huán),這是不會(huì)執(zhí)行下一次next----就不會(huì)出現(xiàn)并發(fā)異常了 * */ @Test public void demo5() { List<String> list = new ArrayList<String>(); list.add("qwer"); list.add("asd"); list.add("zxc"); list.add("123"); list.add("apple"); // 刪除apple // 如果只是刪除一個(gè)的話可以試以下方法 for (String str : list) { if (str.equals("apple")) { list.remove(str); break; } } System.out.println(list); } @Test public void demo4() { List<String> list = new ArrayList<String>(); list.add("qwer"); list.add("asd"); list.add("zxc"); list.add("123"); list.add("apple"); // 遍歷集合移除所有包含字母“a”的字符串 // 遍歷list 三種寫法 // 下標(biāo),Iterator,for/in // 下標(biāo) for (int i = 0; i < list.size(); i++) { String s = list.get(i); if (s.contains("a")) { // 需要將s從list移除 list.remove(s); // 防止元素被跳過 i--; } } System.out.println(list); } /** * List移除練習(xí) * */ @Test public void demo3() { List<String> list = new ArrayList<String>(); list.add("qwer"); list.add("asd"); list.add("zxc"); list.add("123"); list.add("apple"); // 通過for/in for (String s : list) { if (s.contains("a")) { // 以下方法會(huì)產(chǎn)生并發(fā)異常 // list.remove(s);// // error,java.util.ConcurrentModificationException // 第一種解決方法 -- 使用迭代器自身的remove } } // 第一種解決方法 -- 使用迭代器自身的remove // 使用Iterator進(jìn)行List遍歷 Iterator<String> iterator = list.iterator(); while (iterator.hasNext()) { String s = iterator.next(); if (s.contains("a")) { iterator.remove(); } } System.out.println(list); } @Test public void demo1() { List<String> list = new ArrayList<String>(); list.add("qwer"); list.add("asdf"); list.add("zxcv"); // 在JDK5之前有兩種遍歷方式:通過下標(biāo)遍歷,通過iterator Iterator<String> iterator = list.iterator(); while (iterator.hasNext()) { System.out.println(iterator.next()); } System.out.println("----------------"); for (Iterator<String> iterator2 = list.iterator(); iterator2.hasNext();) { String str = (String) iterator2.next(); System.out.println(str); } System.out.println("----------------"); // for in 主要作用就是為了簡(jiǎn)化Iterator,事實(shí)上for in就是Iterator for (String s : list) {// String s表示list中的每一個(gè)字符串 System.out.println(s); } }}可變參數(shù)主要是用來編寫框架。 原理:int… args相當(dāng)于int[] args,調(diào)用可變參數(shù)方法的時(shí)候,傳入任意個(gè)數(shù)參數(shù)。任意個(gè)數(shù)參數(shù)都會(huì)被保存在參數(shù)數(shù)組中, 例如:add(int… args) add(10,20)相當(dāng)于add(new int[]{10,20}); add(20,30,40)相當(dāng)于add(new int[]{20,30,40}); add()相當(dāng)于add(new int[]{});
從JDK5開始,Java允許為方法定義長(zhǎng)度可變的參數(shù),語法為:
public void foo(int... args) {}注意事項(xiàng): 1、調(diào)用可變參數(shù)的方法的時(shí)候,編譯器將自動(dòng)創(chuàng)建一個(gè)數(shù)組保存?zhèn)鬟f給方法的可變參數(shù),因此,程序猿可以在方法體中以數(shù)組的形式訪問可變參數(shù)。 2、可變參數(shù)只能處于參數(shù)列表的最后,所以一個(gè)方法最多只能有一個(gè)長(zhǎng)度可變的參數(shù)。
import java.util.Arrays;import java.util.List;import org.junit.Test;public class VariableTest { @Test public void demo2() { // 數(shù)據(jù)求和 System.out.println(add(10,20,30)); System.out.println(add()); System.out.println(add(new int[]{10,20,30,40})); } // 多個(gè)int類型參數(shù)求和,int... 表示任意個(gè)數(shù)int類型參數(shù) public int add(int ... args) { int sum = 0; for (int i : args) { sum += i; } return sum; }}如果你想要編寫高效、功能強(qiáng)大的Java程序,必須使用發(fā)射技術(shù)。
JAVA反射機(jī)制是在運(yùn)行狀態(tài)中,對(duì)于任意一個(gè)類,都能夠知道這個(gè)類的所有屬性和方法;對(duì)于任意一個(gè)對(duì)象,都能夠調(diào)用它的任意方法和屬性;這種動(dòng)態(tài)獲取信息以及動(dòng)態(tài)調(diào)用對(duì)象方法的功能稱為java語言的反射機(jī)制。
JAVA反射(放射)機(jī)制:“程序運(yùn)行時(shí),允許改變程序結(jié)構(gòu)或變量類型,這種語言稱為動(dòng)態(tài)語言”。從這個(gè)觀點(diǎn)看,Perl,Python,Ruby是動(dòng)態(tài)語言,C++,Java,C#不是動(dòng)態(tài)語言。但是JAVA有著一個(gè)非常突出的動(dòng)態(tài)相關(guān)機(jī)制:Reflection,用在Java身上指的是我們可以于運(yùn)行時(shí)加載、探知、使用編譯期間完全未知的classes。換句話說,Java程序可以加載一個(gè)運(yùn)行時(shí)才得知名稱的class,獲悉其完整構(gòu)造(但不包括methods定義),并生成其對(duì)象實(shí)體、或?qū)ζ鋐ields設(shè)值、或喚起其methods。 總結(jié)部分: 1、剖析Java類的各個(gè)組成部分映射成一個(gè)個(gè)Java對(duì)象 2、類java.lang.Class 3、java.lang.reflect 4、構(gòu)造方法Constructor 5、成員變量Field 6、方法Method
多用于框架和組件,寫出復(fù)用性高的通用程序。
.java文件(源代碼)——(編譯)——>.class字節(jié)碼(java類名)——(運(yùn)行)——>< JVM中 >【 類加載器(字節(jié)碼由類加載器加載)——(生成)——>Class類對(duì)象(位于java.lang包中)】
在.class字節(jié)碼中,被加載器加載之后,整個(gè)字節(jié)碼生成對(duì)象Class對(duì)象,字節(jié)碼中的成員變量生成Field對(duì)象,字節(jié)碼成員方法生成Method對(duì)象,構(gòu)造器則生成Constructor對(duì)象。
public class A { // 成為內(nèi)存Class對(duì)象 int n = 10; // Field public void show() {} // Method public static void init() {} // Method public A() {} // Constructor}1、已知類——可以通過類名.class獲得 2、已知對(duì)象——對(duì)象.getClass獲得 3、完整類名String格式——Class.forName(完整類名)——第三種方法是最常用的方法。
import org.junit.Test;/** * 編寫反射API使用 * */public class ReflfectTest { @Test public void demo1() { // 獲得class對(duì)象的三種方式 // 第一種 已知類 Class c1 = Readable.class; // 第二種 已知對(duì)象 Object obj = new ReflfectTest(); Class c2 = obj.getClass(); // 第三種 未知類和對(duì)象,知道完整類名 String className = "cn.idcast.jdk5.ReflfectTest"; try { Class c3 = Class.forName(className); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } // --------第三種方式是最常用的方法 }}通過類字節(jié)碼對(duì)象獲得構(gòu)造器、成員變量、方法有兩種方法: 1、獲得所有g(shù)etConstructors(); 2、獲得指定的構(gòu)造器,可以使用getConstructor(class
import java.lang.reflect.Constructor;import org.junit.Test;/** * 編寫反射API使用 * */public class ReflfectTest { @Test public void demo2() throws ClassNotFoundException, SecurityException, NoSuchMethodException { // 獲得字節(jié)碼對(duì)象 String className = "cn.idcast.jdk5.ReflfectTest"; Class c = Class.forName(className); // 通過字節(jié)碼獲得一個(gè)類構(gòu)造器 Constructor<String>[] constructors = c.getConstructors(); System.out.println(constructors.length); // 獲得指定構(gòu)造器 // 帶有String參數(shù)的構(gòu)造器 Constructor constructors1 = c.getConstructor(String.class); System.out.println(constructors.length); } public ReflfectTest() { } public ReflfectTest(String str) { }}Field類代表某個(gè)類中的一個(gè)成員變量,并提供動(dòng)態(tài)的訪問權(quán)限。 Field對(duì)象的獲得方法如下所示, 1、得到所有的成員變量:
Field[] fields = c.getFields(); // 獲得所有的public成員變量,包括父類繼承的。Field[] fields = c.getDeclaredFields();// 取得所有聲明的屬性,包括private屬性的成員變量2、得到指定的Field成員變量
Field fields = c.getDeclaredField(name); // 獲得當(dāng)前類中指定名稱name的成員變量設(shè)置Field變量是否可以訪問
// 設(shè)置private變量可以訪問的Field.setAccessible(true);Field變量值的讀取和設(shè)置
// 獲得對(duì)象指定name屬性值Field.get(name);// 修改Object的value的屬性值pnField.set(Object, value);總的代碼:
package cn.idcast.jdk5;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.InvocationTargetException;import org.junit.Test;/** * 編寫反射API使用 * */public class ReflfectTest { public String name; private String priname; public static void main(String[] args) { try { demo4(); } catch (SecurityException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (NoSuchFieldException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalArgumentException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } } // 反射可以對(duì)內(nèi)存中任何字節(jié)碼進(jìn)行操作 @Test public static void demo4() throws ClassNotFoundException, SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException { ReflfectTest reflfectTest = new ReflfectTest("Notzuonotdied"); // 使用反射操作類成員變量——Field類 // 1、必須獲得目標(biāo)類,字節(jié)碼對(duì)象 Class c = Class.forName("cn.idcast.jdk5.ReflfectTest"); // 2.操作成員實(shí)例變量name Field[] fields = c.getFields(); // 獲得所有的public 成員變量,包括父類繼承的 System.out.println("這里變量的數(shù)量為1,是因?yàn)椴话接校? + fields.length); Field[] fields2 = c.getDeclaredFields();// 獲取當(dāng)前類定義的所有成員,包括了private // 當(dāng)前Field是private的(名稱為"priname"的變量是private),我們是沒有操作不了的,因此我們需要設(shè)置Field的變量操作權(quán)限 Field pnField = c.getDeclaredField("priname"); System.out.println("這里變量的數(shù)量為2,是因?yàn)楣屑铀接幸还矁蓚€(gè):" + fields2.length); System.out.println(pnField); // 設(shè)置private變量可以訪問的 pnField.setAccessible(true); // 獲得reflfectTest對(duì)象指定name屬性值 Object pnobj = pnField.get(reflfectTest); System.out.println(pnobj); // 修改reflfectTest的name的屬性值 pnField.set(reflfectTest, "I am Notzuonotdied"); System.out.println(pnField.get(reflfectTest)); /* 輸出結(jié)果: * * 這里變量的數(shù)量為1,是因?yàn)椴话接校? * 這里變量的數(shù)量為2,是因?yàn)楣屑铀接幸还矁蓚€(gè):2 * private java.lang.String cn.idcast.jdk5.ReflfectTest.priname * Notzuonotdied * I am Notzuonotdied */ }}Method類代表某個(gè)類中的一個(gè)成員方法 Method對(duì)象的獲得,可以通過以下方法, 1、獲得所有方法
getDeclareMethods() //獲得當(dāng)前類聲明的方法 getMethods()2、獲得指定的方法
getDeclaredMethod(String name,Class<?>... parameterTypes)// 獲得指定方法getMethod(String name,Class<?>... parameterTypes)通過反射執(zhí)行方法
invoke(Object obj,Object... args)如果執(zhí)行static方法,第一個(gè)參數(shù)obj傳入null。
package cn.idcast.jdk5;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import org.junit.Test;/** * 編寫反射API使用 * */public class ReflfectTest { public String name; private String priname; public void setName(String name) { this.name = name; } public String getName() { return name; } @Test public void demo5() throws ClassNotFoundException, SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException { ReflfectTest reflfectTest = new ReflfectTest(); // 調(diào)用reflfectTest對(duì)象中setName方法設(shè)置name的值 // 1、獲得字節(jié)碼對(duì)象 Class c = Class.forName("cn.idcast.jdk5.ReflfectTest"); // String類型參數(shù)setName方法 Method setName = c.getDeclaredMethod("setName", String.class); // String類型參數(shù)的setName方法 // 調(diào)用reflfectTest對(duì)象中的setName setName.invoke(reflfectTest, "this is a test demo"); // 等價(jià)于reflfectTest.setName("this is a test demo"); // String類型參數(shù)getName方法 Method getName = c.getDeclaredMethod("setName", String.class); // String類型參數(shù)的setName方法 // 調(diào)用reflfectTest對(duì)象中的setName getName.invoke(reflfectTest, "this is a test demo"); // 等價(jià)于reflfectTest.setName("this is a test demo"); }}1、編寫晚會(huì)程序,測(cè)試程序; 2、為了程序更好維護(hù)和擴(kuò)展,需要將做什么(interface)和怎么做(實(shí)現(xiàn)interface中的方法)分離; 問題:在晚會(huì)類中,要維護(hù)所有出演演員,但是不想修改晚會(huì)類,可以怎么寫? 答:可以采用工廠結(jié)構(gòu)(中介)來確定那一個(gè)對(duì)象將會(huì)被運(yùn)行。 3、用工廠將做什么和怎么做進(jìn)行解耦合; 4、將演員實(shí)例類寫入配置文件。(寫入配置文件之后,可以在不修改源代碼的情況下修改程序) 工廠+反射+配置文件模式的作用:編寫便于擴(kuò)展、便于維護(hù)的程序。
工廠類主要通過獲取配置文件party.properties中的定義來獲得類名,
ResourceBundle.getBundle("party").getString( "Singable");并通過類名來創(chuàng)建對(duì)應(yīng)的對(duì)象。
Class.forName(className).newInstance();下面是工廠類完整代碼:
package cn.idcast.jdk5.demo;import java.util.ResourceBundle;import cn.idcast.jdk5.demo.interfaces.Danceable;import cn.idcast.jdk5.demo.interfaces.Performable;import cn.idcast.jdk5.demo.interfaces.Singable;/** * 中介類, * */public class Factory { // 提供準(zhǔn)備 歌手方法 public static Singable getSingable() { // 讀取配置文件 src/party.properties String className = ResourceBundle.getBundle("party").getString( "Singable"); // 已知完整類名 獲得對(duì)象 try { Object obj = Class.forName(className).newInstance(); return (Singable) obj;// 類型轉(zhuǎn)換 } catch (Exception e) { e.printStackTrace(); throw new RuntimeException("中介出問題了!"); } } // 提供準(zhǔn)備 舞蹈方法 public static Danceable getDanceable() { // 讀取配置文件 src/party.properties String className = ResourceBundle.getBundle("party").getString( "Danceable"); // 已知完整類名 獲得對(duì)象 try { Object obj = Class.forName(className).newInstance();// 通過反射新建對(duì)象 return (Danceable) obj; } catch (Exception e) { e.printStackTrace(); throw new RuntimeException("中介出問題了!"); } } // 提供準(zhǔn)備 演員方法 public static Performable getPerformable() { // 讀取配置文件 src/party.properties String className = ResourceBundle.getBundle("party").getString( "Performable"); // 已知完整類名 獲得對(duì)象 try { Object obj = Class.forName(className).newInstance(); return (Performable) obj; } catch (Exception e) { e.printStackTrace(); throw new RuntimeException("中介出問題了!"); } }}新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注