(一)Java8基础知识

一、Java8基础概念

1、流处理

流是一系列数据项,一次只生成一项。程序可以从输入流中一个一个读取数据项,然后利用同样的方式将数据项写入输出流。一个程序的输出流很可能是另一个程序的输入流。
举例:比如在Unix或Linux中,很多程序都从标准输入(Java中的System.in)来读取数据,然后把结果写入标准输出流(System.out)。

2、方法引用

方法引用通过方法的名字来指向一个方法,方法引用可以使语言的构造更紧凑简洁,减少冗余代码。
语法:方法引用使用一对冒号::

传统方式筛选一个目录中的所有隐藏文件

//用isHidden方法筛选文件时,需要把方法包裹在FileFilter对象里,然后才能传递给File.listFiles方法
File[] hiddenFiles = new File(".").listFiles(new FileFilter() {
    @Override
    public boolean accept(File file) {
        //筛选隐藏文件
        return file.isHidden();
    }
});

使用方法引用方式

File[] hiddenFiles = new File(".").listFiles(File::isHidden);

二、行为参数化

1、定义

行为参数化:就是一个方法接受多个不同的行为(或战略)作为参数,并在内部使用它们,完成不同行为的能力。行为参数化可以更好的适应不断变化的需求,减少实际开发工作量。

2、应对不断变化的需求

1、假设有这么一个场景,某农场主有一个果园,第一次他需要选出绿色的苹果,可以有如下解决方案:
@Data
@AllArgsConstructor
public class Apple {
    /** 重量 */
    private int weight;
    
    /** 颜色 */
    private String color;
}
public class Test01 {
    public static void main(String[] args) {
        List<Apple> apples = new ArrayList<>();
        apples.add(new Apple(10, "green"));
        apples.add(new Apple(5, "red"));
        apples.add(new Apple(5, "green"));
        apples.add(new Apple(10, "red"));
        List<Apple> greenApples = filterGreenApples(apples);
        for (Apple greenApple : greenApples) {
            System.out.println(greenApple);
        }
    }

    /**
     * 筛选出绿色的苹果
     * @param apples
     * @return
     */
    private static List<Apple> filterGreenApples(List<Apple> apples) {
        List<Apple> result = new ArrayList<>();
        for (Apple apple : apples) {
            if (apple.getColor().equals("green")) {
                result.add(apple);
            }
        }
        return result;
    }
}
/** 运行结果 */
Apple(weight=10, color=green)
Apple(weight=5, color=green)
2、现在农场主有了新的想法,想要筛选出更多颜色的苹果(红色、绿色、暗红色、黄色等),上面这种方式就应附不了,我们可以把颜色作为参数,这样就能灵活的适应变化了。
public class Test02 {
    public static void main(String[] args) {
        List<Apple> apples = new ArrayList<>();
        apples.add(new Apple(10, "green"));
        apples.add(new Apple(5, "red"));
        apples.add(new Apple(5, "green"));
        apples.add(new Apple(10, "red"));
        System.out.println("=====================筛选绿色=====================");
        List<Apple> greenApples1 = filterGreenApples(apples,"green");
        for (Apple greenApple : greenApples1) {
            System.out.println(greenApple);
        }
        System.out.println("=====================筛选红色=====================");
        List<Apple> greenApples2 = filterGreenApples(apples,"red");
        for (Apple greenApple : greenApples2) {
            System.out.println(greenApple);
        }
    }

    /**
     * 根据不同颜色筛选
     * @param apples
     * @param color
     * @return
     */
    private static List<Apple> filterGreenApples(List<Apple> apples,String color) {
        List<Apple> result = new ArrayList<>();
        for (Apple apple : apples) {
            if (apple.getColor().equals(color)) {
                result.add(apple);
            }
        }
        return result;
    }
}
/** 运行结果 */
=====================筛选绿色=====================
Apple(weight=10, color=green)
Apple(weight=5, color=green)
=====================筛选红色=====================
Apple(weight=5, color=red)
Apple(weight=10, color=red)
3、这是农场主又有了新的想法,要是能筛选重的苹果就好了,这时你将上面的代码复制了一份,并将重量作为参数,但是这种做法有点DRY(Don’t Repeat Yourself,不要重复自己)的软件的工程规则。这时,想到一个标识,来区分不同的属性。
public class Test03 {
    public static void main(String[] args) {
        List<Apple> apples = new ArrayList<>();
        apples.add(new Apple(10, "green"));
        apples.add(new Apple(5, "red"));
        apples.add(new Apple(5, "green"));
        apples.add(new Apple(10, "red"));
        System.out.println("=====================筛选颜色=====================");
        List<Apple> greenApples1 = filterGreenApples(apples,"green",0,true);
        for (Apple greenApple : greenApples1) {
            System.out.println(greenApple);
        }
        System.out.println("=====================筛选重量=====================");
        List<Apple> greenApples2 = filterGreenApples(apples,"",5,false);
        for (Apple greenApple : greenApples2) {
            System.out.println(greenApple);
        }
    }

    /**
     * 根据标识筛选不同属性的
     * @param apples
     * @param color
     * @param weight
     * @param flag
     * @return
     */
    private static List<Apple> filterGreenApples(List<Apple> apples,String color,int weight,boolean flag) {
        List<Apple> result = new ArrayList<>();
        for (Apple apple : apples) {
            if (flag && apple.getColor().equals(color) || !flag && apple.getWeight() > weight) {
                result.add(apple);
            }
        }
        return result;
    }
}
/** 运行结果 */
=====================筛选颜色=====================
Apple(weight=10, color=green)
Apple(weight=5, color=green)
=====================筛选重量=====================
Apple(weight=10, color=green)
Apple(weight=10, color=red)
缺点:这样解决很简单但是很糟糕,因为并不能很好的解决需求的变化,假如还要要求对形状、大小等进行区分等方法将会十分复杂,可读性也会很差。
4、前面的示例已经看到,我们需要一种比添加很多参数更好的方法来应对变化的需求。来看看更高层次的的抽象吧。因为要根据属性进行筛选,所以我们对选择进行标准化建模,根据Apple的属性来返回一个boolean值,我们把它称为谓词(即一个返回boolean值的函数)。

定义一个接口

public interface ApplePredicate {
    boolean appleTest (Apple apple);
}

筛选出重的

public class AppleHeavyWeightPredicate implements ApplePredicate {
    @Override
    public boolean appleTest(Apple apple) {
        return apple.getWeight() > 5;
    }
}

筛选出绿色的

public class AppleGreenColorPredicate implements ApplePredicate {
    @Override
    public boolean appleTest(Apple apple) {
        return "green".equals(apple.getColor());
    }
}
这种设计方式类似设计模式中的策略模式,定义一族算法,把它们封装起来(称为“策略”),然后在运行的时候选择一个算法,这里的算法族就是ApplePredicate,不同的策略就是AppleGreenColorPredicate与AppleHeavyWeightPredicate

行为参数化方式实现

public class Test04 {
    public static void main(String[] args) {
        List<Apple> apples = new ArrayList<>();
        apples.add(new Apple(10, "green"));
        apples.add(new Apple(5, "red"));
        apples.add(new Apple(5, "green"));
        apples.add(new Apple(10, "red"));
        System.out.println("=====================筛选颜色=====================");
        List<Apple> greenApples1 = filterApples(apples,new AppleGreenColorPredicate());
        for (Apple greenApple : greenApples1) {
            System.out.println(greenApple);
        }
        System.out.println("=====================筛选重量=====================");
        List<Apple> greenApples2 = filterApples(apples,new AppleHeavyWeightPredicate());
        for (Apple greenApple : greenApples2) {
            System.out.println(greenApple);
        }
    }

    /**
     * 根据抽象条件筛选
     * @param apples
     * @param applePredicate
     * @return
     */
    private static List<Apple> filterApples(List<Apple> apples,ApplePredicate applePredicate) {
        List<Apple> result = new ArrayList<>();
        for (Apple apple : apples) {
            if (applePredicate.appleTest(apple)) {
                result.add(apple);
            }
        }
        return result;
    }
}
/** 运行结果 */
=====================筛选颜色=====================
Apple(weight=10, color=green)
Apple(weight=5, color=green)
=====================筛选重量=====================
Apple(weight=10, color=green)
Apple(weight=10, color=red)
优点:代码更灵活,易扩展,可以创建不同的类实现ApplePredicate就行了,就能对不同属性进行筛选了。
思考:其实本质上,filterApples在内部进行条件判断的时候,我们使用的是不是仅仅是它内部的代码片段进行判断,即谓语的代码块,我们可不可以直接使用代码块呢?
5、Lambda表达式方式实现
public class Test04 {
    public static void main(String[] args) {
        List<Apple> apples = new ArrayList<>();
        apples.add(new Apple(10, "green"));
        apples.add(new Apple(5, "red"));
        apples.add(new Apple(5, "green"));
        apples.add(new Apple(10, "red"));
        System.out.println("=====================筛选颜色=====================");
        List<Apple> greenApples1 = filterApples(apples,(Apple apple) -> "red".equals(apple.getColor()));
        for (Apple greenApple : greenApples1) {
            System.out.println(greenApple);
        }
        System.out.println("=====================筛选重量=====================");
        List<Apple> greenApples2 = filterApples(apples,(Apple apple) -> apple.getWeight() > 5);
        for (Apple greenApple : greenApples2) {
            System.out.println(greenApple);
        }
    }

    /**
     * 根据抽象条件筛选
     * @param apples
     * @param applePredicate
     * @return
     */
    private static List<Apple> filterApples(List<Apple> apples,ApplePredicate applePredicate) {
        List<Apple> result = new ArrayList<>();
        for (Apple apple : apples) {
            if (applePredicate.appleTest(apple)) {
                result.add(apple);
            }
        }
        return result;
    }
}
/** 运行结果 */
=====================筛选颜色=====================
Apple(weight=10, color=green)
Apple(weight=5, color=green)
=====================筛选重量=====================
Apple(weight=10, color=green)
Apple(weight=10, color=red)
6、假如农场主想要对其他的水果也进行筛选,此时我们可以将List类型抽象化
public interface ApplePredicate<T> {
    boolean appleTest (T t);
}
public class Test05 {
    public static void main(String[] args) {
        List<Orange> oranges = new ArrayList<>();
        oranges.add(new Orange(10, "green"));
        oranges.add(new Orange(5, "yellow"));
        oranges.add(new Orange(5, "green"));
        oranges.add(new Orange(10, "yellow"));
        System.out.println("=====================筛选颜色=====================");
        List<Orange> oranges1 = filterApples(oranges, (Orange apple) -> "yellow".equals(apple.getColor()));
        for (Orange orange : oranges1) {
            System.out.println(orange);
        }
        System.out.println("=====================筛选重量=====================");
        List<Orange> oranges2 = filterApples(oranges,(Orange apple) -> apple.getWeight() > 5);
        for (Orange orange : oranges2) {
            System.out.println(orange);
        }
    }

    /**
     * 根据抽象条件筛选
     * @param apples
     * @param applePredicate
     * @return
     */
    private static <T> List<T> filterApples(List<T> apples,ApplePredicate<T> applePredicate) {
        List<T> result = new ArrayList<>();
        for (T apple : apples) {
            if (applePredicate.appleTest(apple)) {
                result.add(apple);
            }
        }
        return result;
    }
}
/** 运行结果 */
=====================筛选颜色=====================
Orange(weight=5, color=yellow)
Orange(weight=10, color=yellow)
=====================筛选重量=====================
Orange(weight=10, color=green)
Orange(weight=10, color=yellow)

三、Lambda表达式

1、概述

可以把Lambda表达式理解为简洁地表示可传递的匿名函数的一种方式:它没有名称,但它有参数列表、函数主体、返回类型,可能还有一个可以抛出的异常列表。
特点:
  • 没有明确的名称。
  • Lambda函数不像方法那样属于某个特定的类。但和方法一样,Lambda有参数列表、函数主体、返回类型,还可能有可以抛出的异常列表。
  • Lambda表达式可以作为参数传递给方法或存储在变量中。
  • 无需像匿名类那样写很多模板代码。

2、语法

(参数类型 参数1,参数类型 参数2...) -> {代码;}
说明:
  1. 如果参数有多个,那么使用逗号分隔。如果参数没有,则留空。
  2. 箭头是固定写法。
  3. 大括号相当于方法体。
  4. 使用前提:必须是函数式接口。
注意:如果只有一行代码,并且是使用return的情况下,要使此Lambda有效,必须使用花括号,因为return是一个流程控制语句。

3、Lambda省略规则

  • 参数类型可以省略。但是只能同时省略所有参数的类型,或者干脆都不省略。
  • 如果参数有且仅有一个,那么小括号可以省略。
  • 如果大括号内的语句有且仅有一条,那么无论是否有返回值,return、大括号、分号都可以省略。

4、函数式接口

函数式接口就是只定义一个抽象方法的接口,有且仅有一个抽象方法的接口。
作用:Lambda表达式允许你直接以内联的形式为函数式接口的抽象方法提供实现,并把整个表达式作为函数式接口的实例(具体说来,是函数式接口一个具体实现的实例)。
函数描述符:函数式接口的抽象方法的签名基本上就是Lambda表达式的签名。我们将这种抽象方法叫作函数描述符

5、@FunctionalInterface注解

@FunctionalInterface注解标记的类型表明这是一个函数接口。如果你用@FunctionalInterface定义了一个接口,而它却不是函数式接口的话,编译器将返回一个提示原因的错误。请注意,@FunctionalInterface不是必需的,但对于为此设计的接口而言,使用它是比较好的做法。它就像是@Override标注表示方法被重写了。

四、常用函数式接口

1、Predicate接口

java.util.function.Predicate<T>接口定义了一个名叫test的抽象方法,它接受泛型 T对象,并返回一个boolean
public class Test01 {
    public static void main(String[] args) {
        PredicateMethod(s -> s.length()>5,"hello");
    }

    private static void PredicateMethod(Predicate<String> predicate,String str){
        boolean flag = predicate.test(str);
        System.out.println("判断结果"+flag);
    }
}
/** 运行结果 */
判断结果false

2、Consumer接口

java.util.function.Consumer<T>定义了一个名叫accept的抽象方法,它接受泛型T 的对象,没有返回(void)。你如果需要访问类型T的对象,并对其执行某些操作,就可以使用这个接口,消费数据。
public class Test02 {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("1","2","3");
        //Lambda是Consumer中accept方法的实现
        forEach(list,(String i)->System.out.println(i));
    }

    public static <T> void forEach(List<T> list, Consumer<T> c) {
        for (T i : list) {
            c.accept(i);
        }
    }
}
/** 运行结果 */
1
2
3

3、Function接口

java.util.function.Function<T,R>接口用来根据一个类型的数据得到另一个类型的数据,前者称为前置条件,后者称为后置条件。有一个抽象apply的方法,它接受一个 泛型T的对象,并返回一个泛型R的对象。
public class Test03 {
    public static void main(String[] args) {
        //Lambdas是Function接口的apply方法的实现
        List<Integer> list = map(Arrays.asList("java", "c++"), (String s) -> s.length());
        System.out.println(list);
    }

    private static <T,R> List<R> map(List<T> list, Function<T,R> function) {
        List<R> result = new ArrayList<>();
        for (T t : list) {
            result.add(function.apply(t));
        }
        return result;
    }
}
/** 运行结果 */
[4, 3]

4、装箱与拆箱

装箱(boxing):将原始类型转换为对应的引用类型的机制。
拆箱(unboxing):与装箱相反,将引用类型转换为原始类型。
注意:装箱和拆箱操作是自动完成的,但这在性能方面是要付出代价的。装箱后的值本质上就是把原始类型包裹起来,并保存在堆里。因此,装箱后的值需要更多的内存,并需要额外的内存搜索来获取被包裹的原始值。

5、常用的函数式接口及其函数描述符

函数式接口函数描述符原始类型转化
PredicateT -> booleanIntPredicate,LongPredicate,DoublePredicate
ConsumerT -> voidIntConsumer,LongConsumer,DoubleConsumer
Function<T,R>T -> RIntFunction,IntToDoubleFunction,IntToLongFunction,
LongFunction,LongToDoubleFunction,LongToIntFunction,
DoubleFunction,ToIntFunction,ToDoubleFunction,
ToLongFunction
Supplier() -> TBooleanSupplier,IntSupplier,LongSupplier,DoubleSupplier
UnaryOperatorT -> TIntUnaryOperator,LongUnaryOperator,DoubleUnaryOperator
BinaryOperator(T, T) -> TIntBinaryOperator,LongBinaryOperator,DoubleBinaryOperator
BiPredicate<L,R>(L, R) -> boolean
BiConsumer<T,U>(T, U) -> voidObjIntConsumer,ObjLongConsumer,ObjDoubleConsumer
BiFunction<T,U,R>(T, U) -> RToIntBiFunction<T,U>,ToLongBiFunction<T,U>,ToDoubleBiFunction<T,U>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值