泛型、异常、λ(lambda)表达式
泛型
可以在类和方法中预支的使用未知的类型,一般在创建对象时,将位置类型确定为具体类型,当没有指定泛型的时候,默认类型是Obj类型。
使用泛型的好处
- 将运行时时期的异常,转移到了编译时期,变成了编译失败
- 避免了类型强转的麻烦
例
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class GenericDemo {
public static void main(String[] args) {
Collection list=new ArrayList<>();
list.add("nishi");
list.add("shabi");
// list.add(666);集合已经明确了具体元素存放的类型
// 已经明确了类型,在使用迭代器的时候,迭代器也同样知道遍历元素的具体类型
Iterator <String>iterator = list.iterator();
while (iterator.hasNext()){
String str = iterator.next();
System.out.println(str.length());
}
System.out.println(list);
}
}
泛型的定义与使用
泛型,用来灵活的将数据类型应用到不同类,方法,接口当中。将数据类型作为参数进行传递。
格式
修饰符 class 类名<代表泛型的变量>{
}
使用泛型:在创建对象的时候确定泛型
自定义泛型
package Day15.myGemeric;
public class MyGenericClass<MVP> {
//没有MVP类型,在这里代表未知的一种数据类型
private MVP mvp;
public MVP getMvp() {
return mvp;
}
public void setMvp(MVP mvp) {
this.mvp = mvp;
}
}
package Day15.myGemeric;
public class TestGenericDemo {
public static void main(String[] args) {
//创建一个泛型为String类
MyGenericClass<String> my = new MyGenericClass<>();
my.setMvp("Faker");
String mvp = my.getMvp();
System.out.println(mvp);
MyGenericClass<Integer> my2 = new MyGenericClass<>();
my2.setMvp(111);
Integer mvp2=my2.getMvp();
System.out.println(mvp2);
}
}
含有泛型的方法
格式
修饰符 <代表泛型的变量>返回值类型 方法名(参数列表){
}
例:
package Day15.myGemeric;
public class MyGenericMethod {
public <MVP> void show(MVP mvp) {
System.out.println("");
}
public <MVP> MVP show2(MVP mvp) {
return mvp;
}
}
package Day15.myGemeric;
public class TestGenericDemo2 {
public static void main(String[] args) {
MyGenericMethod mgm=new MyGenericMethod();
mgm.show("Uzi");
mgm.show(123);
mgm.show(123.123);
}
}
含有泛型的接口
格式
修饰符 interface 接口名<泛型>{
}
例
package Day15.myGemeric;
public interface MygenericInterface<E> {
public abstract void add(E e);
public abstract E getE();
}
实现类在定义类的时候确定泛型的类型
package Day15.myGemeric;
public class MyGenericImpl implements MygenericInterface<String>{
@Override
public void add(String s) {
}
@Override
public String getE() {
return null;
}
}
上面泛型E的值就是String类型
确定泛型
import java.util.ArrayList;
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 (数值类型)
- ?表示不确定的Java类型
<?>表示不确定的Java类型,一旦使用,<?>只能使用Object类中的共性方法
基本使用
<?>不知道到使用什么Java类型
例
package Day15.myGemeric;
import java.util.ArrayList;
import java.util.Collection;
public class TestGenericDemo3 {
public static void main(String[] args) {
Collection<Integer> list1=new ArrayList<>();
Collection<Integer> list2=new ArrayList<>();
getElement(list1);
getElement(list2);
}
public static void getElement (Collection<?>coll) {
// <?>代表可以接受任意类型
}
}
高级应用——受限类型
在Java中的泛型可以指定一个泛型的上限和下限
泛型的上限:
格式:类型名称<? extends类 >对象名称
意义:只能接受该类型及其父类
例:已知Object类、String类、Number类、Integer类,其中Number类是Integer类的父类
package Day15.myGemeric;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;
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);//会报错
}
//泛型的上限 此时泛型?必须是Number类型或者Number类型子类
public static void getElement1(Collection<? extends Number>coll){
//泛型的下限 此时泛型?必须是Number类型或者Number类型父类
}
public static void getElement2(Collection<? super Number>coll){
}
}
异常
异常:指的是程序在执行过程中,出现的非正常的情况,最终会导致JVM虚拟机的非正常停止,在Java中异常本身是一个类,产生异常就是创建异常对象并且抛出一个异常对象。Java处理异常的方式是中断处理
异常不是语法错误,语法错误是无法通过编译
异常体系
异常的根类是Java.lang.Throwable,两个子类java.lang.Error和java.lang.Exception 平时所说的异常是java.lang.Exception
Throwable体系:
- Error:严重错误,无法通过处理的错误,好比绝症
- Exception:表示异常 异常产生后程序员可以通过代码的方式纠正,使程序继续运行,是必要处理的。好比感冒发烧
常用方法
- public void printStackTrace();打印异常的详细信息
异常的分类
- 编译时期异常:如果没有处理,编译失败(如日期格式)format
-
运行时期异常:在运行时期检查异常(如数学异常)
image.png
异常处理
Java中异常处理的五个关键字:try、catch、finally、throw、throws
抛出异常处理throw
- 创建一个异常对象。封装一些提示信息(信息可以自己编写)
- 通过throw将这个异常对象告知调用者,throw用在方法内,用来抛出一个异常对象,将这个异常对象传递到调用者处,并结束当前方法的执行。
- 格式
throw new异常类名()
例:
throw new NullPointerException("要访问的arr数组不存在");
throw new ArrayIndexOutOfBoundsException("数组越界");
声明异常throws
声明异常:将问题标识出来,报告给调用者,如果方法内通过throw抛出了编译时异常,而没有捕获处理,那么必须通过throws声明,让调用者处理
例
package Day15.exceptionDemo;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Demo3 {
public static void main(String[] args) throws ParseException {
String s = "1999-04";
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
如果异常出现会立刻终止程序,所以我们得处理异常
1.声明抛出,由调用者来处理(throws)
2.使用try catch语句块来处理异常
try catch的方式就是捕获异常
格式
try{
编写可能会出现异常的地方(如文件读取不到)
}catch(异常类型e){
//处理异常的代码
//记录日志、打印异常信息、继续抛出异常
}
- try 编写可能出现异常的代码
- catch 异常的捕获进行处理
- try catch不能单独使用,必须连用
例:
package Day15.exceptionDemo;
import java.io.FileNotFoundException;
public class Demo4 {
public static void main(String[] args) {
try {
read("少年阿宾.txt");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
public static void read(String path) throws FileNotFoundException {
if(!path.equals("少年阿宾.txt")){
throw new FileNotFoundException("文件违规消失");
}
}
}
finally 代码块(子亮侠)
finally有一些特定的代码无论是否会挂上代码都需要执行,另外异常经常会引发程序跳转导致有些语句执行不到,而finally就解决了这个问题,因为他是接盘子亮。
package Day15.exceptionDemo;
import java.io.FileNotFoundException;
public class Demo4 {
public static void main(String[] args) {
try {
read("少年阿宾.txt");
} catch (FileNotFoundException e) {
//在try中抛出什么异常,在括号中就捕获什么异常类型
// e.printStackTrace();
System.out.println("++++++++++");
System.out.println(e);
}finally{
System.out.println("不管catch和try执行什么,在这里都会执行");
System.out.println("何子亮是接盘侠");
}
System.out.println("end");
}
public static void read (String path) throws FileNotFoundException {
if (!path.equals("少年阿宾.txt")) {
throw new FileNotFoundException("文件违规消失");
}
}
}
lambda(λ)表达式
是JDK1.8版本的新特性,lambda 省去面向对象的条条框框,格式由三部分组成
- 一些参数
- 一个箭头
- 一段代码
标准格式:
(参数类型 参数名)->{
代码语句:System.out.println("λ表达式做的饭好了");
}
说明
- 小括号就是传统的参数列表,用多个逗号分隔
- ->代表指向动作
- 大括号和原来一样写方法体
无参无返回
package Day15.lambdaDemo;
public class Demo2 {
public static void main(String[] args) {
invoke(()->{
System.out.println("λ表达式做的饭好了");
});
}
public static void invoke(Cook cook){
cook.makeFood();
}
}
小括号代表Cook接口的makeFood方法参数为空,大括号代表makeFood的方法体
有参有返回值
需求:使用数组存储多个Person对象,对数组中的Person对象使用Arrays的sort方法通过年龄排序;
代码分析
- 为了排序,Arrays.sort需要排序规则,Compareator接口的实例,实现compare方法
- 为了实现compare方法,不得不写一个Comparator的实现类
- 为了省略Comparator的实现类ComparatorImpl,不得不使用匿名内部类
- 必须覆盖compare方法,所有的声明都需要重写一遍。
- 实际上
package Day15.lambdaDemo;
import sun.plugin.javascript.navig.Array;
import java.util.Arrays;
public class Demo4 {
public static void main(String[] args) {
Person[] array = {
new Person("貂蝉", 2013),
new Person("西施", 3011),
new Person("杨玉环", 1400),
new Person("赵飞燕", 1000),
};
Arrays.sort(array, (Person a, Person b) -> {
return a.getAge() - b.getAge();
});
for (Person person : array) {
System.out.println(person);
}
}
}
需求:给定一个计算器Calculator接口,内含抽象方法calc可以将连个int类型的数组相加得到和的值。
Lambda使用前提
1.使用Lambda必须具有接口,且要求接口中有且仅有一个抽象方法(无论是Runable、Comparator接口还是自己定义的接口,都得是抽象方法唯一)
2.使用Lambda必须得有上下推断;也就是方法的参数或者局部变量类型必须为lambda对应的接口类型,才能使用Lambda作为该接口的实例。
有且只有一个抽象方法的接口叫做函数式接口
package Day15.exceptionDemo;
public class Demo5 {
public static void main(String[] args) {
//使用lambda
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 );
}
}
省略模式:
省略规则:
- 小括号内参谋书可以省略;
- 如果小括号内有且仅有一个参,则小括号可以省略;
- 如果大括号内有且仅有一个语句,则无论是否有返回值,都可以省略大括号甚至return语句。