黑馬程序員——高新技術(一)

  ------- android培訓java培訓、期待與您交流! ----------

一、1.5新特性:

靜態導入:例如:我們需要使用工具包的時候,如:Arrays的話,每一次使用都需要前面帶Arrays,直接在導包的時候就導入靜態的 import static java.util.Arrays.*;

以後在使用Arrays前面就不 要帶 Arrays了。直接使用方法即可,如果類中有想通的方法,就還是必須要帶上 類名.方法。

自動拆箱,自動裝箱:基本數據類型包裝類。(自動裝箱,將基本數據類型封裝成對象,可以提供更多的操作方法)

byte short int long boolean char float double
Byte Short Integer Long Boolean Char Float Double
public class JDK15
{
	public static void main(String[] args)
	{
		Integer i = 4;	//自動裝箱,將基本數據類型封裝成對象,可以提供更多的操作方法
		
		System.out.println(i * 2);//8:自動拆箱,拆成基本數據類型
	}

}
享元模式:將使用非常頻繁的小數據緩存都封裝起來(-128~127):其中包括(false,true,byte,    (shrot,char)的關閉區間(0x0,0x7f))。如果下次有在調用的話,就將緩存對象直接給他。這樣可以降低小數據再次創建對象,節約系統資源)

可變參數:在建立方法的時候:使用 數據類型... 來表示,在他後面不能在有其他的數據類型!他其實是將數據都封裝到數組裏面去了。
public class JDK15
{
	public static void main(String[] args)
	{
		print("adsa","vvv","dsa");
	}
	
	public static void print(String... str)
	{
		System.out.println(str[0] + ":" + str[1] + ":" + str[2]);
	}

}

高級for循環,可以 對 數組,集合進行遍歷,只能獲取集合元素,但是不能對集合進行操作。
高級for循環有侷限性,必須有被遍歷的目標,格式爲:for(類型  變量名 : 數組或集合目標){                         },
對集合進行簡單的遍歷打印:
import java.util.ArrayList;
public class For 
{
	public static void main(String[] args)
	{
		ArrayList<String> list = new ArrayList<String>();
		list.add("haha2");
		list.add("haha1");
		list.add("haha1");
		
		for(String element : list)
		{
			System.out.println(element);
		}
	}
}


二、枚舉:是一個特殊的類,目的:爲了解決類型不安全提出的 
/*目的:爲了解決類型不安全提出的 
使用:當我們在類中要指定選項必須爲某些類型時,就要考慮用到枚舉,這是jdk1.5之後才提出的。
類型安全的枚舉類型簡介: 
1)enum:可以定義一個類型,而且還可以限制該類型的取值在某個範圍內 
2)語法:enum enumName{枚舉值1,...,枚舉值n} 
3)適用位置:包中、類中,但不能在方法中 
4)注意:
    1.所有的enum類型都繼承自java.lang.Enum; 
    2.自定義的enum類型不能再有子類; 
    3.枚舉類型的取值默認爲public static final; 
    4.枚舉類型中可以定義屬性和方法; 
    5.構造器必須是私有的; 
    6.枚舉類型的取值就是其實例對象,它會調用該枚舉類型的構造器,還要實現枚舉類型中所有的抽象方法; 
7.枚舉類型可以實現其它接口;*/
public class enumDemo 
{

	public static void main(String[] args) 
	{
		TrefficLamp lamp = TrefficLamp.RED;
		while(true)<span style="white-space:pre">						</span>//不知道這裏是什麼問題。當 YELLOW執行完以後,沒辦法返回繼續執行紅。以後來解決吧
		{
			if(lamp.equals(TrefficLamp.RED))
				lamp = lamp.nextLamp();
			else if(lamp.equals(TrefficLamp.GREEN))
				lamp = lamp.nextLamp();
			else if(lamp.equals(TrefficLamp.YELLOW))
				lamp = lamp.nextLamp();
			
		}
	}
}

/**1、交通燈有三種形態,紅,綠,黃
 * 2、他們都有一個共性的方法,就是當前的燈,返回下一個燈。(定義抽象方法,讓他們自己去實現)
 * 3、每一個燈都有自己的顯示時間,所以在定義一個構造方法,
 * 4、把自己的時間構造進去。
 * 5、循環當自己的時間>=0。則暫停程序1秒,打印。剩餘時間,time--。
 * 6、退出循環的時候,就返回下一個燈。
 * */
enum TrefficLamp
{
	RED(5){
		public TrefficLamp nextLamp()
		{
			countLamp();
			return GREEN;
		}
	},
	GREEN(5){
		public TrefficLamp nextLamp()
		{
			countLamp();
			return YELLOW;
		}
		
	},
	YELLOW(1){
		public TrefficLamp nextLamp()
		{
			countLamp();
			return RED;
		}
	};

	private TrefficLamp(int time)
	{
		this.time = time;
	}
	

	protected void countLamp()
	{
		while(time>=0)
		{
			try 
			{
				Thread.sleep(1000);
				System.out.println(this + "還剩餘:" + time + "秒" );
				time--;
			} 
			catch (InterruptedException e)
			{
				System.out.println("程序異常即將退出");
				System.exit(0);
			}
		}
	}
	
	protected int time;
	
	public abstract TrefficLamp nextLamp();
}


三、反射(Class):動態的獲取指定的類,以及動態的調用類中的類容。反射的好處:大大的提高程序擴展性(不用改源碼,動態的new對象)
反射常用於:框架開發:框架的意思打個比方,比如 開發商 開發了一批房子,但是這些房子都是沒有經過裝修的(毛坯房)。你找開發商買了房子以後,自己裝上門和傢俱,這就變成你的房子了。
反射(Class)就是將Java某個類中的 成員和構造方法 映射成 相應的 Java類。  
獲取代表反射的類Class有三種方式:
1、通過Object中的 getClass()  返回一個 Class 對象。:這種方法雖然通用,但是前提必須是有對象。
2、通過任意數據類型(數據類型包括類)的一個靜態成員class。:這種方法雖然不用new對象,但還需要具體類
3、通過Class中的一個靜態方法forName(String)。:給我 一個類名的字符串,我就可以獲取對應的字節碼文件——這就是反射技術。
類中3大基本組成部分:1、構造方法(Constructor)2、字段(Field)3、方法(Method)   4、數組(Array)
演示①:通過反射技術,創建一個字符串對象。並打印出來。
import java.lang.reflect.Constructor;

/**通過反射技術,創建一個String的對象 
 * */
public class constructorDemo {

	public static void main(String[] args) throws Exception
	{
		Class clazz = Class.forName("java.lang.String");	//加載一個String類進內存,必須要寫清楚 包名+類型
		
	<span style="white-space:pre">	</span>Constructor constructor  = clazz.getConstructor(String.class);	//返回一個參數列表爲 String的構造器
		
		String reflectString = (String)constructor.newInstance("哈哈");	//通過字節碼的構造器方法創建對象。因爲知道他返回的Object,所以強轉一下
		
		System.out.println(reflectString);
		
		
		/**如果直接使用 clazz.newInstance,則是默認調用了 指定字節碼的空參構造方法
		 * */
	}
}


演示②:通過反射技術,獲取字段,並且通過另一種方法,訪問private 修飾的 字段。
import java.lang.reflect.Field;
public class FieldDemo
{
	public static void main(String[] args) throws Exception
	{
		Person3 prs = new Person3("xiaozhang",20);
		Class clazz = prs.getClass();			//通過對象返回字節碼文件
		Field field = clazz.getField("name");	//通過Class的 getField返回  Field對象。這是Field對象,而不是Proson的實例對象
		String name = (String) field.get(prs);		//通過 field的方法,傳入一個對象,我就返回那個對象的name	
		System.out.println(name);

		/**以上的字段反射方法是可以反射類中修飾符爲public 修飾的字段。
		 * 但是修飾符爲private的話,就沒辦法反射。他根本沒看不到private的字段。
		 * 這個時候就只能使用getDeclaredField():該方法,查看到所有的字段。
		 * 但是知道有這個字段,他也沒權限訪問。此時必須將private取消掉(暴力訪問) 
		 * */
		Class clazz1 = prs.getClass();
		Field field1 = clazz1.getDeclaredField("age");	//可以忽略private並找到該字段。
		field1.setAccessible(true);			//取消掉  他的 private 修飾符
		int age = (int)field1.get(prs);		//獲取到的那個屬於是Object,強轉一下
		System.out.println(age);	
	}
}

class Person3
{
	public String name;
	private int age;
	
	public Person3(String name, int age) 
	{
		this.name = name;
		this.age = age;
	}
}

練習一:通過反射的技術,將某一個對象中的所有字段爲String的值,帶有  i  的全部換成  *  
/**通過反射將一個對象中的所有字段 帶 i 的,全部替換成 *
 * 
 * */
import java.lang.reflect.Field;
public class FieldDemoTest1 
{
	public static void main(String[] args) throws Exception
	{
		Person4 ps4 = new Person4();
		
		System.out.println("沒有修改之前" + ps4);
		Class clazz = ps4.getClass();
		Field[] fieldArr = clazz.getFields();
		
		for(Field element : fieldArr)
		{
			if(element.getType() == String.class)
			{
				String oldStr = (String)element.get(ps4);	//如果是String的話,就獲取他的元素
					
				String newStr = oldStr.replace("i","*");	//獲取元素之後,通過replace返回一個新的值
				
				element.set(ps4, newStr);				//在通過set重新設置到對象中
			}
		}
		
		System.out.println("反射修改之後" + ps4);
		
	}

}

class Person4
{
	public String xiaozhang = "xiaozhang";
	public int age = 10;
	public String xiaoming = "xiaoming";

	public String toString() {
		return "Person4 [xiaozhang=" + xiaozhang + ", age=" + age
				+ ", xiaoming=" + xiaoming + "]";
	}
}


③、通過反射運行某個類中的方法:
import java.lang.reflect.Method;
public class MethodDemo
{
	public static void main(String[] args) throws Exception
	{
		Method function1 = String.class.getMethod("length",null);//獲取字符串字節碼中的  length 方法,沒有參數類型.class
									//調用String中的 length方法,並且他是沒有參數的
		
		int length  =  (int) function1.invoke("ler43243ngth", null);//invoke運行該方法,來自於哪一個對象,具體參數!傳入一個對象,並且沒有具體參數
		System.out.println(length);
		
		
		Method function2 = String.class.getMethod("valueOf", int.class );//獲取字符串中的valueOf方法,將一個Int轉成字符串
		
		String str = (String)function2.invoke(null, 18189);		//沒有傳入對象,說明他是一個靜態的方法,傳入一個參數
		
		System.out.println(str);
		
	}
}

④、數組的反射:當數組中,維數相同,類型相同的話,那麼他們獲取的字節碼是同一份。
import java.util.Arrays;

public class ArrayDemo 
{
	public static void main(String[] args) 
	{
		int[] arr1 = {2,3,4,5};
		int[] arr2 = {12,2};
		String[] arr3 = {"12","2"};
		int[][] arr4 = {{123},{456}};
		String[][] arr5 = {{"1","2"},{"3","4"},{"5","6"}};
		
		System.out.println(arr1.getClass() == arr2.getClass());
/*		System.out.println(arr1.getClass() == arr3.getClass());	//如果不是同類型或者同維數的話,編譯器都不讓通過
		System.out.println(arr1.getClass() == arr4.getClass());
		System.out.println(arr1.getClass() == arr5.getClass());*/
		
		/**每一個子類都具備返回父類的class文件*/
		System.out.println(arr1.getClass().getSuperclass().getName());//java.lang.Object每一個子類都具備返回父類的class文件
		System.out.println(arr5.getClass().getSuperclass().getName());//java.lang.Object
		
		/**一維數組基本數據類型只能專爲Object不能轉爲Object[]。
		 * 非基本數據類型一維數組即可以專爲Object,又可以轉OBject[]。
		 * */
		Object o1 = arr1;	//一維基本數據類型 轉 Object
		Object o2 = arr3;	//一維非基本數據類型 轉 Object
	//	Object[] o3 = arr1;//一維基本數據類型  無法轉 Object[]
		Object[] o4 = arr4;//二維基本數據類型 轉 Object[]
		Object[] o5 = arr5;//二維非基本數據類型 轉 Object[]
		
		
		System.out.println(Arrays.asList(arr1));	//輸出對象索引
		System.out.println(Arrays.asList(arr1[0],arr1[1],arr1[2],arr1[3]));	//輸出數據
		System.out.println(Arrays.asList(arr3));	//輸出數據
	}

}


⑤、hashCode的作用,什麼是內存泄漏,什麼是內存溢出
import java.util.HashSet;

/**hashCode的作用:當需要在hash結構的集合中存儲時就需要根據自己的屬性來覆寫hashCode。爲什麼呢?
 * 因爲在hash結構中,必須要確保元素的唯一性,在判斷唯一性的最重要的依據就是 hashCode 和 equals。如果這2個都相等的那就是同一個元素。
 * 程序是有限判斷 hashCode值,如果相等在判斷equals。所以提高程序的效率 最好就根據 自己的屬性來覆寫hashCode.
 * */
public class HashCode {

	public static void main(String[] args) 
	{
		HashSet hs = new HashSet();
		hs.add(new Person5("xiaozhang",20));
		hs.add(new Person5("xiaozi",10));
		hs.add(new Person5("xiaoxia",20));	//不寫hashCode的話。和下面的對象都可以同時存入集合中
		hs.add(new Person5("xiaoxia",20));
		
		Person5 xiaojian = new Person5("xiaojian",15);
		hs.add(xiaojian);
		System.out.println(hs.size());//	4
		
		xiaojian.age = 10;		//我修改一個對象的成員屬性以後。
		System.out.println(hs.remove(xiaojian));	//在刪除對象,是沒辦法刪除的
		/**我在創建的時候是記錄的是一個哈希值,我刪除的時候也必須是當時記錄的哈希值,
		 * 當我修改對象的屬性後,他的哈希值將隨之改變,肯定不是 之前的哈希值,所以沒辦法刪除。
		 * 
		 * 這種現象叫內存泄漏:當我需要刪除一個對象時。這個對象已經不可達,而且也無法回收內存的話,這樣白白的佔用了內存的空間,
		 * 當我的對象很多的,同時這種浪費資源的空間越來越多的時候,就會造成內存溢出
		 * 內存溢出:遞歸的時候也應該儘量控制遞歸次數,不然也容易引發內存溢出。
		 * */				
		System.out.println(hs.size());//	4

	}
}

class Person5
{
	public String name;
	public int age;
	public Person5(String name, int age)
	{
		this.name = name;
		this.age = age;
	}

	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + age;
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		return result;
	}

	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Person5 other = (Person5) obj;
		if (age != other.age)
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		return true;
	}
}


6、使用反射技術,動態的加載一個Random,並且隨機生成大於0,小於100的數
import java.io.FileInputStream;
import java.util.Properties;
import java.util.Random;

public class ResourceDemo 
{
	public static void main(String[] args) throws Exception
	{
		Properties prop = new Properties();
		prop.load(new FileInputStream("src/com/reflectDemo/config.properties")); 
		
		String className = prop.getProperty("className");
		
		Class clazz= Class.forName(className);	//通過forName(className)拿到一個指定的文件字節碼文件
		
		Random random = (Random) clazz.newInstance();	// 創建他的一個無參的構造方法
		
		System.out.println(random.nextInt(100));
	}
}



四、內省(內部審查),內省其實是一個特殊的Java類,主要是對符合某個特定的規則,方法有getXxxxx,setXxxxxx,
如果一個類可以當作JavaBean類來使用的話,則可以通過方法名就可以推測對應的屬性。
去掉前面的get和set以後,留下的則是他的屬性,如果第二個字母是小寫,則把第一個字母變小。
如:getAge ->去掉get -> Age ->第二個字母是小寫  ->age
gettime - >  去掉 get -> time ->第二個字母是小寫 -> time
setTime ->去掉 get -> Time ->第二個字母是小寫  -> time
getCPU ->去掉 get -> CPU -> 第二字幕是大寫  -> CPU

我有一個人類,我要對其獲取年齡。 按照普通類的話,直接 new  person("zhangsan",20).getAge()!!如果按照javaBean的方式的話,有更大的好處,所以我們選擇javaBean:
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
public class BeanDemo
{
	public static void main(String[] args) throws Exception
	{
		Person5  person = new Person5("小張",21);
		
		String propertyName = "age";   //這個age 不是成員變量age,而是我知道有一個getAge方法
		
		PropertyDescriptor pd = new PropertyDescriptor(propertyName, person.getClass()); //屬性描述符:要查找的方法簡寫,對象字節碼文件
		
		Method methodgetAge = pd.getReadMethod();	//獲取讀的方法

		int refVal = (int) methodgetAge.invoke(person);		//使用該方法: 傳給我一個對象,Object,強轉 int 
		
		System.out.println(refVal);		
	}
}


/**創建一個人類,有name 和 age
 * */
class Person5
{
	private String name;
	private int age;
	Person5(String name, int age) 
	{
		this.name = name;
		this.age = age;		
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
}
練習:寫一個方法,可以將obj對象中的propertyName屬性設置成Value

	public static void setPropertyName(Object obj,String propertyName,Object value) throws Exception
	{
		PropertyDescriptor pd = new PropertyDescriptor(propertyName, obj.getClass());
		
		Method setPropertyName = pd.getWriteMethod();//獲取他的該方法
		
		setPropertyName.invoke(obj, value);
	}


五、註解:也是一個特殊的類,主機相當於一種標記,加了註釋,相當於給程序員打上了某種標記,以後在Java編譯器和開發工具和其他程序員就可以用反射來了解你的類及元素上面有沒有標記,加了什麼標記就去幹什標記的動作。
java.lang包中的註解!
1、Depercated:設置某個元素髮出警告提示(已過期)
2、Override:一個方法是否覆寫了父類的方法,如果沒有則編譯失敗
3、SupperssWarnings:當使用了 發出警告的元素時(已過期),會提示警告!SupperssWarnings可以取消某個元素髮出的警告提示。

註解的生命週期:RetentionPolicy
RetentionPolicy.SOURCE  :編譯完成取消註解,生命週期 Java文件
RetentionPolict.CLASS :註釋保存在java 文件中,但是運行時期 不會加載註解。 生命週期 Class文件。
RetentionPolicy.RUNTIME :將註釋保存到運行時期,可以通過反射來讀取註釋。生命週期 內存字節碼文件。

設置註解只能在哪裏使用:Target
Target 接收參數接收的是一個數組類型。而且他的元素是通過枚舉的喲。
ANNOTATION_TYPE:註釋類
CONSTRUCTOR:設置在構造方法中
FIELD:設置在字段中(包括枚舉的常量)
LOCAL_VARIABLE:設置在局部變量中
METHOD:設置在方法中
PACKAGE:設置在包中
PARAMETER:設置在參數列表中
TYPE:類、接口、(包括註釋類型)或枚舉聲明

聲明一個註解,使用 @interface

註解類

@interface A{}

應用了”註解類”的類

@A

Class B {}

對”應用了註解類的類”進行反射操作的類

Class C

{

B.class.isAnnotionPresent(A.class);//判斷某個註解是否存在

A a =(A) B.class.getAnnotion(A.class);//如果存在則創建對象

}

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;


@Retention(RetentionPolicy.RUNTIME)		//設置註解生命週期,如果要供反射使用必須要將屬性設置爲RUNTIME
@Target({ElementType.TYPE , ElementType.METHOD})	//註釋可以應用到 類  和  方法 上

public @interface masaiAnnotatd 		//  定義一個  註解   A類
{

	
}

@masaiAnnotatd				//應用了註解類的類  B類
public class Annotatd {

	public static void main(String[] args) 
	{		
		
	}
}

/**檢查某個類中,是否應用了某個註解<span style="white-space:pre">	</span>C類
 * */
public class AnnotatdTest {
	public static void main(String[] args)
	{
		if(Annotatd.class.isAnnotationPresent(masaiAnnotatd.class))
		{
			masaiAnnotatd ma =(masaiAnnotatd) Annotatd.class.getAnnotation(masaiAnnotatd.class);
			System.out.println(ma);
		}
	}
}

給註解設置一些特有的屬性

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import com.enumDemo.EnumDemo;
import com.enumDemo.EnumDemo.TrefficLamp;


@Retention(RetentionPolicy.RUNTIME)		//設置註解生命週期,如果要供反射使用必須要將屬性設置爲RUNTIME
@Target({ElementType.TYPE,ElementType.METHOD})	//註釋可以應用到 類  和  方法 上

public @interface masaiAnnotatd 		//  定義一個  註解   A類
{
	String name();	//註解裏面的都是抽象方法,但是應用創建的過程中則是按照屬性賦值
	
	int age()  default 1;	//設置缺省值,如果 應用的註解類沒有覆蓋 age 的話,那麼 age 初始化爲1
	
	String[] strArr();	//註解可以是數組
	
	EnumDemo.TrefficLamp lamp() default TrefficLamp.RED;	//可以是 枚舉類
	
	MyAnnotatd myannotatd() default @MyAnnotatd("Myannotatd 註解裏面+ 返回註解");	//註解裏面設置註解
	
	/**註解類型可以是  8大基本數據類型(byte short int long char boolean float double)、所有的原始類型
	 * 可以是 String
	 * 可以是 數組
	 * 可以是 Class
	 * 可以是 枚舉
	 * 可以是 註解
	 * */
}

@masaiAnnotatd(myannotatd=@MyAnnotatd("B 類覆寫的"),name="masai",strArr={"123","123","456"})	//應用了註解類的類  B類
public class Annotatd {

	public static void main(String[] args) 
	{		
		
	}
}

/**檢查某個類中,是否應用了某個註解  C類
 * */
public class AnnotatdTest {
	public static void main(String[] args)
	{
		if(Annotatd.class.isAnnotationPresent(masaiAnnotatd.class))
		{
			masaiAnnotatd ma =(masaiAnnotatd) Annotatd.class.getAnnotation(masaiAnnotatd.class);	//通過B類 創建的註解對象
			
			System.out.println(ma.name());	//masia   可以直接使用 B 類 對 註解的初始化值
			
			System.out.println(ma.age());	//1		B 類沒有 給 age 賦值,所以使用了缺省值
			
			System.err.println(Arrays.toString(ma.strArr()));	//[123,123,456]
			
			System.out.println(ma.lamp().nextLamp());	//還可以讓 我之前的 做的燈 跑起來
			
			System.out.println(ma.myannotatd());	//@com.annotatd.MyAnnotatd(value=B 類覆寫的)
			
		}
	}
}

public @interface MyAnnotatd	// 另外一個註解類
{
	String value();
}
註解給我的理解就是,我們可以把他看作一個全是 屬性類來用。我們可以在裏面給他設置默認初始化值,如果沒有設置的話,則需要應用類來給他 賦值 。
 也可以當作 是有返回值的方法。


六、泛型:JDK1.5版本後出現的新特性——泛型,屬於解決安全問題的,是一個安全類型機制。
好處:1、將運行時期出現的異常轉移到了編譯時期,方便程序員解決問題。2、避免了強制轉換的麻煩。
在集合泛型是給編譯器使用的,但是當編譯完以後,就自動去除了泛型信息,如果我們之前是通過String存入集合的,本來是無法在String的集合中存儲Integer的。
但是由於泛型的編譯去泛型信息特點,我們可以通過 反射 拿到他的字節碼文件,因爲反射屬於運行時期,所以現在的集合沒有了泛型,故 可以給他添加信息。

泛型術語介紹:
ArrayList<E> : E稱爲 類型變量或者類型參數
ArrayList<Integer>:Integer成爲類型的實例實際類型參數
ArrayList<Integer>:發音:ArrayList    typeOf  Integer
ArrayList:原始類型

泛型通配符:
<?>:任意類型。只能調用於參數化無關的方法,不能調用與參數有關的方法。例如:ArrayList 使用了?以後,不能使用 帶參數的方法,只能使用不帶參數的方法。size();
import java.util.ArrayList;
import java.util.Collection;

public class Generic 
{
	public static void main(String[] args) 
	{
		ArrayList<Integer> aList = new ArrayList<Integer>();
		aList.add(312);
		aList.add(1);
		aList.add(32);	
	}
	
	public static void printCollection(Collection<?> element)
	{
		//element.add("123");		這是不行的
		//element.add(123);			這是不行的
		System.out.println(element.size());
	}
}

<? extends xxx>:xxx或者xxx的子類。----->>>>限制上邊界,只要是xxx的子類就行
<? super   xxx>:xxx或者xxx的父類。------->>>限制下邊界,只要是xxx的父類就行
public class Generic2
{
	public static void main(String[] args)
	{
		ArrayList<? extends Number> aList = new ArrayList<Byte>();
	//	ArrayList<? extends Number> aList1 = new ArrayList<String>();	String不是Number 的子類。編譯失敗
		
		ArrayList<? super Integer> aList3 = new ArrayList<Object>();
	//	ArrayList<? super Integer> aList4 = new ArrayList<Byte>();		Byte和Integer是同級的。編譯失敗
		
	}
}

使用泛型來遍歷 Map集合,以前我們可以通過keySet拿到一個Map集合的所有鍵,然後通過鍵get 原Map集合,拿到值。
現在直接使用 entrySet。拿到一個類似於  結婚證(Map.Entry),然後通過這個結婚證就可以找到 老公 和 老婆。
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

public class Generic3
{
	public static void main(String[] args) 
	{
		HashMap<String,Integer> hMap = new HashMap<String,Integer>();
		hMap.put("小張",12);
		hMap.put("小華",15);
		hMap.put("小名",14);
		
		Set<Map.Entry<String,Integer>> entrySet = hMap.entrySet();	//拿到一個全國唯一的  結婚證id號
		
		Iterator<Map.Entry<String,Integer>> it = entrySet.iterator();
		
		while(it.hasNext())			//遍歷 結婚證id 號
		{
			Map.Entry<String, Integer> element = it.next();		//拿到一個結婚證 id到
			
			String key = element.getKey();		//相當於:獲取老公
			Integer value = element.getValue();	//相當於:獲取老婆
			
			System.out.println(key + ":::" + value);
			
		}
	}
}

練習一:定義一個泛型功能,該功能通過傳入一個數組,按照自然順序 返回數組的最大值。
import java.util.Arrays;

public class GenericTest1 {
	public static void main(String[] args)
	{
		Integer[] arrInt = {32,423,3211,2,5,48};
		System.out.println(arrayMax(arrInt));
		
		String[] arrStr = {"axc","fds","jr","fwe"};
		System.out.println(arrayMax(arrStr));
	}
	
<span style="color:#ff0000;">	/**定義一個泛型,接收任意數組
	 * 通過自然排序以後,返回最後的哪一個元素。
	 * 泛型的好處:該方法中,已經看出來。我定義一個功能,就可以多處使用
	 * */</span>
	public static <T> T arrayMax(T[] arr)
	{
		Arrays.sort(arr);
		return arr[arr.length-1];
	}
	
	
<span style="color:#ff0000;">	/**這裏爲什麼不能接收基本數據類型呢?
	 * 這個方法中必須是包裝類才行,否則編譯失敗
	 * 個人認爲:泛型必須要求是對象,而基本數據類型在Java中不屬於對象
	 * */</span>
}


練習二:使用泛型,製作一個2個同類型相加的返回的結果
public class GenericTest2
{
	public static void main(String[] args) throws Exception
	{
		System.out.println(sum(21,23));	//自動裝箱了
		System.out.println(sum("av","vs"));
		
	}
	public static <T> T sum(T element1,T element2) 
	{

		
		if( element1 instanceof String && element1 instanceof String)		//判斷2個是否爲字符串類型,是的話就直接連接起來
			return (T) ((String) element1 + element2);
		
		if(element1 instanceof Integer && element2 instanceof Integer)
		{
			Integer temp = (Integer)element1 + (Integer)element2;	//需要先轉Integer才能相加
			return (T)temp;
		}
		
		<span style="color:#cc0000;">/**此處只做簡單的做了2個功能。肯定還有其他的,可以在添加其他的類型判斷
		 * */</span>
		
		return null;<span style="white-space:pre">	</span>//注意喲。這樣應該會報一個空指針異常。自己catch一下
	}
}

在同一個類中有多個方法都用了泛型,但是他們都是相對自己是獨立的,如果要將他們關聯起來,將泛型定義在累傷。方法中出現的泛型就是同一個類型。
我們可以通過 api  簡單的觀察有哪些用了 類 泛型。。。
只需要 在  類 上 申明瞭 某一個 泛型,則在下面的子類都是使用的同一個泛型。。。如果當方法中需要一個獨立的泛型的話。我們就可以在該方法上在申明一個泛型


public class Generic4
{
	public static void main(String[] args) 
	{
		GenericTest4<String> gt4 = new GenericTest4<String>("哈哈");	//由於類上有泛型,創建對象不指定泛型會出現黃線提醒喲
		
		System.out.println(gt4);	//哈哈
		
		gt4.print("das");	//print 只能接收字符串,因爲他的類型是隨類的
		
		gt4.print2(123);	//該方法又可以打印  基本數據類型類型
		
		gt4.print2("fds");	//又可以打印字符串,下面來看看他的方法
	}

}

/**泛型定義在類上,當使用我這個類的時候,
 * 我的方法,還有屬性都是這個類型。
 * */
class GenericTest4 <F>	
{
	public F name;	
	
	GenericTest4(F name)
	{
		this.name = name;
	}
	public  void print(F obj)	//該方法的類型隨類改變而改變
	{
		System.out.println(obj);
	}
	public String toString()
	{
		return String.valueOf(name);
	}
	
	
	/**因爲我不止打印字符串,所以我必須定義一個屬於我自己的泛型*/
	public <H> void print2(H obj)	//屬於獨立的類型,定義該泛型 與類 泛型無關
	{
		System.out.println(obj);
	}
}

總結:泛型是很強大的,說有多強大就有多強大!!!但是必須注意的事,泛型是給編譯器看的,如編譯器將 代碼變成 class文件以後,他便沒有了泛型這一詞的說法了。
如果在寫代碼之前是使用的 String 類型,編譯成 class文件以後,可以通過反射拿到裏面的字節碼。可以在裏面加隨意類型了。

思考:我們可以通過反射獲得泛型的實際參數嗎???這個其實是可以。不過相當比較麻煩而已。
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;

/**通過反射獲取 參數列表 或 參數實際類型!!
 * 由於編譯器編譯以後會將泛型信息擦除掉,這個時候就必須要通過其他的方法來獲取了
 * */
public class Generic5 {

	public static void main(String[] args) throws Exception
	{
		ArrayList<Integer> aList = new ArrayList<Integer>();
		printType(aList);
	}
	public static void printType(ArrayList<Integer> aList)throws Exception//編譯之後,Integer不見,但是我們可以通過這個方法獲得aList到底是什麼類型
	{
		Method method = Generic5.class.getMethod("printType", ArrayList.class);//返回一個方法,該方法就是接收 了泛型的方法
		
		Type[] types = method.getGenericParameterTypes();	//獲取他的參數列表
		
		ParameterizedType element = (ParameterizedType)types[0]; // 強制轉換 參數化類型,因爲我知道只有一個,所以直接 [0],
		
		System.out.println(element.getRawType());//返回原始類型
		
		System.out.println(element);	//打印參數列表
		
		String typeStr = String.valueOf(element).substring(String.valueOf(element).lastIndexOf("<"));	//處理不需要信息
		
		System.out.println(typeStr);	//<java.lang.Integer>
	}
}



感覺這種方法個人感覺也不是很合理也需要知道他之前的泛型信息, 我試着 將功能函數 裏面的ArrayList<Integer>  換成了 換成泛型,失敗了。在獲取方法的時候,無法通過泛型獲得字節碼。一直卡在這裏。如果有會的朋友。麻煩聯繫我一下。教我一下方法吧。。。如果Java認定這種不可以的話。那就沒話說了?????????

以上java高新部分 參雜了部分個人 總結,如果有什麼不對的,還請批評指教





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章