今日一语:当你发现编程的规律就是世界的规律时,你就是一名真正的程序员
编译后会产生一个$XXXImpl1
的编译文件,与匿名内部类相似,但不等同于匿名内部类。
其原理是将方法作为参数进行传递,
JVM会使用一个动态调用的指令调用函数式接口中的方法。
同时使用lambda
将会使代码更简洁易读。
别名:【闭包】
使用方式:①使用JDK自有的函数式接口【接口列表】②自定义函数式接口使用。
如下:
1)自定义函数式接口,并且只定义一个方法,返回值与参数没有限制
/** 接口1 要点:参数再封装返回 */
@FunctionalInterface
public interface EventExector {
String event(String methodName);
}
/** 接口2 要点:计算两个数字之和返回 */
@FunctionalInterface
public interface LambdaInterface1 {
int mathOption(int a,int b);
}
/** 接口3 要点:获取当前时间的时间戳 */
@FunctionalInterface
public interface LambdaInterface2 {
void catCurrentDate(Date date);
}
/** 接口4 要点:将Map中的value取出放入一个list中 */
@FunctionalInterface
public interface LambdaInterface3 {
void copyMap(Map<String,Integer> map, List<Integer> list);
}
/** 接口5 要点:无返回无参数 */
2)将定义号的函数式接口通过参数传入
/** 接口1注入方法1 */
private static int operation(int a,int b,LambdaInterface1 interface1) {
return interface1.mathOption(a,b);
}
/** 接口2注入方法2 */
private static String realize(String target,EventExector exector) {
return exector.event(target);
}
/** 接口3注入方法3 */
private static void getDate(Date date,LambdaInterface2 interface2) {
interface2.catCurrentDate(date);
}
/** 接口4注入方法4 */
private static void replaceMap(LambdaInterface3 interface3, Map map,List list) {
interface3.copyMap(map,list);
}
/** 接口5注入方法5 */
private static void goToWay(LambdaInterface4 interface4) {
interface4.onTheWay();
}
()
中的参数为传入的参数, ->
代表lambda表达式的箭头指引符,return
返回,没有大括号,直接将返回值写在箭头符后面。/** 传入一个参数 有返回 */
System.out.println(realize("xx", (x) -> "HELLO GUYS! " + x));
/** lambda 传入两个参数 有返回 */
System.out.println(operation(1, 2, (a, b) -> a + b));
/** 传入一个参数,无返回 */
getDate(new Date(), date -> {
long time = date.getTime();
System.out.println("当前内容时间戳"+time);
});
/** 传入两个参数,无返回 */
Map<String,Integer> map = new Hashtable<>();
map.put("one",1);
map.put("two",2);
map.put("three",3);
map.put("four",4);
List list = new ArrayList();
replaceMap((map1, list1) -> {
for (String s : map.keySet()) {
list.add(map.get(s));
}
},map,list);
/** 无参数无返回 */
goToWay(() -> System.err.println("请纠正你的方向!"));
4)使用lambda注意事项
外部局部变量[基本类型]放在lambda
中使用时,必须使用final
修饰.
因为lambda
中外部局部变量无法传递!变量值无法改变。
final
:
① 如果修饰给基本类型变量,则表示值不可改变;
② 如果修饰引用类型变量,那么变量的引用地址不可改变,但是属性值是可以改变的。
考虑性能,如果代码不繁多,可以不用lambda
@Test
public void lambda2() {
String name = "我醉了";
int age = 12;
sayHello("高进",message -> {
name = "dsa"; // 此处编译会报错
age = 11; // 此处编译会报错
System.out.println(message+name+age);
});
}
这种 [方法引用] 或者说 [双冒号运算] 对应的参数类型是 Function
T
表示传入类型,R
表示返回类型。
比如表达式 person -> person.getAge();
传入参数是 person
,返回值是 person.getAge()
,
那么方法引用 Person::getAge
就对应着 Function
类型。
/** 所需函数式接口 */
@FunctionalInterface
public interface Merchant<T> {
T get();
}
/** 接口使用类 */
public class Computer {
public static Computer create(final Merchant<Computer> merchant) {
return merchant.get();
}
public static void getComputer(final Computer computer) {
System.out.println("地址:"+computer.toString());
}
public void getPro(final Computer computer) {
System.out.println(computer.hashCode());
}
public void getDate() {
System.out.println(LocalDate.now().get(ChronoField.YEAR));
}
public void getTime() {
System.out.println(LocalTime.now().get(IsoFields.DAY_OF_QUARTER));
}
}
主要有四种类型:
1)构造器引用 [Class::new]
Computer computer = Computer.create(Computer::new);
2)静态方法引用 [Class::static_method]
List<Computer> list = Arrays.asList(computer);
list.forEach(Computer::getComputer);
3)特定类的任意对象的方法引用 [Class::method]
List<Computer> list = Arrays.asList(computer);
list.forEach(Computer::getDate);
list.forEach(Computer::getTime);
4)特定对象的方法引用 [instance::new]
List<Computer> list = Arrays.asList(computer);
final Computer huawei = Computer.create(Computer::new);`
list.forEach(huawei::getPro);
JDK8处理空指针异常的工具类,减少空指针判断,可以存储空对象
只有两个值,一个是原本值,一个是空值
/** 静态方法 */
Optional.empty() 返回一个空的Optional对象
Optional.of() 返回一个指定非null值的Optional
Optional.ofNullable() 如果为非空,返回 Optional 描述的指定值,否则返回空的 Optional
/** 实例方法 */
optional.hashcode 返回存在值的哈希码,如果值不存在 返回 0
optional.toString 返回一个Optional的非空字符串,用来调试
optional.get 如果在这个Optional中包含这个值,返回值,否则抛出异常:NoSuchElementException
optional.equals 判断其他对象是否等于 Optional
optional.isPresent 如果值存在则方法会返回true,否则返回 false
optional.ifPresent 如果值存在则使用该值调用 consumer , 否则不做任何事情
optional.filter 如果值存在,并且这个值匹配给定的 predicate,返回一个Optional用以描述这个值,否则返回一个空的Optional
optional.flatMap 如果值存在,返回基于Optional包含的映射方法的值,否则返回一个空的Optional
optional.orElse 如果存在该值,返回值, 否则返回 other
optional.map 如果有值,则对其执行调用映射函数得到返回值。如果返回值不为 null,
则创建包含映射返回值的Optional作为map方法返回值,否则返回空Optional
optional.orElseGet 如果存在该值,返回值, 否则触发 other,并返回 other 调用的结果
optional.orElseThrow 如果存在该值,返回包含的值,否则抛出由 Supplier 继承的异常
/** 生成流 */
list.stream() list.parallelStream()
/** 所有操作 */
forEach() -> 循环操作 filter() -> 条件满足 map() -> 值处理 sorted()
-> 排序 limit() -> 控制数量 summaryStatistics() -> 数据统计
在之前需要使用mapTo系列方法,也就是前提需要有数据流(int,float,double),主要有最大值,最小值,总和,总数,平均数
collect(Collectors.toList()) -> 转换成集合 collect(Collectors.joining(分隔符)) -> 将字符串内容合并到一起
/** 代码示例 */
/** Stream 得到集合中元素值大于等于7的元素数量 */
List<Integer> numbers = Arrays.asList(1, 2, 2752, 2200, 7857, 6, 7, 8, 45, 54, 78, 857, 3, 357857, 86, 200780);
numbers.stream()
.filter(obj -> obj % 2 == 0)
.limit(5).map(obj -> obj << 2)
.sorted((o1, o2) -> o2 - o1)
.forEach(integer -> {
System.out.print(integer+"\t");
});
/** 使用Join */
List<String> stringList = Arrays.asList("张三","李四","王五","赵六","钱七","胡八"," ");
String collect = stringList.stream()
.collect(Collectors.joining("先生,"))
.trim();
// 取出字符串尾部的 ','
System.out.println(collect.substring(0,collect.length()-1));
========控制台出入如下=========
11008 8800 32 24 8
张三先生,李四先生,王五先生,赵六先生,钱七先生,胡八先生
/** 月份支持 获取单独月份的支持 */
Month
/*月份天数支持 可以获取月日格式的时间 */
MonthDay
/**年份支持 可以获取年份*/
Year
/*年份月支持 获取年月格式的时间*/
YearMonth
/*时间戳操作 sql包中的获取时间戳中的方法*/
TimeStamp
/*瞬时时间 获取时间戳格式的时间*/
Instant
/*星期支持 星期天数的格式时间获取*/
DayOfWeek
/*日期支持 只获取基本的日期格式的时间。如【年-月-日】*/
LocalDate
/*日期时间支持 获取日期时间支持,不包含时区与星期。,如【年-月-日T时:分:秒.Nano秒】,其中T分隔符*/
LocalDateTime
/*时间支持 只支持时间,不封装时区及任何时间信息*/
LocalTime
/*日期时间转换器 提供日期时间格式的转换*/
DateTimeFormatter
/*单线程日期时间转换器 只有一个返回字符串的静态方法*/
DateTimeFormatterBuilder
/** 重复注解,可以重复使用,原理是用了注解容器 **/
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(Annos.class)
public @interface Anno {
String value();
}
@Retention(RetentionPolicy.RUNTIME)
@interface Annos {
Anno[] value();
}
使用重复注解的原理使用了注解容器,实际上被声明的类上的注解是容器注解
/** 类型注解主要就是泛型与声明类型的时候可以使用,保证了程序的健壮性 */
/** 定义一个可以使用在类型上的注解 */
@Retention(RetentionPolicy.RUNTIME)
// TYPE_USE 用于类型
TYPE_PARAMETER 用于表示泛型
@Target(ElementType.TYPE_USE)
public @interface MyTest {
String value() default "";
}
// JUC包是jdk1.8为并发编程提供的工具包 提供了一些并发工具实现,后续补充
在JDK8之前,一旦修改了接口中的方法,所有实现类都必须改动。
为了不影响之前的版本,提供在接口中定义默认方法的实现。
在方法前修饰default
,并在代码域中编写默认内容
public interface Vehicle {
/** 默认方法 */
default void print() {
System.out.println("我是一辆车!");
}
/** 静态方法 */
static void hornBlock() {
System.out.print("我要超车了!");
}
}
// 编码
private static String printEncoding(String str) {
Base64.Encoder encoder = Base64.getEncoder();
byte[] encode = encoder.encode(str.getBytes());
return new String(encode);
}
// 解码
private static String printDecoding(String str) {
Base64.Decoder decoder = Base64.getDecoder();
byte[] decode = decoder.decode(str.getBytes());
return new String(decode);
}
/** 菜鸟教程实例 */
https://www.runoob.com/java/java8-functional-interfaces.html
/** 方法调用 */
public static void main(String[] args) {
/** BiConsumer 接收两个参数,不返回任何结果 */
getSomeSubject((s, integer) -> System.out.println(integer),"张三",12);
/** BiFunction 接收两个参数,返回一个结果 */
String someSubject = getSomeSubject((num1, num2) -> {
return num1*num2+"";
},12,10);
System.out.println("返回的结果为:"+someSubject); }
// 方法定义
private static String getSomeSubject(BiFunction<Integer,Integer,String> function,Integer num1,Integer num2) {
return function.apply(num1,num2);
}
private static void getSomeSubject(BiConsumer<String,Integer> consumer,String name,Integer age) {
consumer.accept(name,age);
}
感谢你的关注!有更多建议或疑问欢迎下方讨论…