Java8 是 Java 语言开发的一个主要版本。它新增了非常多的特性,这里主要认识常用的几个:
- Lambda表达式
- 函数式接口
- 方法引用
- 接口中的默认方法
- 新的日期 API
- Stream API
- Optional 类
Lambda 表达式的简单demo:
// 根据数组中元素的长度自然排序
public static void main(String[] args) {
String[] strs = new String[]{"as", "lambda", "s", "java", "ad"};
//1、匿名内部类实现
Arrays.sort(strs, new Comparator() {
@Override
public int compare(String o1, String o2) {
return Integer.compare(o1.length(), o2.length());
}
});
System.out.println("匿名内部类实现:"+ Arrays.toString(strs));
// 2、转化为Lambda 表达式1
Arrays.sort(strs, (String o1, String o2) -> { return Integer.compare(o1.length(), o2.length()); });
// 3、转化为Lambda 表达式2
Arrays.sort(strs, (String o1, String o2) -> Integer.compare(o1.length(), o2.length()));
// 4、转化为Lambda 表达式3
Arrays.sort(strs, (o1, o2) -> Integer.compare(o1.length(), o2.length()));
// 5、转化为Lambda 表达式4
Arrays.sort(strs, Comparator.comparingInt(String::length));
System.out.println(Arrays.toString(strs));
}
Lambda:λ(波长单位)允许把函数作为一个方法的参数(函数作为参数传递进方法中)。
Lambda表达式:也可称为闭包,把带有参数变量的表达式称为Lambda表达式。 实质上, 闭包是起函数的作用并可以像对象一样操作的对象。
使用 Lambda 表达式可以使代码变的更加简洁紧凑。注意:使用 Lambda 表达式的前提:接口中有且只有一个必须需要被实现的抽象方法(后面的函数式接口)。
1、Lambda表达式的基本语法
Lambda表达式本质上是一段匿名内部类,也可以是一段可以传递的代码.
Lambda表达式由三部分组成:参数列表、箭头、声明语句;基本语法为:() -> {}
- () 用来描述参数列表,
- {} 用来描述方法体,
- -> 为 Lambda运算符 ,读作(goes to)。
绝大多数情况,编译器都可以从上下文环境中推断出lambda表达式的参数类 型,所以参数可以省略。
1.1)参数列表
(1)如果没有参数,直接使用 () 括号来表示,() 不能省略。
(2)如果有一个参数,有两种写法:
如果参数写了参数类型,则必须要使用 () 括号表示,
如果参数不写参数类型,则 () 括号可以省略。
(3)如果有两个以上参数,不管参数是否写参数类型,都必须使用 () 括号表示,不能省略。
(4)如果参数要加修饰符或者标签,参数一定要加上完整类型,即:(修饰符 参数类型 参数, ... )
1.2)表达式: demo 有体现,这里不再举例
(1)如果表达式代码块只有一行代码,{} 可以省略
若方法有返回值,{}省略时,不能使用return关键字返回,会编译报错;{} 不省略时,{}代码块中必须使用 return返回。
(2)如果表达式代码块有多行,{} 不能省略
若方法有返回值,{}代码块中必须使用 return返回一个返回值。
3、Lambda表达式中的变量和变量作用域
Lambda表达式中的变量有三种:
- 参数:参数列表中的参数
- 局部变量:表达式代码块中的定义的局部变量
- 自由变量:不是参数也不是局部变量,而是外部定义的变量
结论:
1)操作参数和局部变量的使用方式和普通的变量使用方式相同
2)自由变量在 Lambda表达式中使用时,不能被修改(隐式 final修饰),操作自由变量的代码块称为闭包
3)变量作用域:Lambda表达式只能引用标记了 final 的外层局部变量(即自由变量)
Lambda表达式中的自由变量会被保存(Lambda会使用一种技术保存),无论什么时候执行 Lambda表达式,都可以直接使用。
4)Lambda表达式中的 this是指:创建 Lambda表达式的方法中的 this,指方法的该类对象。
public class Demo {
public static void main(String[] args) {
User user = new User();
user.printByCount("abc",5);
}
}
public class User {
public void printByCount(String str, int count) {
/*Runnable runnable = new Runnable() {
@Override
public void run() {
for (int i = 0; i < count; i++) {
System.out.println(i + str);
}
}
};*/
Runnable runnable = () -> {
//User tt = this; // // cn.jq.java8.User@5abfe45d
for (int i = 0; i < count; i++) {
//str = "aaa"; // 编译会报错:Variable used in lambda expression should be final or effectively final
System.out.println(i + str); // cn.jq.java8.User@5abfe45d
System.out.println(this);
}
};
new Thread(runnable).start(); // 线程结束,可能for循环还没结束,Lambda表达式可访问
}
}
4、方法引用:demo 有体现,下面有说明。
方法引用通过方法的名字来指向一个方法。使用一对冒号 :: 。
方法引用提供了非常有用的语法,可以直接引用已有 Java类或对象(实例)的方法或构造器。
方法引用与 Lambda 表达式一起使用,可以是代码更紧凑简洁,减少冗余。
java8里引入的一个方法引用符 ::。使用一对冒号,表示当 Lambda 表达式创建函数式接口的实现类对象,正好 Lambda 要写的抽象体是引用其他类的方法时,就可以使用。
Arrays.sort(strs, Comparator.comparingInt(String::length));
4.1)静态方法引用
格式:类名::方法名
注意事项:
- 被引用的方法参数列表和函数式接口中抽象方法的参数列表要一致。
- 接口的抽象方法没有返回值,引用的方法可以有返回值也可以没有。
- 接口的抽象方法有返回值,引用的方法必须有相同类型的返回值。
4.2)对象方法引用
格式:对象名::非静态方法名
注意事项与静态方法引用完全一致。
4.3)构造方法引用
格式:类名::new
注意事项:
- 被引用的类必须存在一个构造方法与函数式接口的抽象方法参数列表一致。
4.4)数组构造方法引用
格式:数据类型[ ]::new
4.5)特定类型的方法引用
格式:类名::非静态方法
比如:在Comparator函数式接口的抽象方法中传入的参数有两个,可是compareToIgnoreCase()方法参数只有一个,第一个传入的参数作调用对象。这就满足了特定类型的方法引用,所以可以简化成类名::非静态方法的形式。
public static void main(String[] args) {
ArrayList list = new ArrayList();
Collections.addAll(list, "sasdfa", "ASDsda", "aa", "Aa");
// Collections.sort(list, String::compareToIgnoreCase);
list.sort(String::compareToIgnoreCase);
System.out.println(list);
}
4.6)类中方法调用父类或本类方法引用
格式:
this::方法名
super::方法名
注意:
-
在有继承关系的类中,若方法想调用本类或父类的成员方法,在函数式接口抽象方法与成员方法参数列表相同,且返回值类型相同的情况下,也可以使用this和super的方法引用来简写原本的lambda代码。
5、构造器/构造方法引用
对于一个现有构造函数,可以利用它的名称和关键字new来创建它的一个引用ClassName::new
基本语法:类名::new
构造器引用对应的函数式接口的方法格式一定是:返回一个对象并且方法没有参数。
public class Demo {
public static void main(String[] args) throws UnsupportedEncodingException {
//List list2 = Utils.asList(()->{ return new LinkedList(); }, 1, 5, 4, 2, 4);
// 构造方法引用
List list2 = Utils.asList(LinkedList::new, 1, 5, 4, 2, 4);
System.out.println(list2.toString());
list2 = Utils.asList(ArrayList::new, 1, 5, 4, 2, 4);
System.out.println(list2.toString());
}
}
// 根据传入的不同list对象,返回List
public class Utils {
public static List asList(MyCreator creator,T... a) {
List list = creator.create(); // 得到传入的对象
System.out.println(list.getClass());
for (T t : a) list.add(t);
return list; // 把元素放到list并返回
}
}
@FunctionalInterface
public interface MyCreator
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?