1. 程式人生 > >java學習之路 之 泛型及練習題

java學習之路 之 泛型及練習題

泛型

為什麼要有泛型(Generic)?
1. 解決元素儲存的安全性問題
2. 解決獲取資料元素時,需要型別強轉的問題
泛型,JDK1.5新加入的,解決資料型別的安全性問題,其主要原理是在類宣告時通過一個標識表示類中某個屬性的型別或者是某個方法的返回值及引數型別。這樣在類宣告或例項化
時只要指定好需要的具體的型別即可。        
Java泛型可以保證如果程式在編譯時沒有發出警告,執行時就不會產生ClassCastException異常。同時,程式碼更加簡潔、健壯。

使用泛型:
泛型的宣告:
interface List<T> 和 class TestGen<K,V>其中,T,K,V不代表值,而是表示型別。這裡使用任意字母都可以。常用T表示,是Type的縮寫。
泛型的例項化:

一定要在類名後面指定型別引數的值(型別)。如:          
List<String> strList = new ArrayList<String>();        
Iterator<Customer> iterator = customers.iterator();
T只能是類,不能用基本資料型別填充。

泛型的幾個重要使用:
1.在集合中使用泛型
2.自定義泛型類
3.泛型方法
4.泛型介面

對於泛型類(含集合類):

1.物件例項化時不指定泛型,預設為:Object。
2.泛型不同的引用不能相互賦值。
3.加入集合中的物件型別必須與指定的泛型型別一致。
4.靜態方法中不能使用類的泛型。
5.如果泛型類是一個介面或抽象類,則不可建立泛型類的物件。
6.不能在catch中使用泛型
7.從泛型類派生子類,泛型型別需具體化
把一個集合中的內容限制為一個特定的資料型別,這就是generics背後的核心思想。

自定義泛型類:

class Person<T>{//使用T型別定義變數 
	private T info;//使用T型別定義一般方法 

	public T getInfo(){
		return info; 
	} 

	public void setInfo(T info){
		this.info = info;
	}

	//使用T型別定義構造器
	public Person(){}

	public Person(T info){
		this.info = info;
	}
	//static的方法中不能宣告泛型
	//public static void show(T t){
	//}
	//不能在try-catch中使用泛型定義
	//try{}
	//catch(T t){}
} 

對於泛型方法:

方法,也可以被泛型化,不管此時定義在其中的類是不是泛型化的。在泛型方法中可以定義泛型引數,此時,引數的型別就是傳入資料的型別。
泛型方法的格式:[訪問許可權]  <泛型>  返回型別  方法名([泛型標識 引數名稱])  丟擲的異常
public class DAO {
	public <E>  E get(int id, E e){
		E result = null;
		return result;
	}
} 

泛型和繼承的關係:

如果B是A的一個子型別(子類或者子介面),而G是具有泛型宣告的類或介面,G<B>並不是G<A>的子型別!
比如:String是Object的子類,但是List<String >並不是List<Object>的子類。

萬用字元:

1.使用型別萬用字元:? //只讀型
比如:List<?>   ,Map<?,?>
List<?>是List<String>、List<Object>等各種泛型List的父類。
2.讀取List<?>的物件list中的元素時,永遠是安全的,因為不管list的真實型別是什麼,它包含的都是Object。
3.寫入list中的元素時,不行。因為我們不知道c的元素型別,我們不能向其中新增物件。
唯一的例外是null,它是所有型別的成員。


將任意元素加入到其中不是型別安全的:
Collection<?> c = new ArrayList<String>();
c.add(new Object()); // 編譯時錯誤
因為我們不知道c的元素型別,我們不能向其中新增物件。        
add方法有型別引數E作為集合的元素型別。我們傳給add的任何引數都必須是一個未知型別的子類。因為我們不知道那是什麼型別,所以我們無法傳任何東西進去。
唯一的例外的是null,它是所有型別的成員。
另一方面,我們可以呼叫get()方法並使用其返回值。返回值是一個未知的型別,但是我們知道,它總是一個Object

有限制的萬用字元:

<?>
允許所有泛型的引用呼叫
舉例:
<? extends Number>     
(無窮小 , Number]只允許泛型為Number及Number子類的引用呼叫
<? super Number>      
[Number , 無窮大)只允許泛型為Number及Number父類的引用呼叫
<? extends Comparable>
只允許泛型為實現Comparable介面的實現類的引用呼叫

泛型應用:

使用者在設計類的時候往往會使用類的關聯關係,例如,一個人中可以定義一個資訊的屬性,但是一個人可能有各種各樣的資訊(如聯絡方式、基本資訊等),所以

此資訊屬性的型別就可以通過泛型進行宣告,然後只要設計相應的資訊類即可。

練習題

package com.atguigu.javae.generic;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.Vector;

import org.junit.Test;

/**
 * 自定義泛型類, 類名<X>
 * 泛型和物件相關, 型別的資訊最終會在建立物件時確定!
 */
class Person<X> { // X代表某型別, 既然是型別,宣告變數,作為返回值...
// 當在建立物件時未指定X的具體型別, 它的型別就是Object
	//private static int X x;
	
	private X info;
	
	public Person() {
	}
	
	public Person(X info) {
		this.info = info;
	}
	
	public void setInfo(X info) {
		try {
			this.info = info;
		//} catch (X e) { // catch 中只能捕捉Throwable及其子類 ,不可以使用型別不一定的X型別
		} catch (Throwable e) { // catch 中只能捕捉Throwable及其子類 
			e.printStackTrace();
		}
	}
	
	public X getInfo() {
		return info;
	}
	
	@Override
	public String toString() {
		return "某人, 資訊[" + info + "]";
	}
	
	/*
	public static void test(X t) { // 靜態環境中不允許使用和物件相關的泛型
		System.out.println(t);
	}*/
	
	// 泛型方法, 在返回值前面加<型別>, 一定要在形參中帶上型別資訊
	public static <Y> Y testY(Y y) { // 如果泛型方法中的泛型型別名和類中的一樣, 則會遮蔽類中的泛型
		return null;
	}
	
	
}

class Student extends Person<String> { // 在Student子類中,info屬性就是固定的String型別
}

public class GenericTest {
	
	// 寫一個方法引數是(Collection<? extends Comparable> coll)
	// 在方法中返回這個集合中的最小值
	// 寫一個測試方法, 建立List集合,儲存一些字串, 再建立Set集合, 儲存一些char, 分別使用這兩個集合呼叫以上方法,列印輸出結果
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public Comparable getMin(Collection<? extends Comparable> coll) {
		if (coll.size() == 0) {
			return null;
		}
		Iterator<? extends Comparable> iterator = coll.iterator();
		Comparable min = iterator.next();
		while (iterator.hasNext()) {
			Comparable comp = iterator.next();
			if (comp.compareTo(min) < 0) {
				min = comp;
			}
		}
		return min;
	}
	
	@Test
	public void test7() {
		List<String> list = new LinkedList<String>();
		list.add("xxx");
		list.add("bbb");
		list.add("aaa");
		list.add("ddd");
		System.out.println(getMin(list));
		
		Collection<Character> set = new HashSet<Character>();
		set.add('我');
		set.add('好');
		set.add('a');
		set.add(' ');
		System.out.println(getMin(set));
	}
	
	public double sumList(List<? extends Number> list) {
		// list就可以指向以Number及其子類為泛型的集合, 但是引用不能新增元素, 但是可以獲取元素,並且Number
		Iterator<? extends Number> iterator = list.iterator();
		double sum = 0;
		while (iterator.hasNext()) {
			Number number = iterator.next();
			sum += number.doubleValue();
		}
		return sum;
	}
	
	public double getMax(Collection<? extends Number> coll) {
		Iterator<? extends Number> iterator = coll.iterator();
		if (!iterator.hasNext()) {
			throw new RuntimeException("集合為空,沒法找最大值");
		}
		Number maxNumber = iterator.next();
		while (iterator.hasNext()) {
			Number tmpNumber = iterator.next();
			if (tmpNumber.doubleValue() > maxNumber.doubleValue()) {
				maxNumber = tmpNumber;
			}
		}
		return maxNumber.doubleValue();
	}
	
	// 寫一個方法引數是(Collection<? extends Number> coll)
	// 在方法中返回這個集合中的最大值
	// 寫一個測試方法, 建立List集合,儲存一些整數, 再建立Set集合, 儲存一些float, 分別使用這兩個集合呼叫以上方法,列印輸出結果
	

	@Test
	public void test6() {
		ArrayList<Integer> list1 = new ArrayList<Integer>();
		list1.add(100);
		list1.add(20);
		list1.add(30);
		list1.add(1000);
		list1.add(7);
		
		System.out.println(getMax(list1));
		
		Set<Float> set = new HashSet<Float>();
		set.add(234.23f);
		set.add(1234.23f);
		set.add(3234.23f);
		set.add(2334.23f);
		set.add(234.23f);
		
		System.out.println(getMax(set));
	}
	@Test
	public void test5() {
		ArrayList<Integer> list1 = new ArrayList<Integer>();
		list1.add(100);
		list1.add(20);
		list1.add(30);
		list1.add(10);
		list1.add(7);
		
		List<Double> list2 = new Vector<Double>();
		list2.add(3.1);
		list2.add(2.1);
		list2.add(4.1);
		list2.add(5.1);
		list2.add(1.1);
		
		double sum = sumList(list1);
		System.out.println(sum);
		
		sum = sumList(list2);
		System.out.println(sum);
		/*
		List<? extends Number> list2 = list; // list2就可以指向以Number及其子類為泛型的集合, 但是引用不能新增元素, 但是可以獲取元素,並且Number
		Iterator<? extends Number> iterator = list2.iterator();
		while (iterator.hasNext()) {
			Number number = iterator.next();
			sum += number.intValue();
		}
		System.out.println(sum);
		
		list2 = list3;
		*/
	}
	@Test
	public void test4() {
		ArrayList<Integer> list = new ArrayList<Integer>();
		list.add(100);
		list.add(20);
		list.add(30);
		list.add(10);
		list.add(7);
		
		List<?> list2 = list;// 只讀訪問
		Object object = list2.get(1);
		System.out.println(object);
	}
	
	@SuppressWarnings("unused")
	@Test
	public void test3() {
		// 使用?萬用字元 通常用於只讀訪問
		Person<?> person = new Person<>();
		//person.setInfo("abc");
		person.setInfo(null);
		Object object = person.getInfo();
	}
	
	@SuppressWarnings({ "static-access", "rawtypes", "unchecked", "unused" })
	@Test
	public void test2() {
		//Person<Object> person = new Person<String>("張三");
		Person<Integer> person = new Person<Integer>(30);
		String string = person.testY("abc");
		System.out.println(string);
		
		ArrayList list = new ArrayList();
		list.add(100);
		list.add(200);
		list.add(50);
		list.add(10);
		
		Integer[] tmp = new Integer[0];
		//Integer[] arr = list.toArray(tmp);
	}
	
	@SuppressWarnings({ "rawtypes", "unchecked" })
	@Test
	public void test1() {
		Person person = new Person(true);
		Object valaueObject = person.getInfo();
		System.out.println(valaueObject);
		
		Person<String> person2 = new Person<String>("張三");
		String name = person2.getInfo();
		System.out.println(name);
		
		Person<Integer> person3 = new Person<Integer>(30);
		Integer integer = person3.getInfo();
		System.out.println(integer);
	}
}

相關推薦

java學習 練習題

泛型 為什麼要有泛型(Generic)?1. 解決元素儲存的安全性問題2. 解決獲取資料元素時,需要型別強轉的問題泛型,JDK1.5新加入的,解決資料型別的安全性問題,其主要原理是在類宣告時通過一個標識表示類中某個屬性的型別或者是某個方法的返回值及引數型別。這樣在類宣告或例

java學習-list集合有情況新增多種型別資料

//來自一道面試題的分析,大概的意思是,如果讓你在集合中增加不同型別的資料,你會怎麼做?package cn.cnsy123.ypf.thread.test; import java.lang.reflect.InvocationTargetException; impo

學習筆記】 唐大仕—Java程式設計 第5講 深入理解Java語言5.2 多虛方法呼叫

/** * 多型及虛方法呼叫 * @author cnRicky * @date 2018.11.7 */ 多型 多型(Polymorphism)是指一個程式中相同的名字表示不同的含義的情況 多型有兩種情形 編譯時多型:  *過載(Overload)(多個同名的不同方法)  *如 p.sayH

java學習 Java常用類-Data類、Math類、BigInteger類、BigDecimai類 練習題

日期類: java.lang.System類 System類提供的public static long currentTimeMillis()用來返回當前時間與1970年1月1日0時0分0秒之間以毫秒為單位的時間差。此方法適於計算時間差。計算世界時間的主要標準有:UTC(U

java學習 基本語法-方法(函式)練習題

方法(函式):方法是類或物件行為特徵的抽象,也稱為函式。 Java裡的方法不能獨立存在,所有的方法必須定義在類裡。方法的宣告的語法格式: 修飾符 返回值型別 方法名(引數型別 形參1,引數型別 形參2,….){   程式程式碼  return 返回值; } 其中:形式引數:

Java進階學習集合與(1)

[TOC](目錄) ## 1.集合 #### 1.1.集合是什麼 之前的基礎篇中我們知道了一種資料結構:陣列,可以存放很多資料。但是資料有很大的侷限性: - 支援的資料型別單一 - 宣告時需要指定大小,大小固定,可擴充套件性差 - 連續的儲存單元,對記憶體要求苛刻 那麼是否有其他的資料結構或者資料型

Linux學習-Centos7-nmcli命令網橋【21】---20180127

nmcli 網橋 測試網絡工具 網絡客戶端工具 一、nmcli命令1、地址配置工具:nmclinmcli [ OPTIONS ] OBJECT { COMMAND | help } device - show and manage network interfaces nmcl

Python學習:隊列生產者消費者模型

open imp read 之路 while args put bsp tar 隊列: 先進先出 後進先出 VIP(優先級) import queue # q = queue.LifoQueue()#後進先出 # # q.put(1) # q.put(2) # q.p

Java學習--設計模式結構模式(二)

and 它的 null spa bubuko imp AD mco flyweight 一、裝飾器模式(Decorator Pattern)   1、概念    裝飾器模式(Decorator Pattern)允許向一個現有的對象添加新的功能,同時又不改變其結構。這種類

java學習javaSE基礎3

區別 之路 函數重載 while循環 a + b 內存 sum get 錯誤 所有代碼都未經測試。。 1:for循環的格式? for循環格式: for(初始化表達式;條件表達式;循環後的操作表達式) { 循環體; } 執行流程: * a:執行初始化語句 * b:

大資料學習105-視窗函式foreachRDD,foreachPartition,foreach對比

sparkstreaming的視窗函式: 視窗函式的作用主要是計算一段時間之內的資料的變化,那麼就會有人產生疑問,為什麼視窗與視窗之間需要重疊呢? 其實不重疊也是可以的,但是如果不重疊的話,將來做出來的報表一個時間段與另一個時間段的資料就會產生劇烈的變化。 視窗函式可以讓我們一下子操

進階SetValue

public class User{ public int UID{ get;set;} public string UName{ get;set;} } public class ADO&

Java程式設計思想(第4版) 15.5 匿名內部類

15.5   匿名內部類 泛型還可以應用於內部類以及匿名內部類。下面的示例使用匿名內部類實現了Generator介面:     Customer和Teller類都只有private的構造器,這可以強制你必須使用Generator物件。Customer有一個generator(

JAVA基礎集合、

今天我們來聊聊集合; 通常,我們的程式需要根據程式執行時才知道建立多少個物件。但若非程式執行,程式開發階段,我們根本不知道到底需要多少個數量的物件,甚至不知道它的準確型別。為了滿足這些常規的程式設計需要,我們要求能在任何時候,任何地點建立任意數量的物件,而這些物件用什麼來容納呢?我們首先

JAVA學習---面向物件繼承·多

這週六我們學習了面向物件的一些知識,包含繼承和多型的。 以下是我學過後的心得總結: 封裝: 即將構造方法,成員變數,成員方法封裝在一起,形成一個類class檔案,實現呼叫。 繼承: 繼承概述: 繼承即多個類中存在相同的屬性和行為時,我們可以將這些內容抽取到

學習Pythonipython的使用Pycharm的安裝

一、Python的基礎知識 1、概念 Python是一種面向物件的解釋型計算機程式設計語言,由荷蘭人Guido van Rossum於1989年發明,第一個公開發行版發行於1991年。 2、Python的特點 優點: 簡單、優雅、明確 有強大的第三方庫模組 可跨

java學習 網路程式設計-練習題

package com.atguigu.javase.net; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.InputStreamReader; import j

Android 學習(原始碼網站書籍)

Android 開源專案分類彙總 https://github.com/Trinea/android-open-project Android官方培訓課程中文版(v0.9.5) http://hukai.me/android-training-course-in-chin

java學習 高階類特性2-介面(interface)-練習題

// 介面通常用形容詞來描述 , 用以描述一種能力. public interface Flyer { // 介面 : 是對事物行為的抽象定義, 介面中只允許抽象方法,和全域性常量 public static final int num = 10;

java學習基本語法-程式流程控制-switch語句練習題

switch(變數){ case 常量1: 語句1; break; case 常量2: 語句2; break; … … case 常量N: 語句N; break; defa