引例
需求:按照下面要求完成集合的创建和遍历
创建一个集合,存储多个字符串元素
1. 把所有以“曹”开头的元素存储到新集合中
2. 把曹开头,长度为3的元素存储到新集合中
List<String> list = List.of("曹操", "曹孟德", "曹子恒", "曹子建", "司马懿", "司马师", "司马昭", "曹丕");
// 1. 把所有以“曹”开头的元素存储到新集合中
List<String> list1 = new ArrayList<>();
for (String s : list) {
if(s.startsWith("曹")){
list1.add(s);
}
}
System.out.println(list1);
// 2. 把曹开头,长度为3的元素存储到新集合中
List<String> list2 = new ArrayList<>();
for (String s : list1) {
if(s.length() == 3){
list2.add(s);
}
}
System.out.println(list2);
输出结果:
[曹操, 曹孟德, 曹子恒, 曹子建, 曹丕]
[曹孟德, 曹子恒, 曹子建]
上面太麻烦了
用Stream流只需要一行代码:
list.stream().filter(name -> name.startsWith("曹")).filter(name -> name.length() == 3).forEach(name -> System.out.println(name));
先得到一条stream流,并把数据放上去
获取方式 | 方法名 | 说明 |
---|---|---|
单列集合 | default Stream |
Collection中的默认方法 |
双列集合 | 无 | 无法直接使用Stream流,需要通过keySet()或entrySet()转换成单列集合 |
数组 | public static |
Arrays工具类中的静态方法 |
一堆零散数据 | public static |
Stream接口中的静态方法 |
利用stream流中的API进行各种操作:(过滤,转换,统计,打印等等)
使用中间方法对流水线上的数据进行操作
使用终结方法对流水线上的数据进行操作
//单列集合Stream流
List<String> list = List.of("aa", "bb", "cc", "dd");
list.stream().forEach(s -> System.out.println(s));
//双列集合Stream流
Map<String, String> map = Map.of("aa", "11", "bb", "22", "cc", "33");
map.entrySet().stream().forEach(m -> System.out.println(m));
//数组Stream流
int[] arr = {1,2,3,4,5};
Arrays.stream(arr).forEach(a -> System.out.println(a));
//零散数据Stream流
Stream.of(11,12,13,14,15).forEach(s -> System.out.println(s));
注意:Stream接口中静态方法of的细节:方法的形参是一个可变参数,可以传递一堆零散的数据,也可以传递数组,但数组必须是引用数据类型的,如果传递基本数据类型,会把整个数组当做一个元素传到Steam流中
int[] arr1 = {1,2,3,4,5};
String[] arr2 = {"a", "b", "c", "d", "e"};
Stream.of(arr1).forEach(s -> System.out.println(s)); //输出[I@7699a589
Stream.of(arr2).forEach(s -> System.out.println(s)); //输出a b c d e
名称 | 说明 |
---|---|
void forEach(Consumer action) | 遍历 |
long count() | 统计 |
toArray() | 收集流中的数据,放到数组中 |
collect(Collector collrctor) | 收集流中的数据,放到集合中 |
返回值是void,因此是终结方法,不能再在后面调用函数
Consumer的泛型:表示流中数据的类型
List<Integer> list = List.of(1,2,3,4,5);
list.stream().forEach(new Consumer<Integer>() {
@Override
public void accept(Integer integer) {
System.out.println(integer);
}
});
返回值是long类型的整数,因此是终结方法
long c = list.stream().count();
System.out.println(c);
System.out.println("---------------------");
Object[] arr1 = list.stream().toArray(); System.out.println(Arrays.toString(arr1));
//
//IntFunction的泛型:具体类型的数组
//apply的形态:流中数据的个数,要和数组长度保持一致
//apply函数返回值:具体类型的数组
//toArray方法的参数的作用:负责创建一个指定类型的数组
//方法底层会依次得到流里面每一个数据,并把数据放到数组中
//方法返回值:装着流里面所有数组的数组
String[] arr = list.stream().toArray(new IntFunction<String[]>() {
@Override
public String[] apply(int value) {
return new String[0];
}
});
//简化成Lamada表达式形式:
list.stream().toArray(value -> new String[value]);
有参:返回值是任意类型
toArray方法的参数的作用:负责创建一个指定类型的数组
方法底层会依次得到流里面每一个数据,并把数据放到数组中
方法返回值:装着流里面所有数组的数组
//IntFunction的泛型:具体类型的数组
//apply的形态:流中数据的个数,要和数组长度保持一致
//apply函数返回值:具体类型的数组
//toArray方法的参数的作用:负责创建一个指定类型的数组
//方法底层会依次得到流里面每一个数据,并把数据放到数组中
//方法返回值:装着流里面所有数组的数组
String[] arr = list.stream().toArray(new IntFunction<String[]>() {
@Override
public String[] apply(int value) {
return new String[0];
}
});
//简化成Lamada表达式形式:
list.stream().toArray(value -> new String[value]);
/*
collect(Collector collector) 收集流中的数据,放到集合中 (List Set Map)
注意点:
如果我们要收集到Map集合当中,键不能重复,否则会报错
*/
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "张无忌-男-15", "周芷若-女-14", "赵敏-女-13", "张强-男-20",
"张三丰-男-100", "张翠山-男-40", "张良-男-35", "王二麻子-男-37", "谢广坤-男-41");
//收集List集合当中
//需求:
//我要把所有的男性收集起来
List<String> newList1 = list.stream()
.filter(s -> "男".equals(s.split("-")[1])) //把"男"放前面防止list为null时调用equals报错
.collect(Collectors.toList());
//System.out.println(newList1);
//收集Set集合当中
//需求:
//我要把所有的男性收集起来
Set<String> newList2 = list.stream().filter(s -> "男".equals(s.split("-")[1]))
.collect(Collectors.toSet());
//System.out.println(newList2);
//收集Map集合当中
//谁作为键,谁作为值.
//我要把所有的男性收集起来
//键:姓名。 值:年龄
Map<String, Integer> map = list.stream()
.filter(s -> "男".equals(s.split("-")[1]))
/*
* toMap : 参数一表示键的生成规则
* 参数二表示值的生成规则
*
* 参数一:
* Function泛型一:表示流中每一个数据的类型
* 泛型二:表示Map集合中键的数据类型
*
* 方法apply形参:依次表示流里面的每一个数据
* 方法体:生成键的代码
* 返回值:已经生成的键
*
*
* 参数二:
* Function泛型一:表示流中每一个数据的类型
* 泛型二:表示Map集合中值的数据类型
*
* 方法apply形参:依次表示流里面的每一个数据
* 方法体:生成值的代码
* 返回值:已经生成的值
*
* */
.collect(Collectors.toMap(new Function<String, String>() {
@Override
public String apply(String s) {
//张无忌-男-15
return s.split("-")[0];
}
},
new Function<String, Integer>() {
@Override
public Integer apply(String s) {
return Integer.parseInt(s.split("-")[2]);
}
}));
Map<String, Integer> map2 = list.stream()
.filter(s -> "男".equals(s.split("-")[1]))
.collect(Collectors.toMap(
s -> s.split("-")[0],
s -> Integer.parseInt(s.split("-")[2])));
System.out.println(map2);
Stream流的作用
结合了Lambda表达式,简化集合、数组的操作
Stream的使用步骤
获取Stream流对象
使用中间方法处理数据
使用终结方法处理数据
如何获取Stream流对象
单列集合:Collection中的默认方法stream
双列集合:不能直接获取
数组:Arrays工具类型中的静态方法stream
一堆零散的数据:Stream接口中的静态方法of
常见方法
中间方法:filter,limit,skip,distinct,concat,
中间方法 | 用法 | 作用 |
---|---|---|
limit | limit(n) | 保留流中的前n个人 |
skip | skip(n) | 跳过流中的前n个人 |
concat | Stream.concat(stream1, stream2) | 将两个流拼接在一起 |
map | 比较复杂,看练习3 | 转换流的类型 |
终结方法:forEach,count,coIlect
定义一个集合,并添加一些整数 1,2,3,4,5,6,7,8,9,10
过滤奇数,只留下偶数。
并将结果保存起来
//1. 定义一个集合
ArrayList<Integer> list = new ArrayList<>();
//2.添加一些整数
Collections.addAll(list, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
//3.过滤奇数,只留下偶数
//进行判断,如果是偶数,返回true 保留
List<Integer> newList = list.stream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toList());
//4.打印集合
创建一个ArrayList集合,并添加以下字符串,字符串中前面是姓名,后面是年龄
“zhangsan,23”
“lisi,24”
“wangwu,25”
保留年龄大于等于24岁的人,并将结果收集到Map集合中,姓名为键,年龄为值
//1.创建一个ArrayList集合
ArrayList<String> list = new ArrayList<>();
//2.添加以下字符串
list.add("zhangsan,23");
list.add("lisi,24");
list.add("wangwu,25");
//3.保留年龄大于等于24岁的人
/* list.stream()
.filter(s -> Integer.parseInt(s.split(",")[1]) >= 24)
.collect(Collectors.toMap(new Function() {
@Override
public String apply(String s) {
return s.split(",")[0];
}
}, new Function() {
@Override
public Integer apply(String s) {
return Integer.parseInt(s.split(",")[1]);
}
}));*/
Map<String, Integer> map = list.stream()
.filter(s -> Integer.parseInt(s.split(",")[1]) >= 24)
.collect(Collectors.toMap(
s -> s.split(",")[0],
s -> Integer.parseInt(s.split(",")[1])));
System.out.println(map);
现在有两个ArrayList集合,分别存储6名男演员的名字和年龄以及6名女演员的名字和年龄。
姓名和年龄中间用逗号隔开。
比如:张三,23
要求完成如下的操作:
1,男演员只要名字为3个字的前两人
2,女演员只要姓杨的,并且不要第一个
3,把过滤后的男演员姓名和女演员姓名合并到一起
4,将上一步的演员信息封装成Actor对象。
5,将所有的演员对象都保存到List集合中。
备注:演员类Actor,属性有:name,age
男演员: "蔡坤坤,24" , "叶齁咸,23", "刘不甜,22", "吴签,24", "谷嘉,30", "肖梁梁,27"
女演员: "赵小颖,35" , "杨颖,36", "高元元,43", "张天天,31", "刘诗,35", "杨小幂,33"
//1.创建两个ArrayList集合
ArrayList<String> manList = new ArrayList<>();
ArrayList<String> womenList = new ArrayList<>();
//2.添加数据
Collections.addAll(manList, "蔡坤坤,24", "叶齁咸,23", "刘不甜,22", "吴签,24", "谷嘉,30", "肖梁梁,27");
Collections.addAll(womenList, "赵小颖,35", "杨颖,36", "高元元,43", "张天天,31", "刘诗,35", "杨小幂,33");
//3.男演员只要名字为3个字的前两人
Stream<String> stream1 = manList.stream()
.filter(s -> s.split(",")[0].length() == 3)
.limit(2);
//4.女演员只要姓杨的,并且不要第一个
Stream<String> stream2 = womenList.stream()
.filter(s -> s.split(",")[0].startsWith("杨"))
.skip(1);
//5.把过滤后的男演员姓名和女演员姓名合并到一起
//演员信息封装成Actor对象。
//String -> Actor对象 (类型转换)
/* Stream.concat(stream1,stream2).map(new Function() {
@Override
public Actor apply(String s) {
//"赵小颖,35"
String name = s.split(",")[0];
int age = Integer.parseInt(s.split(",")[1]);
return new Actor(name,age);
}
}).forEach(s-> System.out.println(s));*/
List<Actor> list = Stream.concat(stream1, stream2)
.map(s -> new Actor(s.split(",")[0], Integer.parseInt(s.split(",")[1])))
.collect(Collectors.toList());
System.out.println(list);
public class Actor {
private String name;
private int age;
public Actor() {
}
public Actor(String name, int age) {
this.name = name;
this.age = age;
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
* @return age
*/
public int getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(int age) {
this.age = age;
}
public String toString() {
return "Actor{name = " + name + ", age = " + age + "}";
}
}
含义:把已经有的方法拿过来用,当做函数式接口中抽象方法的方法体。
=>
方法引用格式:类名::函数名 例如Arrays.sort(arr, FunctionDemo1::subtraction);(见如下代码)
::是方法引用符
public class FunctionDemo1 {
public static void main(String[] args) {
//需求:创建一个数组,进行倒序排列
Integer[] arr = {3, 5, 4, 1, 6, 2};
//匿名内部类
Arrays.sort(arr, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
});
//lambda表达式
//因为第二个参数的类型Comparator是一个函数式接口
Arrays.sort(arr, (Integer o1, Integer o2)->{
return o2 - o1;
});
//lambda表达式简化格式
Arrays.sort(arr, (o1, o2)->o2 - o1 );
//方法引用
//1.引用处需要是函数式接口
//2.被引用的方法需要已经存在
//3.被引用方法的形参和返回值需要跟抽象方法的形参和返回值保持一致
//4.被引用方法的功能需要满足当前的要求
//表示引用FunctionDemo1类里面的subtraction方法
//把这个方法当做抽象方法的方法体
Arrays.sort(arr, FunctionDemo1::subtraction);
System.out.println(Arrays.toString(arr));
}
//可以是Java已经写好的,也可以是一些第三方的工具类
public static int subtraction(int num1, int num2) {
return num2 - num1;
}
}
方法引用的分类
引用静态方法
引用成员方法
引用其他类的成员方法
引厍本类的成员方法
引用父类的成员方法
引用构造方法
其他调用方式
使用类名引用成员方法
引用数组的构造方法
引用格式:类名::静态方法
/*
方法引用(引用静态方法)
格式
类::方法名
需求:
集合中有以下数字,要求把他们都变成int类型
"1","2","3","4","5"
*/
//1.创建集合并添加元素
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list,"1","2","3","4","5");
//2.把他们都变成int类型
/* list.stream().map(new Function() {
@Override
public Integer apply(String s) {
int i = Integer.parseInt(s);
return i;
}
}).forEach(s -> System.out.println(s));*/
//1.方法需要已经存在
//2.方法的形参和返回值需要跟抽象方法的形参和返回值保持一致
//3.方法的功能需要把形参的字符串转换成整数
list.stream()
.map(Integer::parseInt)
.forEach(s-> System.out.println(s));
注:只有函数式接口才能使用引用方法
格式:对象::成员方法
/*
方法引用(引用成员方法)
格式
其他类:其他类对象::方法名
本类:this::方法名(引用处不能是静态方法)
父类:super::方法名(引用处不能是静态方法)
需求:
集合中有一些名字,按照要求过滤数据
数据:"张无忌","周芷若","赵敏","张强","张三丰"
要求:只要以张开头,而且名字是3个字的
*/
//1.创建集合
ArrayList<String> list = new ArrayList<>();
//2.添加数据
Collections.addAll(list,"张无忌","周芷若","赵敏","张强","张三丰");
//3.过滤数据(只要以张开头,而且名字是3个字的)
//以前的方法
list.stream().filter(s->s.startsWith("张")).filter(s->s.length() == 3).forEach(s-> System.out.println(s));
//引用方法
//filter的Predicate是函数式接口,可以使用引用方法
//可以先写如下代码,以便观察如何写引用方法
list.stream().filter(new Predicate<String>() {
@Override
public boolean test(String s) {
return s.startsWith("张") && s.length() == 3;
}
}).forEach(s-> System.out.println(s));
其他类
//另外创建一个类
package com.itheima.a01myfunction;
public class StringOperation {
public boolean stringJudge(String s){
return s.startsWith("张") && s.length() == 3;
}
}
//修改为引用成员方法形式
StringOperation so = new StringOperation();
list.stream().filter(so::stringJudge)
.forEach(s-> System.out.println(s));
本类
public class FunctionDemo3 {
public static void main(String[] args) {
/*
方法引用(引用成员方法)
格式
其他类:其他类对象::方法名
本类:this::方法名(引用处不能是静态方法)
父类:super::方法名(引用处不能是静态方法)
需求:
集合中有一些名字,按照要求过滤数据
数据:"张无忌","周芷若","赵敏","张强","张三丰"
要求:只要以张开头,而且名字是3个字的
*/
//1.创建集合
ArrayList<String> list = new ArrayList<>();
//2.添加数据
Collections.addAll(list,"张无忌","周芷若","赵敏","张强","张三丰");
//3.过滤数据(只要以张开头,而且名字是3个字的)
//list.stream().filter(s->s.startsWith("张")).filter(s->s.length() == 3).forEach(s-> System.out.println(s));
list.stream().filter(new Predicate<String>() {
@Override
public boolean test(String s) {
return s.startsWith("张") && s.length() == 3;
}
}).forEach(s-> System.out.println(s));
//**静态方法中是没有this的,所以this::stringJudge会报错,需改为new FunctionDemo3()::stringJudge
list.stream().filter(new FunctionDemo3()::stringJudge)
.forEach(s-> System.out.println(s));
}
public boolean stringJudge(String s){
return s.startsWith("张") && s.length() == 3;
}
}
目的:创建对象
格式:类名::new (例如:Student::new)
public class FunctionDemo4 {
public static void main(String[] args) {
/*
方法引用(引用构造方法)
格式
类名::new
目的:
创建这个类的对象
需求:
集合里面存储姓名和年龄,要求封装成Student对象并收集到List集合中
方法引用的规则:
1.需要有函数式接口
2.被引用的方法必须已经存在
3.被引用方法的形参和返回值,需要跟抽象方法的形参返回值保持一致
4.被引用方法的功能需要满足当前的需求
*/
//1.创建集合对象
ArrayList<String> list = new ArrayList<>();
//2.添加数据
Collections.addAll(list, "张无忌,15", "周芷若,14", "赵敏,13", "张强,20", "张三丰,100", "张翠山,40", "张良,35", "王二麻子,37", "谢广坤,41");
//3.封装成Student对象并收集到List集合中
//String --> Student
/* List newList = list.stream().map(new Function() {
@Override
public Student apply(String s) {
String[] arr = s.split(",");
String name = arr[0];
int age = Integer.parseInt(arr[1]);
return new Student(name, age);
}
}).collect(Collectors.toList());
System.out.println(newList);*/
List<Student> newList2 = list.stream().map(Student::new).collect(Collectors.toList());
System.out.println(newList2);
}
}
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String str) {
String[] arr = str.split(",");
this.name = arr[0];
this.age = Integer.parseInt(arr[1]);
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
* @return age
*/
public int getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(int age) {
this.age = age;
}
public String toString() {
return "Student{name = " + name + ", age = " + age + "}";
}
}
格式:类名::成员方法
范例:String::subString
方法引用的规则:
1.需要有函数式接口
2.被引用的方法必须已经存在
3.被引用方法的形参,需要跟抽象方法的第二个形参到最后一个形参保持一致,返回值需要保持一致。
(所以下图中方法参数不一样也没关系,因为类名引用成员方法的参数规则比较特殊)
4.被引用方法的功能需要满足当前的需求
抽象方法形参的详解:
第一个参数:表示被引用方法的调用者,决定了可以引用哪些类中的方法
在Stream流当中,第一个参数一般都表示流里面的每一个数据。
假设流里面的数据是字符串,那么使用这种方式进行方法引用,只能引用String这个类中的方法
第二个参数到最后一个参数:跟被引用方法的形参保持一致,如果没有第二个参数,说明被引用的方法需要是无参的成员方法
局限性:
不能引用所有类中的成员方法。
是跟抽象方法的第一个参数有关,这个参数是什么类型的,那么就只能引用这个类中的方法。
(比如代码中抽象方法的第一个参数是String类型的,那么map中就只能引用String的方法String::toUpperCase)
/*
方法引用(类名引用成员方法)
格式
类名::成员方法
需求:
集合里面一些字符串,要求变成大写后进行输出
*/
//1.创建集合对象
ArrayList<String> list = new ArrayList<>();
//2.添加数据
Collections.addAll(list, "aaa", "bbb", "ccc", "ddd");
//3.变成大写后进行输出
//map(String::toUpperCase)
//拿着流里面的每一个数据,去调用String类中的toUpperCase方法,方法的返回值就是转换之后的结果。
list.stream().map(String::toUpperCase).forEach(s -> System.out.println(s));
//String --> String
/* list.stream().map(new Function() {
@Override
public String apply(String s) {
return s.toUpperCase();
}
}).forEach(s -> System.out.println(s));*/
格式:数据类型[]::new
范例:int[]::new
方法引用(数组的构造方法)
格式
数据类型[]::new
目的:
创建一个指定类型的数组
需求:
集合中存储一些整数,收集到数组当中
细节:
数组的类型,需要跟流中数据的类型保持一致。
//1.创建集合并添加元素
ArrayList<Integer> list = new ArrayList<>();
Collections.addAll(list, 1, 2, 3, 4, 5);
//2.收集到数组当中
Integer[] arr2 = list.stream().toArray(Integer[]::new);
//3.打印
System.out.println(Arrays.toString(arr2));
/*Integer[] arr = list.stream().toArray(new IntFunction() {
@Override
public Integer[] apply(int value) {
return new Integer[value];
}
});*/
练习1
需求:
集合中存储一些字符串的数据,比如:张三,23。
收集到Student类型的数组当中
//1.创建集合并添加元素
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "张无忌,15", "周芷若,14", "赵敏,13", "张强,20", "张三丰,100", "张翠山,40", "张良,35", "王二麻子,37", "谢广坤,41");
//2.先把字符串变成Student对象,然后再把Student对象收集起来
Student[] arr = list.stream().map(Student::new).toArray(Student[]::new);
//打印数组
System.out.println(Arrays.toString(arr));
练习2
/*
* 需求:
* 创建集合添加学生对象
* 学生对象属性:name,age
* 要求:
* 获取姓名并放到数组当中
* 使用方法引用完成
* */
//1.创建集合
ArrayList<Student> list = new ArrayList<>();
//2.添加元素
list.add(new Student("zhangsan",23));
list.add(new Student("lisi",24));
list.add(new Student("wangwu",25));
//3.获取姓名并放到数组当中
String[] arr = list.stream().map(Student::getName).toArray(String[]::new);
/* String[] arr = list.stream().map(new Function() {
@Override
public String apply(Student student) {
return student.getName();
}
}).toArray(String[]::new);*/
System.out.println(Arrays.toString(arr));
技巧:
1.现在有没有一个方法符合我当前的需求
2.如果有这样的方法,这个方法是否满足引用的规则
静态 类名::方法名
成员方法:如果流中数据类型与符合需求的方法属于同一类,用类名::方法名
构造方法 类名::new
异常是什么
程序中可能出现的问题
异常体系的最上层父类是谁?异常分为几类?
父类:Exception。
异常分为两类.编译时异常、运行时异常
编译时异常和运行时异常的区别?
编译时异常:没有继RuntimeException的异常,直接继承于Exception
编译阶段就会错误提示。(必须要手动修改代码,否则代码报错)
除了RuntimeException以外其余都是编译时异常
运行时异常:RuntimeException本身和子类。
编译阶段没有错误提示,运行时出现的
Error:代表的系统级别错误(属于严重问题)
系统一旦出现问题,sun公司会把这些错误封装成ror对象。
Error是给sun公司自己用的,不是给我们程序员用的。
因此我们开发人员不用管它。
Exception:叫做异常,代表程序可能出现的问题。
我们通常会用Exception以及他的子类来封装程序出现的问题。
运行时异常:RuntimeException及其子类,编译阶段不会出现异常提醒。
运行时出现的异常(如.数组索引越界异常)
一般是由于参数传递错误带来的问题
编译时异常:编译阶段就会出现异常提醒的(如:日期解析异常)
作用一:异常是用来查询bug的关键参考信息
作用二:异常可以作为方法内部的一种特殊返回值,以便通知调用者底层的执行情况
主函数:
//1.创建学生对象
Student s1 = new Student();
//年龄:(同学) 18~40岁
s1.setAge(50);//就知道了50赋值失败
学生对象的setAge函数里放了判断年龄的异常:
public void setAge(int age) {
if(age < 18 || age > 40){
//System.out.println("年龄超出范围");
throw new RuntimeException();
}else{
this.age = age;
}
}
运行时控制台的显示:
这样抛出异常就可以清楚地知道错误发生在哪
- 把异常的名称,异常原因及异常出现的位置等信息输出在了控制台
- 程序停止执行,下面的代码不会再执行了
try{
可能出现异常的代码
} catch(异常类名,变量名){
异常的处理代码
}
目的:当代码出现异常时,可以让程序继续执行
public static void main(String[] args) {
System.out.println("臣本布衣躬耕于南阳");
try{
//可能出现异常的代码;
System.out.println(2/0);//算术异常 ArithmeticException
//此处出现了异常,程序就会在这里创建一个ArithmeticException对象
//new ArithmeticException();
//拿着这个对象到catch的小括号中对比,看括号中的变量是否可以接收这个对象
//如果能被接收,就表示该异常就被捕获(抓住),执行catch里面对应的代码
//当catch里面所有的代码执行完毕,继续执行try...catch体系下面的其他代码
}catch (ArithmeticException a){
//如果出现了ArrayIndexOutOfBoundsException异常,我该如何处理
System.out.println("算术异常");
}
System.out.println("苟全性命于乱世,不求闻达于诸侯");
System.out.println("先帝不以臣卑鄙,猥自枉屈");
}
此时下面两个打印语句能被执行
public static void main(String[] args) {
/*
自己处理(捕获异常)灵魂四问:
灵魂一问:如果try中没有遇到问题,怎么执行?
会把try里面所有的代码全部执行完毕,不会执行catch里面的代码
注意:
只有当出现了异常才会执行catch里面的代码
*/
int[] arr = {1, 2, 3, 4, 5, 6};
try{
System.out.println(arr[0]);//1
}catch(ArrayIndexOutOfBoundsException e){
System.out.println("索引越界了");
}
System.out.println("看看我执行了吗?");//看看我执行了吗?
}
public static void main(String[] args) {
/*
自己处理(捕获异常)灵魂四问:
灵魂二问:如果try中可能会遇到多个问题,怎么执行?
会写多个catch与之对应
细节:
如果我们要捕获多个异常,这些异常中如果存在父子关系的话,那么父类一定要写在下面
了解性:
在JDK7之后,我们可以在catch中同时捕获多个异常,中间用|进行隔开
表示如果出现了A异常或者B异常的话,采取同一种处理方案
*/
//JDK7
int[] arr = {1, 2, 3, 4, 5, 6};
try{
System.out.println(arr[10]);//ArrayIndexOutOfBoundsException
System.out.println(2/0);//ArithmeticException
String s = null;
System.out.println(s.equals("abc"));
}catch(ArrayIndexOutOfBoundsException | ArithmeticException e){
System.out.println("索引越界了");
}catch(NullPointerException e){
System.out.println("空指针异常");
}catch (Exception e){ //父类异常需要放在子类的后面,否则会报错
System.out.println("Exception");
}
System.out.println("看看我执行了吗?");
}
如果父类异常放在上面,父类指针可以指向子类对象,所有的异常就都被父类Exception接收了,永远执行不到下面的catch了
public static void main(String[] args) {
/*
自己处理(捕获异常)灵魂三问:
如果try中遇到的问题没有被捕获,怎么执行?
相当于try...catch的代码白写了,最终还是会交给虚拟机进行处理。
*/
int[] arr = {1, 2, 3, 4, 5, 6};
try{
System.out.println(arr[10]);//new ArrayIndexOutOfBoundsException();
}catch(NullPointerException e){
System.out.println("空指针异常");
}
System.out.println("看看我执行了吗?");
}
public static void main(String[] args) {
/*
自己处理(捕获异常)灵魂四问:
如果try中遇到了问题,那么try下面的其他代码还会执行吗?
下面的代码就不会执行了,直接跳转到对应的catch当中,执行catch里面的语句体
但是如果没有对应catch与之匹配,那么还是会交给虚拟机进行处理
*/
int[] arr = {1, 2, 3, 4, 5, 6};
try{
System.out.println(arr[10]);
System.out.println("看看我执行了吗?... try");
}catch(ArrayIndexOutOfBoundsException e){
System.out.println("索引越界了");
}
System.out.println("看看我执行了吗?... 其他代码");
}
上述catch中直接打印语句非常不专业,不应该写打印语句,一般用异常中常见方法
public static void main(String[] args) {
/*
public String getMessage() 返回此 throwable 的详细消息字符串
public String toString() 返回此可抛出的简短描述
public void printStackTrace() 在底层是利用System.err.println进行输出
把异常的错误信息以红色字体输出在控制台
细节:仅仅是打印信息,不会停止程序运行
*/
int[] arr = {1, 2, 3, 4, 5, 6};
/*
try {
System.out.println(arr[10]);
} catch (ArrayIndexOutOfBoundsException e) {
/* String message = e.getMessage();
System.out.println(message);//Index 10 out of bounds for length 6*/
/* String str = e.toString();
System.out.println(str);//java.lang.ArrayIndexOutOfBoundsException: Index 10 out of bounds for length 6*/
e.printStackTrace(); //包含异常的位置信息,最常用
}
System.out.println("看看我执行了吗?");*/
//正常的输出语句
//System.out.println(123);
//错误的输出语句(而是用来打印错误信息)
//System.err.println(123);
}
写在方法定义处的throws异常是runtimeException异常的子类时可以省略不写
public static void main(String[] args) {
/*
throws:写在方法定义处,表示声明一个异常。告诉调用者,使用本方法可能会有哪些异常。
throw :写在方法内,结束方法。手动抛出异常对象,交给调用者。方法中下面的代码不再执行了。
需求:
定义一个方法求数组的最大值
*/
int[] arr = null;
int max = 0;
try {
max = getMax(arr);
} catch (NullPointerException e) {
System.out.println("空指针异常");
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("索引越界异常");
}
System.out.println(max);
}
public static int getMax(int[] arr)/* throws NullPointerException,ArrayIndexOutOfBoundsException*/{
if(arr == null){
//手动创建一个异常对象,并把这个异常交给方法的调用者处理
//此时方法就会结束,下面的代码不会再执行了
throw new NullPointerException();
}
if(arr.length == 0){
//手动创建一个异常对象,并把这个异常交给方法的调用者处理
//此时方法就会结束,下面的代码不会再执行了
throw new ArrayIndexOutOfBoundsException();
}
System.out.println("看看我执行了吗?");
int max = arr[0];
for (int i = 1; i < arr.length; i++) {
if(arr[i] > max){
max = arr[i];
}
}
return max;
}
定义一个女朋友类,键盘输入女朋友的名字和年龄
要求:名字长度为2~10
年龄要在18~40之间
如果输入不符合要求需要重新输入,直到输入正确为止
主函数:
public static void main(String[] args) {
girlFriend gf = new girlFriend();
Scanner sc = new Scanner(System.in);
while (true) {
try {
System.out.println("请输入女朋友的名字:");
String name = sc.nextLine();
gf.setName(name);
System.out.println("请输入女朋友的年龄:");
String age = sc.nextLine();
int age1 = Integer.parseInt(age);
gf.setAge(age1);
break;
} catch (NumberFormatException e) {
System.out.println("年龄输入有误!");
} catch (RuntimeException e) {
System.out.println("名字长度或年龄长度有误!");
}
}
System.out.println(gf);
}
girlfriend类:
package Exception;
import java.util.Objects;
public class girlFriend {
private String name;
private int age;
public girlFriend() {
}
@Override
public String toString() {
return "girlFriend{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
girlFriend that = (girlFriend) o;
return age == that.age && Objects.equals(name, that.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
public String getName() {
return name;
}
public void setName(String name) {
if(name.length() < 2 || name.length() > 10){
throw new RuntimeException();
}
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
if(age < 18 || age > 40){
throw new RuntimeException();
}
this.age = age;
}
public girlFriend(String name, int age) {
this.name = name;
this.age = age;
}
}
有些时候的异常Java本身没有相对应的异常,就需要我们自己去定义异常。
步骤:
还是上面的练习,针对名字长度和年龄范围自定义两个异常
public class AgeOutofBoundsException extends RuntimeException{
public AgeOutofBoundsException() {
}
public AgeOutofBoundsException(String message) {
super(message);
}
}
public class NameFormatException extends RuntimeException{
public NameFormatException() {
}
public NameFormatException(String message) {
super(message);
}
}
girlFriend类的setName和setAge函数:
public void setName(String name) {
if(name.length() < 2 || name.length() > 10){
throw new NameFormatException();
}
this.name = name;
}
public void setAge(int age) {
if(age < 18 || age > 40){
throw new AgeOutofBoundsException();
}
this.age = age;
}
主函数:
public static void main(String[] args) {
girlFriend gf = new girlFriend();
Scanner sc = new Scanner(System.in);
while (true) {
try {
System.out.println("请输入女朋友的名字:");
String name = sc.nextLine();
gf.setName(name);
System.out.println("请输入女朋友的年龄:");
String age = sc.nextLine();
int age1 = Integer.parseInt(age);
gf.setAge(age1);
break;
} catch (NumberFormatException e) {
e.printStackTrace();
} catch (NameFormatException e) {
e.printStackTrace();
} catch (AgeOutofBoundsException e) {
e.printStackTrace();
}
}
System.out.println(gf);
}
File表示什么?
File对象表示路径,可以是文件、也可以是文件夹。
这个路径可以是存在的,也可以是不存在的
绝对路径和相对路径是什么意思?
绝对路径是带盘符的。
相对路径是不带盘符的,默认到当前项目下去找。
File三种构造方法的作用?
public File(Stringpathname) 把字符串表示的路径变成File对象
public File(Stringparent,Stringchild) 把父级路径和子级路径进行拼接
public File(Fi1eparent,Stringchild) 把父级路径和子级路径进行拼接
public static void main(String[] args) {
/*
public File(String pathname) 根据文件路径创建文件对象
public File(String parent, String child) 根据父路径名字符串和子路径名字符串创建文件对象
public File(File parent, String child) 根据父路径对应文件对象和子路径名字符串创建文件对象
C:\Users\alienware\Desktop
\:转移字符
*/
//1.根据字符串表示的路径,变成File对象
String str = "C:\\Users\\alienware\\Desktop\\a.txt";
File f1 = new File(str);
System.out.println(f1);//C:\Users\alienware\Desktop\a.txt
//2.父级路径:C:\Users\alienware\Desktop
//子级路径:a.txt
String parent = "C:\\Users\\alienware\\Desktop";
String child = "a.txt";
File f2 = new File(parent,child);
System.out.println(f2);//C:\Users\alienware\Desktop\a.txt
File f3 = new File(parent + "\\" + child);
System.out.println(f3);//C:\Users\alienware\Desktop\a.txt
//3.把一个File表示的路径和String表示路径进行拼接
File parent2 = new File("C:\\Users\\alienware\\Desktop");
String child2 = "a.txt";
File f4 = new File(parent2,child2);
System.out.println(f4);//C:\Users\alienware\Desktop\a.txt
}
1.length 返回文件的大小(字节数量)
细节1:这个方法只能获取文件的大小,单位是字节
如果单位我们要是M,G,可以不断的除以1024
细节2:这个方法无法获取文件夹的大小
如果我们要获取一个文件夹的大小,需要把这个文件夹里面所有的文件大小都累加在一起。
4.getName 获取名字
细节1: a.txt:a表示文件名,txt表示后缀名,也叫扩展名
细节2:如果路径是文件夹,返回的就是文件夹的名字
public static void main(String[] args) {
/*
public boolean isDirectory() 判断此路径名表示的File是否为文件夹
public boolean isFile() 判断此路径名表示的File是否为文件
public boolean exists() 判断此路径名表示的File是否存在
*/
//1.对一个文件的路径进行判断
File f1 = new File("D:\\aaa\\a.txt");
System.out.println(f1.isDirectory());//false
System.out.println(f1.isFile());//true
System.out.println(f1.exists());//true
System.out.println("--------------------------------------");
//2.对一个文件夹的路径进行判断
File f2 = new File("D:\\aaa\\bbb");
System.out.println(f2.isDirectory());//true
System.out.println(f2.isFile());//false
System.out.println(f2.exists());//true
System.out.println("--------------------------------------");
//3.对一个不存在的路径进行判断
File f3 = new File("D:\\aaa\\c.txt");
System.out.println(f3.isDirectory());//false
System.out.println(f3.isFile());//false
System.out.println(f3.exists());//false
}
public static void main(String[] args) {
/*
public long length() 返回文件的大小(字节数量)
public String getAbsolutePath() 返回文件的绝对路径
public String getPath() 返回定义文件时使用的路径
public String getName() 返回文件的名称,带后缀
public long lastModified() 返回文件的最后修改时间(时间毫秒值)
*/
//1.length 返回文件的大小(字节数量)
//细节1:这个方法只能获取文件的大小,单位是字节
//如果单位我们要是M,G,可以不断的除以1024
//细节2:这个方法无法获取文件夹的大小
//如果我们要获取一个文件夹的大小,需要把这个文件夹里面所有的文件大小都累加在一起。
File f1 = new File("D:\\aaa\\a.txt");
long len = f1.length();
System.out.println(len);//12
File f2 = new File("D:\\aaa\\bbb");
long len2 = f2.length();
System.out.println(len2);//0
System.out.println("====================================");
//2.getAbsolutePath 返回文件的绝对路径
File f3 = new File("D:\\aaa\\a.txt");
String path1 = f3.getAbsolutePath();
System.out.println(path1);
File f4 = new File("myFile\\a.txt");
String path2 = f4.getAbsolutePath();
System.out.println(path2);
System.out.println("====================================");
//3.getPath 返回定义文件时使用的路径
File f5 = new File("D:\\aaa\\a.txt");
String path3 = f5.getPath();
System.out.println(path3);//D:\aaa\a.txt
File f6 = new File("myFile\\a.txt");
String path4 = f6.getPath();
System.out.println(path4);//myFile\a.txt
System.out.println("====================================");
//4.getName 获取名字
//细节1:
//a.txt:
// a 文件名
// txt 后缀名、扩展名
//细节2:
//文件夹:返回的就是文件夹的名字
File f7 = new File("D:\\aaa\\a.txt");
String name1 = f7.getName();
System.out.println(name1);
File f8 = new File("D:\\aaa\\bbb");
String name2 = f8.getName();
System.out.println(name2);//bbb
System.out.println("====================================");
//5.lastModified 返回文件的最后修改时间(时间毫秒值)
File f9 = new File("D:\\aaa\\a.txt");
long time = f9.lastModified();
System.out.println(time);//1667380952425
//如何把时间的毫秒值变成字符串表示的时间呢?
//课堂练习:
//yyyy年MM月dd日 HH:mm:ss
Date date = new Date();
date.setTime(time);
System.out.println(new SimpleDateFormat("yyyy-MM-dd HH-mm-ss").format(date));
}
注:delete方法只能删除文件和空文件夹,直接删除不走回收站
1.createNewFile 创建一个新的空的文件
细节1:如果当前路径表示的文件是不存在的,则创建成功,方法返回true
如果当前路径表示的文件是存在的,则创建失败,方法返回false
细节2:如果父级路径是不存在的,那么方法会有异常IOException
细节3:createNewFile方法创建的一定是文件,如果路径中不包含后缀名,则创建一个没有后缀的文件
2.mkdir make Directory,文件夹(目录)
细节1:windows当中路径是唯一的,如果当前路径已经存在,则创建失败,返回false
细节2:mkdir方法只能创建单级文件夹,无法创建多级文件夹。
3.mkdirs 创建多级文件夹
细节:既可以创建单级的,又可以创建多级的文件夹
4. public boolean delete() 删除文件、空文件夹
细节:
如果删除的是文件,则直接删除,不走回收站。
如果删除的是空文件夹,则直接删除,不走回收站
如果删除的是有内容的文件夹,则删除失败
public static void main(String[] args) throws IOException {
/*
public boolean createNewFile() 创建一个新的空的文件
public boolean mkdir() 创建单级文件夹
public boolean mkdirs() 创建多级文件夹
public boolean delete() 删除文件、空文件夹
*/
//1.createNewFile 创建一个新的空的文件
//细节1:如果当前路径表示的文件是不存在的,则创建成功,方法返回true
// 如果当前路径表示的文件是存在的,则创建失败,方法返回false
//细节2:如果父级路径是不存在的,那么方法会有异常IOException
//细节3:createNewFile方法创建的一定是文件,如果路径中不包含后缀名,则创建一个没有后缀的文件
/*File f1 = new File("D:\\aaa\\ddd");
boolean b = f1.createNewFile();
System.out.println(b);//true*/
//2.mkdir make Directory,文件夹(目录)
//细节1:windows当中路径是唯一的,如果当前路径已经存在,则创建失败,返回false
//细节2:mkdir方法只能创建单级文件夹,无法创建多级文件夹。
/* File f2 = new File("D:\\aaa\\aaa\\bbb\\ccc");
boolean b = f2.mkdir();
System.out.println(b);*/
//3.mkdirs 创建多级文件夹
//细节:既可以创建单级的,又可以创建多级的文件夹
File f3 = new File("D:\\aaa\\ggg");
boolean b = f3.mkdirs();
System.out.println(b);//true
}
public static void main(String[] args) {
/*
public boolean delete() 删除文件、空文件夹
细节:
如果删除的是文件,则直接删除,不走回收站。
如果删除的是空文件夹,则直接删除,不走回收站
如果删除的是有内容的文件夹,则删除失败
*/
//1.创建File对象
File f1 = new File("D:\\aaa\\eee");
//2.删除
boolean b = f1.delete();
System.out.println(b);
}
public static void main(String[] args) {
//public File[] listFiles() 获取当前该路径下所有内容
//1.创建File对象
File f = new File("D:\\aaa");
//2.listFiles方法
//作用:获取aaa文件夹里面的所有内容,把所有的内容放到数组中返回
File[] files = f.listFiles();
for (File file : files) {
//file依次表示aaa文件夹里面的每一个文件或者文件夹
System.out.println(file);
}
}
listRoots() list()了解,看到知道是什么意思就行
要求:获取后缀名为".txt"的文件
public static void main(String[] args) {
/*
public static File[] listRoots() 列出可用的文件系统根
public String[] list() 获取当前该路径下所有内容
public String[] list(FilenameFilter filter) 利用文件名过滤器获取当前该路径下所有内容
(掌握)public File[] listFiles() 获取当前该路径下所有内容
public File[] listFiles(FileFilter filter) 利用文件名过滤器获取当前该路径下所有内容
public File[] listFiles(FilenameFilter filter) 利用文件名过滤器获取当前该路径下所有内容
*/
/* //1.listRoots 获取系统中所有的盘符
File[] arr = File.listRoots();
System.out.println(Arrays.toString(arr));
//2.list() 获取当前该路径下所有内容(仅仅能获取名字)
File f1 = new File("D:\\aaa");
String[] arr2 = f1.list();
for (String s : arr2) {
System.out.println(s);
}*/
//3.list(FilenameFilter filter) 利用文件名过滤器获取当前该路径下所有内容
//需求:我现在要获取D:\\aaa文件夹里面所有的txt文件
File f2 = new File("D:\\aaa");
//accept方法的形参,依次表示aaa文件夹里面每一个文件或者文件夹的路径
//参数一:父级路径
//参数二:子级路径
//返回值:如果返回值为true,就表示当前路径保留
// 如果返回值为false,就表示当前路径舍弃不要
String[] arr3 = f2.list(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
File src = new File(dir,name);
return src.isFile() && name.endsWith(".txt");
}
});
System.out.println(Arrays.toString(arr3));
}
listFiles方法要掌握
public static void main(String[] args) {
/*
(掌握)public File[] listFiles() 获取当前该路径下所有内容
public File[] listFiles(FileFilter filter) 利用文件名过滤器获取当前该路径下所有内容
public File[] listFiles(FilenameFilter filter) 利用文件名过滤器获取当前该路径下所有内容
*/
File f1 = new File("D:/aaa");
File[] files = f1.listFiles();
//方法一
for(File f : files){
if(f.isFile() && f.getName().endsWith(".txt")){
System.out.println(f);
}
}
System.out.println("----------------------");
//方法二
File[] files1 = f1.listFiles(new FileFilter() {
@Override
public boolean accept(File f) {
return f.isFile() && f.getName().endsWith(".txt");
}
});
System.out.println(Arrays.toString(files1));
//方法三
System.out.println("----------------------");
File[] files2 = f1.listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return new File(dir, name).isFile() && name.endsWith(".txt");
}
});
System.out.println(Arrays.toString(files2));
}
练习1:在当前模块下的aaa文件夹中创建一个a.txt文件
练习2:定义一个方法找某一个文件夹中,是否有以png,jpg,jpeg结尾的图片,暂时不需要考虑子文件夹
练习3:定义一个方法找某一个文件夹中,是否有以png结尾的图片,需要考虑子文件夹
练习4:删除一个多级文件夹
练习5:统计一个文件夹中每种文件的个数并打印(考虑子文件夹)
打印格式如下:
txt : 3个
doc : 4个
ipg : 6个
//
package myFile;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class test1 {
static Map<String, Integer> map = new HashMap<>();
public static void main(String[] args) {
String dir2 = "D:/aaa";
File f = new File(dir2);
countFiles(f);
Set<Map.Entry<String, Integer>> entries = map.entrySet();
for(Map.Entry<String, Integer> e : entries){
System.out.println(e.getKey() + ":" + e.getValue());
}
}
//练习1:在当前模块下的aaa文件夹中创建一个a.txt文件
public static void createFile(String dir) throws IOException {
File f = new File(dir);
f.createNewFile();
}
//练习2:定义一个方法找某一个文件夹中,是否有以png,jpg,jpeg结尾的图片
//暂时不需要考虑子文件夹
public static boolean findFile(String dir){
File f2 = new File(dir);
File[] files = f2.listFiles();
for(File f : files){
if(f.getName().endsWith(".jpg") || f.getName().endsWith(".jpeg") || f.getName().endsWith(".png")){
return true;
}
}
return false;
}
//练习3:定义一个方法找某一个文件夹中,是否有以png结尾的图片
//需要考虑子文件夹
public static boolean findFiles(File f){
File[] files = f.listFiles();
for(File file : files){
if(file.isFile() && file.getName().endsWith(".png")){
return true;
}
if(file.isDirectory()){
return findFiles(file);
}
}
return false;
}
//练习4:删除一个多级文件夹
public static void deleteFiles(File f){
if(f.isFile()){
f.delete();
return;
}
File[] files = f.listFiles();
System.out.println(Arrays.toString(files));
if(files.length == 0){
f.delete();
}else{
for(File file : files){
deleteFiles(file);
}
File[] filess = f.listFiles();
if(filess.length == 0){
f.delete();
}
}
}
/*练习5:统计一个文件夹中每种文件的个数并打印(考虑子文件夹)
打印格式如下:
txt : 3个
doc : 4个
ipg : 6个
*/
public static void countFiles(File f){
if(f.isFile()){
String str = f.getName().split("\\.")[1]; //注:“.”是特殊字符,需要用\\转义。其他特殊字符:( [ { / ^ - $ ¦ } ] ) ? * + .
if(map.containsKey(str)){
int count = map.get(str);
count++;
map.put(str, count);
}else {
map.put(str, 1);
}
}else{
File[] files = f.listFiles();
for(File file : files){
countFiles(file);
}
}
}
}