文章目录
参考菜鸟教程,此篇博客的目的是学习java8 的新特性,示例来源与菜鸟教程,学习的过程中加入了自己的感悟,java8 已经出现好久了,现在才耐心的学习下,惭愧惭愧
1.lambda
函数编程 《=》 lambda 表达式,函数式变成强调结果,强调利用若干简单的执行单元让计算结果不断渐进,而不是设计一个复杂的执行过程
_维基百科
逻辑 作为参数
返回一个新的值,尤其是不得修改外部变量的值
//lambda 表达式语法格式
(parameters) -> expression
或
(parameters) ->{ statements; }
lambda 表达式的特点:
可选类型声明: 不需要声明参数类型,编译器可以统一识别参数值
可选的参数圆括号: 一个参数无需定义圆括号,但是多个参数需要定义圆括号
可选大括号: 如果主体包含了一个语句,就不需要使用大括号
可选的返回关键字: 如果主体只有一个表达式返回值则编译器自动返回值,大括号需要指定表达式返回了一个数值
lambda 表达式实例:
()-> 5 //1.不需要参数,返回值为5
x->x*2 //2.接受一个参数(数字类型),返回其2倍的值
(x,y)-> x-y //3.接受2个参数(数字),并返回他们的差值
(int x, int y) -> x + y // 4. 接收2个int型整数,返回他们的和
(String s) -> System.out.print(s)// 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)
java 使用 lambda 表达式
package com.study.java8;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class Lambda {
public static void main(String[] args) {
Lambda tester = new Lambda();
// 类型声明
MathOperation addition = (int a, int b) -> a + b;
// 不用类型声明
MathOperation subtraction = (a, b) -> a - b;
// 大括号中的返回语句
MathOperation multiplication = (int a, int b) -> { return a * b; };
// 没有大括号及返回语句
MathOperation division = (int a, int b) -> a / b;
System.out.println("10 + 5 = " + tester.operate(10, 5, addition));
System.out.println("10 - 5 = " + tester.operate(10, 5, subtraction));
System.out.println("10 x 5 = " + tester.operate(10, 5, multiplication));
System.out.println("10 / 5 = " + tester.operate(10, 5, division));
// 不用括号
GreetingService greetService1 = message ->
System.out.println("Hello " + message);
// 用括号
GreetingService greetService2 = (message) ->
System.out.println("Hello " + message);
greetService1.sayMessage("Runoob");
greetService2.sayMessage("Google");
//集合类的lambda 表达式
collectTest();
}
private static void collectTest(){
List<String> names1 = new ArrayList<String>();
names1.add("Google ");
names1.add("Runoob ");
names1.add("Taobao ");
names1.add("Baidu ");
names1.add("Sina ");
List<String> names2 = new ArrayList<String>();
names2.add("Google ");
names2.add("Runoob ");
names2.add("Taobao ");
names2.add("Baidu ");
names2.add("Sina ");
Lambda tester = new Lambda();
System.out.println("使用 Java 7 语法: ");
tester.sortUsingJava7(names1);
System.out.println(names1);
System.out.println("使用 Java 8 语法: ");
tester.sortUsingJava8(names2);
System.out.println(names2);
}
interface MathOperation {
int operation(int a, int b);
}
interface GreetingService {
void sayMessage(String message);
}
private int operate(int a, int b, MathOperation mathOperation){
return mathOperation.operation(a, b);
}
// 使用 java 7 排序
private void sortUsingJava7(List<String> names){
Collections.sort(names, new Comparator<String>() {
@Override
public int compare(String s1, String s2) {
return s1.compareTo(s2);
}
});
}
// 使用 java 8 排序
private void sortUsingJava8(List<String> names){
Collections.sort(names, (s1, s2) -> s1.compareTo(s2));
}
}
总结: 使用lambda 表达式,就像使用使用就是想参数一样使用逻辑,将逻辑带入引用的地方。如上程序,将逻辑带入算数,将逻辑带入输出,相当与使用了实现类,但没有实际的实现类。
画重点:lamdb 表达式,放在使用接口的地方,程序就会将lambda 表达式的逻辑,认为成对应接口抽象方法的逻辑,就相当实现了这个接口的抽象方法,只不过没有对应的实现类,对应的实现方法罢了。
2.java 8 方法的引用
概念: 通过方法的名字来指向一个方法,使语言构造更紧凑简介,使用 ::
下面是四个方法区分,java 中四个不同方法的引用
package com.runoob.main;
@FunctionalInterface
public interface Supplier<T> {
T get();
}
class Car {
//Supplier是jdk1.8的接口,这里和lamda一起使用了
public static Car create(final Supplier<Car> supplier) {
return supplier.get();
}
public static void collide(final Car car) {
System.out.println("Collided " + car.toString());
}
public void follow(final Car another) {
System.out.println("Following the " + another.toString());
}
public void repair() {
System.out.println("Repaired " + this.toString());
}
}
构造器的引用:它的语法是Class::new ,或者更一般的Class::new
final Car car = Car.create(Car::new);
final List<Car> cars = Arrays.asList(car)
静态方法的引用:他的语法是Class::static_method,实例如下
cars.forEach( Car::collide );
特定类的任意对象的方法引用:它的语法是Class::method实例如下
cars.forEach( Car::repair );
特定对象的方法引用:它的语法是instance::method实例如下:
final Car police = Car.create( Car::new );
cars.forEach( police::follow );
画中点: 方法引用 :: , 就是用方法名称使用方法, 点击源码forEach 接收的参数类型式java8函数接口编程中java.util.function 中的 Consumer 接口,即这个接口式lambda 表达式中的 代表了接受一个输入参数并且无返回的操作(参考函数式编程)。 方法引用应该也是一种lambda 表达式的形式,即可理解成 方法引用,像是将方法伪装成类似lambda,或者说方法引用就是一种lambda 表达式,而lambda 表达式的逻辑就相当于 方法的实现逻辑。
3.函数式接口
函数式接口(functional interface),就是一个有且但有一个抽象方法,但是可以有多个非抽象方法的接口
函数式接口可以被隐式转换为lambda 表达式
Lambda 表达式和方法引用(实际上也可认为是Lambda表达式)上。
如定义了一个函数式接口如下:
@FunctionalInterface
interface GreetingService
{
void sayMessage(String message);
}
这就可以使用Lambda 表达式来表示的一个实现(注:Java 8 之前一般是用匿名类实现的)
GreetingService greetService1 = message -> System.out.println("Hello" + message)
函数式接口可以对现有的函数友好地支持lambda
java.lang.Runnable
java.util.concurrent.Callable
java.security.PrivilegedAction
java.util.Comparator
java.io.FileFilter
java.nio.file.PathMatcher
java.lang.reflect.InvocationHandler
java.beans.PropertyChangeListener
java.awt.event.ActionListener
javax.swing.event.ChangeListener
JDK 1.8 新增加的函数接口
java.util.function
java.util.function 它包括了很多类,用来支持java 的函数式编程,该包中的函数式接口有:
java.util.function函数式编程函数list参考
貌似要常用的接口:
BiConsumer<T,U> 代表了一个接受两个输入参数的操作,并且不返回任何结果
BiFunction<T,U,R> 代表了一个接受两个输入参数的方法,并且返回一个结果
BinaryOperator 代表了一个作用于于两个同类型操作符的操作,并且返回了操作符同类型的结果
Consumer 代表了接受一个输入参数并且无返回的操作
Function<T,R> 接受一个输入参数,返回一个结果。
ObjDoubleConsumer 接受一个object类型和一个double类型的输入参数,无返回值。
ObjIntConsumer 接受一个object类型和一个int类型的输入参数,无返回值。
ObjLongConsumer 接受一个object类型和一个long类型的输入参数,无返回值。
Predicate 接受一个输入参数,返回一个布尔值结果。
UnaryOperator 接受一个参数为类型T,返回值类型也为T。
代码示例:
package com.study.java8;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
/**
* 函数式编程
*/
public class FunctionFormula {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
System.out.println("输出所有数据");
// 传递参数 n
// Predicate 接受一个输入参数,返回一个boolean结果
eval(list,n->true);
System.out.println("输出所有的偶数");
eval(list,n->n%2 == 0);
//
System.out.println("输出大于3的所有数字");
eval(list,n -> n > 3);
}
public static void eval(List<Integer>list, Predicate<Integer> predicate){
for (Integer n :list){
if (predicate.test(n)){
System.out.println(n + " ");
}
}
}
}
java8 默认方法 default
java8 接口可以有实现方法,而且不需要实现类去实现这个方法。默认的方法的好处是,在不改变接口现有实现的基础上,可以对接口作出一些修改。
一个接口有默认方法,一个类实现了多个接口,且这些接口有相同的默认方法,那么这个类选择那个默认方法应用那
方法:
(1)类实现自己的同名默认方法
(2)用super 关键字指定接口的默认方法
public class Car implements Vehicle, FourWheeler {
public void print(){
Vehicle.super.print();
}
}
4.静态默认方法
public interface Vehicle {
default void print(){
System.out.println("我是一辆车!");
}
// 静态方法
static void blowHorn(){
System.out.println("按喇叭!!!");
}
}
默认方法实例
public class Java8Tester {
public static void main(String args[]){
Vehicle vehicle = new Car();
vehicle.print();
}
}
interface Vehicle {
default void print(){
System.out.println("我是一辆车!");
}
static void blowHorn(){
System.out.println("按喇叭!!!");
}
}
interface FourWheeler {
default void print(){
System.out.println("我是一辆四轮车!");
}
}
class Car implements Vehicle, FourWheeler {
public void print(){
Vehicle.super.print();
FourWheeler.super.print();
Vehicle.blowHorn();
System.out.println("我是一辆汽车!");
}
}
输出
我是一辆车!
我是一辆四轮车!
按喇叭!!!
我是一辆汽车!
5. Stream
概念: java 8 api 添加了一个新的抽象称为流Stream,可以以一种声明的方法处理数据
Stream 使用一种类似用SQL 语句从数据库查询数据的直观方式来提供一种对Java 集合运算和表达的高阶抽象。Stream API 可以极大提高Java 程序员的生产力,让程序员写出高效,干净,简洁的代码(喜欢这几个字),这种风格将要处理的元素集合看做一种流,流在管道中传输,并且可以在管道的节点上进行处理,比如筛选,排序,聚合等。元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation) 得到前面处理的结果。
如下图:
+--------------------+ +------+ +------+ +---+ +-------+
| stream of elements +-----> |filter+-> |sorted+-> |map+-> |collect|
+--------------------+ +------+ +------+ +---+ +-------+
以上的流程转换为 Java 代码为:
List<Integer> transactionsIds =
widgets.stream()
.filter(b -> b.getColor() == RED)
.sorted((x,y) -> x.getWeight() - y.getWeight())
.mapToInt(Widget::getWeight)
.sum();
什么是Stream
Stream (流) 是一个来自数据源的元素队列并支持聚合操作
数据源: 流的来源。可以是集合,数组,I/O channel , 产生器 generator 等。
聚合操作: 类似SQL语句一样的操作,比如filter,map,reduce,match,sorted 等
和以前Collection 操作不同,Stream 操作还有两个基础得特征
**Pipelining:**中间操作都会返回对象本身。这样多个操作可以串联成一个管道,如同流式风格(flent style).这样做可以对操作进行优化,比如延迟执行(laziness) 和短路(short-circuiting).
内部迭代: 以前对集合遍历都是通过Iterator 或者 For-each 得方式,显示的在集合外部进行迭代,这叫做外迭代,Stream 提供了内部迭代的方式,通过访问者模式(Vistor)实现。
生成流
集合接口有两个方法来生成流:
串行流: stream()
并行流: parallelStream()
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());
画重点
forEach : Stream 提供新的方法forEach 来迭代流中的每个数据
Random random = new Random();
random.ints().limit(10).forEach(System.out::printLn)
map: 用于映射每个元素对应的结果
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
// 获取对应的平方数
List<Integer> squaresList = numbers.stream().map( i -> i*i).distinct().collect(Collectors.toList());
filter: 方法用于通过设置的条件过滤元素
List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
// 获取空字符串的数量
long count = strings.stream().filter(string -> string.isEmpty()).count();
limit: 方法用于获取指定数量的流。
Random random = new Random();
random.ints().limit(10).forEach(System.out::println);
sorted: 方法用于对流进行排序
Random random = new Random();
random.ints().limit(10).sorted().forEach(System.out::println);
并行(parallel) 程序
parallelStream 是流并行处理程序的代替方法。
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
// 获取空字符串的数量
int count = strings.parallelStream().filter(string -> string.isEmpty()).count();
Collectors: 类实现了很多归约操作,例如将流转换成集合和聚合元素。Collectors 可用于返回列表或字符串
List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());
System.out.println("筛选列表: " + filtered);
String mergedString = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.joining(", "));
System.out.println("合并字符串: " + mergedString);
统计:另外,一些产生统计结果的收集器也非常有用。它们主要用于int、double、long等基本类型上,它们可以用来产生类似如下的统计结果。
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
IntSummaryStatistics stats = numbers.stream().mapToInt((x) -> x).summaryStatistics();
System.out.println("列表中最大的数 : " + stats.getMax());
System.out.println("列表中最小的数 : " + stats.getMin());
System.out.println("所有数之和 : " + stats.getSum());
System.out.println("平均数 : " + stats.getAverage());
6.Optional 类
Optional 类是一个可以为null的容器对象。如果值存在则isPresent() 方法会返回true,调用get() 方法会返回给对象。Optional是个容器:可以保存类型T的值,或者仅仅保存null.Optional 提供很多有用的方法,这样我们就不用显式进行控制检测。Optional 类的引入很好的解决空指针异常。
类声明
以下是一个java.util.Optional 类的声明:
public final class Optional<T> extends Object
类方法:参考菜鸟教程
7、Nashorn , JavaScript 引擎
8、 新的日期时间API
Java 8通过发布新的Date-Time API (JSR 310)来进一步加强对日期与时间的处理。
在旧版的 Java 中,日期时间 API 存在诸多问题,其中有:
非线程安全 − java.util.Date 是非线程安全的,所有的日期类都是可变的,这是Java日期类最大的问题之一。
设计很差 − Java的日期/时间类的定义并不一致,在java.util和java.sql的包中都有日期类,此外用于格式化和解析的类在java.text包中定义。java.util.Date同时包含日期和时间,而java.sql.Date仅包含日期,将其纳入java.sql包并不合理。另外这两个类都有相同的名字,这本身就是一个非常糟糕的设计。
时区处理麻烦 − 日期类并不提供国际化,没有时区支持,因此Java引入了java.util.Calendar和java.util.TimeZone类,但他们同样存在上述所有的问题。
Java 8 在 java.time 包下提供了很多新的 API。以下为两个比较重要的 API:
Local(本地) − 简化了日期时间的处理,没有时区的问题。
Zoned(时区) − 通过制定的时区处理日期时间。
新的java.time包涵盖了所有处理日期,时间,日期/时间,时区,时刻(instants),过程(during)与时钟(clock)的操作。
java 用例
package com.study.java8;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.Month;
public class TimeExample {
public static void main(String[] args) {
TimeExample timeExample = new TimeExample();
timeExample.testLocalDateTime();
}
private void testLocalDateTime() {
//获取当前的日期时间
LocalDateTime now = LocalDateTime.now();
System.out.println("当前时间:" + now);
LocalDate localDate = now.toLocalDate();
System.out.println("date1:" + localDate);
Month month = now.getMonth();
int dayOfMonth = now.getDayOfMonth();
int second = now.getSecond();
System.out.println("月:"+ month+",日:" + dayOfMonth + ", 秒" +second);
// 12 december 2014
LocalDate of = LocalDate.of(2014, Month.DECEMBER, 12);
System.out.println("date3:"+ of);
//12 小时 15 分钟
LocalTime date4 = LocalTime.of(12, 15);
System.out.println("date4:"+date4);
//解析字符串
LocalTime date5 = LocalTime.parse("20:15:30");
System.out.println("date5: " + date5);
}
}
9、Base64
java 8 中,Base64 编程已经称为Java 类库的标准
Java8 内置Base64 编码的编码器和解析器
基本:输出被映射到一组字符A-Za-z0-9+/,编码不添加任何行标,输出的解码仅支持A-Za-z0-9+/。
URL:输出映射到一组字符A-Za-z0-9+_,输出是URL和文件。
MIME:输出隐射到MIME友好格式。输出每行不超过76字符,并且使用'\r'并跟随'\n'作为分割。编码输出最后没有行分割。
要克服生活的焦虑和沮丧,得先学会做自己的主人,有问题留言,没问题留下你的赞
博客声明:
1.博客内容全是对工作学习的总结
2.知识点都经过测试和推敲,如有疑问请留言,一定及时解决