JDK8新特性学习总结
public class DemoLambdaIntro {
public static void main(String[] args) {
//匿名内部类
//1.定义了一个没有名字的类
//2.这个类实现了runnable接口
//3.创建了这个类的对象
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程执行");
}
}).start();
}
}
匿名内部类的语法是很冗余的,其实我们关注的是run方法和里面要执行的代码,lambda表达式体现的是函数式变成思想,只需要将要执行的代码放到函数中(函数就是类中的方法),lambda就是一个匿名函数,我们只需要将要执行的代码放到lambda表达式中即可
public class DemoLambdaIntro {
public static void main(String[] args) {
//匿名内部类
//1.定义了一个没有名字的类
//2.这个类实现了runnable接口
//3.创建了这个类的对象
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程执行");
}
}).start();
//体验lambda表达式
new Thread(()->{
System.out.println("lambda表达式执行啦");
}).start();
}
}
lambda表达式的好处:可以简化匿名内部类,让代码更加精简
练习使用lambda表达式
public class DemoLambdaIntro {
/*
的标准格式:
lambda表达式是一个匿名函数,而函数相当于JAVA中的方法
(参数列表) -> {
}
(参数列表):参数列表
{}方法体
->没有实际含义,起到连接的作用
public static void main(String[] args) {
}
*/
public static void main(String[] args) {
goSwimming(new Swimmable() {
@Override
public void swimming() {
System.out.println("我是匿名内部类的游泳");
}
});
//小结:以后我们看到方法的参数是接口就可以考虑使用lambda表达式
//我们可以这么认为,Lambda表达式就是对接口中的抽象方法的重写
goSwimming(() ->{
System.out.println("我是lambda的游泳");
});
System.out.println("------------------");
goSiking(new Smokeable() {
@Override
public int smoking(String name) {
System.out.println("匿名内部类抽了"+ name +"的烟");
return 5;
}
});
goSiking((String name)->{
System.out.println("lambda抽了"+ name +"的烟");
return 6;
});
}
//练习有参数有返回值的lambda
private static void goSiking(Smokeable s){
int i = s.smoking("中华");
System.out.println("返回值:"+i);
}
//练习无参数无返回值的lambda
public static void goSwimming(Swimmable s){
s.swimming();
}
}
public interface Smokeable {
public abstract int smoking(String name);
}
public interface Swimmable {
public abstract void swimming();
}
增强练习案例
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.stream.Collectors;
public class DemoLambdaIntro {
public static void main(String[] args) {
ArrayList<Person> persons = new ArrayList<>();
persons.add(new Person("刘德华",58,174));
persons.add(new Person("张学友",58,176));
persons.add(new Person("刘德华",54,171));
persons.add(new Person("黎明",53,178));
/* Collections.sort(persons, new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
return o2.getAge()-o1.getAge();
}
});*/
Collections.sort(persons,(Person o1,Person o2) ->{
return o2.getAge()-o1.getAge();
});
for (Person person : persons) {
System.out.println(person);
}
}
}
public class Person {
String name;
int age;
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", height=" + height +
'}';
}
public Person(String name, int age, int height) {
this.name = name;
this.age = age;
this.height = height;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
int height;
}
小结:首先学习了lambda表达式的标准格式
(参数列表) -> {
}
以后我们看到调用的方法他的参数是接口就可以考虑使用lambda表达式来替代匿名内部类,不是所有的匿名内部类都能使用lambda来替换,lambda表示式相当于接口的抽象方法重写
小结
匿名内部类在编译的时候会一个class文件
Lambda在程序运行的时候形成一个类
1.在类中新增一个方法,这个方法的方法体就是Lambda表达式中的代码
2.还会形成一个匿名内部类,实现接口,,重写抽象方法
3.在接口的重写方法中会调用新生成的方法
Lambda省略格式
目标
掌握Lambda省略格式
在Lambda标准格式的基础上,使用省略写法的规则为:
1.小括号内参数的类型可以省略
2.如果小括号内有且仅有一个参数,则小括号可以省略
3.如果大括号内有且仅有一个语句,可以同时省略大括号、return关键字及语句分号(同时省略)
(int a) -> {
return new Person( ) ;
}
a -> new Person()
import java.util.ArrayList;
import java.util.Collections;
public class DemoLambdaIntro {
public static void main(String[] args) {
ArrayList<Person> persons = new ArrayList<>();
persons.add(new Person("刘德华",58,174));
persons.add(new Person("张学友",58,176));
persons.add(new Person("刘德华",54,171));
persons.add(new Person("黎明",53,178));
Collections.sort(persons,(o1,o2) ->o2.getAge()-o1.getAge());
persons.forEach(t -> System.out.println(t));
}
}
public class DemoLambdaIntro {
public static void main(String[] args) {
//方法的参数或局部变量类型必须为接口才能使用Lambda
test(() -> {
});
Runnable r = new Runnable() {
@Override
public void run() {
System. out. println("aa");
}
};
Flyable f =() -> {
System.out.println("我会飞啦");
};
}
public static void test (Flyable a) {
}
//只有一个抽象方法的接口称为函数式接口,我们就能使用Lambda
@FunctionalInterface //检测这个接口是不是只有一个抽象方法
interface Flyable {
//接口中有且仅有一个抽象方法
public abstract void eat() ;
// public abstract void eat2() ;
}
}
小结:
Lambda的语法非常简洁,但是Lambda表达式不是随便使用的,使用时有几个条件要特别注意:
1.方法的参数或局部变量类型必须为接口才能使用Lambda
2.接口中有且仅有一个抽象方法
Lambda和匿名内部类对比
目标
了解Lambda和匿名内部类在使用上的区别
1.所需的类型不一样
匿名内部类需要的类型可以是类、抽象类、接口
Lambda表达式需要的类型必须是接口
2.抽象方法的数是不一样
匿名内部类所需的接口中抽象方法的数量随意
Lambda表达式所需的接口只能有一个抽象方法
3.实现原理不同
置名内部类是在编译后会形成class
Lambda表达式是在程序运行的时候动态生成class
小结
当接口中只有一个抽象方法时,建议使用Lambda表达式其他其他情况还是需要使用匿名内部类
JDK 8接口增强介绍
JDK 8以前的接口:
interface 接口名{
静态常量;
抽象方法;
}
JDK 8对接口的增强,接口还可以有默认方法和静态方法
JDK 8的接口:
interface 接口名{
静态常量;
抽象方法;
默认方法:
静态方法;
}
interface A {
public abstract void test01() ;
//此时如果需要在A接口新增一个方法,所有继承这些接口的实现类都要重新去实现它,非常麻烦
// public abstract void test02() ;
}
class B implements A {
@Override
public void test01() {
System.out.println("B test01");
}
}
class C implements A {
@Override
public void test01() {
System.out.println("C test01");
}
}
接口中的默认方法实现类不必重写,可以直接使用,实现类也可以根据需要重写。这样就方便接口的扩展。
接口默认方法的定义格式
interface 接口名{
修饰符 default 返回值类型 方法名() {
代码;
}
}
接口默认方法的使用
方式一:实现类直接调用接口默认方法
方式二:实现类重写接口默认方法
public class DemoLambdaIntro {
public static void main(String[] args) {
BB bb = new BB() ;
bb. test01() ;
CC cc = new CC() ;
cc. test01() ;
}
}
interface AA {
public default void test01() {
System.out.println("我是接口AA默认方法");
}
}
//默认方法使用方式一:实现类可以直接使用
class BB implements AA{
}
//默认方法使用方式二:实现类可以重写默认方法
class CC implements AA {
@Override
public void test01(){
System.out.println("我是CC类重写的默认方法");
}
}
接口静态方法
为了方便按口扩展,JDK 8为法口新增了静态方法。
接口静态方法的定义格式
interface 接口名《
修饰符 static 返回值 类型方法名() {
代码;
}
}
接口静态方法的使用
直接使用接口名调用即可:接口名.静态方法名():
public class DemoLambdaIntro {
public static void main(String[] args) {
BBB bbb = new BBB() ;
// bbb.test01();
//使用接口名.静态方法名(;
AAA.test01();
}
}
interface AAA {
public static void test01() {
System.out.println("我是接口静态方法");
}
}
class BBB implements AAA{
/* @Override 静态方法不能重写
public static void test01() {
System.out.println("我是接口静态方法");
}*/
}
接口默认方法和静态方法的区别
1.默认方法通过实例调用,静态方法通过接口名调用。
2.默认方法可以被继承,实现类可以直接使用接口默认方法,也可以重写接口默认方法。
3.静态方法不能被继承,实现类不能重写接口静态方法,只能使用接口名调用。
小结
接口中新增的两种方法:
默认方法和静态方法
如何选择呢?如果这个方法需要被实现类继承或重写,使用默认方法,如果接口中的方法不需要被继承就使用静态方法
常用内置函数式接口
目标
了解内置函数式接口由来
了解常用内置函数式接口
内置函数式接口来由来
我们知道使用Lambda表达式的前提是需要有函数式接口。而Lambda使用时不关心接口名、抽象方法名,只关心抽象方法的参数列表和返回值类型。因此为了让我们使用Lambda方便,JDK提供了大量常用的函数式接口。
常用内置函数式接口介绍
它们主要在java.util.function包中。下面是最常用的几个接口。
1. Supplier接口
java.util.function.supplier<T>接口,它意味看“供给”,对应的Lambda表达式需要“对外提供"一个符合泛型类型的对象数据。
@FunctionalInterface
public interface Supplier<T> {
public abstract T get();
}
供给型接口,通过Supplier接口中的get方法可以得到一个值,无参有返回的接口。
使用Lambda表达式返回数组元素最大值
使用Supplier接口作为方法参数类型,通过Lambda表达式求出int数组中的最大值。提示:接口的泛型请使用java.lang.Integer类。
import java.util.Arrays;
import java.util.function.Supplier;
public class DemoLambdaIntro {
//使用Lambda表达式返回数组元素最大值
public static void main(String[] args) {
printMax(() -> {
int[]arr = {11,99,88,77,22};
Arrays.sort(arr); //升序排序
return arr[arr. length - 1];
});
}
public static void printMax (Supplier<Integer> supplier) {
int max = supplier.get() ;
System. out. println("max ="+ max) ;
}
}
2. Consumer接口
java.util.function.Consumer<T>接口则正好相反,它不是生产一个数据, 而是消费一个数据, 其数据类型由泛型参数决定。
@FunctionalInterface
public interface Consumer<T> {
public abstract T accept();
}
使用Lambda表达式将一个字符串转成大写和小写的字符串
Consumer消费型接口,可以拿到accept方法参数传递过来的数据进行处理,有参无返回的接口。基本使用如:
import java.util.function.Consumer;
public class DemoLambdaIntro {
public static void main(String[] args) {
System.out.println("开始啦");
printHello((String str) -> {
System. out. println(str. toUpperCase());
});
}
public static void printHello (Consumer<String> consumer){
System.out.println("aaa");
consumer.accept( "Hello World");
}
}
import java.util.function.Consumer;
public class DemoLambdaIntro {
public static void main(String[] args) {
System.out.println("开始啦");
printHello((String str) -> {
System. out. println(str. toLowerCase());
},(String str) -> {
System. out. println(str. toUpperCase());
});
}
public static void printHello (Consumer<String> c1,Consumer<String> c2) {
System.out.println("aaa");
String str = "Hello World";
c1.accept(str);
c2.accept(str);
}
}
默认方法: andThen
如果一个方法的参数和返回值全都是Consumer类型,那么就可以实现效果:消费一个数据的时候, 首先做一个操作,然后再做一个操作, 实现组合。而这个方法就是Consumer接口中的default方法andThen。
import java.util.function.Consumer;
public class DemoLambdaIntro {
public static void main(String[] args) {
System.out.println("开始啦");
printHello((String str) -> {
System. out. println(str. toLowerCase());
},(String str) -> {
System. out. println(str. toUpperCase());
});
}
public static void printHello (Consumer<String> c1,Consumer<String> c2) {
System.out.println("aaa");
String str = "Hello World";
/*c1.accept(str);
c2.accept(str);*/
c1.andThen(c2).accept(str);
}
}
3. Function接口
java.util.function.Function<T, R>接口用来根据一个类型的数据得到另一个类型的数据, 前者称为前置条件,后者称为后置条件。有参数有返回值。
@FunctionalInterface
public interface Function<T, R> {
public abstract R apply(T t);
}
使用Lambda表达式将字符串转成数字
Function转换型接口,对apply方法传入的T类型数据进行处理,返回R类型的结果,有参有返回的接口。使用的场景例如:将String类型转换为Integer类型。
import java.util.function.Function;
public class DemoLambdaIntro {
//使用Lambda表达式将字符串转成数字
public static void main(String[] args) {
getNumber((String str) -> {
int i = Integer. parseInt(str);
return i;
});
}
public static void getNumber (Function<String,Integer> function) {
Integer numl = function.apply( "10" );
System.out.println(" num1 ="+ numl) ;
}
}
默认方法: andThen
Function接口中有一个默认的andThen方法,用来进行组合操作。
import java.util.function.Function;
public class DemoLambdaIntro {
//使用Lambda表达式将字符串转成数字,第二个操作将这个数字乘以5
public static void main(String[] args) {
getNumber((String str)->{
return Integer. parseInt(str) ;
},(Integer i) -> {
return i * 5;
});
}
public static void getNumber (Function<String,Integer> f1, Function<Integer, Integer> f2) {
System. out. println("aa");
Integer num = f1. apply("6");
Integer num2 = f2. apply (num) ;
System.out.println("num2 =" + num2) ;
}
}
import java.util.function.Function;
public class DemoLambdaIntro {
//使用Lambda表达式将字符串转成数字,第二个操作将这个数字乘以5
public static void main(String[] args) {
getNumber((String str)->{
return Integer. parseInt(str) ;
},(Integer i) -> {
return i * 5;
});
}
public static void getNumber (Function<String,Integer> f1, Function<Integer, Integer> f2) {
System. out. println("aa");
/*Integer num = f1. apply("6");
Integer num2 = f2. apply (num) ;*/
Integer num2 = f1.andThen(f2).apply("6");
System.out.println("num2 =" + num2) ;
}
}
4. Predicate接口
有时候我们需要对某种类型的数据进行判断,似而得到一个boolean值结果。这时可以使用java.util.function.Predicate<T>接口。
@FunctionalInterface
public interface Predicate<T> {
public abstract boolean test(T t);
}
Predicate接口用于做判断,返回boolean类型的值
使用Lambda判断一个人名如果超过3个字就认为是很长的名字
对test方法的参数T进行判断,返回boolean类型的结果。用于条件判断的场景:
import java.util.function.Predicate;
public class DemoLambdaIntro {
//使用Lambda判断一个人名如果超过3个字就认为是很长的名字
public static void main(String[] args) {
System.out.println("开始啦");
isLongName((String name) -> {
return name.length() > 3;
});
}
public static void isLongName (Predicate<String> predicate){
System.out.println(" aa");
boolean isLong = predicate. test("迪丽热巴");
System.out.println("是否是长名字:"+ isLong) ;
}
}
条件判断的标准是传入的Lambda表达式逻辑,只要名称长度大于3则认为很长。
默认方法: and
既然是条件判断,就会存在与、或、非三种常见的逻辑关系。其中将两个Predicate条件使用”与”逻辑连接起来实现"并且”的效果时,可以使用default方法and.
使用Lambda表达式判断一个字符串中即包含W,也包含H
使用Lambda表达式判断一个字符串中包含W或者包含H
使用Lambda表达式判断一个字符串中即不包含W
如果要判断一个字符串既要包含大写"H",又要包含大写"W",那么:
import java.util.function.Predicate;
public class DemoLambdaIntro {
/*使用Lambda表达式判断-个字符串中即包含W,也包含H
使用Lambda表达式判断-个字符串中包含W或者包含H
使用Lambda表达式判断-个字符串中即不包含W*/
public static void main(String[] args) {
System.out.println("开始啦");
test((String str) -> {
//判断是否包含W
return str. contains("W") ;
},(String str) -> {
//判断是否包含H
return str. contains("H");
});
}
public static void test (Predicate<String> p1, Predicate<String> p2){
String str = "Hello World" ;
boolean b1 = p1. test(str);
boolean b2 = p2. test(str) ;
if (b1 && b2){
System.out.println("即包含W,也包含H");
}
}
}
import java.util.function.Predicate;
public class DemoLambdaIntro {
/*使用Lambda表达式判断-个字符串中即包含W,也包含H
使用Lambda表达式判断-个字符串中包含W或者包含H
使用Lambda表达式判断-个字符串中即不包含W*/
public static void main(String[] args) {
System.out.println("开始啦");
test((String str) -> {
//判断是否包含W
return str. contains("W") ;
},(String str) -> {
//判断是否包含H
return str. contains("H");
});
}
public static void test (Predicate<String> p1, Predicate<String> p2){
String str = "Hello World" ;
/* boolean b1 = p1. test(str);
boolean b2 = p2. test(str) ;
if (b1 && b2){
System.out.println("即包含W,也包含H");
}*/
// 使用Lambda表达式判断-个字符串中即包含W,也包含H
boolean b = p1.and(p2).test(str);
if(b){
System.out.println("即包含W,也包含H");
}
// 使用Lambda表达式判断-个字符串中包含W或者包含H
boolean b1 = p1.or(p2).test(str);
if(b1){
System.out.println("包含W或者包含H");
}
// 使用Lambda表达式判断-个字符串中即不包含W
boolean b2 = p1.negate().test("Hello");
//negate相当于取反 !boolean
if(b2){
System.out.println("不包含W");
}
}
}
介绍方法引用
目标
了解Lambda的冗余场景
掌握方法引用的格式
了解常见的方法引用方式
Lambda的冗余场景
使用Lambda表达式求一个数组的和
import java.util.function.Consumer;
public class DemoLambdaIntro {
//求一个数组的和
public static void getMax(int[] arr) {
int sum = 0;
for (int n : arr) {
sum += n;
}
System.out.println(sum);
}
public static void main(String[] args) {
//使用Lambda表达式求一个数组的和
printMax((int[] arr) ->{
/* int sum = 0;
for (int n : arr) {
sum += n;
}
System.out.println(sum);*/
getMax(arr);
});
//使用方法引用
//让这个指定的方法去重写接口的抽象方法,到时候调用接口的抽象方法就是调用传递过去的这个方法
printMax(DemoLambdaIntro::getMax);
}
public static void printMax(Consumer<int[]> consumer) {
int[] arr = {11, 22, 33, 44, 55};
consumer.accept(arr);
}
}
请注意其中的双置号::写法,这被称为方法引用",是一种种新的语法。
方法引用的格式
符号表示: ::
符号说明:双冒号为方法引用运算符,而它所在的表达式被称为方法引用。
应用场景:如果Lambda所要实现的方案,已经有其他方法存在相同方案,那么则可以使用方法引用。
常见引用方式
方法引用在JDK 8中使用方式相当灵活,有以下几种形式:
1. instanceName:: methodName 对象::方法法名
2. ClassName::stat icMethodName 类名::静态方法
3. ClassName::methodName 类名::普通方法
4. ClassName::new 类名::new调用的构造器
5. TypeName[]::new String[]::new调用数组的构造器
小结
首先了解Lambda表达式的冗余情况体验了方法引用,了解常见的方法引用方式
对象名::引用成员方法
这是最常见的一种用法,与上例相同。如果一个类中已经存在了一个成员方法,则可以通过对象名引用成员方法,
代码为:
//对象::实例方法
@Test
public void test01(){
Date now = new Date();
Supplier<Long> supp = () -> {
return now.getTime();
};
System.out.println(supp.get());
//使用方法引用
Supplier<Long> supp2 = now::getTime;
System.out.println(supp2.get());
//注意:方法引用有两个注意事项
// 1.被引用的方法,参数要和接口中抽象方法的参数一样
// 2.当接口抽象方法有返回值时,被引用的方法也必须有返回值
/* Supplier<Long> su3 = now::setTime;
su3. get();*/
/*Supplier<Long> su4 = now::setDate;
su4. get() ;*/
}
}
方法引用的注意事项
1.被引用的方法,参数要和接口中抽象方法的参数一样
2.当接口抽象方法有返回值时,被引用的方法也必须有返回值
类名::引用静态方法
由于在java. lang. System类中已经存在了静态方法currentTimeMillis,所以当我们需要通过Lambda来调用该方法时,可以使用方法引用,写法是:
//类名::静态方法
@Test
public void test02() {
/* Supplier<Long> su = () -> {
return System.currentTimeMillis();
};*/
Supplier<Long> su = System::currentTimeMillis;
Long time = su.get();
System. out. println("time ="+ time);
}
类名::引用实例方法
Java面向对象中,类名只能调用静态方法,类名引用实例方法是有前提的,实际上是拿第一个参数作为方法的调用者。
//类名::实例方法
@Test
public void test03(){
/* Function<String,Integer> f1 = (String str) -> {
return str.length();
};*/
//类名::实例方法(注意: 类名::实例方法实际上会将第一个参数作为方法的调用者)
Function<String,Integer> f1 = String::length;
int length = f1.apply( "hello");
System. out. println("length ="+ length) ;
// BiFunction<String,Integer, String> f2 = String:: substring;
//相当于这样的Lambda
BiFunction<String,Integer,String> f2 = (String str, Integer index) -> {
return str.substring (index) ;
};
String str2 = f2. apply( "helloworld", 3);
System. out. println(" str2 ="+ str2); // loworld
}
类名::new引用构造器
由于构造器的名称与类名完全一样。 所以构造器引用使用类名称: :new的格式表示。首先是一个简单的Person类:
public class Person {
String name;
int age;
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public Person() {
System.out.println("执行无参构造");
}
public Person(String name, int age) {
System.out.println("执行有参构造");
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
//类名::new 引用类的构造器
@Test
public void test04(){
/* Supplier<Person> su1 = ()->{
return new Person();
};*/
Supplier<Person> su1 = Person::new;
Person person = su1.get();
System.out.println("person =" + person);
/*BiFunction<String,Integer,Person> su2 = (String name,Integer age) ->{
return new Person(name,age);
};*/
BiFunction <String,Integer,Person> su2 = Person::new;
Person person1 = su2.apply("迪丽热巴",18);
System.out.println("person =" + person1);
}
数组::new引用数组构造器
数组也是object的子类对象,所以山同样具有构造器,只是语法稍有不同。
//类型[]::new
@Test
public void test05() {
/* Function<Integer, int[]> f1 = (Integer length) -> {
return new int [length];
};*/
Function<Integer, int[]> f1 = int[]::new;
int[] arr1 = f1.apply(10);
System.out.println(Arrays.toString(arr1));
}
本文地址:https://blog.csdn.net/qq_37725560/article/details/109599306
上一篇: JVM类的加载机制
下一篇: java基础面试题总结