美文网首页
day15 泛型、异常、lambda表达式

day15 泛型、异常、lambda表达式

作者: __method__ | 来源:发表于2020-08-04 09:34 被阅读0次

    泛型、异常、lambda表达式

    泛型

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

    使用泛型的好处

    • 将运行时时期的异常,转移到了编译时期变成了编译失败
    • 避免了类型强转的麻烦
    public class GenericDemo {
        public static void main(String[] args) {
            Collection<String> list = new ArrayList<>();
            list.add("abc");
            list.add("def");
            // list.add(5); 集合已经明确了具体元素存放的类型
            // 已经明确了类型,在使用迭代器的时候,迭代器也同样知道遍历元素的具体类型
            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> 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);
    
    
        }
    }
    

    含有泛型的方法

    格式:

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

    举个栗子

    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();
            // 在调用方法时, 确定泛型的类型
            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();
        
    }
    

    实现类在定义类的时候确定泛型的类型

    public class MyGenericImpl implements MyGenericInterface<String> {
    
        @Override
        public void add(String s) {
    
        }
    
        @Override
        public String getE() {
            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 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){
            // <?>代表可以接受任意类型
        }
    }
    

    高级应用--- 受限类型

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

    泛型的上限:

    格式  : 类型名称<? extends 类 > 对象名称
    意义 : 只能接受该类型及其子类
    

    泛型的下限:

    格式  : 类型名称<? super 类 > 对象名称
    意义 : 只能接受该类型及其父类
    

    举个栗子:现在已知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){
        }
    }
    
    

    异常

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

    异常不是语法错误, 语法错误是无法通过编译

    异常体系

    异常的根类是 java.lang.Throwable,两个子类 java.lang.Error和 java.lang.Exception
    平时所说的异常是java.lang.Exception
    Throwable体系:

    • Error: 严重错误,无法通过处理的错误,好比绝症
    • Exception表示异常, 异常产生后程序员可以通过代码的方式纠正,使程序继续运行,是必须处理的。好比感冒发烧
    常用方法
    • public void printStackTrace(); 打印异常的详细信息并且输出到控制台中
    • public String getMessage(); 获取发生异常的原因

    举个栗子

    public class Demo1 {
        public static void main(String[] args) {
            int[] arr = {1, 2, 4};
            System.out.println(arr[3]);
        }
    }
    
    image

    异常的分类

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


      image

    异常处理

    Java中异常处理的五个关键字:try、catch、finally、throw、throws

    抛出异常throw

    • 创建一个异常对象。封装一些提示信息(信息可以自己编写)
    • 通过throw将这个异常对象告知调用者,throw用在方法内,用来抛出一个异常对象,将这个异常对象传递到调用者处,并结束当前方法的执行。
    • 格式
    throw new 异常类名(参数);
    

    举个栗子

    throw new NullPointerException("要访问的arr数组不存在");
    throw new ArrayIndexOutOfBoundsException("数组越界了,兄弟");
    
    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("数组越界了,兄弟");
            }
           return arr[index];
        }
    }
    

    声明异常throws

    声明异常:将为题标识出来, 报告给调用者, 如果方法内通过throw 抛出了编译时异常, 而没有捕获处理,那么必须通过throws进行声明,让调用者去处理

    修饰符 <代表泛型的变量> 返回值类型 方法名(参数列表) 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

    如果异常出现会立刻终止程序, 所以我们得处理异常

    1. 声明抛出, 由调用者来处理(throws)
    2. 使用try catch语句块来处理异常
      try catch的方式就是捕获异常
      格式:
    try {
        编写可能会出现异常的地方
    }catch(异常类型 e){
        // 处理异常的代码
        // 记录日志、打印异常信息、继续抛出异常
    }
    
    • try 编写可能会出现异常的代码
    • 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: 有一些特定的代码,无论是否发生异常都要执行,另外,因为异常会引发程序跳转,导致有些语句执行不到, 而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执行啥了,我这里都会执行");
                System.out.println("我是接盘侠");
            }
            System.out.println("end");
        }
        public static void read(String path) throws FileNotFoundException {
            if (!path.equals("xiaomimi.txt")){
                throw new FileNotFoundException("你的文件怎么消失了呢");
            }
        }
    }
    

    l# ambda表达式
    是JDK1.8版本的新特性, lambda 省去面向对象的条条框框,格式由3部分组成

    • 一些参数
    • 一个箭头
    • 一段代码
      标准格式:
    (参数类型  参数名) -> { 代码语句 }
    

    说明

    • 小括号就是传统的参数列表,多个用逗号分隔
    • -> 代表指向动作
    • 大括号和原来一样写方法体
    无参无返回
    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方法参数为空, 大括号代表makeFood的方法体

    有参有返回值

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

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

    Lambda写法

    public class Demo4 {
        public static void main(String[] args) {
            Person[] array = {
                    new Person("貂蝉", 223),
                    new Person("孙尚香", 18),
                    new Person("妲己", 300),
                    new Person("杨玉环", 221),
            };
            Arrays.sort(array, (Person a, Person b)->{
                return a.getAge()-b.getAge();
            });
            for (Person person :array){
                System.out.println(person);
            }
        } 
    }
    
    需求:给定一个计算器Calculator接口,内含抽象方法calc可以将连个int类型的数组相加得到和的值
    public interface Calculator {
        int calc(int a , int b);
    }
    
    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);
        }
    }
    
    省略格式:

    Lambda强调做什么, 而不是怎么做, 凡是可以根据上下文推导得知的消息,都可以省略

    public class Demo6 {
        public static void main(String[] args) {
            // 使用lambda表达式 调用测试
            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);
        }
    }
    

    省略规则:

    • 小括号内参数可以省略
    • 如果小括号内有且仅有一个参, 小括号可以省略
    • 如果大括号内有有且仅有一个语句,则无论是否有返回值,都可以省略大括号、return及语句分号
    public class Demo7 {
        public static void main(String[] args) {
            // 使用lambda表达式 调用测试
            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使用前提

    1. 使用Lambda必须具有接口,且要求接口中有且仅有一个抽象方法(无论是Runable、Comparator接口还是自己定义的接口,都得是抽象方法唯一)
    2. 使用Lambda必须具有上下文推断.;也就是方法的参数或者局部变量类型必须为Lambda对应的接口类型,才能使用Lambda作为该接口的实例
      有且只有一个抽象方法的接口叫做函数式接口

    相关文章

      网友评论

          本文标题:day15 泛型、异常、lambda表达式

          本文链接:https://www.haomeiwen.com/subject/yfbirktx.html