文章目錄
- Lambda表達式調用Arrays類中的方法
-
- parallelSetAll()
- parallelPrefix()
- Stream()
- Lambda練習
- 異常處理
-
- 異常的分類
-
- Error 異常
- Exception異常
-
- 運作時異常
- 非運作時異常
- 常見的異常
- 異常的處理方式
-
- try-catch
-
- 文法:
- 執行結果
- 多重catch
-
- 文法
- catch塊中異常類類型的順序
- try-catch-finally
-
- 特殊情況
- throws
- throw
- 自定義異常
-
- 自定義 運作時異常
- 自定義 非運作時異常
- 總結
-
- throw和throws的差別
- 關于循環中Scanner重複輸入的問題
- 異常處理原則
- 泛型
-
- 回顧方法重寫
- 概念
- 類型參數
- 參數化類型
- 好處
- 定義泛型類
-
- 文法
- 參數類型
- 多參數類型
- 原生類型
- 位元組碼檔案對泛型的處理
- 通配符
- 提問
- 類型參數與通配符的差別
- 泛型構造器
- 泛型方法
- 擦除的原則
-
- 對于參數化類型,
- 對于類型參數
-
- 無界類型參數,擦除後為Object;
- 有一個上限的類型參數,用上限類型替換;
- 有多個上限的類型參數,用第一個上限來替換;
- 泛型重載和重寫
Lambda表達式調用Arrays類中的方法
parallelSetAll()
package day14;
import java.util.Arrays;
import java.util.function.IntUnaryOperator;
public class TestLambda2 {
public static void main(String[] args) {
int[] arr = {1, 2, 3, 4, 5};
// 一進制運算,用運算的結果來替換數組中的所有元素
Arrays.parallelSetAll(arr, new IntUnaryOperator() {
@Override // 數組的索引,
public int applyAsInt(int operand) {
return operand + 1;
}
});
Arrays.parallelSetAll(arr, index -> {
return index+1;
});
System.out.println(Arrays.toString(arr));
}
}
parallelPrefix()
package day14;
import java.util.Arrays;
import java.util.function.IntBinaryOperator;
public class TestLambda2 {
public static void main(String[] args) {
int[] arr = {1, 2, 3, 4, 5};
// 二進制運算,用于運算的結果,來替換數組中的所有元素
Arrays.parallelPrefix(arr, new IntBinaryOperator() {
@Override // left:前一個元素,right目前元素,目前元素是數組中第一個元素,前一個元素是1;
public int applyAsInt(int left, int right) {
return left * right;
}
});
// 目前元素 和 替換後的元素進行計算,得出的結果在指派給目前元素的位置;
System.out.println(Arrays.toString(arr));
Arrays.parallelPrefix(arr,((left, right) -> {
return left* right;
}));
System.out.println(Arrays.toString(arr));
}
}
Stream()
package day14;
import java.util.Arrays;
import java.util.function.IntConsumer;
public class TestLambda2 {
public static void main(String[] args) {
int[] arr = {1, 2, 3, 4, 5};
// Stream
Arrays.stream(arr).forEach(new IntConsumer() {
@Override // value 數組中的每個元素
public void accept(int value) {
System.out.println(value);
}
});
// Lambda
Arrays.stream(arr).forEach(value -> System.out.println(value));
// 方法引用
Arrays.stream(arr).forEach(System.out::println);
}
}
Lambda練習

package day14;
abstract class Animal{
public abstract void breed();
}
@FunctionalInterface
interface Fly{
void fly();
}
@FunctionalInterface
interface Talk{
void talk();
}
class WileGoose extends Animal{
@Override
public void breed() {
System.out.println("大雁能繁殖");
}
Fly fly = () -> System.out.println("大雁能飛");
/*public void fly(Fly fly){
fly.fly();
}*/
}
class Duck extends Animal{
@Override
public void breed() {
System.out.println("鴨子能繁殖");
}
public void swimming(){
System.out.println("鴨子會遊泳");
}
}
class DonaldDuck extends Duck{
// 直接在屬性中設定Lambda,
Talk talk = ()-> System.out.println("唐老鴨能說話");
// 需要在主函數設定;不推薦;
public void speak(Talk talk){
talk.talk();
}
}
public class TestLamabda3 {
public static void main(String[] args) {
DonaldDuck donaldDuck = new DonaldDuck();
donaldDuck.talk.talk();
WileGoose wileGoose = new WileGoose();
wileGoose.fly.fly();
}
}
異常處理
一般都是為了處理非正常的情況,改變程式的執行流程。
異常的分類
異常分類圖
Error 異常
Error異常都是用代碼處理不了的
Exception異常
用代碼可以處理的異常;
Exception類的包含了運作時異常和非運作時異常;
運作時異常
RunTimeException類和它的子類;
編譯可以通過,但是在運作期出現的問題;使程式中斷了;
非運作時異常
Exception自身 和 它的部分子類(因為包含了RunTimeException)
編譯時出現問題
是必須要處理的異常。
常見的異常
package day14;
import java.lang.reflect.Field;
import java.util.Scanner;
class A{
private int x;
}
public class TestException1 {
public static void main(String[] args) throws Exception {
//空指針 java.lang.NullPointerException
String s = " ";
System.out.println(s.equalsIgnoreCase("abc"));
// java.util.InputMismatchException
Scanner input = new Scanner(System.in);
int n = input.nextInt();
// 類型轉換java.lang.ClassCastException
/* Object obj = new String();
String s1 = (String)obj;
Integer n1 = (Integer)obj;*/
// 數字格式化 java.lang.NumberFormatException
String s2 = "12a";
//int n3 = Integer.valueOf(s2);
// 不能加載類 java.lang.ClassNotFoundException
// ClassLoader.getSystemClassLoader().loadClass("day14.TestException");
// 通路x:到位元組碼檔案中
Class c = A.class;
Field f = c.getDeclaredField("x");
f.setAccessible(true);//需要授權,否則報錯
Object o = c.newInstance();
//java.lang.IllegalAccessException 參數異常
f.set(o,22);
System.out.println(f.get(o));
}
}
異常的處理方式
try-catch
文法:
try{
可能會出現異常的代碼
} catch(異常類類型 對象名){
處理異常
}
執行結果
-
沒有異常;
try執行,catch沒有執行
-
異常類型比對,
try執行,catch也執行
-
異常類型不比對:
try執行,catch不執行;但是程式會中斷;
多重catch
文法
try{
可能會出現異常的代碼
} catch(異常類類型 對象名){
處理異常
} catch(異常類類型 對象名){
處理異常
} catch(異常類類型 對象名){
處理異常
} ……
catch塊中異常類類型的順序
- 有普通 到特殊,
- 由子類到父類;
- catch塊中可以增加return;catch塊中可以增加return;來結束目前方法;
try-catch-finally
try{
可能會出現異常的代碼
} catch(異常類類型 對象名){
處理異常
} …… catch(異常類類型 對象名){
處理異常
}finally{ //關閉流
一定會執行
}
特殊情況
- catch塊中有
,return
塊也會執行;finally
- catch塊中System.exit(0),就退出JVM了,是以就不執行finally塊了;
throws
- 聲明異常,把異常抛給調用者;
- 方法頭部聲明了Excepion,那麼就說明方法體中存在 非運作時異常,是必須要處理的;
- 方法頭部聲明了RunTimeException,那麼就說明方法體中存在 運作時異常,不是必須處理的;
++問題:什麼時候需要throws聲明異常++
throws與try-catch是一個級别的;
throws是将異常抛出給調用者,try-cathc是将異常自己處理了;
也就是說當調用了一個聲明了異常的方法時,要麼使用try捕獲異常,要麼使用throws抛出異常
throw
- 抛異常,
- 自己抛異常
- 如果抛出的異常是 運作時異常(RunTimeException),在方法頭部不用throws聲明Exception
package homework02;
/**
* 自定義顔色異常,繼承自RunTimeException
*/
class ColorException extends RuntimeException{
private String emessage;
ColorException(String emessage){
super();
this.emessage = emessage;
}
@Override
public String toString() {
return this.emessage;
}
}
/**
* 貓類
*/
class Cat{
/**
* 顔色
*/
private String color;
public String getColor() {
return color;
}
/**
* 如果顔色不是紅,黃,藍,就抛出異常
* @param color
*/
public void setColor(String color) {
if("紅".equals(color) || "黃".equals(color) || "藍".equals(color)) {
this.color = color;
}else{
throw new ColorException("顔色隻能是紅、黃、藍");
}
}
}
public class TestException {
public static void main(String[] args) {
Cat cat = new Cat();
cat.setColor("紅");
cat.setColor("黑");
}
}
- 如果抛出的異常是 非運作時異常,那麼就需要使用throws在方法頭部聲明Exception;
package homework02; /** * 自定義顔色異常,繼承自Exception */ class ColorException extends Exception{ private String emessage; ColorException(String emessage){ super(); this.emessage = emessage; } @Override public String toString() { return this.emessage; } } /** * 貓類 */ class Cat{ /** * 顔色 */ private String color; public String getColor() { return color; } /** * 如果顔色不是紅,黃,藍,就抛出異常 * @param color */ public void setColor(String color) throws Exception { if("紅".equals(color) || "黃".equals(color) || "藍".equals(color)) { this.color = color; }else{ throw new ColorException("顔色隻能是紅、黃、藍"); } } } public class TestException { public static void main(String[] args) throws Exception{ Cat cat = new Cat(); cat.setColor("紅"); cat.setColor("黑"); } }
- 作用:手動抛出異常;
自定義異常
自定義異常,一般都是繼承自兩個類,分别是RunTimeException(運作時異常)和 Exception(非運作時異常);
當自定義異常繼承自 Excetpion時,手動抛出異常的方法的頭部必須 聲明異常(throws Exception),
- 并且如果在調用時使用try-catch捕獲異常,那麼最後必須有一個catch (Exception e)的catch語句;
- 如果不捕獲異常,那麼需要在主方法的頭部,聲明異常(throws Exception);
當自定義異常繼承自 RunTimeExcetpion時,手動抛出異常的方法的頭部不需要 聲明異常(throws),
- 在使用try-catch捕獲異常時,也不需要catch (Exception e)語句。
- 如果不捕獲異常,那麼需要在主方法的頭部,也不需要聲明異常;
自定義 運作時異常
package homework01;
import java.util.Scanner;
/**
* 自定義分數異常類,繼承自RunTimeException,
*/
class ScoreException extends RuntimeException{
/**
* 分數錯誤時的資訊
*/
private String smessing;
public ScoreException(String smessing) {
super();
this.smessing = smessing;
}
@Override
public String getMessage() {
return this.smessing;
}
}
/**
* 學生類
*/
class Student {
private int Score;
public int getScore() {
return Score;
}
/**
* 當分數不在 0~100之間時,抛出分數異常,
* @param score 分數
*/
public void setScore(int score) {
if (score >=0 && score <=100) {
Score = score;
}else{
throw new ScoreException("分數必須在0-100之間");
}
}
}
public class TestStudent{
public static void main(String[] args) {
Student s1 = new Student();
Scanner input = new Scanner(System.in);
/**
* 當分數不在1~100範圍時,需要重新輸入;
*/
while(true) {
try {
System.out.println("請輸入成績:");
s1.setScore(input.nextInt());
break;
} catch (ScoreException e) {
System.out.println(e.getMessage());
// 由于Scanner裡面有緩存,無法将錯誤的值指派給變量。是以需要清空緩存,重新建立一個Scanner類;
input = new Scanner(System.in);
}
}
System.out.println("設定成功。。。");
}
}
自定義 非運作時異常
package homework01;
import java.util.Scanner;
/**
* 自定義分數異常類,繼承自Exception,
*/
class ScoreException extends Exception{
/**
* 分數錯誤時的資訊
*/
private String smessing;
public ScoreException(String smessing) {
super();
this.smessing = smessing;
}
@Override
public String getMessage() {
return this.smessing;
}
}
/**
* 學生類
*/
class Student {
private int Score;
public int getScore() {
return Score;
}
/**
* 當分數不在 0~100之間時,抛出分數異常,
* @param score 分數
*/
public void setScore(int score) throws Exception{
if (score >=0 && score <=100) {
Score = score;
}else{
throw new ScoreException("分數必須在0-100之間");
}
}
}
public class TestStudent{
public static void main(String[] args) {
Student s1 = new Student();
Scanner input = new Scanner(System.in);
/**
* 當分數不在1~100範圍時,需要重新輸入;
*/
while(true) {
try {
System.out.println("請輸入成績:");
s1.setScore(input.nextInt());
break;
} catch (ScoreException e) {
System.out.println(e.getMessage());
} catch (Exception e){
System.out.println("未知錯誤");
}
}
System.out.println("設定成功。。。");
}
}
總結
throw和throws的差別
-
throw
在代碼塊中執行,主要是手工進行異常類的抛出
-
throws
在方法定義上使用,表示此方法可能産生的異常明确告訴給調用處,有調用處進行處理;
關于循環中Scanner重複輸入的問題
Scanner 類的input方法在try語句中時,如果需要在catch語句中重新指派,那麼就需要在catch中重新建立一個Scanner的對象,否則Scanner中有緩存機制,可能會出現跳過指派,直接抛出異常的情況;
異常處理原則
- 隻用于處理非正常的情況
- 避免過大的try塊
- 推薦使用多重catch
- 不要忽略catch塊中的異常
- 改正代碼
- 文檔聲明 - 聲明了異常的方法要詳細的說明異常;
泛型
回顧方法重寫
- 子類中,執行個體方法,方法名相同,參數清單相同,傳回值類型形同
- 通路修飾符權限比父類要大
- 抛出的異常範圍要比父類小
- 傳回值類型可以是父類傳回值類型的子類;
- 父類參數擦除後與子類一緻;
概念
參數化類型
類型參數
- 定義時:形式類型參數
- 應用時:實際類型參數(必須是引用類型)
參數化類型
類型<實際類型參數>
合在一起就叫參數化類型;
好處
- 在編譯器進行類型檢查;
- 類型不确定。
定義泛型類
文法
Class 類名<類型參數1,類型參數2>{
}
參數類型
用一個大寫字母表示;
- T -> Type
- K -> Key
- V -> Value
- E -> Element
多參數類型
可以定義多個類型參數,用逗号分隔;
原生類型
一個類型的後邊沒有指定具體的類型參數,這樣的泛型類型,稱為原生類型
位元組碼檔案對泛型的處理
位元組碼檔案中 擦除泛型類型資訊
通配符
- ? 無界通配符 比對任意類型
- ? extends 上限類 比對上限類和上限的子類
- ? Super 下限類 比對下限類和下限類的父類
提問
String類 是 Object類 的子類,Object 對象 = String 對象,自動轉型,那麼Point和Point有什麼關系?
Point<String>
不是
Point<Object>
的子類型;
通過檢視位元組碼檔案發現 Point的class檔案就一個,是以不存在繼承關系;
類型參數與通配符的差別
- 類型參數可以表示一種類型,泛型類型;通配符不能表示為一種類型。
- 類型參數隻能指定上限;通配符能指定上限,和下限;
- 類型參數 可以指定多個上限;(能比對各個上限的任一子類,); 通配符不能指定多個上限;
泛型構造器
類名<對象泛型> 對象名 = new <構造器泛型>類名<對象泛型>();
package day14;
class Point2<T> {
private T x;
private T y;
// 泛型構造器的定義
public <E> Point2(E e) {
System.out.println(e);
}
public T getX() {
return x;
}
public void setX(T x) {
this.x = x;
}
public T getY() {
return y;
}
public void setY(T y) {
this.y = y;
}
}
public class TestPoint3 {
public static void main(String[] args) {
// 自動類型推斷: 用參數的類型 自動推斷出構造器的類型
Point2<String> p = new Point2<>(11);
//構造器的類型如果是顯示指定,那麼對象的類型也要用顯示指定的 格式 new <構造器泛型>類名<對象泛型>();
Point2<String> p1 = new <Integer>Point2<String>(11);
}
}
泛型方法
package day14;
class Demo{
// 泛型方法
public <T> void f(T t){
System.out.println(t);
}
public <T> T ff(T t){
// 類型推斷
f(22);// 調用方法
// 顯示指定,必須使用對象調用
this.<Double>f(22.2);
return t;
}
}
public class TestPoint4 {
public static void main(String[] args) {
Demo demo = new Demo();
// 類型推斷
demo.f("hello");
demo.f(123);
//顯示指定方法類型
demo.<Double>f(22.2);
}
}
擦除的原則
對于參數化類型,
擦除之後為 原生類型
Point3<String> p; -> point3 p
對于類型參數
無界類型參數,擦除後為Object;
Class Point<T>{
T x;
} -> class Point<Object>{
Object x;
}
有一個上限的類型參數,用上限類型替換;
<T extends A> -> A
有多個上限的類型參數,用第一個上限來替換;
<T extends A && B> --> A
泛型重載和重寫
package day14;
// 泛型類
class Point3<T>{
private T x;
private T y;
public T getX() {
return x;
}
public void setX(T x) {
this.x = x;
}
public T getY() {
return y;
}
public void setY(T y) {
this.y = y;
}
}
class Demo2{}
interface IX{}
interface IY{}
class Demo1{
// public void f(Object t){}
public <T> void f(T t){}
// 是否能重載:擦除後就一樣了;
// public void f(Point3<String> p){ }
public void f(Point3<Integer> p){}
// Demo2
public <T extends Demo2> void f(T t){}
// IX
public <T extends IX & IY> void f(T t){}
// IY
// public <T extends IY & IX> void f(T t){}
}
class Parent1{
// f(Point3 p)
public void f(Point3<String> p){}
}
class Child2 extends Parent1{
@Override
public void f(Point3 p) {
}
}
public class TestPoint5 {
public static void main(String[] args) {
}
}