泛型
可以在類和方法中預(yù)支地使用未知的類型律姨,一般在創(chuàng)建對象時,將位置類型確定為具體類型臼疫,當(dāng)沒有指定泛型時候择份, 默認(rèn)類型是Obj類型。
使用泛型的好處
- 將運行時時期的異常烫堤,轉(zhuǎn)移到了編譯時期變成了編譯失敗
- 避免了類型強轉(zhuǎn)的麻煩
public class GenericDemo {
public static void main(String[] args) {
Collection<String> list = new ArrayList<>();
list.add("abc");
list.add("def");
// list.add(5); 集合已經(jīng)明確了具體元素存放的類型
// 已經(jīng)明確了類型荣赶,在使用迭代器的時候,迭代器也同樣知道遍歷元素的具體類型
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()){
String str = iterator.next();
// 使用iterator()在這里可以直接獲取String類型
System.out.println(str.length());
}
System.out.println(list);
}
}
泛型的定義與使用
泛型鸽斟,用來靈活的將數(shù)據(jù)類型應(yīng)用到不同類讯壶、方法、接口當(dāng)中湾盗。將數(shù)據(jù)類型作為參數(shù)進行傳遞。
格式
修飾符 class 類名<代表泛型的變量>{
}
使用泛型:在創(chuàng)建對象的時候確定泛型
自定義泛型
public class MyGenericClass<MVP> {
// 沒有MVP類型立轧, 在這里代表未知的一種數(shù)據(jù)類型
// 未來傳遞什么就是什么類型
private MVP mvp;
public MVP getMvp() {
return mvp;
}
public void setMvp(MVP mvp) {
this.mvp = mvp;
}
}
測試
public class TestGenericDemo {
public static void main(String[] args) {
// 創(chuàng)建一個泛型為String類
MyGenericClass<String> my = new MyGenericClass<>();
my.setMvp("哈登哥");
String mvp = my.getMvp();
System.out.println(mvp);
MyGenericClass<Integer> my2 = new MyGenericClass<>();
my2.setMvp(13);
Integer mvp2 = my2.getMvp();
System.out.println(mvp2);
}
}
含有泛型的方法
格式:
修飾符 <代表泛型的變量> 返回值類型 方法名(參數(shù)列表){
}
舉個栗子
public class MyGenericMethod {
public <MVP>void show(MVP mvp){
System.out.println(mvp.getClass());
}
public <MVP> MVP show2(MVP mvp){
return mvp;
}
}
測試
public class TestGenericDemo2 {
public static void main(String[] args) {
MyGenericMethod mgm = new MyGenericMethod();
// 在調(diào)用方法時格粪, 確定泛型的類型
mgm.show("aaa");
mgm.show(123);
mgm.show(12.45);
}
}
含有泛型的接口
格式
修飾符 interface 接口名<泛型>{
}
舉個栗子
public interface MyGenericInterface<E> {
public abstract void add(E e);
public abstract E getE();
}
實現(xiàn)類在定義類的時候確定泛型的類型
public class MyGenericImpl implements MyGenericInterface<String> {
@Override
public void add(String s) {
}
@Override
public String getE() {
return null;
}
}
上面泛型 E 的值就是String類型
始終不確定泛型的類型,直到創(chuàng)建對象的時候氛改,確定泛型的類型
public class MyGenericImpl2<E> implements MyGenericInterface<E> {
@Override
public void add(E e) {
}
@Override
public E getE() {
return null;
}
}
確定泛型
public class TestGenericDemo2 {
public static void main(String[] args) {
MyGenericImpl2<String> impl2 = new MyGenericImpl2<>();
ArrayList<Object> list = new ArrayList<>();
impl2.add("hehe");
}
}
泛型通配符
常用的通配符含義
- E Element(在集合中使用)
- T Type(Java類)
- K Key(鍵)
- V Value (值)
- N Number(數(shù)值類型)
- 帐萎? 表示不確定的Java類型
<?> 表示不確定的Java類型胜卤, 一旦使用<疆导?> 只能使用Object類中的共性方法
基本使用
<?> 不知道到使用什么類型來接受的時候
public class TestGenericDemo3 {
public static void main(String[] args) {
Collection<Integer> list1 = new ArrayList<>();
Collection<String> list2 = new ArrayList<>();
getElement(list1);
getElement(list2);
}
public static void getElement(Collection<?> coll){
// <?>代表可以接受任意類型
}
}
高級應(yīng)用— 受限類型
在Java中的泛型可以指定一個泛型的上限和下限
泛型的上限
:
格式 : 類型名稱<葛躏? extends 類 > 對象名稱
意義 : 只能接受該類型及其子類
泛型的下限
:
格式 : 類型名稱<澈段? super 類 > 對象名稱
意義 : 只能接受該類型及其父類
舉個栗子:現(xiàn)在已知Object類、String類舰攒、Number類败富、Integer類,其中Number類是Integer類的父類
public class TestGenericDemo4 {
public static void main(String[] args) {
Collection<Integer> list1 = new ArrayList<>();
Collection<String> list2 = new ArrayList<>();
Collection<Number> list3 = new ArrayList<>();
Collection<Object> list4 = new ArrayList<>();
getElement1(list1);
getElement1(list2);// 報錯
getElement1(list3);
getElement1(list4); // 報錯
getElement2(list1)// 報錯
getElement2(list2)// 報錯
getElement2(list3)// 報錯
getElement2(list4)// 報錯
}
// 泛型的上限 此時泛型摩窃?必須是Number類型或者Number類型子類
public static void getElement1(Collection<? extends Number> coll){
}
// 泛型的下限 此時泛型兽叮?必須是Number類型或者Number類型父類
public static void getElement2(Collection<? super Number> coll){
}
}
異常
異常: 指的是程序在執(zhí)行過程中,出現(xiàn)的非正常的情況,最終會導(dǎo)致JVM的非正常停止
在Java中異常本身是一個類鹦聪, 產(chǎn)生異常就是創(chuàng)建異常對象并且拋出了一個異常對象账阻。Java處理異常的方式是中斷處理
異常不是語法錯誤, 語法錯誤是無法通過編譯
異常體系
異常的根類是 java.lang.Throwable泽本,兩個子類 java.lang.Error和 java.lang.Exception
平時所說的異常是java.lang.Exception
Throwable體系:
- Error: 嚴(yán)重錯誤淘太,無法通過處理的錯誤,好比絕癥
- Exception表示異常观挎, 異常產(chǎn)生后程序員可以通過代碼的方式糾正琴儿,使程序繼續(xù)運行,是必須處理的嘁捷。好比感冒發(fā)燒
常用方法
- public void printStackTrace()造成; 打印異常的詳細信息并且輸出到控制臺中
- public String getMessage(); 獲取發(fā)生異常的原因
舉個栗子
public class Demo1 {
public static void main(String[] args) {
int[] arr = {1, 2, 4};
System.out.println(arr[3]);
}
}
異常的分類
- 編譯時期異常: 如果沒有處理,編譯失斝巯(如日期格式化異常)
-
運行時期異常: 在運行時期檢查異常 (如數(shù)學(xué)異常)
異常處理
Java中異常處理的五個關(guān)鍵字:try晒屎、catch、finally缓升、throw鼓鲁、throws
拋出異常throw
- 創(chuàng)建一個異常對象。封裝一些提示信息(信息可以自己編寫)
- 通過throw將這個異常對象告知調(diào)用者港谊,throw用在方法內(nèi)骇吭,用來拋出一個異常對象,將這個異常對象傳遞到調(diào)用者處歧寺,并結(jié)束當(dāng)前方法的執(zhí)行燥狰。
- 格式
throw new 異常類名(參數(shù));
舉個栗子
throw new NullPointerException("要訪問的arr數(shù)組不存在");
throw new ArrayIndexOutOfBoundsException("數(shù)組越界了斜筐,兄弟");
public class Demo2 {
public static void main(String[] args) {
int[] arr = {1, 2, 4, 5};
int index = 4;
int element = getElement(arr, index);
System.out.println(element);
}
public static int getElement(int[] arr, int index){
// 判斷
if (index < 0 || index> arr.length-1){
throw new ArrayIndexOutOfBoundsException("數(shù)組越界了龙致,兄弟");
}
return arr[index];
}
}
聲明異常throws
聲明異常:將為題標(biāo)識出來, 報告給調(diào)用者顷链, 如果方法內(nèi)通過throw 拋出了編譯時異常目代, 而沒有捕獲處理,那么必須通過throws進行聲明嗤练,讓調(diào)用者去處理
修飾符 <代表泛型的變量> 返回值類型 方法名(參數(shù)列表) throws 異常類1榛了, 異常類2 {
}
舉個栗子
public class Demo3 {
public static void main(String[] args) throws ParseException {
String s = "1994-12";
timeFormat(s);
}
public static void timeFormat(String str) throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM");
Date date = sdf.parse(str);
System.out.println(date);
}
}
捕獲異常try catch
如果異常出現(xiàn)會立刻終止程序, 所以我們得處理異常
- 聲明拋出, 由調(diào)用者來處理(throws)
- 使用try catch語句塊來處理異常
try catch的方式就是捕獲異常
格式:
try {
編寫可能會出現(xiàn)異常的地方
}catch(異常類型 e){
// 處理異常的代碼
// 記錄日志潭苞、打印異常信息忽冻、繼續(xù)拋出異常
}
- try 編寫可能會出現(xiàn)異常的代碼
- catch 異常的捕獲進行處理
- try catch不能單獨使用, 必須連用
public class Demo4 {
public static void main(String[] args) {
try {
read("xiaomiMi.txt");
} catch (Exception e) {
// 在try中拋出什么異常此疹,在括號中就捕獲什么異常類型
// e.printStackTrace();
System.out.println("++++++++");
System.out.println(e);
}
System.out.println("end");
}
public static void read(String path) throws FileNotFoundException {
if (!path.equals("xiaomimi.txt")){
throw new FileNotFoundException("你的文件怎么消失了呢");
}
}
}
finally 代碼塊
finally: 有一些特定的代碼僧诚,無論是否發(fā)生異常都要執(zhí)行遮婶,另外,因為異常會引發(fā)程序跳轉(zhuǎn)湖笨,導(dǎo)致有些語句執(zhí)行不到旗扑, 而finally就解決了這個問題。
- finally不能單獨使用
public class Demo4 {
public static void main(String[] args) {
try {
read("xiaomiMi.txt");
} catch (Exception e) {
// 在try中拋出什么異常慈省,在括號中就捕獲什么異常類型
// e.printStackTrace();
System.out.println("++++++++");
System.out.println(e);
}finally {
System.out.println("不管try和catch執(zhí)行啥了臀防,我這里都會執(zhí)行");
System.out.println("我是接盤俠");
}
System.out.println("end");
}
public static void read(String path) throws FileNotFoundException {
if (!path.equals("xiaomimi.txt")){
throw new FileNotFoundException("你的文件怎么消失了呢");
}
}
}
ambda表達式
是JDK1.8版本的新特性, lambda 省去面向?qū)ο蟮臈l條框框边败,格式由3部分組成
- 一些參數(shù)
- 一個箭頭
- 一段代碼
標(biāo)準(zhǔn)格式:
(參數(shù)類型 參數(shù)名) -> { 代碼語句 }
說明
- 小括號就是傳統(tǒng)的參數(shù)列表袱衷,多個用逗號分隔
- -> 代表指向動作
- 大括號和原來一樣寫方法體
無參無返回
public interface Cook {
void makeFood();
}
public class Demo2 {
public static void main(String[] args) {
invoke(()->{
System.out.println("lambda表達式做的飯好了");
});
}
public static void invoke(Cook cook){
cook.makeFood();
}
}
小括號代表Cook接口的makeFood方法參數(shù)為空, 大括號代表makeFood的方法體
有參有返回值
需求:使用數(shù)組存儲多個Person對象笑窜,對數(shù)組中的Person對象使用Arrays的sort方法通過年齡排序致燥;
代碼分析
- 為了排序, Arrays.sort需要排序規(guī)則排截, Comparator接口的實例嫌蚤,實現(xiàn)compare方法
- 為了實現(xiàn)compare方法,不得不寫一個Comparator的實現(xiàn)類
- 為了省略Comparator的實現(xiàn)類ComparatorImpl断傲,不得不使用匿名內(nèi)部類
- 必須覆蓋compare方法脱吱,所有的聲明都需要重寫一遍
- 實際上, 只有參數(shù)和方法體是關(guān)鍵部分
Lambda寫法
public class Demo4 {
public static void main(String[] args) {
Person[] array = {
new Person("貂蟬", 223),
new Person("孫尚香", 18),
new Person("妲己", 300),
new Person("楊玉環(huán)", 221),
};
Arrays.sort(array, (Person a, Person b)->{
return a.getAge()-b.getAge();
});
for (Person person :array){
System.out.println(person);
}
}
}
需求:給定一個計算器Calculator接口认罩,內(nèi)含抽象方法calc可以將連個int類型的數(shù)組相加得到和的值
public interface Calculator {
int calc(int a , int b);
}
public class Demo5 {
public static void main(String[] args) {
// 使用lambda表達式 調(diào)用測試
invokeCalc(5,6, (int a, int b)->{
return a + b;
});
}
public static void invokeCalc(int a, int b, Calculator calculator){
int res = calculator.calc(a, b);
System.out.println("res = "+ res);
}
}
省略格式:
Lambda強調(diào)做什么箱蝠, 而不是怎么做, 凡是可以根據(jù)上下文推導(dǎo)得知的消息垦垂,都可以省略
public class Demo6 {
public static void main(String[] args) {
// 使用lambda表達式 調(diào)用測試
invokeCalc(5,6, (a, b)-> a + b);
}
public static void invokeCalc(int a, int b, Calculator calculator){
int res = calculator.calc(a, b);
System.out.println("res = "+ res);
}
}
省略規(guī)則:
- 小括號內(nèi)參數(shù)可以省略
- 如果小括號內(nèi)有且僅有一個參抡锈, 小括號可以省略
- 如果大括號內(nèi)有有且僅有一個語句,則無論是否有返回值乔外,都可以省略大括號、return及語句分號
public class Demo7 {
public static void main(String[] args) {
// 使用lambda表達式 調(diào)用測試
invokeShow(100, a -> a + 100);
}
public static void invokeShow(int a, Show show){
int res = show.showNum(a);
System.out.println(res);
}
}
改寫之前廚子
public class Demo8 {
public static void main(String[] args) {
invoke(()-> System.out.println("省略在做飯"));
}
public static void invoke(Cook cook){
cook.makeFood();
}
}
Lambda使用前提
- 使用Lambda必須具有接口一罩,且要求接口中有且僅有一個抽象方法(無論是Runable杨幼、Comparator接口還是自己定義的接口,都得是抽象方法唯一)
- 使用Lambda必須具有上下文推斷.聂渊;也就是方法的參數(shù)或者局部變量類型必須為Lambda對應(yīng)的接口類型差购,才能使用Lambda作為該接口的實例
有且只有一個抽象方法的接口叫做函數(shù)式接口