1 Java8接口新特性
-
Java8之前接口定义:只可以定义抽象方法,不能定义非抽象方法的特殊类型 -
Java8接口的定义:即可以定义抽象方法,也可以定义非抽象方法 如果需要定义非抽象方法,那么当前方法需要使用static或者default修饰 -
改变的原因:
改变之前:如果定义了一个接口,接口已经参与使用,后续想给接口增加一个功能,只能添加抽象方法,但是接口已经在使用,新增一个抽象方法之后,使用过该接口的类型 就报错。
改变之后,为了保证接口可以新增功能,要保证使用过接口的类型不出问题,jdk1.8之 后接口就重新定义:接口中既可以定义抽象方法也可以定义非抽象方法。
1.1 default修饰的方法
- 定义方式:
直接在在修饰符位置上加上default关键字即可 - 注意事项:
(1)如果某个类型实现两个接口,两个接口中定义了两个相同方法声明的抽象方法,那么该实现类只需要重写其中一个即可。 (2)如果某个类型实现两个接口,两个接口中定义了两个相同方法声明,不同方法实现的非抽象方法,那么该实现类必须强制重写或者只能继承其中一个,不能同时继承两个。 强制继承方式:接口名.super.方法名(); (3)如果某个类型即继承一个父类,又实现一个接口,父类和接口中有两个相同的方 法声明,不同的实现内容的方法;该类可以选择默认继承父类的方法,或者强制 继承接口的方法。 强制继承方式:接口名.super.方法名();
代码
package demos;
public class InterClass extends Fu implements Inter,Inter2{
//重写接口中定义的抽象方法
@Override
public void show() {
System.out.println("show方法");
}
//重写接口中定义的非抽象方法
@Override
public void print(){
//强制继承Inter2中的方法
// Inter2.super.print();
//强制继承Inter中的方法
// Inter.super.print();
System.out.println("实现类的print方法");
}
//强制继承接口中的方法
public void eat(){
Inter.super.eat();
}
}
1.2 static修饰的方法
- 父类中定义的静态方法可以被子类继承,但是不能被重写
- 接口中定义的静态方法,实现类不能继承的
2 Lambda表达式
-
概念: 本质上是一个对象,可以当作是匿名内部类创建的对象的简写格式。 -
格式: (参数)->{方法体} -
说明: 参数:需要重写的抽象方法中的形参列表 -> :为了分隔前后两部分,Lambda运算符或者箭头运算符 方法体:需要对抽象方法重写的内容 -
举例: (1)如果抽象方法没有参数,方法体只有一句: 方法体外的大括号可以省略。 格式:()->方法体语句; (2)如果抽象方法中有一个参数: 参数的类型可以省略,参数外的小括号可以省略 格式:参数名->{方法体语句}; (3)如果方法的参数有多个: 参数外的小括号不能省略,参数类型可以省略,方法体外的大括号可以省略。 格式:(参数名称1,参数名称2)->{方法体语句}; (4)如果方法有返回值: 直接在方法体中的大括号中对数据进行return即可 格式:(参数名称)->{ 方法体语句; return语句; }; (5)如果方法需要返回值,而且方法体只有一句: 可以将大括号和return关键字一起省略 格式:(参数名称)->要返回的数据; -
Lambda表达式和匿名内部类的区别: (1)如果接口中只有一个抽象方法,既可以使用匿名内部类创建,也可以使用Lambda创建。 如果接口中有多个抽象方法,那么只能使用匿名内部类创建 (2)Lambda表达式只适用于接口的使用,不适用于类型的使用 (3)匿名内部类会在磁盘中生成一个字节码文件,Lambda表达式则没有
3 函数式接口
- 概念:
一个接口中的抽象方法只有一个,那么这个接口就是一个函数式接口。 - 通过注解检测一个接口是否是一个函数式接口:
@FunctionalInterface 在接口上直接加上注解,如果这个接口是一个函数式接口则不报错,否则编译报错
代码
package demos2_interface;
//接口中抽象方法只有一个,就是一个函数式接口
//使用注解标记接口是一个函数式接口
@FunctionalInterface
public interface Inter {
public void show();
public default void print(){
System.out.println("print方法");
}
}
3.1 内置的函数式接口
- 在jdk8之后,官方定义了一些常用的函数式接口,如果以后需要使用类似的接口,直接 使用即可,不需要再单独定义。
- 分类:
Consumer<T> :消费型接口
void accept(T t)
Supplier<T> :供给型接口
T get()
Function<T,R> :函数型接口
R apply(T t)
Predicate<T> :断言型接口
boolean test(T t);
3.2 消费型接口
- 接口名称:
Consumer<T> - 抽象方法:
void accept(T t) :消费一个参数数据 - 概述:该接口中的方法可以接收一个参数,接收的参数类型由泛型指定,对参数的操作方式根据该接口的实现类决定,不需要返回值。
- 拓展的非抽象方法:
default Consumer<T> andThen(Consumer<? super T> after) : 返回一个组合的 Consumer ,按顺序执行该操作,然后执行 after操作。
代码
package demos2_interface;
import java.util.function.Consumer;
public class Demo01 {
public static void main(String[] args) {
Consumer<String> con1 = str-> System.out.println(str);
Consumer<String> con2 = str-> System.out.println(str.length());
//将两个消费型接口的实现类对象进行合并(两个功能合并为一个功能)
con1.andThen(con2).accept("hello");
// get("hello",str -> System.out.println(str));
// get("hello",str-> System.out.println(str.length()));
}
//对参数做处理,打印一遍参数
//对参数做处理,打印参数的字符个数
//如果方法对参数的使用不明确,可以使用消费型接口
//好处:(提高代码扩展性) 不需要反复对方法进行修改
// 如果方法接收参数之后不知道应该怎么处理,就可以使用消费型接口来处理该参数
// 具体如何处理:将来调用该方法时,如何定义实现类对象就如何处理
public static void get(String str,Consumer<String> con){
con.accept(str);
}
}
3.3 供给型接口
- 接口名:
Supplier<T> - 抽象方法:
T get() :该方法不需要参数,它会按照某种逻辑,返回一个具体的数据 - 概述:
该接口也被称为生产型接口,如果指定了泛型是什么类型,那类中的get方法就会返回 一个该类型的一个具体数据。返回的数据,由该接口的实现类对象决定。
代码
package demos2_interface;
import java.util.ArrayList;
import java.util.Random;
import java.util.function.Supplier;
public class Demo02 {
public static void main(String[] args) {
//调用getList方法获取一个集合,要求集合中的随机数是1-100
System.out.println(getList(()->new Random().nextInt(100)+1));
//调用getList方法获取一个集合,要求集合中的随机数范围:1-1000
System.out.println(getList(()->new Random().nextInt(1000)+1));
}
//定义一个方法,该方法可以返回一个集合,集合中要求存储十个随机数
//要求:获取的随机数范围 1-100
//要求:获取的随机数范围 1-1000
//如果对方法的需求不断改变,定义的方法也要不断修改,方法的扩展性太低
//Supplier<Integer> sup = ()->new Random().nextInt(100)+1;
//如果需要获取一个不确定的数据,可以就可以定义一个供给型接口
//需要什么数据在方法中不需要定义,只需要通过供给型接口获取即可
//具体供给型接口提供何种数据:根据对接口的实现类重写方式决定
public static ArrayList<Integer> getList(Supplier<Integer> sup){
ArrayList<Integer> list = new ArrayList<>();
for(int i = 1;i <= 10;i++){
list.add(sup.get());
}
return list;
}
}
3.4 函数型接口
- 接口名:
Function<T,R> - 抽象方法:
R apply(T) :接收一个数据,操作数据之后,返回一个新的数据 - 概述:
该接口可以接收一个数据,数据的类型根据泛型指定,然后通过该接口的实现类对象对 该数据进行操作,操作之后返回一个新的数据。 - 拓展的非抽象方法:
default Function andThen(Function f) : 先通过调用者对象处理参数,将处理的结果再通过f对象处理,将两个处理的结果进行返回。
代码
package demos2_interface;
import java.util.function.Function;
public class Demo03 {
public static void main(String[] args) {
Function<String,String> fun1 = str->str+"hello";
Function<String,Integer> fun2 = str->str.length();
//将fun1和fun2合并到一起
//接收一个参数之后,先通过fun1的内容处理,处理的结果再通过fun2处理
//两个对象处理之后的最终结果,返回
System.out.println(fun1.andThen(fun2).apply("java"));
//使用change方法,传递一个字符串,将字符串转为int返回
int i = change("123", s -> Integer.parseInt(s));
// System.out.println(i);
//使用change方法,传递一个字符串,需要获取字符串字符个数
int i2 = change("123", s -> s.length());
// System.out.println(i2);
}
//定义一个方法,该方法可以接收一个数字字符串,将字符串转为int整数返回
//定义一个方法,接收一个数字字符串,将字符串中的字符个数返回
//如果定义方法,不确定如何处理接收的参数,可以定义一个函数型接口
//后续调用该方法时,传递一个对应的接口实现类对象即可
//具体如何重写接口的实现类对象:根据需求决定
//好处:提高方法的扩展性,不用每次对方法进行修改
public static int change(String str, Function<String,Integer> fun){
return fun.apply(str);
}
}
3.5 断言型接口
- Predicate:
boolean test(T t) :对数据做出指定的判断 - 概述:
该接口是一个判断接口,接口可以接收一个指定泛型的参数,并根据该接口的实现类 对象对该参数做出对应的判断,返回只为boolean类型 - 额外功能:
and(Predicate<T> p) :先将参数通过调用者判断真假,再将参数通过p判断真假,全真为真,否则为假 or(Predicate<T> p) :全假为假,否则为真 negate() :取反
代码
package demos2_interface;
import java.util.function.Predicate;
public class Demo04 {
public static void main(String[] args) {
Predicate<String> pre = s->s.startsWith("a");
Predicate<String> pre2 = s->s.length()>=10;
//判断and两边的结果:全真为真,否则为假
System.out.println(pre.and(pre2).test("abcdefghijklmn"));
//判断or两边的结果:全假为假,否则为真
System.out.println(pre.or(pre2).test("abcd"));
//negate对判断结果取反
System.out.println(pre.negate().test("abcd"));
//使用testValue方法,判断一个字符串的长度是否是大于10
// System.out.println(testValue("abcdefg", s -> s.length() >= 10));
//使用testValue方法,判断字符串是否是以a开头
// System.out.println(testValue("abc",s->s.startsWith("a")));
}
public static boolean testValue(String str, Predicate<String> pre){
return pre.test(str);
}
}
3.6 方法引用
- 概念:
对lambda表达式的扩展,在定义lambda表达式的内容时,如果这个内容之前已经定 义过,那么就不需要再定义一遍,直接调用即可。 - 格式:
如果是一个构造方法:类名::new 如果是一个静态方法:类名::方法名 如果是一个非静态方法:对象名::方法名
代码
package demos2_interface;
import java.util.function.Consumer;
public class Demo05 {
public static void main(String[] args) {
Consumer<String> con = s-> System.out.println(s+"你真帅!!!");
con.accept("刘功睿");
//在定义lambda表达式的时候,如果定义的内容之前已经定义过,可以拿来调用一下,不需要重复定义
//如果内容是在构造方法中定义的:类名::new
Consumer<String> con2 = Person::new;
con2.accept("王浩");
//如果内容是在静态方法中定义的:类名::静态方法名
Consumer<String> con3 = Person::print;
con3.accept("王硕新");
//如果内容是在非静态方法中定义的:对象名::方法名
Consumer<String> con4 = new Person()::show;
con4.accept("孙伟博");
}
}
class Person{
private String name;
public Person(){}
public Person(String name){
System.out.println(name + "你真帅!!!");
}
public static void print(String name){
System.out.println(name + "你真TM帅!!!");
}
public void show(String name){
System.out.println(name + "你帅的掉渣,简直无法形容了!!!");
}
}
4 Stream类
- 概述:Java8之后提供的一个新的类型。
- 作用:使用集合和数组操作元素时,可以使用此类代替循环和判断语句完成代码的简化
4.1 Stream类对象的获取
- Collection集合的获取方式:
对象名.stream(); - Map集合获取方式:
ketSet().stream(); values().stream(); entrySet.stream(); - 数组的获取方式:
Strem.of(数组名); - 注意事项:
因为Stream类型提供的功能主要是对循环做替换,所以只有集合和数组才需要使用
代码
package demos3_stream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Stream;
public class Demo02 {
public static void main(String[] args) {
//如果操作的对象是一个单列集合
Collection<String> c = new ArrayList<>();
Stream<String> stream1 = c.stream();
//如果操作的对象是一个双列集合
Map<Integer,String> map = new HashMap<>();
Stream<Integer> stream2 = map.keySet().stream();
Stream<String> stream3 = map.values().stream();
Stream<Map.Entry<Integer, String>> stream4 = map.entrySet().stream();
//如果操作的对象是一个数组
Integer[] arr = {1,2,3};
Stream<Integer> stream5 = Stream.of(arr);
}
}
4.2 Stream类的常用方法
-
Stream类中包含了一些对数据过滤和筛选以及获取的方法,由此来代替循环和判断语句。 -
分类:
延迟方法:使用完该类方法之后,返回值是一个Stream类的对象,可以继续调用类中的方法。
终结方法:使用完该类方法之后,返回值不再是一个Stream类的对象,不能继续使用类中的方法。
-
方法罗列: (1)forEach(Consumer<? super T> action) :终结方法 该方法可以自动获取流中每一个数据,并对获取的数据操作 数据的操作方式根据消费型接口的实现类重写。 (2)count() :返回此流中的元素数。终结方法 (3)filter(Predicate<? super T> predicate) :延迟方法 该方法可以自动获取流中的每一个数据进行判断,如果数据判断结果为真,就在流中存储,否则不存储该数据 (4)limit(long m) :将流中的前m个元素保留,其余的删除 延迟方法 (5)skip(long n) : 跳过前n个元素,保留后面的元素 延迟方法 (6)map(Function<? super T,? extends R> mapper) : 延迟方法 该方法自动获取流中的每一个数据,并对数据处理,处理之后返回一个新 的数据。具体的处理方式根据实现类对象确定。 (7)concat(Stream<? extends T> a, Stream<? extends T> b) :延迟方法 将两个流中的数据进行合并,合并为一个流对象 (8)distinct() :去除流中重复的元素 延迟方法 (9)toArray() :把stream流中的数据收集到数组中 (10)collect(Collector c) :把stream流中的数据收集到指定的集合中 Collector:参数的类型 是一个接口获取可以通过工具类Collectors的方法获取 ???常用: ???????获取List集合:Collectors.toList() ???????获取Set集合:Collectors.toSet()
练习
有两个Arraylist集合,存储队伍中的多个成员姓名,使用Stream方式,对以下步骤进行操作 1、第一个队伍只要名字为3个字的成员姓名 2、第一个队伍只要筛选之后的前三个人 3、第二个队伍只要姓张的 4、第二个队伍筛选之后不要前两个人 5、将两个队伍合并成一个队伍 6、合并之后的队伍中的所有人的Person(自定义类型)对象,存储到一个ArrayList集合中 队伍1:宫本武藏、宋公明、苏有朋、石头人、时传祥、李耳、庄子、洪七公 队伍2:帕瓦罗蒂、张三疯、赵薇薇、张自忠、孛儿只斤铁木真、张天爱、张翠花
package demos3_stream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class Test {
public static void main(String[] args) {
// 有两个Arraylist集合,存储队伍中的多个成员姓名,使用Stream方式,对以下步骤进行操作
// 1、第一个队伍只要名字为3个字的成员姓名
// 2、第一个队伍只要筛选之后的前三个人
// 3、第二个队伍只要姓张的
// 4、第二个队伍筛选之后不要前两个人
// 5、将两个队伍合并成一个队伍
// 6、合并之后的队伍中的所有人的Person(自定义类型)对象,存储到一个ArrayList集合中
// 队伍1:宫本武藏、宋公明、苏有朋、石头人、时传祥、李耳、庄子、洪七公
// 队伍2:帕瓦罗蒂、张三疯、赵薇薇、张自忠、孛儿只斤铁木真、张天爱、张翠花
List<String> list1 = Arrays.asList("宫本武藏、宋公明、苏有朋、石头人、时传祥、李耳、庄子、洪七公".split("、"));
List<String> list2 = Arrays.asList("帕瓦罗蒂、张三疯、赵薇薇、张自忠、孛儿只斤铁木真、张天爱、张翠花".split("、"));
Stream<String> team1 = list1.stream().filter(x -> x.length() == 3).limit(3);
Stream<String> team2 = list2.stream().filter(x -> x.startsWith("张")).skip(2);
Stream<String> team = Stream.concat(team1, team2);
// Stream<Person> personteam = team.map(x -> new Person(x));
Stream<Person> personteam = team.map(Person::new);
// ArrayList<Person> list = (ArrayList<Person>)personteam.collect(Collectors.toList());
ArrayList<Person> list = new ArrayList<>();
personteam.forEach(list::add);
System.out.println(list);
}
}
|