目录
1、JDK8新特性
接口中可以有的内容:
JDK1.7之前:抽象方法和常量
JDK1.8:默认方法和静态方法
JDK1.9:私有方法
1.1、接口默认方法的定义与使用
默认方法定义格式
public default 返回值类型 方法的名称(参数列表){
方法体
}
默认方法的好处
接口的默认方法,实现类可以不用重写,默认方法可以用于接口升级。
代码演示
public interface LiveAble {
//接口的默认方法
public default void fly(){
System.out.println("天上飞");
}
}
实现类
public class LiveAbleImpl implements LiveAble {
// default 可以选择是否重写,也可以根据实际需求进行重写 不重写也不会报错
/*
@Override
public void fly() {
System.out.println("自由自在的飞");
}
*/
}
测试类
public class InterfaceDemo {
public static void main(String[] args) {
// 创建子类对象
LiveAble a = new LiveAbleImpl();
// 调用默认方法
a.fly();
}
}
1.2、接口静态方法的定义和使用
静态方法:使用 static 修饰,供接口直接调用。
静态与.class文件相关,只能使用接口名调用,不可以通过实现类的类名或者实现类的对象调用。
静态方法定义格式
public static 返回值类型 方法名称(参数列表){
方法体
}
静态方法的使用
定义接口
public interface LiveAble {
//静态方法
public static void show(){
System.out.println("静态方法-show");
}
}
实现类
public class LiveAbleImpl implements LiveAble {
// 无法重写静态方法
}
测试类
public class InterfaceDemo {
public static void main(String[] args) {
//无法调用
// LiveAble l = new LiveAbleImpl();
// l.show2();
//接口名.静态方法(参数列表)
LiveAble.show2();
}
}
1.3、接口私有方法的定义和使用(JDK1.9)
如果一个接口中有多个默认方法,并且方法中有重复的内容,那么可以抽取出来,封装到私有方法中,供默认方法 去调用。从设计的角度讲,私有的方法是对默认方法和静态方法的辅助。jdk1.9的新特性。
私有方法的分类
普通私有方法
只有默认方法可以调用。
静态私有方法
默认方法和静态方法可以调用。
代码演示
public interface MyInterfacePrivateA {
//默认方法
public default void methodDefault1() {
System.out.println("默认方法1");
//调用私有方法
methodCommon();
}
//默认方法
public default void methodDefault2() {
System.out.println("默认方法2");
//调用私有方法
methodCommon();
}
//普通-私有方法
private void methodCommon() {
System.out.println("AAA");
System.out.println("BBB");
System.out.println("CCC");
}
}
定义接口
public interface MyInterfacePrivateB {
//静态方法
public default void methodStatic1() {
System.out.println("静态方法1");
//调用私有-静态方法
methodStaticCommon();
}
//静态方法
public static void methodStatic2() {
System.out.println("静态方法2");
//调用私有-静态方法
methodStaticCommon();
}
//私有-静态方法
private static void methodStaticCommon() {
System.out.println("AAA");
System.out.println("BBB");
System.out.println("CCC");
}
}
私有方法主要解决的问题是:默认方法和static方法代码重复的问题,private方法可作为抽取共性。
2、Lambda表达式
2.1、什么是Lambda表达式
Lambda 表达式是 Java8 新增的重要特性,Lambda 使 Java 具有了类似函数式编程的风格,其实质也是由编译器根据表达式推断最终生成原始语法的字节码方式。函数式思想则尽量忽略面向对象的复杂语法:“强调做什么,而不是以什么形式去做” ,而Lambda表达式就是函数式思想的体现。
2.2、使用Lambda表达式启动线程
线程实现类
public class RunableImpl implements Runnable {
@Override
public void run() {
System.out.println("线程启动了");
}
}
测试类
public class Test {
public static void main(String[] args) {
//第一种方式 实现类形式
RunableImpl runable = new RunableImpl();
Thread t = new Thread(runable);
t.start();
//第二种方式 匿名内部类
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("通过匿名内部类启动线程");
}
}).start();
//第三种方式 Lambda表达式
new Thread(() ->
System.out.println("lambda表达式启动线程")
).start();
}
}
2.3、Lambda表达式练习 (抽象方法无参无返回值)
格式:(形式参数)->{代码块}
Lambda表达式前提:有一个接口、这个接口里有且仅有一个抽象方法
定义接口和无参方法
public interface Eatable {
void eat();
}
实现类
public class EatableImpl implements Eatable {
@Override
public void eat() {
System.out.println("吃水果");
}
}
测试类
public class Test {
public static void main(String[] args) {
//第一种形式
Eatable e=new EatableImpl();
useEatable(e);
//第二种形式
useEatable(new Eatable() {
@Override
public void eat() {
System.out.println("吃苹果");
}
});
System.out.println("--------------------");
//第三种形式
useEatable(() ->
System.out.println("吃梨")
);
}
//定义方法,用来传递参数调用eat方法
public static void useEatable(Eatable eatable) {
eatable.eat();
}
}
2.4、Lambda表达式练习 (抽象方法有参无返回值)
定义接口和有参方法
public interface Flyable {
void fly(String s);
}
实现类
public class FlyableImpl implements Flyable {
@Override
public void fly(String s) {
System.out.println(s+"飞机自驾游");
}
}
测试类
public class Test2 {
public static void main(String[] args) {
//第一种形式 实现类
Flyable f=new FlyableImpl();
useFlyable(f);
//第二种形式 匿名内部类
useFlyable(new Flyable() {
@Override
public void fly(String s) {
System.out.println("看风景");
}
});
//第三种形式 Lambda表达式
useFlyable(s->
System.out.println(s+"风和日丽")
);
}
public static void useFlyable(Flyable flyable){
flyable.fly("一路顺风");
}
}
2.5、Lambda表达式练习 (抽象方法有参有返回值)
定义接口
public interface Addable {
int add(int x,int y);
}
实现类
public class AddableImpl implements Addable {
@Override
public int add(int x, int y) {
return x+y;
}
}
测试类
public class Test {
public static void main(String[] args) {
//第一种方式 实现类
Addable addable=new AddableImpl();
useAddable(addable);
System.out.println("-------------------");
//第二种方式 匿名内部类
useAddable(new Addable() {
@Override
public int add(int x, int y) {
return x+y;
}
});
System.out.println("-------------------");
//第三种方式 Lambda表达式
useAddable((x,y)->
x+y
);
}
public static void useAddable(Addable addable){
int add = addable.add(10, 20);
System.out.println(add);
}
}
3、Stream流
3.1、获取Stream流的三种方式
-
Collection体系集合
使用默认方法stream()生成流, default Stream<E> stream()
-
Map体系集合
把Map转成Set集合,间接的生成流
-
数组
通过Stream接口的静态方法of(T... values)生成流
代码演示
public class Demo {
public static void main(String[] args) {
//list集合获取stream流
ArrayList<String> list =new ArrayList<>();
Stream<String> stream1= list.stream();
//set集合获取stream流
Set<String> set=new HashSet<>();
Stream<String> stream2 = set.stream();
//map集合获取stream流
Map<String,Integer> map=new HashMap<>();
Stream<String> stream3 = map.keySet().stream();
Stream<Integer> stream4 = map.values().stream();
//数组可以通过Stream接口的静态方法of(T... values)生成流
String[] s={"hello","java","world"};
Arrays.stream(s).forEach(System.out::println);
//Stream类的of()方法直接操作数据,获取Stream流对象
Stream<String> hello = Stream.of("hello", "java", "world");
Stream<Integer> integerStream = Stream.of(10, 20, 30, 40);
}
}
3.2、Stream流中间操作方法
方法名 | 说明 |
---|---|
Stream<T> filter(Predicate predicate) | 用于对流中的数据进行过滤 |
Stream<T> limit(long maxSize) | 返回此流中的元素组成的流,截取前指定参数个数的数据 |
Stream<T> skip(long n) | 跳过指定参数个数的数据,返回由该流的剩余元素组成的流 |
static <T> Stream<T> concat(Stream a, Stream b) | 合并a和b两个流为一个流 |
Stream<T> distinct() | 返回由该流的不同元素(根据Object.equals(Object) )组成的流 |
Stream<T> sorted() | 返回由此流的元素组成的流,根据自然顺序排序 |
<R> Stream<R> map(Function mapper) | 返回由给定函数应用于此流的元素的结果组成的流 |
IntStream mapToInt(ToIntFunction mapper) | 返回一个IntStream其中包含将给定函数应用于此流的元素的结果 |
3.3、filter方法
public class Demo2 {
public static void main(String[] args) {
//创建一个集合,存储多个字符串元素
ArrayList<String> list = new ArrayList<String>();
list.add("林青霞");
list.add("张曼玉");
list.add("王祖贤");
list.add("柳岩");
list.add("赵敏");
list.add("张无忌");
//需求1:把list集合中以张开头的元素在控制台输出
list.stream().filter(s->s.startsWith("张")).forEach(System.out::println);
System.out.println("------------------");
//需求2:把list集合中长度为3的元素在控制台输出
list.stream().filter(s->s.length()==3).forEach(System.out::println);
System.out.println("------------------");
//需求3:把list集合中以张开头的,长度为3的元素在控制台输出
list.stream().filter(s->s.startsWith("张")).filter(s->s.length()==3).forEach(System.out::println);
System.out.println("------------------");
//简化for循环
list.forEach(System.out::println);
}
}
3.4、limit&skip方法
public class Demo3 {
public static void main(String[] args) {
//返回此流中的元素组成的流,截取前指定参数个数和跳过指定个数的数据
//创建一个集合,存储多个字符串元素
ArrayList<String> list = new ArrayList<String>();
list.add("林青霞");
list.add("张曼玉");
list.add("王祖贤");
list.add("柳岩");
list.add("张敏");
list.add("张无忌");
//需求1:取前3个数据在控制台输出
list.stream().limit(3).forEach(System.out::println);
System.out.println("--------------------");
//需求2:跳过3个元素,把剩下的元素在控制台输出
list.stream().skip(3).forEach(System.out::println);
System.out.println("--------------------");
//需求3:跳过2个元素,把剩下的元素中前2个在控制台输出
list.stream().skip(2).limit(2).forEach(System.out::println);
}
}
3.5、concat&distinct方法
concat:将两个流合并成为一个流
distinct:由该流的不同元素(根据Object.equals(Object) )组成的流
public class Demo4 {
public static void main(String[] args) {
// concat:将两个流合并成为一个流
// distinct:由该流的不同元素(根据Object.equals(Object) )组成的流
//创建一个集合,存储多个字符串元素
ArrayList<String> list = new ArrayList<String>();
list.add("林青霞");
list.add("张曼玉");
list.add("王祖贤");
list.add("柳岩");
list.add("张敏");
list.add("张无忌");
//需求1:取前4个数据组成一个流
Stream<String> limit = list.stream().limit(4);
//需求2:跳过2个数据组成一个流
Stream<String> skip = list.stream().skip(2);
//需求3:合并需求1和需求2得到的流,并把结果在控制台输出
// Stream.concat(limit,skip).forEach(System.out::println);
System.out.println("-----------------------");
//需求4:合并需求1和需求2得到的流,并把结果在控制台输出,要求字符串元素不能重复
Stream.concat(limit,skip).distinct().forEach(System.out::println);
}
}
3.6、sorted方法
由此流的元素组成的流,根据自然顺序排序
public class Demo5 {
public static void main(String[] args) {
//创建一个集合,存储多个字符串元素
ArrayList<String> list = new ArrayList<String>();
list.add("linqingxia");
list.add("zhangmanyu");
list.add("wangzuxian");
list.add("liuyan");
list.add("zhangmin");
list.add("zhangwuji");
//需求1:按照字母顺序把数据在控制台输出
list.stream().sorted().forEach(System.out::println);
}
}
3.7、map&mapToInt
map() 返回由给定函数应用于此流的元素的结果组成的流(将流中的元素映射到另一个流中)
mapToInt 计算流中的数据并返回结果
public class Demo6 {
public static void main(String[] args) {
//创建一个集合,存储多个字符串元素
ArrayList<String> list = new ArrayList<String>();
list.add("10");
list.add("20");
list.add("30");
list.add("40");
list.add("50");
//需求:将集合中的字符串数据转换为整数之后在控制台输出
//list.stream().map(s -> Integer.parseInt(s)).forEach(System.out::println);
//类名::静态方法
list.stream().map(Integer::parseInt).forEach(System.out::println);
//int sum() 返回此流中元素的总和
int sum = list.stream().mapToInt(Integer::parseInt).sum();
System.out.println(sum);
}
}
3.8、forEach()和count()方法
Stream流终结操作方法,一旦Stream调用了终结方法,流的操作就全部终结了,不能继续使用,只能创建新的Stream操作。 终结方法: foreach , count。
方法名 | 说明 |
---|---|
void forEach(Consumer action) | 对此流的每个元素执行操作 |
long count() | 返回此流中的元素数 |
public class Demo7 {
public static void main(String[] args) {
//创建一个集合,存储多个字符串元素
ArrayList<String> list = new ArrayList<String>();
list.add("林青霞");
list.add("张曼玉");
list.add("王祖贤");
list.add("柳岩");
list.add("张敏");
list.add("张无忌");
//需求1:把集合中的元素在控制台输出,直接输出结果就不能返回stream对象了
// list.stream().forEach(System.out::println);
//需求2:统计集合中有几个以张开头的元素,并把统计结果在控制台输出
long count = list.stream().filter(s -> s.startsWith("张")).count();
System.out.println(count);
}
}
在需求1执行后,需求2就不能执行了,就会报错异常,所以需求1和需求2只能执行一个,因为它们是终结流方法。
3.9、Stream流的收集操作
对数据使用Stream流的方式操作完毕后,可以把流中的数据收集到集合中。
方法名 | 说明 |
---|---|
R collect(Collector collector) | 把结果收集到集合中 |
工具类Collectors提供了具体的收集方式
方法名 | 说明 |
---|---|
public static <T> Collectors toList() | 把元素收集到List集合中 |
public static <T> Collectors toSet() | 把元素收集到Set集合中 |
public static Collectors toMap(Function keyMapper,Function valueMapper) | 把元素收集到Map集合中 |
List集合
public class Demo {
public static void main(String[] args) {
//创建List集合对象
List<String> list = new ArrayList<String>();
list.add("林青霞");
list.add("张曼玉");
list.add("王祖贤");
list.add("柳岩");
//需求1:得到名字为3个字的流
Stream<String> stringStream = list.stream().filter(s -> s.length() == 3);
//需求2:把使用Stream流操作完毕的数据收集到List集合中并遍历
List<String> collect = stringStream.collect(Collectors.toList());
for (String s : collect) {
System.out.println(s);
}
}
}
Set集合
public class Demo1 {
public static void main(String[] args) {
//创建Set集合对象
Set<Integer> set = new HashSet<Integer>();
set.add(10);
set.add(20);
set.add(30);
set.add(33);
set.add(35);
//需求3:得到年龄大于25的流
Stream<Integer> integerStream = set.stream().filter(s -> s > 25);
//需求4:把使用Stream流操作完毕的数据收集到Set集合中并遍历
Set<Integer> set1 = integerStream.collect(Collectors.toSet());
for (Integer integer : set1) {
System.out.println(integer);
}
}
}
Map集合
public class Demo3 {
public static void main(String[] args) {
//定义一个字符串数组,每一个字符串数据由姓名数据和年龄数据组合而成
String[] strArray = {"林青霞,30", "张曼玉,35", "王祖贤,33", "柳岩,25"};
//需求5:得到字符串中年龄数据大于28的流
Stream<String> stringStream = Arrays.stream(strArray).filter(s -> Integer.parseInt(s.split(",")[1]) > 28);
//需求6:把使用Stream流操作完毕的数据收集到Map集合中并遍历,字符串中的姓名作键,年龄作值
Map<String, Integer> map = stringStream.collect(Collectors.toMap(s -> s.split(",")[0], s -> Integer.parseInt(s.split(",")[1])));
//map遍历
map.forEach((key,value)->{
System.out.println(key+":"+value);
});
}
}
4、Stream流综合案例
现在有两个ArrayList集合,分别存储6名男演员名称和6名女演员名称,要求完成如下的操作
-
男演员只要名字为3个字的前三人
-
女演员只要姓林的,并且不要第一个
-
把过滤后的男演员姓名和女演员姓名合并到一起
-
把上一步操作后的元素作为构造方法的参数创建演员对象,遍历数据
演员类Actor已经提供,里面有一个成员变量,一个带参构造方法,以及成员变量对应的get/set方法
public class Actor {
private String name;
public Actor(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Actor{" +
"name='" + name + '\'' +
'}';
}
}
public class StreamTest {
public static void main(String[] args) {
//创建集合
ArrayList<String> manList = new ArrayList<String>();
manList.add("周润发");
manList.add("成龙");
manList.add("刘德华");
manList.add("吴京");
manList.add("周星驰");
manList.add("李连杰");
ArrayList<String> womanList = new ArrayList<String>();
womanList.add("林心如");
womanList.add("张曼玉");
womanList.add("林青霞");
womanList.add("柳岩");
womanList.add("林志玲");
womanList.add("王祖贤");
//男演员只要名字为3个字的前三人
Stream<String> man = manList.stream().filter(s -> s.length() == 3).limit(3);
//女演员只要姓林的,并且不要第一个
Stream<String> woman = womanList.stream().filter(s -> s.startsWith("林")).skip(1);
//把过滤后的男演员姓名和女演员姓名合并到一起
Stream<String> concat = Stream.concat(man, woman);
//使用map映射到一个新的对象中
// concat.map(Actor::new).forEach(System.out::println);
concat.map(Actor::new).forEach(s->System.out.println(s.getName()));
//链式写法 代替以上操作
// Stream.concat(manList.stream().filter(s -> s.length() == 3).limit(3),
// womanList.stream().filter(s -> s.startsWith("林")).skip(1)).map(Actor::new).
// forEach(p -> System.out.println(p.getName()));
}
}