文章目录
- 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) {
}
}