虽然现在的JDK版本已经到达了11,但是jdk1.8版本的改变依然是浓墨重彩的一笔。本篇博文为自己梳理一些1.8版本的特性。由最重要的开始
关键点 1.lambda表达式 2.流以及流的实际编程使用情况
3..函数式编程 4.相关的面试题目
3.5 默认方法 3.6用Optional
博客推荐:https://blog.csdn.net/yitian_66/article/details/81010434
前言:Java8的一些概念
1.第一个编程概念是流处理:思路变成 了把这样的流变成那样的流(就像写数据库查询语句时的那种思路),而不是一次只处理一个项 目。另一个好处是,Java 8可以透明地把输入的不相关部分拿到几个CPU内核上去分别执行你的 Stream操作流水线——这是几乎免费的并行,用不着去费劲搞Thread了
2.用行为参数化把代码传递给方法 (将新的行为作为一个参数进行传递)
3.Java 8也用Stream API(java.util.stream)解决了这两个问题:集合处理时的套路和晦 涩,以及难以利用多核
1..lambda表达式:
1.概念:简洁地表示可传递的匿名函数的一种方式:它没有名称,但它 有参数列表、函数主体、返回类型,可能还有一个可以抛出的异常列表
传递:可以作为参数传递给方法或者赋值给变量
简洁:无需像匿名类那样写很多模板代码
函数——我们说它是函数,是因为Lambda函数不像方法那样属于某个特定的类。但和方 法一样,Lambda有参数列表、函数主体、返回类型,还可能有可以抛出的异常列表
2.语法:
基本语法样式:
(parameters) -> expression
(parameters) -> { statements; }
1.创建对象 ()->new Apple(10)
2.比较两个对象 (Apple a1, Apple a2)->a1.getWeight.compator((a2.getWeight))
3.消费一个对象
3.函数式接口
函数式接口(Functional Interface)就是一个有且仅有一个抽象方法,但是 可以有多个非抽象方法的接口。
用函数式接口可以干什么呢?Lambda表达式允许你直接以内联的形式为函数式接口的抽象方法提供实现,并把整个表达式作为函数式接口的实例(具体说来,是函数式接口一个具体实现 的实例)
比较:Runnable r1 = () -> System.out.println("Hello World 1"); (使用Lambda )
(使用内部类)
Runnable r2 = new Runnable(){
public void run(){
System.out.println("Hello World 2");
}
};
例子:
@FunctionalInterface interface GreetingService { void sayMessage(String message); }
//那么就可以使用Lambda表达式来表示该接口的一个实现(注:JAVA 8 之前一般是用匿名类实现的):
GreetingService greetService1 = message -> System.out.println("Hello " + message);
3.函数式编程:
3.5流(并行流:parallelStream)(作用:高效地处理集合)
流是Java API的新成员,它允许你以声明性方式处理数据集合(通过查询语句来表达,而不 是临时编写一个实现)。就现在来说,你可以把它们看成遍历数据集的高级迭代器。此外,流还
可以透明地并行处理,你无需写任何多线程代码了
好处
声明性——更简洁,更易读
可复合——更灵活
可并行——性能更好
流的API使用
//筛选出所有素菜,创建一张素食菜单:
@Test
public void test1() {
List<Dish> sucai = menu.stream()
.filter(Dish::isVegetarian)
.collect(Collectors.toList());
sucai.forEach(a->System.out.println(a.getName()+" "));
}
区分:中间操作与终端操作
。重要的是,除非流水线上触发一个终端操作,否则中间操作不会执行任何处理——它们很懒。 这是因为中间操作一般都可以合并起来,在终端操作时一次性全部处理。
流的使用API
distinct
limit
skip(n)
map
查找和匹配
1. anyMatch方法可以回答“流中是否有一个元素能匹配给定的谓词
if(menu.stream().anyMatch(Dish::isVegetarian)){
System.out.println("The menu is (somewhat) vegetarian friendly!!");
}
anyMatch方法返回一个boolean,因此是一个终端操作
默认方法
介绍:在Java8中为interface中的接口提供了默认的实现
解决的问题是:接口与实现类之间的耦合度太高,修改接口里的抽象方法,每个实现类都必须去修改
默认方法解决了这个问题,它可以为接口添加新的方法,而不会破坏已有的接口的实现
例子如下:使用default
- interface InterfaceA {
- default void foo() {
- System.out.println("InterfaceA foo");
- }
- }
JDK8及以后,允许我们在接口中定义static方法和default方法。
public interface JDK8Interface {
// static修饰符定义静态方法
static void staticMethod() {
System.out.println("接口中的静态方法");
}
// default修饰符定义默认方法
default void defaultMethod() {
System.out.println("接口中的默认方法");
}
}
静态方法,只能通过接口名调用,不可以通过实现类的类名或者实现类的对象调用。default方法,只能通过接口实现类的对象来调用。
default方法也可以被实现类复写
Optional代替 null(Optional不是对null关键字的一种替代,而是对于null判定提供了一种更加优雅的实现。)
变量存在时,Optional类只是对类简单封装。变量不存在时,缺失的值会被建模成一个“空” 的Optional对象,由方法Optional.empty()返回。Optional.empty()方法是一个静态工厂 方法,它返回Optional类的特定单一实例。你可能还有疑惑,null引用和Optional.empty() 有什么本质的区别吗?从语义上,你可以把它们当作一回事儿,但是实际中它们之间的差别非常 大:如果你尝试解引用一个null,一定会触发NullPointerException,不过使用 Optional.empty()就完全没事儿,它是Optional类的一个有效对象,多种场景都能调用,非 常有用
4.Java8面试题目:
0. Java1.8引入了哪一些新特性,解决了什么问题
1.Java8支持函数编程是什么意思?
在jdk1.8之前所有东西都是面向对象的java中的 所有内容都作为对象存在。对方法/函数的所有调用都是使用对象或类引用进行的。方法/功能本身并不是独立存在的。使用Java 8,引入了函数式编程。所以我们可以使用匿名函数。Java是一种一流的面向对象语言。除了原始数据类型之外,Java中的所有内容都是一个对象。即使是一个数组也是一个对象。每个类都创建对象的实例。没有办法只定义一个独立于Java的函数/方法。无法将方法作为参数传递或返回该实例的方法体。
2.什么是Lambda表达式?
以定义为允许用户将方法作为参数传递的匿名函数。这有助于删除大量的样板代码。Lambda函数没有访问修饰符(私有,公共或受保护),没有返回类型声明和没有名称。
Lambda表达式允许用户将“函数”传递给代码。所以,与以前需要一整套的接口/抽象类想必,我们可以更容易地编写代码。例如,假设我们的代码具有一些复杂的循环/条件逻辑或工作流程。使用lambda表达式,在那些有难度的地方,可以得到很好的解决。
3.lambda表达式的优点是什么?
- 通过Java 8,可以更轻松地在多个线程上分发集合的处理。 集合现在可以在内部组织自己的迭代,将并行化的责任从客户端代码转移到库代码中。
- 更少的代码行。
- 使用Java 8 Lambda表达式可以实现更高的效率。通过使用具有多核的CPU,用户可以通过使用lambda并行处理集合来利用多核CPU。
4.Java8你了解吗
1、 HashMap 的底层实现有变化:
HashMap 是数组 + 链表 + 红黑树(JDK1.8 增加了红黑树部分)实现。
2.在接口定义中可以使用默认方法
3、Lambda 表达式(也称为闭包),允许我们将函数当成参数传递给某个方法,或者把代码本身当做数据处理。使用Lambda 表达式可以使代码变的更加简洁紧凑。
4、 函数式接口:
指的是只有一个函数的接口,java.lang.Runnable 和 java.util.concurrent.Callable 就是函数式接口的例子;java8 提供了一个特殊的注解 @Functionallnterface 来标明该接口是一个函数式接口。
5.Optional来优化null
Optional 类是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。
Optional 是个容器:它可以保存类型T的值,或者仅仅保存null。Optional提供很多有用的方法,这样我们就不用显式进行空值检测。
Optional 类的引入很好的解决空指针异常
6.提供了Stream API来高效地处理集合
可以让你以一种声明的方式处理数据。
Stream使用一种类似用SQL语句从数据库查询数据的直观方式来提供一种对Java集合运算和表达的高阶抽象。
Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。
思路:为什么需要lambda表达式:(传递行为化参数)
demo:按照条件过滤集合
不优化原始方法:每次都是for循环按照条件过滤
优化方式一:按照策略模式进行优化,将行为条件实现特定的接口方法进行传参
缺点:麻烦,每次一个新的策略都要重新写一个类
优化方式二:在优化方式一的情况之下使用匿名内部类
相比优化方式一:显得便捷一点
缺点:主要的代码就是一句,所以还是挺麻烦的。
优化方式三:lambda表达式
贴图
lambda基础语法:
左侧是函数式借口的参数,右侧是方法体
语法一:无参数,无返回值
( ) -> System.out.println("hello");
Runnable runnable = () ->System.out.println("hello");
语法二:有参数,无返回
Consumer<String> consumer = (m)->System.out.println(m);
consumer.accept("你哈");
对consumer的accept()抽象方法进行实现
注意:左侧只有一个参数,()可以忽略
语法三:两个以上参数,有返回值,方法体有多条语句
要使用大括号哦
Comparator<Integer> comparator = (x,y) ->{
System.out.println("多条语句实现");
return Integer.compare(x, y);
};
comparator.compare(4, 74);
语法格式六:lambda表达式的参数类型可以不写,因为JVM的编译器通过上下文进行推断
二、lambada表达式需要函数式接口的支持
@FunctionalInterface
interface ILambdaCaculator{
int result(int a,int b);
}
public class LambdaTest {
public static void main(String[] args) {
System.out.println("add :"+LambdaUse((a,b)-> a+b,12,14));
}
//将lambada表达式作为行为化参数传入方法中
public static int LambdaUse(ILambdaCaculator lambda,int a,int b){
return lambda.result(a, b);
}
}
Java8内置的四大核心函数式接口
1.Consumer<T> :消费型接口
void accept(T t)
2.Function<T,R>:函数型
R apply(T, t)
3.Predicate<T>:断言接口
boolean test(T t)
4.Supplier<T>:供给接口
T get()
四大接口类型的Demo
public class TestLambda2 {
// 消费性接口(无返回值)
public void happy(double money, Consumer<Double> con) {
con.accept(money);
}
@Test
public void test1() {
happy(1111, (m) -> System.out.println("消费了" + m));
}
//////////////////////////////////////////
// 供给型接口
// 需求:产生特定的整数,并放入集合中
public List<Integer> getNumberList(int num, Supplier<Integer> sup) {
List<Integer> list = new ArrayList<Integer>();
for (int i = 0; i < num; i++) {
list.add(sup.get());
}
return list;
}
@Test
public void test2() {
List<Integer> list = getNumberList(100, () -> (int) (Math.random() * 100));
for (Integer num : list) {
System.out.println(num);
}
}
// Function<T,R>函数型接口
public String stringHandler(String str, Function<String, String> fun) {
return fun.apply(str);
}
@Test
public void test3() {
String aString = stringHandler("hasfasfad", str -> str.toUpperCase());
System.out.println("转化为大写:" + aString);
}
}
//需求:将满足条件的字符串放入集合中
public List<String> filterStrings (List<String> list, Predicate<String> pre){
List<String> strList = new ArrayList<String>();
for(String str :list){
if(pre.test(str)){
strList.add(str);
}
}
return strList;
}
@Test
public void test4(){
List<String> list = new ArrayList<String>();
list.add("hesdsfd");
list.add("sfsdf");
list.add("s");
List<String> list2 = filterStrings(list, (str) ->str.length() >3);
for(String str:list2){
System.out.println(str);
}
}
}
方法引用:如果lambda表达式中的内容有方法已经实现了,我们就可以使用方法引用
主要的语法形式:
对象::实例方法名
类::静态方法名
类::实例方法名