1 Stream流
1.1 Stream流概述
什么是Stream流?
在Java 8中,得益于Lambda所带来的函数式编程,引入了一一个全新的Stream流概念。
目的:用于简化集合和数组操作的API。
体验Stream流的作用
需求:按照下面的要求完成集合的创建和遍历
- 创建一个集合, 存储多个字符串元素
List<String> list = new ArrayList<>();
list.add("张麻子");
list.add("张三丰");
list.add("马邦德");
list.add("张三");
list.add("张德发");
- 把集合中所有 以"张"开头的元素存储到一个新的集合
- 把"张"开头的集合中 的长度为3的元素存储到-个新的集合
- 遍历上一步得到的集合中 的元素输出。
package com.itzw.d1_stream;
import java.util.ArrayList;
import java.util.List;
public class Test {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("张麻子");
list.add("张三丰");
list.add("马邦德");
list.add("张德发");
list.add("张三");
System.out.println(list);
//传统方法
List<String> zhangList = new ArrayList<>();
for (String s : list) {
if (s.startsWith("张")){
zhangList.add(s);
}
}
System.out.println(zhangList);
List<String> threeList = new ArrayList<>();
for (String s : zhangList) {
if (s.length() == 3){
threeList.add(s);
}
}
System.out.println(threeList);
System.out.println("============使用stream流=============");
//一行搞定
list.stream().filter(s -> s.startsWith("张")).filter(s -> s.length() == 3).forEach(s -> System.out.println(s));
}
}
1.2 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流
演示:
//Collection集合获取Stream流
Collection<String> list = new ArrayList<>();
Stream<String> listStream = list.stream();
//Map集合获取Stream流
Map<String,Integer> map = new HashMap<>();
//键流
Stream<String> keyStream = map.keySet().stream();
//值流
Stream<Integer> valueStream = map.values().stream();
//键值对流
Stream<Map.Entry<String, Integer>> entryStream = map.entrySet().stream();
//数组获取Stream流
String[] arr = {"张麻子","马邦德","黄四郎"};
Stream<String> arrayStream1 = Arrays.stream(arr);
Stream<String> arrayStream2 = Stream.of(arr);
1.3 Stream流常用API
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两个流为一个流
演示:
package com.itzw.d1_stream;
import javax.crypto.spec.PSource;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Stream;
public class Test02 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("张麻子");
list.add("张三丰");
list.add("马邦德");
list.add("张德发");
list.add("张三");
System.out.println(list);
list.stream().filter(s -> s.startsWith("张"));
//limit 获取前几个元素
list.stream().filter(s -> s.startsWith("张")).limit(2).forEach(s -> System.out.println(s));
System.out.println("============================");
//skip 跳过前几个元素
list.stream().filter(s -> s.startsWith("张")).skip(2).forEach(System.out::println);
System.out.println("=============================");
//contact 合并两个流
Stream<String> zhangStream = list.stream().filter(s -> s.startsWith("张"));
Stream<String> stream = Stream.of("嗨嗨嗨", "Java");
Stream.concat(zhangStream,stream).forEach(s -> System.out.println(s));
//给集合元素前面都加上一个“鹅城的”
list.stream().map(s -> "鹅城的" + s).forEach(s -> System.out.println(s));
System.out.println("=====================================");
//把所有的名称都加工成一个学生对象
list.stream().map(s -> new Student(s)).forEach(student -> System.out.println(student));
}
}
class Student{
private String name;
public Student() {
}
public Student(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
'}';
}
}
注意:
- 中间方法也称为非终结方法,调用完成后返回新的Stream流可以继续使用,支持链式编程。
- 在Stream流中无法直接修改集合、数组中的数据。
Stream流的常见终结操作方法
void forEach(Consumer action )对此流的每个元素执行遍历操作
long count()返回此流中的元素数
注意:终结操作方法,调用完成后流就无法继续使用了,原因是不会返回Stream了。
1.4 案例
需求:某个公司的开发部门,分为开发一部和二部,现在需要进行年中数据结算。
分析:
- 员工信息至少包含了(名称、性别、工资、奖金、处罚记录)
- 开发一部有4个员工、开发二部有5名员工
- 分别筛选出2个部门的最高工资的员工信息,封装成优秀员工对象Topperformer
- 分别统计出2个部门的平均月收入,要求去掉最高和最低工资。
- 统计2个开发部门]整体的平均工资,去掉最低和最高工资的平均值。
package com.itzw.d1_stream;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
public class Test03 {
public static double allMoney = 0;
public static double allMoney2 = 0;
public static void main(String[] args) {
List<Employee> one = new ArrayList<>();
one.add(new Employee("诸葛亮",'男',50000,100000,"木鱼哦"));
one.add(new Employee("关羽",'男',5000,10000,"木鱼哦"));
one.add(new Employee("张飞",'男',5000,10000,"木鱼哦"));
one.add(new Employee("孙尚香",'女',40000,90000,"木鱼哦"));
List<Employee> two = new ArrayList<>();
two.add(new Employee("吴用",'男',40000,20000,"木鱼哦"));
two.add(new Employee("武松",'男',20000,20000,"木鱼哦"));
two.add(new Employee("鲁智深",'男',33000,20000,"木鱼哦"));
two.add(new Employee("林冲",'男',34200,20000,"木鱼哦"));
two.add(new Employee("孙二娘",'女',32000,20000,"木鱼哦"));
//开发一部最高工资的员工
/*Employee e = one.stream().max((e1, e2) -> Double.compare(e1.getBonus() + e1.getSalary(),
e2.getBonus() + e2.getSalary())).get();
System.out.println(e);*/
//将一部门最高工资员工放入优秀员工中
Topperformer oneTopper = one.stream().max((e1, e2) -> Double.compare(e1.getBonus() +
e1.getSalary(), e2.getBonus() + e2.getSalary())).
map(e -> new Topperformer(e.getName(), e.getBonus() + e.getSalary())).get();
System.out.println(oneTopper);
//将二部门最高工资员工放入优秀员工中
Topperformer twoTopper = two.stream().max((e1, e2) -> Double.compare(e1.getBonus() +
e1.getSalary(), e2.getBonus() + e2.getSalary())).
map(e2 -> new Topperformer(e2.getName(), e2.getBonus() + e2.getSalary())).get();
System.out.println(twoTopper);
//统计平均工资,去掉最高最低工资
one.stream().sorted((e1,e2) -> Double.compare(e1.getBonus()+e1.getSalary(),
e2.getBonus()+e2.getSalary())).skip(1).limit(one.size()-2).forEach(e -> {
allMoney += e.getBonus()+e.getSalary();
});//这里相当于一个方法,无法用main方法中的变量,所以要创建静态变量
System.out.println("开发一部平均工资:" + allMoney/(one.size()-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.getBonus()+e1.getSalary(),
e2.getBonus()+e2.getSalary())).skip(1).limit(one.size()+two.size()-2).forEach(e -> {
allMoney2 += e.getBonus()+e.getSalary();
});
//使用
BigDecimal b1 = BigDecimal.valueOf(allMoney2);
BigDecimal b2 = BigDecimal.valueOf(one.size()+two.size()-2);
System.out.println("两个部门平均工资:" + b1.divide(b2,2, RoundingMode.HALF_UP));
//System.out.println("两个部门平均工资:" + allMoney2/(one.size()+two.size()-2));
}
}
class Employee{
private String name;
private char sex;
private double salary;
private double bonus;
private String punish;
public Employee(){
}
public Employee(String name, char sex, double salary, double bonus, String punish) {
this.name = name;
this.sex = sex;
this.salary = salary;
this.bonus = bonus;
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 getBonus() {
return bonus;
}
public void setBonus(double bonus) {
this.bonus = bonus;
}
public String getPunish() {
return punish;
}
public void setPunish(String punish) {
this.punish = punish;
}
@Override
public String toString() {
return "Employee{" +
"name='" + name + '\'' +
", sex=" + sex +
", salary=" + salary +
", bonus=" + bonus +
", punish='" + punish + '\'' +
'}';
}
}
class Topperformer{
private String name;
private double bonus;
public Topperformer(){
}
public Topperformer(String name, double bonus) {
this.name = name;
this.bonus = bonus;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getBonus() {
return bonus;
}
public void setBonus(double bonus) {
this.bonus = bonus;
}
@Override
public String toString() {
return "Topperformer{" +
"name='" + name + '\'' +
", bonus=" + bonus +
'}';
}
}
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集合中
List<String> list = new ArrayList<>();
list.add("张麻子");
list.add("张三丰");
list.add("马邦德");
list.add("张德发");
list.add("张三");
System.out.println(list);
//转成集合
Stream<String> s1 = list.stream().filter(s -> s.startsWith("张"));
List<String> list1 = s1.collect(Collectors.toList());
System.out.println(list1);
//转成数组
Stream<String> s2 = list.stream().filter(s -> s.startsWith("张"));
Object[] array = s2.toArray();
System.out.println(Arrays.toString(array));
2 异常
2.1 概述
什么是异常?
- 异常是程序在“编译”或者“执行”的过程中可能出现的问题,注意:语法错误不算在异常体系中。
- 比如:数组索引 |越界、空指针异常、日期格式化异常, 等..
为什么要学习异常?
- 异常一旦出现了,如果没有提前处理,程序就会退出JVM虚拟机而终止.
- 研究异常并且避免异常,然后提前处理异常,体现的是程序的安全,健壮性。
2.2 常见异常
2.2.1 运行时异常
- 直接继承自RuntimeException或者其子类,编译阶段不会报错,运行时可能出现的错误。
运行时异常示例
- 数组 索引越界异常: ArrayIndexOutOfBoundsException
- 空指针异常: NullPointerException,直接输出没有问题,但是调用空指针的变量的功能就会报错。
- 数学操作异常: ArithmeticException
- 类型转换异常: ClassCastException
- 数字转换异常 : NumberFormatException
运行时异常:一般是程序 员业务没有考虑好或者是编程逻辑不严谨引起的程序错误,
//1.数组索引越界异常:ArrayIndexOutOfBoundsException
int[] a = {1,2,3};
System.out.println(a[0]);
System.out.println(a[1]);
System.out.println(a[2]);
//System.out.println(a[3]);
//2.空指针异常NullPointerException
String s = null;
System.out.println(s);
//System.out.println(s.length());
//3.数学操作异常 ArithmeticException
//int i = 10 / 0;
//4.类型转换异常ClassCastException
Object c = "哈哈哈";
int d = (int)c;
//5.数字转换异常NumberFormatException
//String ss = "123a";
//Integer sss = Integer.valueOf(ss);
//System.out.println(sss + 1);
2.2.2 编译时异常
- 不是RuntimeException或者其子类的异常,编译阶就报错,必须处理,否则代码不通过。
编译时异常的作用是什么:
- 是担心程序员的技术不行,在编译阶段就爆出一一个错误,目的在于提醒不要出错!
2.3 异常处理
2.3.1 默认处理
- 默认会在出现异常的代码那里自动的创建一个异 常对象: ArithmeticException。
- 异常会从方法中出现的点这里抛出给调用者,调用者最终抛出给JVM虛拟机。
- 虚拟机接收到异常对象后,先在控制台直接输出异常栈信息数据。
- 直接从当前执行的异常点干掉当前程序。
- 后续代码没有机会执行了,因为程序已经死亡。
System.out.println("程序开始");
divide(10,0);
System.out.println("程序结束");
}
public static void divide(int a,int b){
System.out.println(a);
System.out.println(b);
int c = a / b;
System.out.println(c);
}
程序遇到错误就会立刻终止。
2.3.2 throws
- throws:用在方法上,可以将方法内部出现的异常抛出去给本方法的调用者处理。
- 这种方式并不好, 发生异常的方法自己不处理异常,如果异常最终抛出去给虚拟机将引起程序死亡。
2.3.3 try...catch...
-
监视捕获异常, 用在方法内部,可以将方法内部出现的异常直接捕获处理。这种方式还可以,发生异常的方法自己独立完成异常的处理,程序可以继续往下执行。
-
程序错误不会抛给虚拟机,仍然继续运行
-
如有多个异常建议放在一起try
package com.itzw.d2_exception;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Test04 {
public static void main(String[] args){
System.out.println("程序开始");
parseTime("2022-8-27 16:30:30");
System.out.println("程序结束");
}
public static void parseTime(String time){
try {
SimpleDateFormat sdf = new SimpleDateFormat("yy-MM-dd HH:mm:ss");
Date parse = sdf.parse(time);
System.out.println(parse);
InputStream is = new FileInputStream("D:/haihai");
} catch (Exception e) {
e.printStackTrace();
}
}
}
2.3.4 前两者结合
- 方法直接将异通过throws抛出去给调用者
- 调用者收到异常后直接捕获处理。
2.4 案例
异常处理使代码更稳健的案例
需求
- 键盘录入一个合理 的价格为止(必须是数值,值必须大于0)。
分析
- 定义一个死循环,让用户不断的输入价格。
package com.itzw.d2_exception;
import java.util.Scanner;
public class Test06 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while (true){
try {
System.out.println("请输入一个价格:");
String s = sc.nextLine();
Double price = Double.valueOf(s);
if (price > 0){
System.out.println("输入完成!");
break;
}else {
System.out.println("输入有误,请输入正数!");
}
} catch (Exception e) {
System.out.println("您输入的数字有毛病,请重新输入!!");
}
}
}
}
2.5 自定义异常
自定义异常的必要?
- Java无法为这个世界上全部的问题提供异常类。
- 如果企业想通过异常的方式来管理自己的某个业务问题,就需要自定义异常类了。
自定义异常的好处:
- 可以使用异常的机制管理业务问题,如提醒程序员注意。
- 同时一旦出现bug,可以用异常的形式清晰的指出出错的地方。
2.5.1 自定义编译时异常
-
定义一个异 常类继承Exception.
-
重写 构造器。
-
在出现异常的地方用thrownew自定义对象抛出,
作用:编译时异常是编译阶段就报错,提醒更加强烈,一定需要处理! !
自定义编译时异常
public class ItZwAgeException extends Exception{
public ItZwAgeException() {
}
public ItZwAgeException(String message) {
super(message);
}
}
自定义异常的使用
package com.itzw.d3_myselfexception;
public class ExceptionTest {
public static void main(String[] args) {
try {
checkAge(-12);
} catch (ItZwAgeException e) {
e.printStackTrace();
}
}
public static void checkAge(int age) throws ItZwAgeException {
if (age < 0 || age > 200){
throw new ItZwAgeException(age + " is xiagao");
}else {
System.out.println("年龄合法");
}
}
}
2.5.2 自定义运行时异常
- 定义一个异 常类继承RuntimeException.
- 重写构造器。
- 在出现异常 的地方用throw new自定义对象抛出!
作用:提醒不强烈,编译阶段不报错!N运行时才可能出现! !