java jdk1.8新特性粗略总结

JDK1.8中的特性:

  1. 接口方法
  2. lambda表达式
  3. 方法引用
  4. Stream API

接口增强

JDK1.8中引入了一种新的机制:接口可以支持在声明方法的同时,提供实现。
主要通过两种方式可以完成这种操作:

  1. 默认方法
  2. 静态方法

实现接口的类,可以继承这些方法并直接使用,就不再需要强制在实现类中的去实现(重写)接口中的方法了。这种机制可以使我们更平滑地进行接口的优化和演进。

方法调用的判断规则:

  1. 类中声明的方法优先级最高。
    类或父类中,声明的方法要高于任何默认方法的优先级
  2. 如果无法依据第一条进行判断,那么子接口的优先级更高
    例如,如果 B 接口继承了 A接口 ,那么 B 就比 A 更加具体,优先级更高
    所以,在上述例子中,B是子接口,优先级别更高,调用test方法后输出:defaut method test in B.
  3. 最后,如果还是无法判断,那么继承了多个接口的类,必须通过实现(重写)方法来确定方法的
    调用
class A{
    public void test(){
    	System.out.println("Default Method test in A");
    }
}

interface B {
    default void test(){
   	 	System.out.println("default method test in B");
    }
}

class C extends A implements B{
    public static void main(String[] args){
        C c = new C();
        c.test();
   	}
}

//运行结果:default method test in A
//因为A是类,B是接口,A里面的test方法优先级更高
interface A{
    default void test(){
    	System.out.println("Default Method test in A");
    }
}

interface B {
    default void test(){
    	System.out.println("default method test in B")
    }
}

//如下代码编译会报错。
/*class C implements A,B{
    public static void main(String[] args){
        C c = new C();
        c.test();
    }
}*/

//如果C需要同时实现A和B接口,那么必须显示覆盖
class C implements A,B{
    public void test(){
    //如果在C中需要显示访问A/B的默认方法,可以使用接口名.super.方法名();
        A.super.test();
        B.super.test();
    //或者自己编写test方法的实现
    }
}

静态方法

public interface StaticMethod{
    public static void test(){
    	System.out.println("我是StaticMethod接口中的静态方法!");
    }
}

class A implements StaticMethod{}
    class Test{
    public static void main(String args[]){
        //运行成功
        StaticMethod.test();
        //编译报错:
        //Static method may be invoked on containing interface class only
        //A.test();
    }
}

注意,接口中的静态方法,只能使用当前接口的名字来调用

Lambda表达式

行为参数化 在java代码中,我们如果需要运算,那么大部分我们的过程是确定的,但是实际参与运算的数却不确定。

在实际项目中,用户需求的变动,是很正常的一件事情,所以在上述的案例中,我们不断的增加方法,来解决用户新的需求,但是在整个过程中,我们复制了大量的相同的代码。

这几个方法之间相同的地方,例如:
方法的修饰符
方法的返回类型
方法的参数列表
方法中对数据的遍历
这几个方法之间不同的地方,例如:
方法的名字
方法中遍历数据后的核心操作

需要注意的是,方法的名字其实是无关紧要的,它只是在调用时,用到的一个标示符,最关键的是,对这个对数据的核心操作,也就是代码中核心的行为操作。 这时候,我们可以将这个核心的行为操作,进行抽象,变成一个参数

interface Action{
	int action(int result,int i);
}

public static int calculate(int a,int b, Action cal){
    int result = a;
    for(int i = a+1;i<=b;i++){
    	result = cal.action(result,i);
    }
    return result;
}

class Test{
    public static void main(String[] args){
        int result = 0;
        /*
        //忽略掉之前匿名内部类形式中,不重要的部分
        Action add = (int result,int i) -> {
        return result + i;
        };
        */
        //简化写法
        Action add = (result,i) -> result + i;
        result = calculate(3,5,add);
        //去掉中间变量add
        //相当于,第三个参数,我们传了一个核心的计算行为
        //这个核心的计算行为,就是对Action接口中action方法的实现
        //result = calculate(3,5,(result,next) -> result + next);
        //[3,5]之间,累加的结果
        System.out.println(result);
    }
}

常用接口
JDK1.8中,已经定了一些会常用到的函数式接口,这些函数式接口都定义在 java.lang.function 包中,

例如 Predicate 、 Consumer 、 Function 、 Supplier 、 UnaryOperator 和BinaryOperator 等。

注意,如果需要,也可以自己定义类似的函数式接口,并不是必须要使用这些定义好的接口。

Predicate
java.util.function.Predicate 接口定义了一个名叫 test 的抽象方法,它接受泛型T 对象,
并返回一个 boolean 类型的结果

例如:定义一个方法,用来过滤数组中所有符合要求的数据,选出大于50的数据

public class Test {
    public static void main(String[] args) {
        Test t = new Test();
        Integer[] arr = {12,3,43,123,34,6,56,7};
        arr = t.filter(arr,e->e>50);
        System.out.println(Arrays.toString(arr));
    }
    
    public Integer[] filter(Integer[] arr, Predicate<Integer> p){
        List<Integer> list = new ArrayList<>();
        for(Integer i: arr){
            //判断当前数据是否符合要求
            if(p.test(i)){
                list.add(i);
            }
        }
        //把集合转为Integer类型数组
        return list.toArray(new Integer[list.size()]);
    }
}
//运行结果:	[123, 56]

Predicate 接口中,还定义了一些默认方法和静态方法:
and()
or()
negate()
在使用该接口来做判断的时候,经常需要几个条件同时成立,或者其中一个条件成立,或者求反。
在这种情况下,除了可以在代码中选择使用&&,||,!之外,也可以分别使用这三个方法来代替。

public class Test {
    public static void main(String[] args) {
        Test t = new Test();
        Integer[] arr = {12,3,43,123,34,6,56,7};
        //条件1,数据大于10
        Predicate<Integer> p1 = e -> e>10;
        //条件2,数据小于50
        Predicate<Integer> p2 = e -> e<50;
        //俩个条件同时成立
        Predicate<Integer> p = p1.and(p2);
        arr = t.filter(arr,p);
        System.out.println(Arrays.toString(arr));
    }
    
    public Integer[] filter(Integer[] arr, Predicate<Integer> p){
        List<Integer> list = new ArrayList<>();
        for(Integer i: arr){
            //判断当前数据是否符合要求
            if(p.test(i)){
                list.add(i);
            }
        }
        //把集合转为Integer类型数组
        return list.toArray(new Integer[list.size()]);
    }
}
//运行结果:[12, 43, 34]
public class Test {
    public static void main(String[] args) {
        Test t = new Test();
        Integer[] arr = {12,3,43,123,34,6,56,7};
        //条件1,数据小于10
        Predicate<Integer> p1 = e -> e<10;
        //条件2,数据大于50
        Predicate<Integer> p2 = e -> e>50;
        //俩个条件成立一个即可
        Predicate<Integer> p = p1.or(p2);
        arr = t.filter(arr,p);
        System.out.println(Arrays.toString(arr));
    }
    
    public Integer[] filter(Integer[] arr, Predicate<Integer> p){
        List<Integer> list = new ArrayList<>();
        for(Integer i: arr){
            if(p.test(i)){
                //判断当前数据是否符合要求
                list.add(i);
            }
        }
        //把集合转为Integer类型数组
        return list.toArray(new Integer[list.size()]);
    }
}
//运行结果:	[3, 123, 6, 56, 7]
public class Test {
    public static void main(String[] args) {
        Test t = new Test();
        Integer[] arr = {12,3,43,123,34,6,56,7};
        //条件1,数据大于10
        Predicate<Integer> p1 = e -> e>10;
        //获取条件1相反的数据
        Predicate<Integer> p = p1.negate();
        arr = t.filter(arr,p);
        System.out.println(Arrays.toString(arr));
    }
    
    public Integer[] filter(Integer[] arr, Predicate<Integer> p){
        List<Integer> list = new ArrayList<>();
        for(Integer i: arr){
            //判断当前数据是否符合要求
            if(p.test(i)){
            	list.add(i);
            }
        }
        //把集合转为Integer类型数组
        return list.toArray(new Integer[list.size()]);
    }
}
//运行结果:[3, 6, 7]
//JDK1.8中,给 Collection 集合增加了默认方法: removeIf
//此方法就使用了 Predicate 作为自己的参数,来移除符合条件的数据,实现如下:
public class Test {
    public static void main(String[] args) {
        Collection<String> col = new ArrayList<String>();
        col.add("abc");
        col.add("hello");
        col.add("world");
        col.removeIf((str)->str.contains("o"));
        System.out.println(col);
    }
}
//运行结果:[abc]

Consumer

java.util.function.Consumer 接口:

定义一个方法,用来对学生对象进行操作

public class Test {
    public static void main(String[] args) {
        Test t = new Test();
        Student stu = new Student("tom");
        //操作1,给stu对象的name属性值加前缀
        Consumer<Student> consumer1 = (s) -> s.name = "briup_"+s.name;
        //操作2,给stu对象的name属性值加后缀
        Consumer<Student> consumer2 = (s) -> s.name =
        s.name+"_"+System.currentTimeMillis();
        //操作3,给stu对象的name属性值,先加前缀,再加后缀
        Consumer<Student> consumer3 = consumer1.andThen(consumer2);
        //如果传入consumer1,表示只加前缀
        //如果传入consumer2,表示只加后缀
        //如果传入consumer3,表示先加前缀,再加后缀
        t.operStu(stu,consumer3);
        System.out.println(stu.name);
    }
    
    public void operStu(Student stu, Consumer<Student> consumer){
    	consumer.accept(stu);
    }
}

class Student{
    String name;
    public Student(String name) {
    	this.name = name;
    }
}
//运行结果:briup_tom_1598282322884

JDK1.8中,给Collection集合增加了默认方法: forEach 用来遍历集合

public class Test {
    public static void main(String[] args) {
        Collection<String> col = new ArrayList<String>();
        col.add("abc");
        col.add("hello");
        col.add("world");
        //去掉中间变量,直接把Lambda表达式当前参数传入到forEach方法中
        col.forEach((t)->System.out.println(t));
    }
}
//运行结果:
abc
hello
world

Function
java.util.function.Function<T, R> 接口,

public class Test {
    public static void main(String[] args) {
        String str = "a-b-c-a-b-c";
        //传入字符串,返回数组,操作为把字符串按照 "-" 进行分割为字符串数组
        // "a-b-c-a-b-c" 转换为 {"a","b","c","a","b","c"}
        Function<String,String[]> f1 = s -> s.split("-");
        //传入字符串数组,返回Set<String>集合,目的是去除数组中重复的数据,存放把结果存放到Set集合中
        //{"a","b","c","a","b","c"} 转换为集合 [a, b, c]
        Function<String[], Set<String>> f2 = arr -> {
            Set<String> set = new HashSet<>();
            for(String string : arr){
                set.add(string);
            }
            return set;
        };
        //刚好,f1函数的结果,作为f2函数的参数,f1和f2组合成f3函数
        //f3函数表示传入字符串,最后返回Set<String>集合
        //其实内部是先将字符串交给f1函数转换数组,在将数组交给f2函数转换Set集合
        //通过上面列出的andThen源码也可以看出是这个效果
        Function<String,Set<String>> f3 = f1.andThen(f2);
        Set<String> set = f3.apply(str);
        System.out.println(set);
    }
}
//运行结果:
[a, b, c]

理解了andThen方法,那么compose方法也就理解:f1.andThen(f2),f1.compose(f2)
俩个方法的区别是,把f2操作放在f1操作之前还是之后的问题

方法引用

使用Lambda表达式,可以表示一个函数,这个函数由 “参数列表、函数主体、返回类型” 这三部分组 成,同时这个函数还是一个函数式接口中,唯一的抽象方法的实现

方法引用三大类:方法/构造器:类名::new/数组:类型[]::new

方法:
静态:类名::方法名/实例

实例:类名::非静态方法名

构造方法引用:类名::new

使用对象引用方法:对象::方法名

数组构造:数组类型::new

Optional

在Java8之前,一个函数可能因为代码逻辑问题,最终返回一个null,这时候程序中很可能出现空指针异常。而在Java8中,不推荐返回 null ,而是返回 Optional

Optional 中常用的方法: of ofNullable isPresent ifPresent get orElse orElseGet map flatMap filter

Stream

java.util.stream.Stream 接口,表示能应用在一组元素上,一次执行的操作序列,也就是可以对一组数据进行连续的多次操作。

Stream在使用的时候,需要指定一个数据源,比如 java.util.Collection 的子类, List 或者 Set都可以,但是 Map 类型的集合不支持。
Stream是对集合功能的增强,它提供了各种非常便利、高效的聚合操作,可以大批量数据操作,同时再结合Lambda表达式,就可以极大的提高编程效率。

Stream的API提供了串行和并行两种模式进行操作数据。
Stream操作分为中间操作或者最终操作两种:

中间操作,返回Stream本身,这样就可以将多个操作依次串起来
例如, map、flatMap、filter、distinct、sorted、peek、limit、skip、parallel、
sequential、unordered

最终操作,返回一特定类型的计算结果
例如, forEach、forEachOrdered、toArray、reduce、collect、min、max、count、
anyMatch、allMatch、noneMatch、findFirst、findAny、iterator

基本类型
对于基本数值类型,有专门的三种Stream类型:
IntStream
LongStream
DoubleStream
虽然也可以用Stream类型,并指定泛型:
Stream<\Integer>
Stream<\Long>
Stream<\Double>
但是,在数据量较大的时候,自动拆箱/装箱会比较消耗性能,所以提供了上面三种专门针对基本类型的
Stream

Stream转其他
使用Stream的API对数据操作后,还可以把结果转换为其他类型

一个Stream在代码中,只能使用一次,再次使用就会报错

Stream操作主要分为中间操作或者最终操作:
最终操作,就是把Stream处理完了,已经有结果了,之后就不能再使用这个Stream了
中间操作,对Stream进行一个中间操作后,还可以对其进行下一步的继续操作

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值