欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

java语法知识之泛型、异常、lambda表达式

程序员文章站 2022-07-03 19:59:23
泛型、异常、lambda表达式泛型可以在类和方法中预支地使用未知的类型,一般在创建对象时,将未知类型确定为具体类型,当没有指定泛型的时候,默认是Obj类型。使用泛型的好处将运行时时期的异常,转移到了编译时期变成了编译失败避免了类型强转的麻烦public class GenericDemo { public static void main(String[] args) { Collection list = new ArrayList<...

泛型、异常、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();
        }

        System.out.println(list);
    }
} 

泛型的定义与使用

泛型,用来灵活的将数据类型应用到不同类、方法、接口当中。将数据类型作为参数进行传递。
格式:

修饰符 class 类名<代表泛型的变量>{

} 

使用泛型:在创建对象的时候确定泛型
自定义泛型:

public class MyGenericClass<MVP> {
        //没有MVP类型,在这里代表未知的一种数据类型
       //未来传递什么就是什么类型
    private MVP mvp;
    
    public MyGenericClass() {
    }

    public MyGenericClass(MVP mvp) {
        this.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);
        System.out.println(my2.getMvp());

        MyGenericClass<Double> my3 = new MyGenericClass<>();
        my3.setMvp(13.00);
        System.out.println(my3.getMvp());
    }
} 

含有泛型的方法

格式:

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

举个例子:

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 :Elements(在集合中使用)
  • T :Type(Java类)
  • K : Key(键)
  • V : Value(值)
  • N : Numebr(数据类型)
  • ? : 表示不确定Java类型
    <?> 表示不确定Java类型,一旦使用<?>只能使用Object类中的共性方法
基本使用

<?>不知道使用什么类型来接收的时候可以使用

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类> 对象名称
意义:只能接收该类型及其子类 

泛型的下限:

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

举个例子:现在已知Object类、String类、Number类、Integer类,其中Number类是Integer的父类

public class TestGenerciDemo4 {
    public static void main(String[] args) {
        //现在已知Object类、String类、Number类、Integer类,其中Number类是Integer的父类
        ArrayList<Integer> list1 = new ArrayList<>();
        ArrayList<String> list2 = new ArrayList<>();
        ArrayList<Number> list3 = new ArrayList<>();
        ArrayList<Object> list4 = new ArrayList<>();
        getElement1(list1);
//        getElement1(list2);//报错
        getElement1(list3);
//        getElement1(list4);//报错
        
//        getElement2(list1);//报错
//        getElement2(list2);//报错
        getElement2(list3);
        getElement2(list4);
    }

    //泛型的上限 此时泛型? 必须是Numbr类型或者Number类型子类
    public static void getElement1(Collection<? extends Number> coll){

    }

    //泛型下限 此时泛型? 必须是Numbr类型或者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]);
    }
} 

java语法知识之泛型、异常、lambda表达式

异常的分类

  • 编译时期异常:如果没有处理,编译失败(如日期格式化异常)
  • 运行时期异常:在运行时期检查异常(如数字异常)
    java语法知识之泛型、异常、lambda表达式

异常处理

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 异常类 

举个例子

public class Demo3 {
    public static void main(String[] args) throws ParseException {
        String s = "1994-01";
        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.声明抛出,由调用者来处理
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("你的文件怎么消失了呢");
        }
    }
} 

lambda表达式

是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作为接口的实例
    有且只有一个抽象方法的接口叫做函数式接口

本文地址:https://blog.csdn.net/outof_control/article/details/107681616