day15 ---- 泛型

泛型、异常、lambda表达式

泛型

  • 可以在类和方法预支地使用未知的类型,一般在创建对象时,将位置类型确定为具体类型,当没有指定泛型时,默认类型是Object类型

使用

  • 将运行时时期的异常,转移到了编译时期变成了编译失败
  • 避免类型强转的麻烦
    public static void main(String[] args) {
        Collection<String> list = new ArrayList<>();
        // 把泛型删掉后默认为Object类
        list.add("abc");
        list.add("def");
//        list.add("def");

        // 已经明确了类型,在使用迭代器的时候,迭代器也同样知道遍历元素的具体类型
        Iterator<String> iterator = list.iterator();
        while (iterator.hasNext()){
            String str = iterator.next();
            // 使用iterator控制元素后 在这里可以直接获取String类型
            System.out.println(str.length());
        }

//        System.out.println(list);

    }
    

泛型的定义与使用

  • 泛型 用来灵活的将数据类型应用到不同类,方法、接口当中,将数据类型作为参数进行传递。
  • 格式
修饰符 class 类名<代表泛型的变量>{
}
  • 使用泛型:在创建对象的时候确定泛型
  • 自定义泛型
public class MyGenericClass <MVP>{
    // 没有MVP类型,在这里代表未知的一种 数据类型
    // 未来传递什么就是什么类型
    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) {
        // 创建一个泛型为String的类
        MyGenericClass<String> mine = new MyGenericClass<>();
//        不写默认为Object类
//        MyGenericClass mine1 = new MyGenericClass<>();
        mine.setMvp("哈登");
        // 传入什么类型就是什么类型
        String mvp = mine.getMvp();
        System.out.println(mvp);

        MyGenericClass<Integer> mine2 = new MyGenericClass<>();
        mine2.setMvp(13);
        Integer mine2Mvp = mine2.getMvp();
        System.out.println(mine2Mvp);
    }
}

含有泛型的方法

  • 格式:
修饰符<代表泛型的变量> 返回值类型 方法名 (参数列表)}{
}

  • 举个例子
public class MyGenericMethod {
    public<MVP> void show(MVP mvp){
        System.out.println(mvp.getClass());
    }
    public <MVP> MVP show2(MVP mvp){
        return mvp;
    }
}

  • 测试

public class TestGenericMethod {
    public static void main(String[] args) {
        MyGenericMethod mgm = new MyGenericMethod();
        // 在调用方法时,确定泛型的类型
        mgm.show("罗斯");
        mgm.show("德里克");
        mgm.show(123);

    }
}

含有泛型的接口

  • 格式:
修饰符 interface 接口名<泛型>{
}
  • 定义类
package com.company.day15.myGeneric;

public interface MyGenericInterface <E>{
    public abstract void add(E e);
    public abstract E get(E e);
    
}

  • 实现类在定义类的时候确定泛型的类型
public class MyGenericImpL implements MyGenericInterface<String> {


    @Override
    public void add(String s) {

    }

    @Override
    public String get(String s) {
        return null;
    }
}

  • 此时类型E的值就是String类型
始终不确定泛型的类型,知道创建对象的时候,确定泛型的类型
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 (数量类型)
  • ?表示不确定的Java类型
    < ? >表示不确定的Java类型,一旦使用<?> 只能使用Object类中的共性方法
基本使用
<?> 不知道使用什么类型来接收的时候,
public class TestGenericDemo03 {
    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){
        // <?> 可以接收任意类型
        
    }
}

高级应用 – 受限类型

在Java中 泛型可以指定一个泛型的上限和下限

  • 泛型的上限
格式:
	类型名称 <? extends 类> 对象名称 
意义:
	只能接收该类型及其子类
格式:
	类型名称 <? super 类> 对象名称 
意义:
	只能接收该类型及其父类
  • 例子:已知object类、String类、Number类、Integer类、其中Number类是Integer的父类
public class TestGenericDemo04 {
    public static void main(String[] args) {
        // 已知object类、String类、Number类、Integer类、其中Number类是Integer的父类
        Collection<Integer> list1 = new ArrayList<>();
        Collection<String> list2 = new ArrayList<>();
        Collection<Number> list3 = new ArrayList<>();
        Collection<Object> list4 = new ArrayList<>();
        
        getElement01(list1); // Integer是Number的子类 所以可以使用
//        getElement01(list2);  String不是Number的子类 报错
        getElement01(list3); 
//        getElement01(list4);  Object是Number父类 报错
//        getElement02(list1);  下限以下
        getElement02(list3); // 本类 可以使用
    }
    public static void getElement01(Collection<? extends Number> coll){
        // 泛型的上限 此时必须是Number类型或者子类
    }
    public static void getElement02(Collection<? super Number> coll){
        // 泛型的下限 此时必须是Number类型或者父类
    }
}

异常

  • 异常

指的是程序在执行过程中,出现的非正常的情况,最终会导致JVM的非正常停止 在Java中异常本身是一个类,产生异常就是创建异常对象并且抛出一个异常对象。Java处理异常的方式是中断处理。
异常不是语法错误,语法错误是无法通过编译

异常体系

  • 异常的根类

异常的根类是java.lang.Throwable,有两个子类 Java.lang.Error 和 Java.lang.Exception 平时说的异常是java.lang.Exception

Throwable体系:
  • Error严重错误,无法通过处理的错误,好比绝症
  • Exception 表示异常 异常产生后程序员可以通过代码纠正的方式,使程序正常运行,好比感冒发烧
常用方法:
  1. printStackTrace() 打印异常的常用信息
public void printStackTrace() {
        printStackTrace(System.err);
    }
  1. getMessage() 获取发生异常的原因
 public String getMessage() {
        return detailMessage;
    }
  1. 例子
public class Demo01 {
    public static void main(String[] args) {
        int[] arr = {1,2,3};
        System.out.println(arr[3]);
    }
}

在这里插入图片描述

异常的分类

  • 编译时期异常:如果没有处理,编译失败(比如日期格式化异常)
  • 运行时期异常:在运行时期检查异常(如数字异常)

在这里插入图片描述

异常处理

Java中异常处理关键字
  1. try
  2. catch
  3. finally
  4. throw
  5. throws
抛出异常throw
  • 创建一个异常对象,封装一些提示信息(信息可以自己编写)
  • 通过throw这个异常对象告知调用者,throw用在方法内,用来抛出一个异常对象,将这个异常对象传递给调用者处,并结束当前方法的执行
  • 格式:
throw new 异常类名(参数);
  • 例子
public class Demo02 {
    public static void main(String[] args) {
        int[] arr = {1,2,3};
        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("数组越界了,兄弟");
        }
        return arr[index];
    }
}

throws 声明异常
  • 声明异常:将问题标识出来,报告给调用者,如果方法内通过throw,跑出来编译时异常,而没有捕获处理,那么必须通过throws进行声明,让调用者去处理
修饰符 <代表泛型的变量> 返回值类型 方法名(参数列表)throws 异常类
public class Demo3 {
    public static void main(String[] args) throws ParseException {
        String s = "1994-01-1";
        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异常的捕获处理
try catch不能单独使用 必须连用

格式:

try{
	// 编写可能会出现异常的地方
}catch(异常类型){
	// 处理异常的代码
	// 记录日志,打印异常信息,继续抛出异常
}
public class Demo04 {
    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 有一些特定的代码,无论是否发生异常都会执行,另外异常会引发程序跳转,导致有些语句执行不到,而finally就解决了这个问题
finally不能单独使用

public class Demo04 {
    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执行什么了,我这里都会执行");
            System.out.println("我是接盘侠");
        }
        System.out.println("end");
    }
    public static void read(String path) throws FileNotFoundException {
        if (!path.equals("xiaomimi.txt")){
            throw new FileNotFoundException("你的文件怎么消失了呢");
        }
    }
}

lambda表达式

使JDK1.8 版本的新特性 lambda省去面向对象的条条框框,格式由三部分组成

  • 一些参数
  • 一个箭头
  • 一段代码

标准格式:

(参数类型 参数名)->{
 //代码语句    System.out.println("lambda的饭好了");
        }
说明
  • 小括号就是传统的参数列表,多个用括号分隔
  • -> 代表指向动作
  • 大括号和原来一样写方法体
无参无返回
public class Demo02 {
    public static void main(String[] args) {
        // 格式: (参数类型 参数名称)-> {代码语句}
        invoke(()->{
            System.out.println("lambda的饭好了");
        });
    }
    public static void invoke(Cook cook){
        cook.makeFood();
    }
}

小括号代表Cook接口的makeFood方法 参数为空,大括号代表方法体

有参有返回值

需求 使用数组存储多个Person对象,对数组中的Person对象使用Arrays的sort方法通过年龄排序

代码分析

  • 为了排序 ArrayList.sort需要排序规则,Comparator接口的实例。
  • 实现compare方法,不得不写一个Comparator的实现类
  • 为了省略Comparator的实现类ComparatorImpl,不得不使用匿名内部类
  • 必须覆盖compare方法 所有的声明都需要重写一遍
  • 实际上 只有参数和方法体是关键部分

lambda写法

public class Demo06 {
    public static void main(String[] args) {
        Person[] array = {
                new Person("貂蝉",23),
                new Person("妲己",26),
                new Person("西施",25),
                new Person("杨玉环",29),
        };
        Arrays.sort(array,(Person a,Person b)->{
            return a.getAge()-b.getAge();
        });

        for (Person person:array
             ) {
            System.out.println(person);
        }
    }
}
需求:定义一个计算器接口Caculator 内涵抽象方法可以将 int类型的数组相加得到的和的值
public interface Caculator {
    int cacu(int a,int b);
}

省略格式
  • Lambda强调做什么,而不是怎么做,凡是可以根据上下文推到得知的消息,都可以省略
public class Demo08 {
    public static void main(String[] args) {
        // 使用lambda表达式调用测试
        invokeCacu(5,6,(a,b) -> a + b);

    }
    public static void invokeCacu(int a,int b,Caculator caculator){
        int result = caculator.cacu(a,b);
        System.out.println("结果是" + result);
    }
 }

省略规则
  • 小括号参数可以省略
  • 如果小括号内有且仅有一个参数,小括号可以省略
  • 如果大括号内且仅有一个语句,则无论是否有返回值 都可以省略大括号、return及分号
lambda使用前提
  • 必须具有接口,且要求接口中有且仅有一个抽象方法(无论是Runable、Comparator还是自己定义的接口,都得是抽象方法唯一)
  • 使用lambda必须具有上下文推断:也就是方法的参数或者局部变量类型必须lambda对应的接口类型,才能使用,Lambda作为该接口的实例
  • 有且只有一个抽象方法的接口叫做函数式接口
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java是Java 5引入的新特性,可以提高代码的可读性和安全性,降低代码的耦合度。是将类参数化,实现代码的通用性。 一、的基本语法 在声明类、接口、方法时可以使用的声明方式为在类名、接口名、方法名后面加上尖括号<>,括号中可以声明一个或多个类参数,多个类参数之间用逗号隔开。例如: ```java public class GenericClass<T> { private T data; public T getData() { return data; } public void setData(T data) { this.data = data; } } public interface GenericInterface<T> { T getData(); void setData(T data); } public <T> void genericMethod(T data) { System.out.println(data); } ``` 其中,`GenericClass`是一个类,`GenericInterface`是一个接口,`genericMethod`是一个方法。在这些声明中,`<T>`就是类参数,可以用任何字母代替。 二、的使用 1. 类的使用 在使用类时,需要在类名后面加上尖括号<>,并在括号中指定具体的类参数。例如: ```java GenericClass<String> gc = new GenericClass<>(); gc.setData("Hello World"); String data = gc.getData(); ``` 在这个例子中,`GenericClass`被声明为一个类,`<String>`指定了具体的类参数,即`data`字段的类为`String`,`gc`对象被创建时没有指定类参数,因为编译器可以根据上下文自动推断出类参数为`String`。 2. 接口的使用 在使用接口时,也需要在接口名后面加上尖括号<>,并在括号中指定具体的类参数。例如: ```java GenericInterface<String> gi = new GenericInterface<String>() { private String data; @Override public String getData() { return data; } @Override public void setData(String data) { this.data = data; } }; gi.setData("Hello World"); String data = gi.getData(); ``` 在这个例子中,`GenericInterface`被声明为一个接口,`<String>`指定了具体的类参数,匿名内部类实现了该接口,并使用`String`作为类参数。 3. 方法的使用 在使用方法时,需要在方法名前面加上尖括号<>,并在括号中指定具体的类参数。例如: ```java genericMethod("Hello World"); ``` 在这个例子中,`genericMethod`被声明为一个方法,`<T>`指定了类参数,`T data`表示一个类为`T`的参数,调用时可以传入任何类的参数。 三、的通配符 有时候,我们不知道的具体类,可以使用通配符`?`。通配符可以作为类参数出现在方法的参数类或返回类中,但不能用于声明类或接口。例如: ```java public void printList(List<?> list) { for (Object obj : list) { System.out.print(obj + " "); } } ``` 在这个例子中,`printList`方法的参数类为`List<?>`,表示可以接受任何类的`List`,无论是`List<String>`还是`List<Integer>`都可以。在方法内部,使用`Object`类来遍历`List`中的元素。 四、的继承 类和接口可以继承或实现其他类或接口,可以使用子类或实现类的类参数来替换父类或接口的类参数。例如: ```java public class SubGenericClass<T> extends GenericClass<T> {} public class SubGenericInterface<T> implements GenericInterface<T> { private T data; @Override public T getData() { return data; } @Override public void setData(T data) { this.data = data; } } ``` 在这个例子中,`SubGenericClass`继承了`GenericClass`,并使用了相同的类参数`T`,`SubGenericInterface`实现了`GenericInterface`,也使用了相同的类参数`T`。 五、的限定 有时候,我们需要对的类参数进行限定,使其只能是某个类或接口的子类或实现类。可以使用`extends`关键字来限定类参数的上限,或使用`super`关键字来限定类参数的下限。例如: ```java public class GenericClass<T extends Number> { private T data; public T getData() { return data; } public void setData(T data) { this.data = data; } } public interface GenericInterface<T extends Comparable<T>> { T getData(); void setData(T data); } ``` 在这个例子中,`GenericClass`的类参数`T`被限定为`Number`的子类,`GenericInterface`的类参数`T`被限定为实现了`Comparable`接口的类。 六、的擦除 在Java中,信息只存在于代码编译阶段,在编译后的字节码中会被擦除。在运行时,无法获取的具体类。例如: ```java public void genericMethod(List<String> list) { System.out.println(list.getClass()); } ``` 在这个例子中,`list`的类为`List<String>`,但是在运行时,`getClass`返回的类为`java.util.ArrayList`,因为信息已经被擦除了。 七、的类推断 在Java 7中,引入了钻石操作符<>,可以使用它来省略类参数的声明。例如: ```java List<String> list = new ArrayList<>(); ``` 在这个例子中,`ArrayList`的类参数可以被编译器自动推断为`String`。 八、总结 Java是一个强大的特性,可以提高代码的可读性和安全性,降低代码的耦合度。在使用时,需要注意它的基本语法、使用方法、通配符、继承、限定、擦除和类推断等问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值