1、不可变集合简介

  • 不可变集合,就是不可被修改的集合。
  • 集合的数据项在创建的时候提供,并且在整个生命周期中都不可改变。否则报错。

为什么要创建不可变集合?

  • 如果某个数据不能被修改,把它防御性地拷贝到不可变集合中是个很好的实践。
  • 或者当集合对象被不可信的库调用时,不可变形式是安全的。

如何创建不可变集合?

在List、Set、Map接口中,都存在of方法,可以创建一个不可变的集合。

这个集合不能添加,不能删除,不能修改。

方法名称

说明

static <E>  List<E>  of(E…elements)

创建一个具有指定元素的List集合对象

static <E>  Set<E>  of(E…elements)

创建一个具有指定元素的Set集合对象

static <K , V>   Map<K,V>  of(E…elements)

创建一个具有指定元素的Map集合对象

public static void main(String[] args) {// 1、不可变的List集合List<Double> lists = List.of(569.5, 700.5, 523.0,  570.5);// lists.add(689.0);// lists.set(2, 698.5);// System.out.println(lists);double score = lists.get(1);System.out.println(score);// 2、不可变的Set集合Set<String> names = Set.of("迪丽热巴", "迪丽热九", "马尔扎哈", "卡尔眨巴" );// names.add("三少爷");System.out.println(names);// 3、不可变的Map集合Map<String, Integer> maps = Map.of("huawei",2, "Java开发", 1 , "手表", 1);// maps.put("衣服", 3);System.out.println(maps);}

2、Stream流体系

Stream流的概述

什么是Stream流?

  • 在Java 8中,得益于Lambda所带来的函数式编程, 引入了一个全新的Stream流概念。
  • 目的:用于简化集合和数组操作的API。

体验Stream流的作用

需求:按照下面的要求完成集合的创建和遍历;创建一个集合,存储多个字符串元素

List<String> list = new ArrayList<>();

list.add("张无忌");

list.add("周芷若");

list.add("赵敏");

list.add("张强");

list.add("张三丰");

  • 把集合中所有以"张"开头的元素存储到一个新的集合
  • 把"张"开头的集合中的长度为3的元素存储到一个新的集合
  • 遍历上一步得到的集合中的元素输出。
    public static void main(String[] args) {List<String> names = new ArrayList<>();Collections.addAll(names, "张三丰","张无忌","周芷若","赵敏","张强");System.out.println(names);//[张三丰, 张无忌, 周芷若, 赵敏, 张强]// 1、从集合中找出姓张的放到新集合List<String> zhangList = new ArrayList<>();for (String name : names) {if(name.startsWith("张")){zhangList.add(name);}}System.out.println(zhangList);//[张三丰, 张无忌, 张强]// 2、找名称长度是3的姓名List<String> zhangThreeList = new ArrayList<>();for (String name : zhangList) {if(name.length() == 3){zhangThreeList.add(name);}}System.out.println(zhangThreeList);//[张三丰, 张无忌]// 3、使用Stream实现的names.stream().filter(s -> s.startsWith("张")).filter(s -> s.length() == 3).forEach(s -> System.out.println(s));//张三丰//张无忌}

Stream流式思想的核心:

  • 先得到集合或者数组的Stream流(就是一根传送带)

  • 把元素放上去

  • 然后就用这个Stream流简化的API来方便的操作元素。

Stream流的获取

Stream流的三类方法:

  • 获取Stream流:创建一条流水线,并把数据放到流水线上准备进行操作。
  • 中间方法:流水线上的操作。一次操作完毕之后,还可以继续进行其他操作。
  • 终结方法:一个Stream流只能有一个终结方法,是流水线上的最后一个操作。

Stream操作集合或者数组的第一步是先得到Stream流,然后才能使用流的功能。

集合获取Stream流的方式

  • 可以使用Collection接口中的默认方法stream​()生成流。

名称

说明

default Stream<E> stream​()

获取当前集合对象的Stream流

数组获取Stream流的方式

名称

说明

public static <T> Stream<T> stream(T[] array)

获取当前数组的Stream流

public static<T> Stream<T> of(T... values)

获取当前数组/可变数据的Stream流

public static void main(String[] args) {/** --------------------Collection集合获取流-------------------------------   */Collection<String> list = new ArrayList<>();Stream<String> s =  list.stream();/** --------------------Map集合获取流-------------------------------   */Map<String, Integer> maps = new HashMap<>();// 键流Stream<String> keyStream = maps.keySet().stream();// 值流Stream<Integer> valueStream = maps.values().stream();// 键值对流(拿整体)Stream<Map.Entry<String,Integer>> keyAndValueStream =  maps.entrySet().stream();/** ---------------------数组获取流------------------------------   */String[] names = {"张三","李四","王五","赵六"};Stream<String> nameStream = Arrays.stream(names);Stream<String> nameStream2 = Stream.of(names);}

小结:

  • 集合获取Stream流用: stream();
  • 数组:Arrays.stream(数组)   /  Stream.of(数组);

Stream流的常用API

中间操作方法

名称

说明

Stream<T> filter(Predicate<? super T> predicate)

用于对流中的数据进行过滤。

Stream<T> limit​(long maxSize)

获取前几个元素

Stream<T> skip​(long n)

跳过前几个元素

Stream<T> distinct​()

去除流中重复的元素。依赖(hashCode和equals方法)

static <T> Stream<T> concat​(Stream a, Stream b)

合并a和b两个流为一个流

注意:

  • 中间方法也称为非终结方法,调用完成后返回新的Stream流可以继续使用,支持链式编程。
  • 在Stream流中无法直接修改集合、数组中的数据。

Stream流的常见终结操作方法

名称

说明

void forEach​(Consumer action)

对此流的每个元素执行遍历操作

long count​()

返回此流中的元素数

注意:终结操作方法,调用完成后流就无法继续使用了,原因是不会返回Stream了。

/**目标:Stream流的常用APIforEach : 逐一处理(遍历)count:统计个数-- long count();filter : 过滤元素-- Stream<T> filter(Predicate<? super T> predicate)limit : 取前几个元素skip : 跳过前几个map : 加工方法concat : 合并流。*/
public static void main(String[] args) {List<String> list = new ArrayList<>();list.add("张无忌");list.add("周芷若");list.add("赵敏");list.add("张强");list.add("张三丰");list.add("张三丰");// Stream<T> filter(Predicate<? super T> predicate)/*list.stream().filter(new Predicate<String>() {@Overridepublic boolean test(String s) {s.startsWith("张");return false;}});*/list.stream().filter(s -> s.startsWith("张")).forEach(s -> System.out.println(s));long size = list.stream().filter(s -> s.length() == 3).count();System.out.println(size);// list.stream().filter(s -> s.startsWith("张")).limit(2).forEach(s -> System.out.println(s));list.stream().filter(s -> s.startsWith("张")).limit(2).forEach(System.out::println);list.stream().filter(s -> s.startsWith("张")).skip(2).forEach(System.out::println);// map加工方法: 第一个参数原材料  -> 第二个参数是加工后的结果。// 给集合元素的前面都加上一个:Lovo的:list.stream().map(s -> "Lovo的:" + s).forEach(a -> System.out.println(a));// 需求:把所有的名称 都加工成一个学生对象。list.stream().map(s -> new Student(s)).forEach(s -> System.out.println(s));
//        list.stream().map(Student::new).forEach(System.out::println); // 构造器引用  方法引用// 合并流。Stream<String> s1 = list.stream().filter(s -> s.startsWith("张"));Stream<String> s2 = Stream.of("java1", "java2");// public static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b)Stream<String> s3 = Stream.concat(s1 , s2);s3.distinct().forEach(s -> System.out.println(s));}

Stream流的综合应用

需求:

某个公司的开发部门,分为开发一部和二部,现在需要进行年中数据结算。

分析:

  • 员工信息至少包含了(名称、性别、工资、奖金、处罚记录)
  • 开发一部有4个员工、开发二部有5名员 :
  • 分别筛选出2个部门的最高工资的员工信息,封装成优秀员工对象Topperformer
  • 分别统计出2个部门的平均月收入,要求去掉最高和最低工资。
  • 统计2个开发部门整体的平均工资,去掉最低和最高工资的平均值。

员工类:

/*** 员工类:员工信息至少包含了(名称、性别、工资、奖金、处罚记录)*/
public class Employee {private String name;//名字private char sex;//性别private double salary;//工资private double bouns;//奖金private String punish;//处罚记录public Employee() {}public Employee(String name, char sex, double salary, double bouns, String punish) {this.name = name;this.sex = sex;this.salary = salary;this.bouns = bouns;this.punish = punish;}public String getName() {return name;}public void setName(String name) {this.name = name;}public char getSex() {return sex;}public void setSex(char sex) {this.sex = sex;}public double getSalary() {return salary;}public void setSalary(double salary) {this.salary = salary;}public double getBouns() {return bouns;}public void setBouns(double bouns) {this.bouns = bouns;}public String getPunish() {return punish;}public void setPunish(String punish) {this.punish = punish;}@Overridepublic String toString() {return "Employee{" +"name='" + name + '\'' +", sex=" + sex +", salary=" + salary +", bouns=" + bouns +", punish='" + punish + '\'' +'}';}
}

优秀员工类

public class GoodPerformer {private String name;//姓名private double money;//工资+奖金public GoodPerformer() {}public GoodPerformer(String name, double money) {this.name = name;this.money = money;}public String getName() {return name;}public void setName(String name) {this.name = name;}public double getMoney() {return money;}public void setMoney(double money) {this.money = money;}@Overridepublic String toString() {return "GoodPerformer{" +"name='" + name + '\'' +", money=" + money +'}';}
}
public class Demo {public static double allMoney;//两个部门去掉最高工资,去掉最低工资,的平均工资public static double allMoney1;//开发一部去掉最高工资,去掉最低工资,的平均工资public static double allMoney2;//开发二部去掉最高工资,去掉最低工资,的平均工资public static void main(String[] args) {//开发一部有4个员工、开发二部有5名员 :List<Employee> one = new ArrayList<>();one.add(new Employee("张三",'男',5000,5000,null));one.add(new Employee("李四",'女',8000,4000,"顶撞上司"));one.add(new Employee("王五",'男',3000,3000,null));one.add(new Employee("赵六",'女',12000,1000,null));List<Employee> two = new ArrayList<>();two.add(new Employee("武松",'男',15000 , 9000, null));two.add(new Employee("李逵",'男',20000 , 1000, null));two.add(new Employee("西门庆",'男',5000 , 10000, "被打"));two.add(new Employee("潘金莲",'女',3500 , 1000, "被打"));two.add(new Employee("武大郎",'女',20000 , 0, "下毒"));//分别筛选出2个部门的最高工资的员工信息,封装成优秀员工对象Topperformer//开发一部的工资最高员工/*Employee employee = one.stream().max((e1,e2) -> Double.compare(e1.getSalary()+e1.getBouns(),e2.getSalary()+e2.getBouns())).get();System.out.println(employee);*/GoodPerformer goodPerformer1 = one.stream().max((e1,e2) -> Double.compare(e1.getSalary()+e1.getBouns(),e2.getSalary()+e2.getBouns())).map(e -> new GoodPerformer(e.getName(),e.getSalary()+e.getBouns())).get();System.out.println(goodPerformer1);GoodPerformer goodPerformer2 = two.stream().max((e1,e2) -> Double.compare(e1.getSalary()+e1.getBouns(),e2.getSalary()+e2.getBouns())).map(e -> new GoodPerformer(e.getName(),e.getSalary()+e.getBouns())).get();System.out.println(goodPerformer2);//分别统计出2个部门的平均月收入,要求去掉最高和最低工资。//sorted:排序one.stream().sorted((e1,e2) -> Double.compare(e1.getSalary()+e1.getBouns(),e2.getSalary()+e2.getBouns())).skip(1).limit(one.size()-2).forEach(e->{//求和allMoney1 += e.getSalary() + e.getBouns();});System.out.println("开发一部的平均工资是:" + allMoney1/(one.size()-2));two.stream().sorted((e1,e2) -> Double.compare(e1.getSalary()+e1.getBouns(),e2.getSalary()+e2.getBouns())).skip(1).limit(two.size()-2).forEach(e->{//求和allMoney2 += e.getSalary() + e.getBouns();});BigDecimal a2 = BigDecimal.valueOf(allMoney2);BigDecimal b2 = BigDecimal.valueOf(two.size()-2);System.out.println("开发二部的平均工资是:" + a2.divide(b2,2,RoundingMode.HALF_UP));//统计2个开发部门整体的平均工资,去掉最低和最高工资的平均值。Stream<Employee> s1 = one.stream();Stream<Employee> s2 = two.stream();Stream<Employee> s3 = Stream.concat(s1,s2);s3.sorted((e1,e2) -> Double.compare(e1.getSalary()+e1.getBouns(),e2.getSalary()+e2.getBouns())).skip(1).limit(one.size() + two.size() -2).forEach(e->{//求和allMoney += e.getSalary() + e.getBouns();});//精度问题BigDecimal a = BigDecimal.valueOf(allMoney);BigDecimal b = BigDecimal.valueOf((one.size() + two.size()-2));System.out.println("两个部门的平均工资是:" + a.divide(b,2, RoundingMode.HALF_UP));//保留两位,四舍五入}
}

收集Stream流

  • 收集Stream流的含义:就是把Stream流操作后的结果数据转回到集合或者数组中去
  • Stream流:方便操作集合/数组的手段。
  • 集合/数组:才是开发中的目的。

Stream流的收集方法

名称

说明

R collect​(Collector collector)

开始收集Stream流,指定收集器

Collectors工具类提供了具体的收集方式

名称

说明

public static <T> Collector toList​()

把元素收集到List集合中

public static <T> Collector toSet​()

把元素收集到Set集合中

public static  Collector toMap​(Function keyMapper , Function valueMapper)

把元素收集到Map集合中

public static void main(String[] args) {List<String> list = new ArrayList<>();list.add("张无忌");list.add("周芷若");list.add("赵敏");list.add("张强");list.add("张三丰");list.add("张三丰");Stream<String> s1 = list.stream().filter(s -> s.startsWith("张"));List<String> zhangList = s1.collect(Collectors.toList()); // 可变集合zhangList.add("java1");System.out.println(zhangList);//[张无忌, 张强, 张三丰, 张三丰, java1]//       List<String> list1 = s1.toList(); // 得到不可变集合//jdk16开始
//       list1.add("java");
//       System.out.println(list1);// 注意注意注意:“流只能使用一次”Stream<String> s2 = list.stream().filter(s -> s.startsWith("张"));Set<String> zhangSet = s2.collect(Collectors.toSet());System.out.println(zhangSet);//[张强, 张三丰, 张无忌]Stream<String> s3 = list.stream().filter(s -> s.startsWith("张"));
//         Object[] arrs = s3.toArray();String[] arrs = s3.toArray(String[]::new); // 可以不管,拓展一下思维!!System.out.println("Arrays数组内容:" + Arrays.toString(arrs));}

3、异常处理

异常概述、体系

什么是异常?

  • 异常是程序在“编译”或者“执行”的过程中可能出现的问题
  • 注意:语法错误不算在异常体系中。 比如:数组索引越界、空指针异常、 日期格式化异常,等…

为什么学习异常?

  • 异常一旦出现了,如果没有提前处理,程序就会退出JVM虚拟机而终止.
  • 研究异常并且避免异常,然后提前处理异常,体现的是程序的安全, 健壮性。

很多人说异常就是报错吗。这个说法其实是不对的:

  • 首先第一个:异常不是语法错误;虽然他在编译期或运行期都有可能出现,但它是一种程序在执行过程里遇到的问题,而语法错误是程序在编译过程出现的错误。
  • 其次:它是Java在预先定义好的一系列可能出现的问题,然后给我们进行报告。Java把它们这些问题全部设计成了类,然后遇到问题以后JVM就会用对应的异常类产生对象,交给我们进行处理。

当然很多时候我们会看到这里会显示好几行的at,那么到底哪个才是异常发生的位置呢?

  • 从下往上找你写的第一个文件的位置。就是异常发生的真正位置。

异常体系

Error:

系统级别问题、JVM退出等,代码无法控制。错误的意思,严重错误Error,无法通过处理的错误,一旦出现,程序员无能为力了,只能重启系统,优化项目。比如内存奔溃,JVM本身的奔溃。这个程序员无需理会。

Exception:java.lang包下,称为异常类,它表示程序本身可以处理的问题。

异常类,它才是开发中代码在编译或者执行的过程中可能出现的错误,它是需要提前处理的。以便程序更健壮!

  • RuntimeException及其子类:运行时异常,编译阶段不会报错。 (空指针异常,数组索引越界异常)
  • 除RuntimeException之外所有的异常:编译时异常,编译期必须处理的,否则程序不能通过编译。 (日期格式化异常)

编译时和运行时异常

  • 编译时异常,是在编译成class文件时必须要处理的异常,也称之为受检异常。
  • 运行时异常,在编译成class文件不需要处理,在运行字节码文件时可能出现的异常。

简单来说: 编译时异常就是在编译的时候出现的异常, 运行时异常就是在运行时出现的异常。

常见异常

常见运行时异常(面试题

直接继承自RuntimeException或者其子类,编译阶段不会报错,运行时可能出现的错误。

运行时异常示例:

  • 数组索引越界异常: ArrayIndexOutOfBoundsException
  • 空指针异常 : NullPointerException,直接输出没有问题,但是调用空指针的变量的功能就会报错。
  • 数学操作异常:ArithmeticException
  • 类型转换异常:ClassCastException
  • 数字转换异常: NumberFormatException

运行时异常:一般是程序员业务没有考虑好或者是编程逻辑不严谨引起的程序错误, 自己的水平有问题!

public static void main(String[] args) {System.out.println("程序开始。。。。。。");/** 1.数组索引越界异常: ArrayIndexOutOfBoundsException。*/int[] arr = {1, 2, 3};System.out.println(arr[2]);// System.out.println(arr[3]); // 运行出错,程序终止/** 2.空指针异常 : NullPointerException。直接输出没有问题。但是调用空指针的变量的功能就会报错!! */String name = null;System.out.println(name); // null// System.out.println(name.length()); // 运行出错,程序终止/** 3.类型转换异常:ClassCastException。 */Object o = 23;// String s = (String) o;  // 运行出错,程序终止/** 5.数学操作异常:ArithmeticException。 *///int c = 10 / 0;/** 6.数字转换异常: NumberFormatException。 *///String number = "23";String number = "23aabbc";Integer it = Integer.valueOf(number); // 运行出错,程序终止System.out.println(it + 1);System.out.println("程序结束。。。。。");}

常见编译时异常

不是RuntimeException或者其子类的异常,编译阶就报错,必须处理,否则代码不通过。

编译时异常的作用是什么:

  • 是担心程序员的技术不行,在编译阶段就爆出一个错误, 目的在于提醒不要出错!
  • 编译时异常是可遇不可求。遇到了就遇到了呗。

示例:

public static void main(String[] args) throws ParseException {String date = "2015-01-12 10:23:21";// 创建一个简单日期格式化类:SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");// 解析字符串时间成为日期对象//这里的parse编译阶段会变红,使用alt加enter处理异常Date d = sdf.parse(date);//System.out.println(d);}

异常对象的传播

当程序运行起来之后,如果发生了某种异常,那么JVM就会终止我们程序的运行,然后根据这种异常产生一个异常对象。再把这个异常对象,拿到我们的代码中看是否进行了处理,如果没有处理,它就会退出当前方法,带着异常对象返回方法调用处;并记录下退出的位置。

如果方法调用处也没有处理这个异常,那么调用处的方法也被终止,带着异常对象在网上一级传播。最终来到main方法,如果main方法还是没有处理,那么只能结束main方法,把异常对象信息打印在控制台,而main方法结束当然整个程序结束。

这个过程,就是异常的传播,在这种情况下,我们可以感受到,如果想让程序发生异常后不死,其实我们只需要在传播过程中的任意一个地方进行处理就可以了。

注意:

这里的细节就是结束方法的方式不仅仅是前面学到的两种,而是三种:、

1、执行到方法的末尾;

2、执行中遇到return语句;

3、执行中产生了异常且没有进行处理;

而一个方法无论用哪一种方式结束,它的流程都是到方法调用处。

异常的处理机制

默认处理机制

  1. 默认会在出现异常的代码那里自动的创建一个异常对象:ArithmeticException。
  2. 异常会从方法中出现的点这里抛出给调用者,调用者最终抛出给JVM虚拟机。
  3. 虚拟机接收到异常对象后,先在控制台直接输出异常栈信息数据。
  4. 直接从当前执行的异常点干掉当前程序。
  5. 后续代码没有机会执行了,因为程序已经死亡。

代码演示:

public static void main(String[] args) {System.out.println("程序开始。。。。。。。。。。");chu(10, 0);System.out.println("程序结束。。。。。。。。。。");}public static void chu(int a , int b){System.out.println(a);System.out.println(b);int c = a / b;System.out.println(c);}

小结:

  • 异常一旦出现,会自动创建异常对象,最终抛出给虚拟机,虚拟机
  • 只要收到异常,就直接输出异常信息,干掉程序!!
  • 默认的异常处理机制并不好,一旦真的出现异常,程序立即死亡!

编译时异常的处理机制

编译时异常是编译阶段就出错的,所以必须处理,否则代码根本无法通过

三种编译时异常处理形式:

  • 出现异常直接抛出去给调用者,调用者也继续抛出去。
  • 出现异常自己捕获处理,不麻烦别人。
  • 前两者结合,出现异常直接抛出去给调用者,调用者捕获处理。

异常处理方式1 —— throws

  • throws:用在方法上,可以将方法内部出现的异常抛出去给本方法的调用者处理。
  • 这种方式并不好,发生异常的方法自己不处理异常,如果异常最终抛出去给虚拟机将引起程序死亡。

格式:方法 throws 异常1 ,异常2 ,异常3 ..{ }

规范做法:方法 throws Exception{ }

异常处理方式2 —— try…catch…finally

这是Java当中专门提供的异常处理语句,也是Java特性中健壮性的体现。

try

try的含义就是试一试,所以try快的任务是在里面书写我们正常逻辑的代码,当然这段代码是有可能发生异常的。然后我们让它在try快当中试着运行。

在一个try块当中有可能发生多个异常,但是运行的时候只会发生一个。

catch

catch的含义就是捕获,它的任务是捕获某种类型的异常,然后书写捕获后你想执行的内容。

finally

是一个关键字,它表示的是最终。它里面的代码指的是:不管是否发生异常,都必须要执行的代码。即使遇到了return语句也不能阻止它。唯一能够阻止它的代码是:System.exit(0).因为这句代码效果是关闭JVM。

什么样的代码写在finally当中呢?往往都是资源的清理工作,管道/连接的关闭动作。

细节:

1、catch后面的()书写的是申明一个异常变量。一旦try块发生异常,产生了异常对象,就会跳到catch块,用()里面的这个异常变量去匹配这个异常对象。如果匹配上说明该catch块就是处理该异常对象的,就会打断异常的传播,进入catch快的{}执行;

2、就算catch块的{}里面什么代码都不写,只要匹配上,就算把异常给处理了。程序流程回归正常,不会往上一级跳。

3、在一个try块当中如果有可能发生多种异常,那么我们可以连续书写多个catch块;

我们也可以利用动态绑定(父类的引用可以指向子类的对象),书写一个catch块让它捕获Exception。

这两种方式在选择上都可以,如果所有异常的处理的方式都是一样的,那么建议捕获父类异常;如果需要有分开的不同处理,那么建议写多个catch。

注意:当我们书写多个catch快的时候,如果这多个catch块捕获的异常有继承关系,那么必须把捕获子类异常的catch块写在前面,捕获父类异常的catch块写在后面。

  • 监视捕获异常,用在方法内部,可以将方法内部出现的异常直接捕获处理。
  • 这种方式还可以,发生异常的方法自己独立完成异常的处理,程序可以继续往下执行。

格式:

       try{       // 监视可能出现异常的代码!   }catch(异常类型1 变量){// 处理异常   }catch(异常类型2 变量){       // 处理异常   }...

建议格式:

try{    // 可能出现异常的代码!
}catch (Exception e){    e.printStackTrace(); // 直接打印异常栈信息
}Exception可以捕获处理一切异常类型!

练习:

/*** *练习:** 申明一个int[],里面方上5个数字。* 然后让用户输入一个下标,根据该下标取出数组的元素。用try-catch捕获可能出现的异常(输入不匹配异常,数组下标越界异常)。*/
public class Test {public static void main(String[] args) {int [] array = {10,20,30,40,50};try{Scanner sc = new Scanner(System.in);System.out.println("请输入一个下标(0-4)");int index = sc.nextInt();//输入不匹配异常,数组下标越界异常System.out.println("下标为" + index + "的数组元素为:" + array[index]);}catch (ArrayIndexOutOfBoundsException a){System.out.println("数组下标越界异常");}catch(InputMismatchException i){System.out.println("输入不匹配异常");}catch (Exception e){System.out.println("发生了未知异常");}}
}

异常的处理方式3

在出现异常的地方把异常一层一层的抛出给最外层调用者,
最外层调用者集中捕获处理!!(规范做法)

异常处理总结:

  • 编译时异常的处理方式三按照规范来说是最好的:底层出现的异常抛出给最外层调用者集中捕获处理。
  • 这种方案最外层调用者可以知道底层执行的情况,同时程序在出现异常后也不会立即死亡,这是理论上最好的方案。
  • 虽然异常有三种处理方式,但是开发中只要能解决你的问题,每种方式都又可能用到!!

运行时异常的处理机制

  • 运行时异常编译阶段不会出错,是运行时才可能出错的,所以编译阶段不处理也可以。
  • 按照规范建议还是处理:建议在最外层调用处集中捕获处理即可。
public static void main(String[] args) {System.out.println("程序开始。。。。。。。。。。");try {chu(10, 0);} catch (Exception e) {e.printStackTrace();}System.out.println("程序结束。。。。。。。。。。");}public static void chu(int a , int b) { // throws RuntimeException{System.out.println(a);System.out.println(b);int c = a / b;System.out.println(c);}

异常处理使代码更稳健的案例

需求:键盘录入一个合理的价格为止(必须是数值,值必须大于0)。

定义一个死循环,让用户不断的输入价格。

public static void main(String[] args) {Scanner sc  = new Scanner(System.in);while (true) {try {System.out.println("请您输入合法的价格:");String priceStr = sc.nextLine();// 转换成double类型的价格double price = Double.valueOf(priceStr);// 判断价格是否大于 0if(price > 0) {System.out.println("定价:" + price);break;}else {System.out.println("价格必须是正数~~~");}} catch (Exception e) {System.out.println("用户输入的数据有毛病,请您输入合法的数值,建议为正数~~");}}}

自定义异常

1、自定义异常类,必须让它继承Throwable或者Exception 否则它是没有办法通过throw关键字纳入到Java的异常机制当中的。

2、我们还需要带入异常的信息,所以我们通常都会再定义异常的带参构造。

3、还可以给它添加自定义的行为。比如说写日志的行为。

自定义异常的必要?

  • Java无法为这个世界上全部的问题提供异常类。
  • 如果企业想通过异常的方式来管理自己的某个业务问题,就需要自定义异常类了。

自定义异常的好处:

  • 可以使用异常的机制管理业务问题,如提醒程序员注意。
  • 同时一旦出现bug,可以用异常的形式清晰的指出出错的地方。

自定义异常的分类

1、自定义编译时异常

  • 定义一个异常类继承Exception.  重写构造器。
  • 在出现异常的地方用throw new 自定义对象抛出,
  • 作用:编译时异常是编译阶段就报错,提醒更加强烈,一定需要处理!!

需求:认为年龄小于0岁,大于200岁就是一个异常。

public static void main(String[] args) {try {checkAge(-34);} catch (ItheimaAgeIlleagalException e) {e.printStackTrace();}}public static void checkAge(int age) throws ItheimaAgeIlleagalException {if(age < 0 || age > 200){// 抛出去一个异常对象给调用者// throw :在方法内部直接创建一个异常对象,并从此点抛出// throws : 用在方法申明上的,抛出方法内部的异常throw new ItheimaAgeIlleagalException(age + " is illeagal!");}else {System.out.println("年龄合法:推荐商品给其购买~~");}}
/**自定义的编译时异常1、继承Exception2、重写构造器*/
public class ItheimaAgeIlleagalException extends Exception{public ItheimaAgeIlleagalException() {}public ItheimaAgeIlleagalException(String message) {super(message);}
}

2、自定义运行时异常

  • 定义一个异常类继承RuntimeException. 重写构造器。
  • 在出现异常的地方用throw new 自定义对象抛出!
  • 作用:提醒不强烈,编译阶段不报错!!运行时才可能出现!!
public static void main(String[] args) {try {checkAge2(-23);} catch (Exception e) {e.printStackTrace();}}public static void checkAge2(int age)  {if(age < 0 || age > 200){// 抛出去一个异常对象给调用者// throw :在方法内部直接创建一个异常对象,并从此点抛出// throws : 用在方法申明上的,抛出方法内部的异常throw new ItheimaAgeIlleagalRuntimeException(age + " is illeagal!");}else {System.out.println("年龄合法:推荐商品给其购买~~");}}
/**自定义的运行时异常1、继承RuntimeException2、重写构造器*/
public class ItheimaAgeIlleagalRuntimeException extends RuntimeException{public ItheimaAgeIlleagalRuntimeException() {}public ItheimaAgeIlleagalRuntimeException(String message) {super(message);}
}

对应try-catch-finally在面试时有很多题目和细节

1、try-catch-finally是不是处理异常的唯一手段?

不是,处理异常还有一种提前判断的手段。大部分运行时异常,都应该使用提前判断。编译时异常才更多的采用这种手段。

2、try-caych以后,是不是就万事大吉了?

并不是,异常处理的关键并不是把异常捕获住就完了啊。如果只是让程序不报错,那很简单,我们就在main方法里面一个try-catch就可以了。很明显这是不合适的。

更多的工作是需要我们在捕获了之后,能够让程序回到正确的业务执行顺序上,继续完成我们的工作。

3、try-catch-finally是否必须同时出现?

不是,try后面必须接catch或finally,也可以都有;catch和finally前面必须要有try。

4、final、finally和finalize的区别?

final是关键字(最终的),用来修饰变量的时候,那就成为一个常量;用来修饰方法的时候,这个方法不能被重写;用来修饰类的时候,这个类不能被继承。

finally也是关键字,用在异常处理的try-catch-finally机制里。写它当中代码是表示不管是否发生异常都必须要执行的代码,通常是资源的清理,管道的关闭等等。

finalize不是关键字,它是一个方法名。它是Object类的方法,专门用来销毁对象的,由垃圾回收调用。JDK9之后这个方法过时了。

5、finally中的代码在return之前执行还是return之后执行?

finally是在return之前被执行的。

严格意义上:finally不是在return这句代码之前被执行的,而是在return这个流程跳转动作之前被执行。return时流程跳转前的准备工作(返回值已经准备好了),已经做完了,然后在执行finally,然后再完成流程跳转。

记住:finally里面是不能够改变return后面的返回值的。

6、看代码说结果

public void methodA(){int r = methodB();System.out.println(r);
}
public int methodB(){int num = 20;try{num ==;return num;//21}finally{num += 5;}
}

throw和throws

throw关键字的主要语法呢,就是在方法内部作为一条指令,主动的抛出一个异常对象。

用它代表的场景就是发生了异常,但是从职责上来说,不归当前方法处理,而应该交给当前方法的调用者去完成处理。

我们在前面的学习中曾经学过,异常是有两大类:运行时异常、编译时异常。

当我们在一个方法里面throw了一个运行时异常的时候,那么编译器是不会在编译期检查的;

当我们在一个方法里面throw了一个编译时异常的时候,那么编译器就有责任在编译期警告调用者。警告它,你调用这个方法是有可能会发生异常的哦。所以在语法上,它会要求在这个方法的声明处加一个throws,这样编译器在编译调用处代码的时候就知道需要报“此处有未处理异常”的错误。

throw和throws之间的区别

1、两者书写的位置不同

throw是写在方法内部的一条执行语句;

throws是写在方法声明末尾的;

2、两者后面跟的东西不一样

throw后面跟的是一个异常对象;这句代码一旦在运行期被执行到,那么就真的会有一个异常对象被抛出来;

throws后面跟的不是异常对象,而是异常的类名,而且可以跟多个类名,它是编译期的时候告诉编译器本方法有可能出现某几种异常,要求调用者现在就用代码进行处理。

3、两者的关联

如果在方法里有throw语句,且后面跟的是一个编译时异常的话,那么编译器会强制要求在该方法的声明处加throws。而如果跟的是运行时异常对象,那么就不会在编译期要求加throws。

面试题:写了throw就一定要写throws吗?

不是,答案在上面。

在调用API的过程中,经常遇到人家设计的throws。这时,要有意识,这个时候不是语法错误,也不是真的发生异常了,只是提醒你检查一下你的参数对不对。如果你能确保你传入的参数是正确的,那么就用一个try-catch把调用代码括起来,让它编译通过即可。

注意:

throws的出现才让我们大家看到了完整的方法声明。

修饰符 返回类型 方法名 (参数列表) throws 异常类......

面试题目

1、以下哪个是正确的main方法?

标准写法:

public static void main(String[] args)

变形1:

static public void main(String[] args)

在完整方法声明中要求修饰符写在最前面。而修饰符分为两种:访问修饰符(public,protected,private)和可选修饰符(static,abstract,final,default)。所有的修饰符都要写在最前面,但他们之间的顺序是可以任意的。

变形2:

public static void main(String ... args)

Java设计的新语法,针对的是如果用数组做参数,可以简化传参的语法

变形3:

public static void main(String args[])

在老的c语言当中,数组声明的语法是:类型 数组名[]; int num[];而Java的语法是:类型[] 数组名;Java是c过渡过来的,它把数组声明语法改了是希望在Java当中,所有变量声明的语法都是统一的,即先写数据类型,再写变量名。但是,Java当年为了从c程序员当中吸引人,它保留了c的这个语法,也认为是正确的。

变形4:

public static void main (String[] fuck)

形参的名字对于编译器来说是可以任意取的,不做强制要求,只要符合命名规范就可以了。

变形5:

public static void main(String [] args) throws Exception

方法声明可以加上throws表示本方法可能会发生某种或某几种异常。

变形6:把以上五种变形进行混合。

2、方法重写的规范

2-1、方法名必须一致;

2-2、返回类型必须一致;

2-3、参数列表必须一致;

2-4、子类重写后的方法的访问修饰符不能比父类重写前的小;

那么我们学了throws了,应该增加一条。

2-5、子类重写后的方法不能比父类重写前的方法throws更多的异常。这里的更多不是指抛出的异常类的数量,而是指范围(异常类是有继承关系的,父类异常表示的范围是大于子类异常)。

4、JDK高版本的新语法

JDK版本从95年的1.0到现在已经发展到18了。但是不是所有的JDK版本都是所谓的生产板(或者叫LTS 版本——Long Time Support——长时间支持版)。这些LTS版本才是我们可以用在生产环节当中的,也是大家需要下载使用的。而其他版本呢,很多都是测试版本,里面的很多功能并不能保证稳定性。

目前Java的LTS版本常见的就3个:JDK8、JDK11、JDK17

现实当中,目前使用量最大的是JDK8,然后是JDK11,再然后是JDK17.但是现在Java里最重要的一个Sping框架已经宣布,在它将要推出的最新版Spring6当中不再支持JDK8,所以为了能够适应以后的这个变化,我们选择了JDK11。

但是从19年开始的面试题观察中,不少公司开始会考察对新技术和新语法的了解程度(即便它们自己公司根本不用)

var关键字

JDK10里提出的。用来声明局部变量。var是js的一个关键字,Java本身是没有的。JS是一门弱类型的编程语言

var i = 10;
i = "hello";

Java是强类型的编程语言

int i = 10;
String s = "hello";

弱类型的语法写起来更简单一些,但是一旦代码规模增加,容易出错。强类型语言虽然写得多了一点点,规范多了一点点,却更容易构建大型程序规模。

现在Java学了这一点,也引入了var这个关键字用来声明变量,但是又和JS的var有很大的不同

1、Java里的var只能用来声明局部变量 2、Java里的var声明的变量在第一次赋值以后就知道自己是什么类型的变量了,后面就不可以改了 3、var声明了变量以后,必须马上在同一条语句进行赋值。 不然JVM没有办法通过值的类型来进行类型推断,也就没有办法给这个变量分配空间。 4、var在实际使用过程里,不太受欢迎,大家都不喜欢用,因为没有提供太大的好处,只是在面试里可能会被问道。 5、唯一想用它的时候,是当类型名字非常长的时候,可以减少工作量。

对于这个关键字,知道就可以了,在面试当中不至于说不知道就行

       var v = "hello";v = "world";
//        v = 250;//报错的 v变量在第一次赋值的时候就确定了自己是String类型,不能变了var i = 10;

String文本块

String内容在操作的时候,有一个很麻烦的事情,那就是格式化的操作。我们需要在”“内部通过\n,\t这些来控制字符串的现实格式。

那么在JDK15里,提供了String的文本块功能。它的语法如下:

String str = """请选择你要执行的操作:1.查询余额;2.存款;3.取款;......""";

三个引号的字符串:

1、虽然是三个引号,但是没有改变这个文本块也是一个String类型的事实;

2、三个开始引号和三个结束引号不能在同一行,字符串内容也要单独成行。

在很多时候需要调整字符串格式的应用场景里,这个新语法非常有帮助。

switch可以作为表达式返回一个值

在JDK12中,增加了一个yield关键字,可以直接从switch的case和default语句中返回值。

yield关键字的作用:

1、像break一样跳出case分支;

2、同时,把yield后面的值返回出来交给前面的c变量

注意:1、每个yield返回的类型必须保持一致(兼容);2、在每个yield之前是可以书写任意代码的;

        int num = new Scanner(System.in).nextInt();char c = 0;switch (num){case 1:c = 'A';break;case 2:c = 'B';break;case 3:c = 'C';break;case 4:c = 'D';break;}/*        char c1 = switch(num){case 1:yield 'A';case 2:yield 'B';case 3:yield 'C';case 4:yield 'D';};*//*这是一种更特使的语法,用->替换yield返回值注意:这种情况每个分支只能返回一个值,不能有其他的处理语句。*/
/*        char c1 = switch(num){case 1 -> 'A';case 2 -> 'B';case 3 -> 'C';case 4 -> 'D';default -> 'E';};*/

JavaSE基础笔记——不可变集合简介、Stream流体系、异常处理相关推荐

  1. Java SE基础知识详解第[13]期—不可变集合、Stream流、异常

    写在前面: 每一个不曾起舞的日子,都是对生命的辜负. 希望看到这里的每一个人都能努力学习,不负韶华,成就更好的自己. 以下仅是个人学习过程中的一些想法与感悟,Java知识博大精深,作为初学者,个人能力 ...

  2. Java笔记_16(不可变集合、Stream流、方法引用)

    Java笔记_16 一.创建不可变集合 1.1.创建不可变集合的应用场景 1.2.创建不可变集合的书写格式 二.Stream流 2.1.体验Stream流 2.2.Stream流的思想和获取Strea ...

  3. 黑马笔记---创建不可变集合与Stream流

    目录 1.不可变集合 1.1什么是不可变集合? 1.2如何创建不可变集合? 总结: 2.Stream流 2.1Stream流的概述 什么是Stream流? 案例:Stream流的作用 Stream流式 ...

  4. 【Java基础16】不可变集合、Stream流和异常

    目录 1. 不可变集合 1.1 创建 2. Stream流 2.1 Stream流的创建 2.1.1 获得Stream流 2.1.2 中间操作 2.1.3 终结方法 2.1.4 Stream流数据收集 ...

  5. list转map stream_advancedday10可变参数不可变集合及Stream流

    可变参数 可变参数指的是,方法参数的个数可以发生改变.但是其本质是一个数组,在方法把可变参数当做数组来使用就可以了. //可变参数的格式:数据类型...参数名public static void 方法 ...

  6. 7. Java不可变集合和Stream流

    Java不可变集合和Stream流 1. 不可变集合 2. Stream流 2.1 概述 2.2 Stream流的获取 2.3 Stream流的常用API 2.4 Stream流的综合应用 2.5 收 ...

  7. javase加强(七、 不可变集合、Stream、异常)

    文章目录 不可变集合 Stream流 Stream流的常用API(中间操作方法) Stream流案例: 收集Stream流: 异常 编译时异常的处理形式 不可变集合 不可变集合:集合的数据在创建时提供 ...

  8. JavaSE基础笔记——常用API、Lambda、常见算法

    日期与时间 时间日期是在任何一个程序系统里几乎都不可能忽略掉的数据量,而且大量的算法在底层都会使用到时间日期数据值作为算法的基本种子(随机数算法或加密算法都经常用到). 计算机里,时间日期的本质 作为 ...

  9. 不可变集合、Stream、异常

    1.不可变集合 不可变集合,就是不可被修改的集合. 集合的数据项在创建的时候提供,并且在整个生命周期中都不可改变.否则报错. 创建不可变集合: 如果某个数据不能被修改,把它防御性地拷贝到不可变集合中是 ...

最新文章

  1. python如何小写p转换p_Python进阶---python 中字符串大小写转换
  2. php根据指定字段去重,php二维数组根据某个字段去重
  3. [TIPS]数据库 应用软件 MSSMS
  4. 项目中记录影响性能的缓慢数据库查询
  5. 在js中访问html页面,javascript – 在IE9的html页面中访问js里面的全局函数
  6. 利用 caffe的 python接口测试训练好的 mnist 模型
  7. Android Native 代码NDK开发学习笔记
  8. picasa csdn_使用Picasa网络相册开发PHP应用程序
  9. 佳博GP2120TU标签打印机 安装和使用教程(PC)
  10. 激光雷达电力巡基于机载激光雷达技术的输电线路树障普查及预警
  11. prepareStatement的批量处理数据
  12. 神秘的罗斯柴尔德家族
  13. html 图片右侧空白,CSS缩小窗口时,背景图出现右侧空白
  14. Angular+PrimeNg 分页器给后端传参分页
  15. SNS2124SNS2224SNS2248 光纤交换机配置
  16. 【分页存储管理】将十六进制的虚拟地址0A5C、103C、4251转换成物理地址
  17. 【MySQL 数据库】JDBC 编程之 Java 连接 MySQL
  18. Linux-命令:ll命令报错-bash: ll: command not found
  19. 设计模式——开闭原则
  20. 深度置信网 DBNs

热门文章

  1. 关于父进程和子进程的关系
  2. 最新苹果手机iphone x拆机报告图解(图文)
  3. 仓库拣货标签11代----亮灯拣选
  4. 【考研】东北大学二叉树相关算法(2)
  5. 利用双网卡来提升网速,解决网卡瓶颈
  6. 既然选择了远方,便只顾风雨兼程--myvue
  7. 职场小技能——如何画好流程图?
  8. .NET 托管vs非托管
  9. 悟透javscript
  10. DELL工作站进PE盘无法找到索引,从而无法找到磁盘(mbr分区改为gpt分区)