java之jdk8新特性

函数式编程:
函数式接口:有且只有一个抽象方法的接口,称之为函数式接口
当然接口中可以包含其他的方法(默认,静态,私有)
@FunctionInterface注解:
作用:可以检测接口是否是一个函数式接口
是:编译成功
否:编译失败(接口中没有抽象方法,抽象方法的个数多余1个)

常用的函数式接口
1 java.util.function Supplier接口仅包含一个无参的方法 :T get(),用来获取一个泛型参数指定类型的对象数据。
Supplier接口被称之为生产型接口,指定接口的泛型是什么类型,那么接口中的get方法就会生产什么类型的数据。

2 java.util.function.Consumer接口则正好与Supplier接口相反。
它不是生产一个数据,而是消费一个数据,其数据类型由泛型决定。
Consumer接口中包含抽象方法 void accept(T t),意味消费一个指定泛型的数据。

Consumer接口是一个消费型接口,泛型执行什么类型,就可以用accept方法消费什么类型的数据,至于具体怎么消费(使用),需要自己定义(输出,计算....);

Consumer接口的默认方法andThen
作用:需要两个Consumer接口,可以把Consumer接口组合到一起,在对数据进行消费

例如:
Consumer< String> con1
Consumer< String> con2;
String s="hello’;
con1.accept(s);
con2.accept(s);
连接两个Consumer接口,再进行消费
con1.andThen(con2).accept(s);谁写前边谁先消费
//定义一个方法,方法的参数传递一个字符串和两个Consumer接口,Consumer接口的泛型使用字符串
public static void method(String s,Consumer< String> con1,Consumer< String> con2){
//con1.accept(s);
//con2.accept(s);
//使用andThen方法,把两个Consumer接口连接到一起,在消费数据。
con1.andThen(con2).accept(s);//con1连接con2,先执行con1消费数据,在执行con2消费数据。

}

public static void main(String[] args){
//调用method方法,传递一个字符串,两个Lambda表达式
method(“Hello”,
(t)->{
//消费方式:把字符串转化为大写输出
System.out.println(t.toUpperCase());
};
(t)->{
//消费方式:把字符串转换为小写输出
System.out.println(t.toLowerCase());
}
);

}

java.util.function.Predicate< T>接口
作用:对某种数据类型的数据进行判断,结果返回一个boolean值

Predicate接口中包含一个抽象方法:
boolean test(T t):用来对指定数据类型进行判断的方法
结果:
符合条件,返回true
不符合条件,返回false
使用步骤:
1 定义一个方法
参数传递一个String 类型的字符串
传递一个Predicate接口,泛型使用String
使用Predicate中的方法test对字符串进行判断,并把判断的结果返回
Predicate接口中有一个方法add,表示并且关系,也可以用于连接两个判断条件
default Predicate< T> and(Predicate< ? super T> other){
Object.requireNonNull(other);
return (t)->this.test(t)&&other.test(t);
}
方法内部的两个判断条件,也是使用&&运算符连接起来的

Predicate接口中有一个方法or,表示或者关系,也可以用于连接两个判断条件

Predicate接口中有一个方法negate,表示取反。

java.util.function.Function<T,R>接口用来根据一个类型的数据得到另一个类型的数据。
前者称为前置条件,后者称为后置条件。
Function接口中最主要的抽象方法为:R apply(T t),根据类型T的参数获取类型R的结果。
使用场景例如:将String类型转换为Integer类型。
Function接口中的默认方法andThen:用来进行组合操作
例如:
需求:
把string类型的“123”,转换为Inteter类型,把转换后的结果加10
把增加之后的Integer类型的数据,转换为String类型
分析:
转换了两次
第一次是String类型转换为了Integer类型
所以我们可以使用Function< String, Integer> fun1
Integer i=fun1.apply("123’)+10;
第二次是把Integer类型转换为String类型
所以我们可以使用Function< Integer ,String> fun2
String s=fun2.apply(i);
我们可以使用andThen方法,把两次转换组合在一起使用
String s=fun1.andThen(fun2).apply(“123”);
fun1先调用apply方法,把字符串转换为Integer
fun2再调用apply方法,把Integer转换为字符串。
使用Stream流的方式,遍历集合,对集合中的数据进行过滤
Stream流失jdk1.8之后出现的
关注的是做什么,而不是怎么做。

例如:
1 对list集合中的元素进行过滤,只要以张开头的元素,存储到一个新的集合中
2 对list集合进行过滤,只要姓名长度为3的人,存储到一个新集合中
3 遍历list集合
list.stream().filter(name->name.starswith(“张”)).filter(name->name.length()==3).forEach(name->System.out.println(name));

Stream流属于管道流,只能被消费(使用)一次
第一个Stream流调用完毕方法,数据就会流到下一个Stream上
而这时第一个Stream流已经使用完毕,就会关闭了
所以第一个Stream流就不能再调用方法了
IllegalStateException:stream has already been operated upon or closed 抛出的异常。
两种获取stream流的方式:
java.util.stream.Stream< T>是java 8新加入的最常用的流接口。(这并不是一个函数式接口。)
获取一个流非常简单,有以下几种常用的方式。
所有的Collection集合都可以通过stream默认方法获取流。
default Stream< E > stream()
Stream接口的静态方法of可以获取数组对应的流。
static < T> stream< T> of(T …values)
参数是一个可变参数,那么我们就可以传递一个数组。
例如:
List< String> list=new ArrayList<>();
Stream< String> stream1=list.stream();

			Set< String> set=new HashSet<>();
			Stream< String> stream2= set.stream();

		Map<String ,String> map=new HashMap<>();
		//获取键,储存到一个Set集合中
		Set<String> keySet=map.keySet();
		Stream<String> stream3=keySet.stream();
		
		//获取值,储存到一个Collection集合中
		Collection<String> values=map.values();
		Stream<String> stream4=values.stream();	
		//获取键值对(键与值的映射关系 entrySet)
		Set<Map.Entry<String,String>> entries=map.entrySet();
		Stream<Map.Entry<String,String>> stream5=entries.stream();

		把数组转换为stream流
		Stream< T> stream6=Stream.of(数组<T>);

Stream流中的常用方法 forEach
void forEach(Consumer< ? super T> action):
该方法接受一个Consumer接口函数,会将每一个流元素交给该函数进行处理
Consumer接口是一个消费型的函数式接口,可以传递Lambda表达式,消费数据

简单记忆:
forEache方法,用来遍历流中的数据
是一个终结方法,遍历之后就不能继续调用stream流中的其他方法。
例如:
Stream< String> stream=Stream.of(“张三”,“李四”,“王五”,“赵六”);
//使用stream流中的方法forEache对stream流中的数据进行遍历
stream.forEache((String name)->{
System.out.println(name);
//System.forEach(name->System.out.println(name));
})

Stream流中的常用方法 filter:用于对Stream流中的数据进行过滤
Stream< T> filterr(Predicaterr< ? super T>) Predicate;
filter方法的参数Predicate是一个函数式接口,所以可以传递Lambda表达式,对数据进行过滤。
Predicate中的抽象方法:
boolean test( T t);

如果需要将流中的元素映射到另一个流中,可以使用map方法
< R> Stream< R> map( Function< ? superr T, ? extends R > mapper);
该接口需要一个Function函数式接口参数,可以将当前流中的T类型转换为另一种R类型的数据。
Function中的抽象方法:
R apply( T,t);

Count方法:
Stream流中的常用方法 count:用于统计Stream流中元素的个数
Long Count();
count方法是一个终结方法,返回值是一个Long类型的整数
所以不能再调用Stream流中其他方法

Limit方法:
Stream流中的常用方法 Limit:用于截取流中的元素
Limit方法可以对流进行截取,只取用第n个
Stream< T> limit(Long maxSize);
参数是一个long类型,如果集合当前长度大于参数则进行截取,否则不进行操作
Limit方法是一个延迟方法,只是对流中的元素进行截取,返回的是一个新的流,所以可以继续调用Stream流中的其他方法。

Stream流中的常用方法: skip 用于跳过元素
如果希望跳过前几个元素,可以使用skip方法获取一个截取之后的新流;
Stream< T> skip( long n);
如果流的当前长度大于n,则跳过前n个,否则将会得到一个长度为0的空值。

Stream流中的常用方法:concat:用于把流组合到一起
如果有两个流,希望合并成为一个流,那么可以使用Stream接口的concat
static< T> Stream< T> concat( Stream< ? extends T> a, Stream< ? extends T> b)
	例如:
			Stream< String> stream1=Stream.of("张三丰","张无忌","张翠山");
			String[] arr={"美洋洋","喜洋洋","懒洋洋"};
			Stream< String> stream2=Stream.of(arr);
			Streamd< Stream> concat=Stream.concat(stream1,streram2);
			concat.forEach(name->System.out.println(name));

//第一个队伍只要名字为3个字的成员姓名,储存到一个新集合中
//第二个队伍筛选之后只要前3个人,储存到一个新集合中

Stream< String> oneStream=one.stream.filter(name->name.length()==3).limit(3);

Stream< String> twoStream=two.stream().filter(name->name.startWith("张’)).skip(2);

//将两个队伍合并为一个队伍;储存到一个新集合中,根据姓名创建Person对象,储存到一个新集合中,打印整个队伍的Person对象信息

Stream.concat(oneStream,twoStream).map(name->new Person(name)).forEach(p->System.out.println§);

方法引用:
1 通过对象名引用成员方法:
用来简化Lambda表达式,通过对象名引用成员方法,前提是对象名是已经存在的,成员方法也已经是存在的。
例如:
public static void printStream(Printable p){
p.print("Hello’);
}

public static void main(String[] args){
//调用printStream方法,方法的参数Printable是一个函数式接口,所以可以传递Lambda表达式
printStream((s))->{
//创建MethodRerObject对象
MethodRerObject obj=new MethodRerObject();
//调用MethodRerObject对象中的成员方法printUpperCaseString,把字符串按照大写输出
obj.printUpperCaseString(s);
}

使用方法引用优化上述Lambda表达式:
//对象已经存在 MethodRerObject,成员方法也是已经存在的printUperCaseString
所以我们可以使用对象名引用成员方法
MethodRerObject obj=new MethodRerObject();
printString(obj::printUpperCaseString);

}

二:
通过类名引用静态成员方法:
//定义一个方法,方法的参数传递要计算绝对值的整数,和函数式接口Calcable
//首先得创建函数式接口Calale里面有一个抽象方法calsAbs(int number);
public static int method(int number,Calcabl c){
return c.calsAbs(number);
}

public static void main(String[] args){
//调用method方法,传递计算绝对值得整数,和Lambda表达式
int numberr=method(-10,(n)->{
//对参数进行绝对值得计算并返回结果
return Math.abs(n);
});
System.out.println(number);
//使用方法引用优化Lambda表达式
Math类是存在的
abs计算绝对值的静态方法也是已经存在的
所以我们可以直接通过类名引用静态方法。

int number2=method(-10,Math::abs);
System.out.println(number2);
}

通过super引用成员方法:
//定义一个函数式接口
@FunctionalInterface
public interface Greetable{
//定义一个方法
public abstract void greet();
}

//定义一个父类
public class Human{
//定义一个sayHello的方法
System.out.println(’'Hello 我是 Human")
}

//定义子类
public class extends Human{
//子类重写父类sayHello的方法
@Override
public void sayHello(){
System.out.println(“Hello 我是man!”);
}
//定义一个方法参数传递Greetable接口
public void method(Greetable g){
g.greet();

}

public void show(){
//调用method方法,方法的参数Greetable是一个函数式接口,所以可以传递一个Lambda
method(()->{
//创建父类Human对象
Human h=new Human();
//调用父类的sayHello方法
h.sayHello();
});

//因为有字符类关系,所以存在的一个关键字supper,代表父类,所以我们可以直接使用supper调用父类的成员方法
method(()->{
super.sayHello();
});

//使用super引用父类的成员方法
super是已经存在的
父类的成员方法sayHello也是已经存在的
所以我们可以直接使用super引用父类的成员方法
method(super::sayHello);
}

public staic void main(String[] args){

new Main().show();

}
}

通过this引用本类的成员方法:
优化Lambda表达式
this是已经存在的
本类的成员方法也是已经存在的
例如:
marry(()->{
this.buyHouse();
});
优化:
marry(this::buyHouse);

类的构造器引用:
由于构造器的名称与类名完全一样,并不固定,所以构造器引用使用 类名称::new 的格式表示。

例如:
public static void printName(String name,PersonBuilder pb){
Person person=pb.builderPerson(name);
System.out.println(Person.getName());

}

public static void main(String[] args){
//调用printName方法,方法的参数PersonBuilder接口是一个函数式接口,可以传递Lambda表达式
printName("李明’,(String name)->{
return new Person(name);

//使用方法引用代替上述代码
使用方法引用优化Lambda表达式
构造方法new Person(String name)已知
创建对象已知 new 
我们可以使用Person引用new 创建对象。
printName("小明",Person::new);

}
}

方法引用_数组的构造器引用:
//定义一个创建数组的函数式接口
@FunctionlInterface
public interface ArrayBuilder{
//定义一个创建int 类型数组的方法,
}
数组的构造器引用——使用方法的引用创建数组
例如:
//定义一个创建数组的函数式接口
@FuncationlInterface
public interface ArrayBuilder{
//定义一个创建int类型数组的方法,参数传递数组的长度,返回创建好的int类型数组
int[] builderArray(int length);
}
//数组的构造器引用
public class Demo{
//定义一个方法,方法的参数传递创建数组的长度和ArrayBuilder接口,
方法的内部根据传递的长度使用ArrayBuilder中的方法创建数组并返回
public static int[] createArray(int length,ArrayBuilder ab){
return ab.builderArray(length);
}
public static void main(String[] args){
//调用createArray方法,传递数组的长度和Lambda表达式
int[] arr1=createArray(10,(len)->{
//根据数组的长度,创建数组并返回
return new int[len];
});
使用方法引用优化Lambda表达式
已知创建的就是int[] 数组
数组的长度也是已知的
就可以使用方法引用
int[] 引用new,根据参数传递的长度来创建数组。

}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qq_43557743

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值