Java新特性
Java8接口新特性
概述
- java8中的接口,不仅能定义抽象方法,还能定义非抽象方法
- 抽象方法定义方式和之前的一样。默认加上public abstract,不能拥有方法体的。连大括号都没有
- 非抽象方法拥有方法体,必须使用默认default或者static修饰
Default
- 使用原因:
- 为了增加List接口的功能,如果增加抽象方法,那么所有实现List接口的实现类都要将新增的抽象方法进行实现,结果就可能导致原来的旧的项目无法编译通过。新增的方法,就加上了默认的实现,但是这种默认的实现不会强制要求实现类进行重写。
- 使用规则:
- 加上default,实现类可以不重写,直接调用
- 特殊情况:实现类实现了两个接口,如果有相同的方法的默认方法,则强制要求在实现类中重写这个方法,以确定实现的内容
- 如果实现类在重写的过程中,就想指定其中某个接口的默认方法的实现的时候,此时可以使用接口名.super.默认方法名()的格式,指定 想要在实现类中重写后实现的接口中的默认方法
- 特殊情况:实现类在继承一个类的前提下,又实现了一个接口,在父类中和接口中出现相同的方法的声明的时候,采用“类优先”,在实现类 中不需要再对该方法进行重写
- 产生影响:
- 接口中如果也可以定义非抽象方法,那么接口和抽象类的差距就越来越小了。
- java中一个类只能继承一个类的,但是可以实现多个接口。
- 有了默认方法,抽象类地位就越来越弱。
static
- 接口的静态方法可以定义方法实现的内容的
- 静态方法不能被抽象
- 外界只能通过接口名称.静态方法来访问接口中的静态方法,实现类中不会继承接口中的静态方法
- 如果一个类同时实现了两个接口,具有相同的静态方法签名,继承之后不知道以哪个为准
package com.danny.interstatic;
public interface Inter {
void inter() ;
static void show() {
System.out.println("静态的抽象方法");
}
}
package com.danny.interstatic;
public class InterImpl implements Inter {
@Override
public void inter() {
System.out.println("inter方法");
}
}
package com.danny.interstatic;
public class Test {
public static void main(String[] args) {
Inter.show();
InterImpl impl = new InterImpl();
}
}
二. Lambda表达式
概述
- 本质:
- 前提:
- 好处:
- 匿名内部类的格式的一种简写,可以间接表示一个方法实现的逻辑。可以提升开发效率
格式详解
- 格式:
函数式接口名 对象名 = (参数列表)->{方法体}
;
- 详细说明:
- 函数式接口名:接口名称(接口中的抽象方法只能有一个)
- 对象名:合法的标识符即可
- (参数列表):表示的是要实现的接口中,抽象方法的参数
- ->:箭头运算符,或者称为lambda,运算符,用于分隔前后两部分
- 方法体:也称为lambda体,表示对抽象方法重写的内容
- 语法格式:
- 没有参数,没有返回值
- ()->{方法体};
- 方法体如果是一条语句,此时可以省略大括号
- eg:()->System.out.println(“内容”);
- 有一个参数,没有返回值
- (参数)->{方法体};
- 参数列表中的数据类型可以省略不写的,甚至连变量的名称都能和抽象方法中的不一样
- 如果参数只有一个的时候,可以省略小括号的
- eg: m -> System.out.println(m);
- 有多个参数,有返回值
- (参数1,参数2,…)->{方法体};
- 简写格式:
- 针对的是lambda体只有一句,且有返回值,此时return和大括号可以一起省略
- eg:(x,y)->{return x + y} 等价于 (x,y)-> x + y;
package com.danny.lambda;
public class Demo_1 {
public static void main(String[] args) {
Inter_3 i33 = (x, y) -> {
System.out.println(y.length());
return x + y;
};
String show = i33.show(100, "java");
System.out.println(show);
Inter_3 i333 = (x, y) -> x + y;
String show1 = i333.show(200, "hello");
System.out.println(show1);
}
private static void test_2() {
Inter_2 i2 = new Inter_2() {
@Override
public void show(int x) {
System.out.println(x);
}
};
i2.show(100);
Inter_2 i22 = (int x) -> {
System.out.println(x);
};
i22.show(999);
Inter_2 i33 = m -> System.out.println(m);
i33.show(888);
}
private static void test_1() {
Inter_1 i1 = new Inter_1() {
@Override
public void show() {
System.out.println("匿名内部类实现的");
}
};
i1.show();
Inter_1 i11 = () -> {
System.out.println("lambda表达式实现的");
};
i11.show();
Inter_1 i111 = () -> System.out.println("lambda表达式简写格式");
i111.show();
}
}
interface Inter_1 {
void show();
}
interface Inter_2 {
void show(int x);
}
interface Inter_3 {
String show(int x, String y);
}
三. 函数式接口
- 函数式接口是lambda表达式的前提
- 定义:
- 如果在一个接口中,只有一个抽象方法的声明,那么这个接口就是函数式接口
- 格式说明;
- 使用注解来检查接口是否是一个函数式接口
- @FunctionalInterface
- 如果不是函数式接口,就会编译报错
- 理解:
- 函数:想表达的是一个方法的内容,由于方法不再任何类中,所以称为函数
- 函数式接口:其实想表达的是一个函数的声明
- 作用:
- 使用函数式接口表达函数的声明:使用函数式接口的实现类表达函数的实现
- 使用原因:
- java中不支持将函数作为一个数据,也就不能将这个函数进行传递,也就不能作为一个对象的成员而存在
- 只有在方法的外面加一层接口的声明,将来可以传递方法所在接口的实现类对象,来间接的传递方法的实现逻辑
package com.danny.lambda;
public class Demo_2 {
public static void main(String[] args) {
int b = 10;
InterFun_1 i1 = ()->System.out.println("test");
InterFun_1 i2 = ()->{
System.out.println("test");
System.out.println("i2");
};
test(()->System.out.println("test"));
}
public static void test(InterFun_1 i1) {
i1.show();
}
}
@FunctionalInterface
interface InterFun_1{
void show();
}
常用的内置型函数式接口
- java8中提供了一些常用的内置型函数式接口,在使用类似功能的时候,就不需要自己再去定义了,而是直接使用内置型函数式接口即可。
- 罗列:
Consumer<T>
:消费型接口
Supplier<T>
: 供给型接口
Function<T,R>
:函数型接口
Predicate<T>
:断言型接口
消费型接口
Consumer<T>
:- 消费型接口
- 抽象方法
- 作用:
- 当某个函数可以接收一个数据,并且处理这个数据,处理完之后,不需要返回任何数据的时候,这个函数(处理数据的逻辑)当做数据进行传递的时候,就可以使用消费型接口了
- 以前只能传递要处理的数据,现在可以传递处理数据的逻辑
package com.danny.functioninter;
import java.util.function.Consumer;
public class Demo_1 {
public static void main(String[] args) {
test("java");
Consumer<String> con = (t)->{
String substring = t.substring(0,2);
System.out.println(substring);
};
Consumer<String> con1 = (t)->{
char c = t.charAt(0);
System.out.println(c);
};
testCon("中国成都",con1);
}
public static void testCon(String str,Consumer<String> con) {
con.accept(str);
}
public static void test(String str) {
int length = str.length();
System.out.println(length);
}
}
方法引用
- 写一个函数式接口时,方法的实现(lambda体),已经被其他的某个对象实现了,就不需要再lambda体中,将该方法再次实现,而是可以直接借用一下已经在其他对象中对该方法的实现。直接调用,直接使用其他对象中已经定义好的方法。
- 格式:
- 函数式接口 名称 = 对象名::方法名称;
- 函数式接口 名称 = 类名 :: 静态方法名;
- 作用:
- 把已经实现的方法,作为一个数据,作为实现类的对象,赋值给函数式接口的引用。
- 可以把这个引用当做方法的返回值,也可以作为方法的实际参数进行传递
- 本质:
- 可以把任意一个方法,作为函数式接口的一个实现类对象
package com.danny.functioninter;
public class Demo_2 {
public static void main(String[] args) {
Method m = new Method();
FunInter_1 f1 = m :: test_1;
f1.printSum(200, 300);
test(m :: test_1);
FunInter_2 f2 = System.out::println;
f2.print("java");
}
public static void test(FunInter_1 f1) {
f1.printSum(900, 100);
}
}
class Method{
public void test_1(int x,int y) {
System.out.println(x + y + "Method");
}
}
interface FunInter_1{
void printSum(int x,int y);
}
interface FunInter_2{
void print(String str);
}
供给型接口
Supplier<T>
- 供给型接口
- 抽象方法
- 作用:
- 如果定义一个函数(方法的实现的逻辑),可以生产一个需要的数据,这个函数需要作为一个数据进行传递,就可以使用此接口
- 以前我们只能传递数据,可以传递生产数据的逻辑
package com.danny.functioninter;
import java.util.Random;
import java.util.function.Supplier;
public class Demo_3 {
public static void main(String[] args) {
Supplier<Integer> sup = ()->{
return 1;
};
Supplier<Integer> sup1 = ()->{
Random ran = new Random();
int nextInt = ran.nextInt(99);
return nextInt;
};
int num = getNum(sup1);
System.out.println(num);
}
public static int getNum(Supplier<Integer> sup) {
return sup.get();
}
public static int getNum() {
Random ran = new Random();
int nextInt = ran.nextInt(99);
return nextInt;
}
}
函数型接口
Function<T,R>
- 函数型接口
- 抽象方法
- 作用:
- 如果需要定义一个函数,接口一个数据,将数据进行处理,处理完之后进行返回,可以使用函数型接口
- 以前我们只能传递要处理的数据,现在可以传递处理数据的逻辑
- 提供功能
andThen(Function<T,R> fun)
- 在调用者处理方式之后,再将调用者处理的结果作为fun的参数,再次进行处理
package com.danny.functioninter;
import java.util.function.Function;
public class Demo_4 {
public static void main(String[] args) {
int dealData = dealData("I love java");
System.out.println(dealData);
Function<String, Integer> fun = t -> {
int indexOf = t.indexOf("o");
return indexOf;
};
Function<String, Integer> fun1 = t -> t.length();
int dealData2 = dealData("I love java",fun1);
System.out.println(dealData2);
Function<String, Integer> fun11 = t -> t.length();
Function<Integer, String> fun2 = t -> "处理之后的结果:" + t;
String dealData3 = dealData("I love java",fun11,fun2);
System.out.println(dealData3);
}
public static String dealData(String str,Function<String,Integer> fun1,Function<Integer,String> fun2) {
String apply = fun1.andThen(fun2).apply(str);
return apply;
}
public static int dealData(String str,Function<String,Integer> fun) {
Integer apply = fun.apply(str);
return apply;
}
public static int dealData(String str) {
int length = str.length();
return length;
}
}
断言型接口
Predicate<T>
- 断言型接口
- 抽象方法
- 作用:
- 如果需要定义一个函数,接口一个数据,判断数据是否合法,返回一个boolean类型的结果,此时就可以使用断言型接口
- 以前只能定义判断数据的逻辑,现在可以传递判断数据的逻辑
- 提供的方法:
- and(Predicate pre):在调用者判断之后,再由参数pre进行判断,返回两个对象都满足的内容
- or(Predicate pre):在调用者判断之后,再由参数pre进行判断,返回两个对象满足其一的结果
- negate():返回调用者判断条件的取反
package com.danny.functioninter;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
public class Demo_5 {
public static void main(String[] args) {
Predicate<String> pre = t -> {
boolean startsWith = t.startsWith("大");
return startsWith;
};
boolean right = isRight("大熊猫", pre);
System.out.println(right);
System.out.println("------------------------------");
List<String> list = new ArrayList<String>();
list.add("肖战");
list.add("王一博");
list.add("蔡徐坤");
list.add("马保国");
list.add("马云");
list.add("迪丽热巴");
System.out.println(list);
Predicate<String> pre1 = t -> t.length() == 3;
Predicate<String> pre2 = t -> t.length() == 4;
List<String> newList = getNewList(list, pre2);
System.out.println(newList);
Predicate<String> pre11 = t -> t.length() == 3;
Predicate<String> pre22 = t -> t.startsWith("马");
List<String> newList2 = getNewList(list, pre11, pre22);
System.out.println(newList2);
}
public static List<String> getNewList(List<String> list, Predicate<String> pre1, Predicate<String> pre2) {
ArrayList<String> newList = new ArrayList<String>();
for (String string : list) {
if (pre1.negate().test(string)) {
newList.add(string);
}
}
return newList;
}
public static List<String> getNewList(List<String> list, Predicate<String> pre) {
ArrayList<String> newList = new ArrayList<String>();
for (String string : list) {
if (pre.test(string)) {
newList.add(string);
}
}
return newList;
}
public static boolean isRight(String str, Predicate<String> pre) {
boolean test = pre.test(str);
return test;
}
public static boolean isRight(String str) {
if (str.length() >= 3) {
return true;
} else {
return false;
}
}
}
四. Stream
概述
- 在java1.8中,提供了一个Stream类型,可以对数据进行过滤
- 好处:
Stream类型的获取
- Collection的获取
- 调用stream()方法即可,返回Stream类型的对象
- Map的获取:不能直接获取Stream类型
- keySet().stream();
- entrySet().stream();
- values().stream();
- 数组的获取
- static of(T… values) 可变参数
package com.danny.stream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.stream.Stream;
public class Demo_1 {
public static void main(String[] args) {
Collection<String> coll = new ArrayList<String>();
Stream<String> stream = coll.stream();
Map<String,Integer> map = new HashMap<>();
Set<String> keySet = map.keySet();
Stream<String> stream2 = keySet.stream();
Set<Entry<String, Integer>> entrySet = map.entrySet();
Stream<Entry<String, Integer>> stream3 = entrySet.stream();
Collection<Integer> values = map.values();
values.stream();
Integer[] arr = {1,2,3,4,5};
Stream<Integer> of = Stream.of(arr);
sum(1,2,3,4,4,5,3,3,2,32,32,323,23,2,32,323);
}
public static void sum(int... d) {
for(int i = 0;i<d.length;i++) {
System.out.println(d[i]);
}
}
}
Stream中常用的方法
- 常用方法:用于对获取到的数据流对象进行数据的处理。可以筛选,可以转型,可以过滤
- 分类:
- 终结方法:调用完之后,返回值不在是Stream类型本身,无法继续调用Stream中的方法
- 延迟方法:调用完之后,返回值还是一个Stream类型,可以继续调用Stream中的方法
- 罗列:
Stream<T> filter(Predicate<T> pre)
forEach(Consumer<? super T> con)
- 将流中的数据,按照指定con描述对数据的处理方式,进行处理
Stream<T> limit(long maxSize)
- 根据参数maxSize描述的个数,对流中的数据进行截取
Stream<T> skip(long n)
- 根据参数n描述的个数,跳过参数n的个数的数据,流中剩余的就是n之后的内容
Stream<R> map(Function<T,R> fun)
- 将流中所有T类型的数据,都根据fun这个函数型接口,转换成其他R类型的数据
Stream<R> count()
;
static Stream<R> concat(Stream<? extends T> a,Stream<? extends T> b)
package com.danny.stream;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Stream;
public class Demo_2 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("宫本武藏");
list.add("上官婉儿");
list.add("不知火舞");
list.add("孙悟空");
list.add("猪八戒");
list.add("达摩");
list.add("鲁班");
System.out.println(list);
Stream<String> stream = list.stream();
stream.filter(x -> x.length() == 4).filter(x -> x.startsWith("不")).forEach(x -> System.out.println(x));
System.out.println("---------limit(long maxSize)-----------");
Stream<String> stream2 = list.stream();
stream2.limit(4).forEach(System.out::println);
System.out.println("---------skip(long maxSize)-----------");
Stream<String> stream3 = list.stream();
stream3.skip(4).forEach(System.out::println);
System.out.println("---------Stream<R> map(Function<T,R> fun)-----------");
Stream<String> stream4 = list.stream();
Function<String, Integer> fun = x -> {return x.length();};
stream4.map(fun).forEach(System.out::println);
System.out.println("---------count)-----------");
Stream<String> stream5 = list.stream();
long count = stream5.count();
System.out.println(count);
System.out.println("--------concat()-----------");
Stream<String> stream6 = list.stream();
Stream<String> filter1 = stream6.filter(x->x.length()==4);
Stream<String> stream7 = list.stream();
Stream<String> filter2 = stream7.filter(x->x.length()==2);
Stream<String> concat = Stream.concat(filter1,filter2);
concat.forEach(System.out::println);
}
}