Stream、异常体系
创建不可变集合
什么是不可变集合?
不可变集合,就是不可被修改的集合。
集合的数据项在创建的时候提供,并且在整个生命周期中都不可改变。否则报错。
为什么要创建不可变集合?
如果某个数据不能被修改,把它防御性地拷贝到不可变集合中是个很好的实践。或者当集合对象被不可信的库调用时,不可变形式是安全的。

//目标:不可变集合
public class CollectionDemo {
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);//报错
double score = lists.get(1);
//lists.set(2,698.5);//报错
System.out.println(lists);
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);
}
}
不可变集合的特点?
定义完成后不可以修改,或者添加、删除
如何创建不可变集合?
List、Set、Map接口中,都存在of方法可以创建不可变集合。
Stream流
Stream流的概述



//初步体验Stream流的方便与快捷
public class StreamTest {
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流式思想的核心:
是先得到集合或者数组的Stream流(就是一根传送带)
然后就用这个Stream流操作集合或者数组的元素。
然后用Stream流简化替代集合操作的API.
集合获取流的API:
(1) default Stream<E> stream();
小结:
集合获取Stream流用: stream();
数组:Arrays.stream(数组) / Stream.of(数组);
*/
public class StreamDemo02 {
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流的常用方法
中间方法

public class StreamDemo03 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("张无忌");
list.add("周芷若");
list.add("赵敏");
list.add("张强");
list.add("张三丰");
list.add("张三丰");
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);//方法引用
/**
* System.out::println
* 当对传入参数进行打印时,可以直接用方法引用
*/
list.stream().filter(s->s.startsWith("张")).skip(2).forEach(System.out::println);//方法引用
//Map加工方法
//给集合元素的前面都加上一个 黑马的:
// list.stream().map(new Function<String, String>() {
// @Override
// public String apply(String s) {
// return "黑马的:" + s;
// }
// }).forEach(System.out::println);
//简化后的代码:
list.stream().map(s->"黑马的"+s).forEach(System.out::println);
//需求:把所有的名称 都加工成一个学生对象。
// 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");
Stream<String> s3 = Stream.concat(s1,s2);
//s3.forEach(System.out::println);
//去重复
System.out.println("---------去重复------------");
s3.distinct().forEach(System.out::println);
}
}
终结方法

终结和非终结方法的含义是什么?
终结方法后流不可以继续使用,非终结方法会返回新的流,支持链式编程。
Stream流的综合应用

public 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;
}
public double getTotalSalay(){
return salary * 12 + bonus;
}
@Override
public String toString() {
return "Employee{" +
"name='" + name + '\'' +
", sex=" + sex +
", salary=" + salary +
", bonus=" + bonus +
", punish='" + punish + '\'' +
'}'+"\n";
}
}
public class Topperformer {
private String name;
private double money;//月薪
public Topperformer() {
}
public Topperformer(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;
}
@Override
public String toString() {
return "Topperformer{" +
"name='" + name + '\'' +
", money=" + money +
'}';
}
}
public class StreamDemo04 {
public static double allMoney;
public static double allMoney2;//2个部门去掉最高工资,最低工资
public static void main(String[] args) {
List<Employee> one = new ArrayList<>();
one.add(new Employee("猪八戒",'男',30000 , 25000, null));
one.add(new Employee("孙悟空",'男',25000 , 1000, "顶撞上司"));
one.add(new Employee("沙僧",'男',20000 , 20000, null));
one.add(new Employee("小白龙",'男',20000 , 25000, null));
List<Employee> two = new ArrayList<>();
two.add(new Employee("武松",'男',15000 , 9000, null));
two.add(new Employee("李逵",'男',20000 , 10000, null));
two.add(new Employee("西门庆",'男',50000 , 100000, "被打"));
two.add(new Employee("潘金莲",'女',3500 , 1000, "被打"));
two.add(new Employee("武大郎",'女',20000 , 0, "下毒"));
//1.开发一部的最高工资的员工
//制定大小规则
// Employee e = one.stream().max((e1,e2)->Double.compare(e1.getSalary()+e1.getBonus(),e2.getSalary()+ e2.getBonus())).get();
// System.out.println(e);
Topperformer t = one.stream().max((e1,e2)->Double.compare(e1.getSalary()+e1.getBonus(),e2.getSalary()+ e2.getBonus()))
.map(e ->new Topperformer(e.getName(),e.getSalary()+e.getBonus())).get();
System.out.println(t);
//2.统计平均工资,去掉最高工资和最低工资
one.stream().sorted((e1,e2)->Double.compare(e1.getSalary()+e1.getBonus(),e2.getSalary()+ e2.getBonus()))
.skip(1).limit(one.size()-2).forEach(e->{
allMoney += (e.getSalary()+e.getBonus());
});
System.out.println("开发一部的平均工资是:" + allMoney / (one.size() - 2));
//3.合并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.getBonus(),e2.getSalary()+ e2.getBonus()))
.skip(1).limit(one.size()+ two.size()-2).forEach(e->{
allMoney2 += (e.getSalary()+e.getBonus());
});
//BigDecimal
BigDecimal a = BigDecimal.valueOf(allMoney2);
BigDecimal b = BigDecimal.valueOf(one.size()+ two.size() - 2);
System.out.println("开发部门的平均工资是:" + a.divide(b,2,RoundingMode.HALF_UP));
}
}
收集Stream流
收集Stream流的含义:就是把Stream流操作后的结果数据转回到集合或者数组中去。
Stream流:方便操作集合/数组的手段。
集合/数组:才是开发中的目的。

/**
目标:收集Stream流的数据到集合和数组中去
*/
public class StreamDemo05 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("张无忌");
list.add("周芷若");
list.add("赵敏");
list.add("张强");
list.add("张三丰");
list.add("张三丰");
//下面是自己写的方法
// List<String> zhanglist1 = new ArrayList<>();
// list.forEach(l->{
// if(l.startsWith("张")){
// zhanglist1.add(l);
// }
// });
// System.out.println(zhanglist1);
Stream<String> s1 = list.stream().filter(s->s.startsWith("张"));
List<String> zhanglist = s1.collect(Collectors.toList());
zhanglist.add("Java1");
System.out.println(zhanglist);
// //新版本支持的方法
// List<String> list1 = s1.toList();//但得到的是不可变集合
// //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(new IntFunction<String[]>() {
// @Override
// public String[] apply(int value) {
// return new String[value];
// }
// });
//简化之后
//String[] arrs = s3.toArray(s->new String[s]);
//进一步简化
//String[] arrs = s3.toArray(String[]::new);
System.out.println("Arrays数组内容:" + Arrays.toString(arrs));
}
}
异常处理
异常概述、体系
异常是程序在“编译”或者“执行”的过程中可能出现的问题,注意:语法错误不算在异常体系中。
比如:数组索引越界、空指针异常、 日期格式化异常,等…
异常一旦出现了,如果没有提前处理,程序就会退出JVM虚拟机而终止.
研究异常并且避免异常,然后提前处理异常,体现的是程序的安全, 健壮性。

异常分为几类?
编译时异常、运行时异常。
编译时异常:没有继承RuntimeExcpetion的异常,编译阶段就会出错。
运行时异常:继承自RuntimeException的异常或其子类,编译阶段不报错,运行可能报错

常见运行时异常

public class ExceptionDemo {
public static void main(String[] args) {
/** 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;
// System.out.println(s);//运行出错,程序终止
/** 5.数学操作异常:ArithmeticException。 */
// int c = 10/0;//运行出错,程序终止
/** 6.数字转换异常: NumberFormatException。 */
// String number = "23";//写这个不报错
String number = "23aabbcc";
// Integer it = Integer.valueOf(number);//运行出错,程序终止
// System.out.println(it + 1);
}
}
常见编译时异常

异常的默认处理流程

编译时异常的处理机制

方式1-throws

/**
目标:编译时异常的处理方式一。
编译时异常:编译阶段就会报错,一定需要程序员处理的,否则代码无法通过!!
抛出异常格式:
方法 throws 异常1 , 异常2 , ..{
}
建议抛出异常的方式:代表可以抛出一切异常,
方法 throws Exception{
}
方式一:
在出现编译时异常的地方层层把异常抛出去给调用者,调用者最终抛出给JVM虚拟机。
JVM虚拟机输出异常信息,直接干掉程序,这种方式与默认方式是一样的。
虽然可以解决代码编译时的错误,但是一旦运行时真的出现异常,程序还是会立即死亡!
这种方式并不好!
小结:
方式一出现异常层层跑出给虚拟机,最终程序如果真的出现异常,程序还是立即死亡!这种方式不好!
*/
public class ExceptionDemo01 {
// public static void main(String[] args) throws ParseException, FileNotFoundException {
// System.out.println("程序开始。。。。。");
// parseTime("2011-11-11 11:11:11");
// System.out.println("程序结束。。。。。");
// }
//
// public static void parseTime(String date) throws ParseException, FileNotFoundException {
// SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM-dd HH:mm:ss");
// Date d = sdf.parse(date);
// System.out.println(d);
//
// InputStream is = new FileInputStream("E://meinv.jpg");
// }
public static void main(String[] args) throws Exception {
System.out.println("程序开始。。。。。");
parseTime("2011-11-11 11:11:11");
System.out.println("程序结束。。。。。");
}
public static void parseTime(String date) throws Exception {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date d = sdf.parse(date);
System.out.println(d);
InputStream is = new FileInputStream("E://meinv.jpg");
}
}
方式2 try…catch…

public class ExceptionDemo02 {
public static void main(String[] args) {
System.out.println("程序开始。。。。。");
parseTime("2011-11-11 11:11:11");
System.out.println("程序结束。。。。。");
}
// 第一种方式:分开判断异常类型(形式一)
// public static void parseTime(String date) {
// try {
// SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// Date d = sdf.parse(date);
// System.out.println(d);
// } catch (ParseException e) {
// //解析出现问题
// System.out.println("出现了解析时间异常哦,走点心!!");
// }
//
// try {
// InputStream is = new FileInputStream("E://meinv.jpg");
// } catch (FileNotFoundException e) {
// System.out.println("目录下无该文件!");
// }
// }
//形式二:
// public static void parseTime(String date) {
// try {
// SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// Date d = sdf.parse(date);
// System.out.println(d);
// InputStream is = new FileInputStream("E://meinv.jpg");
// } catch (ParseException e) {
// e.printStackTrace();//打印异常栈信息
// } catch (FileNotFoundException e) {
// e.printStackTrace();
// }
// }
//形式三:
public static void parseTime(String date) {
try {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date d = sdf.parse(date);
System.out.println(d);
InputStream is = new FileInputStream("E://meinv.jpg");
} catch (Exception e) {//此处Exception可以改成ParseException|FileNotFoundException
e.printStackTrace();//打印异常栈信息
}
}
}
方式3 前两者结合

public class ExceptionDemo03 {
public static void main(String[] args) {
System.out.println("程序开始。。。。。");
try {
parseTime("2011-11-11 11:11:11");
System.out.println("功能操作成功~~~");
} catch (Exception e) {
e.printStackTrace();
System.out.println("功能操作失败~~~");
}
System.out.println("程序结束。。。。。");
}
public static void parseTime(String date) throws Exception {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date d = sdf.parse(date);
System.out.println(d);
InputStream is = new FileInputStream("E://meinv.jpg");
}
}
运行时异常的处理机制

/**
* 目标:运行时异常的处理机制
* 可以不处理,编译阶段又不报错
* 按照理论规则:建议还是处理,只需要最外层捕获处理即可
*/
public class Test {
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 class Test2 {
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);
//判断价格是否大于0
if(price > 0){
System.out.println("定价:" + price);
break;
}else{
System.out.println("价格必须是正数~~");
}
} catch (Exception e) {
System.out.println("说明用户输入的数据有毛病,请您输入合法的数值,建议为正数~~");
}
}
}
}
自定义异常
原因和好处
Java无法为这个世界上全部的问题提供异常类。
如果企业想通过异常的方式来管理自己的某个业务问题,就需要自定义异常类了
自定义异常的好处:
可以使用异常的机制管理业务问题,如提醒程序员注意。
同时一旦出现bug,可以用异常的形式清晰的指出出错的地方。
分类
1、自定义编译时异常
定义一个异常类继承Exception.
重写构造器。
在出现异常的地方用throw new 自定义对象抛出,
作用:编译时异常是编译阶段就报错,提醒更加强烈,一定需要处理!!
/**
* 自定义的编译时异常
* 1.继承Exception
* 2.重写构造器
*
*/
public class ItheimaAgeIllegalException extends Exception{
public ItheimaAgeIllegalException() {
}
public ItheimaAgeIllegalException(String message) {
super(message);
}
}
2、自定义运行时异常
定义一个异常类继承RuntimeException.
重写构造器。
在出现异常的地方用throw new 自定义对象抛出!
作用:提醒不强烈,编译阶段不报错!!运行时才可能出现!!
/**
* 自定义的编译时异常
* 1.继承RuntimeException
* 2.重写构造器
*
*/
public class ItheimaAgeIllegalRuntimeException extends RuntimeException{
public ItheimaAgeIllegalRuntimeException() {
}
public ItheimaAgeIllegalRuntimeException(String message) {
super(message);
}
}
/**
目标:自定义异常(了解)
引入:Java已经为开发中可能出现的异常都设计了一个类来代表.
但是实际开发中,异常可能有无数种情况,Java无法为
这个世界上所有的异常都定义一个代表类。
假如一个企业如果想为自己认为的某种业务问题定义成一个异常
就需要自己来自定义异常类.
需求:认为年龄小于0岁,大于200岁就是一个异常。
自定义异常:
自定义编译时异常.
a.定义一个异常类继承Exception.
b.重写构造器。
c.在出现异常的地方用throw new 自定义对象抛出!
编译时异常是编译阶段就报错,提醒更加强烈,一定需要处理!!
自定义运行时异常.
a.定义一个异常类继承RuntimeException.
b.重写构造器。
c.在出现异常的地方用throw new 自定义对象抛出!
提醒不强烈,编译阶段不报错!!运行时才可能出现!!
*/
public class ExceptionDemo {
public static void main(String[] args) {
// try {
// checkAge2(-34);
// } catch (Exception e) {
// e.printStackTrace();
// }
try {
checkAge2(-23);
} catch (ItheimaAgeIllegalRuntimeException e) {
e.printStackTrace();
}
}
// public static void checkAge(int age) throws ItheimaAgeIllegalException {
// if(age < 0 || age > 200){
// //抛出去一个异常对象给调用者
// //throw:在方法内部直接创建一个异常对象,并从此抛出
// //throws:用在方法声明上,抛出方法内部的异常
// throw new ItheimaAgeIllegalException(age + " is llegal!");
// }else{
// System.out.println("年龄合法:推荐商品给其购买~~");
// }
// }
//运行时异常
public static void checkAge2(int age) throws ItheimaAgeIllegalRuntimeException {
if(age < 0 || age > 200){
//抛出去一个异常对象给调用者
//throw:在方法内部直接创建一个异常对象,并从此抛出
//throws:用在方法声明上,抛出方法内部的异常
throw new ItheimaAgeIllegalRuntimeException(age + " is llegal!");
}else{
System.out.println("年龄合法:推荐商品给其购买~~");
}
}
}
本文介绍了Java中如何创建和使用不可变集合,包括List、Set和Map的of方法。接着讲解了Stream流的基本概念、获取、中间方法和终结方法,展示了如何简化集合操作。此外,文章还详细讨论了异常处理机制,包括编译时异常和运行时异常的处理方式,以及自定义异常的原因和好处。
236

被折叠的 条评论
为什么被折叠?



