Java8新特性

一、Lambda表达式
二、函数式接口
三、Stream API
四、默认方法和静态方法
五、新时间日期API


一、Lambda表达式

Java8中引入了一种新的语法元素和操作符”->”该符号被称作为Lambda操作符,格式如下:

(param)->System.out.println("param:"+param);

“->”将Lambda表达式分成了两个部分:
左侧括号内指定了Lambda表达式需要的所有参数
右侧为Lambda体

优点:让代码变的简洁、灵活

1.使用Lambda表达式代替匿名

public interface A {
public void Demo();
}

//一般的写法,首先实现接口然后再实现方法
public class TestA implements A {
	@Override
	public void Demo() {
		System.out.println("这是A的实现类");
	}
}
public class Lambda {
	public static void main(String[] args) {
		A a=new TestA();
		a.Demo();
	}
}
//(2).匿名内部类的写法
public class Lambda {
	public static void main(String[] args) {
		A a=new A(){
			@Override
			public void Demo() {
				System.out.println("这是匿名内部类");
			}
		};
		a.Demo();
	}
}
//(3).使用Lambda表达式的写法
public class Lambda {
	public static void main(String[] args) {
		A a=()->System.out.println("这是Lambda表达式");
		a.Demo();
	}
}

2、Lambda表达式语法
(1)无返回值
1种:无参的情况
() ->System.out.println(“Hello”);
2种:有一个参数的情况,当只有一个参数时可以将参数两边的括号省略和参数类型
写法一、(str) ->System.out.println(“str:”+str);
写法二、str ->System.out.println(“str:”+str);
3种:多个参数的情况,可以省去参数类型,参数的类编译器能够自行推断
写法一、(String str1,String str2) ->System.out.println(“str1:”+str1+”str2:”+str2);
写法二、(str1,str2) ->System.out.println(“str1:”+str1+”str2:”+str2);
(2)有返回值
第1种:当Lambda体中只有一条语句时,大括号{}和return都可以省略。
写法一、(x,y) ->{return x+y;}
写法二、(x,y) ->x+y;
注意:当Lambda体中有多条语句时要将语句写在大括号{}中,用分号”;”隔开。只用一条语句时就不需要带大括号了。
例1:() ->System.out.println(“Hello”);
例2:
() ->{ System.out.println(“Hello”); return “World”; }

二、函数式接口

函数式接口@FunctionalInterface,简单的来说函数式接口就是指仅含有一个抽象方法的接口,以@Functionalnterface标注,注意这里的抽象方法指的是该接口自己特有的抽象方法,而不包含它从其上级继承过来的抽象方法。

1、将Lambda作为参数传递

@FunctionalInterface
public interface A {
	public String Demo(String str);
}

public class Lambda {
	public static void main(String[] args) {
		String str0=DemoPlay(
				(str1->{ System.out.println("******");return str1; }),
				"hello");
		System.out.println(“输出结果:”str0);   //输出结果:hello
	}
	
/**
 * @param a 函数式接口A
 * @param str
 * @return
 */
	public static String DemoPlay(A a,String str){
		return a.Demo(str);
	}	
}

2、Java8内置的四大核心函数式接口
Consumer: 消费型接口
void accept(T t);
Supplier:供给型接口
T get();
Function<T, R>: 函数型接口
R apply(T t);
Predicate: 断言型接口:
boolean test(T t);
T:是参数,R:是返回值

public class Lambda1 {
	@Test
	public void test1(){
		System.out.println("这是测试");
		testConsumer(1000,(el) -> System.out.println("消费了"+el+"元"));	
	}
	public void testConsumer(Integer money,Consumer<Integer> con){
		con.accept(money);
	}
	@Test
	public void test2(){
	System.out.println(testSupplier(() -> {return "供给型接口";}));
	}
	public String testSupplier(Supplier<String> sup){
		return sup.get();
	}
	
	@Test
	public void test3(){
	System.out.println(testFunction(123,(n) -> {return "函数型接口"+n;}));
	}
	public String testFunction(int num,Function<Integer,String> fun){
		return fun.apply(num);
	}
	
	@Test
	public void test4(){
	System.out.println(testPredicate("123",(str)->{
		if(str.equals("1234")){
			return true;
		}
		return false;
	}));
	}
	public boolean testPredicate(String str,Predicate<String> pre){
		return pre.test(str);
	}	
}

三、Stream API

Stream是Java8中处理集合的关键抽象概念,可以对指定的集合进行复杂的查找、过滤、映射数据等各种操作,和数据库中使用SQL查询数据相类似。
注意:Stream不会存储数据元素,使用Stream也不会改变源对象,只是会返回一个被包装过的新Stream(符合查找、过滤条件的)。此外Stream操作是延迟执行的,也就是会等到需要的结果的时候才执行。
对Stream操作的三个步骤
1、创建Stream
创建Stream需要有一个数据源,像集合、数组等,不同类型的数据源有不同的获取流的方式,
(1)集合:Java8中的Collection被扩展后提供了两个获取流的方法
default Stream<E> stream() //获取顺序流
default Stream<E> parallelStream() //并行流
例1:
List<String> list = Arrays.asList("aaa","bbb","bcc","add","aee","aff");
list.stream().forEach(System.out::println);
例2:
list.parallelStream().forEach(System.out::println);
(2)数组:Java8中的数组采用Arrays的静态方法stream()获取流
Static <T> Stream<T> stream(T[] array)
例3:
String[] list = { "a", "b", "c" };
Arrays.stream(list).forEach(System.out::print);
(3)值:可以使用静态方法Stream.of(),通过显示值创建一个流,并且它可以接收任意数量的不同类型的参数
Public static<T> Stream<T> of(T.. values)
Stream.of(11,12,13,14).forEach(System.out::println);
2、中间操作
在一组中间操作中如果没有触发终止操作的操作存在,那么这一组中间操作将不会执行任何处理,而是在最后的终止操作中一次性处理,这种操作被称为”惰性求值”
筛选与切片

方法描述
filter(Predicate p)接收lambda,从流中排除某些元素
limit(long maxSize)截断流,使其元素不超过给定数量
distinct()筛选,通过流所生成元素的hashcode()和equals()去重复元素
skip(long n)跳过元素,返回一个扔掉了前n个元素的流。若流中元素不足n个,则返回一个空流,与limit(n)互补

映射

方法描述
map(Function f)接收Lambda,将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每一个元素上,并将其映射成一个新的元素

排序

方法描述
sorted()产生一个新流,其中按自然顺序排序
sorted(Comparator comp)产生一个新流,其中按比较器顺序排序

例1

@Test
 public void test3(){
   List<String> list =Arrays.asList("aa","bb","cc","dd");
   list.stream().sorted().forEach(System.out::print);
//输出结果:aa bb cc dd
   list.stream().sorted((x,y) -> {
   if(x.equals(y)){
        return 1;
   }else{
        return -1;
   }
  } ).forEach(System.out::println);
//输出结果:dd cc bb aa
}

3、终止操作
终止操作会从中间操作生成结果,结果可以是任何不是流的值,例如:List、Integer,甚至是void。
查找与匹配

方法描述
findFirst返回第一个元素
findAny返回当前流中的任意元素
count返回流中元素的总数
max(Comparator c)返回流中最大值
min(Comparator c)返回流中最小值
froEach(Consumer c)内部迭代

归约

方法描述
reduce(T iden,BinaryOperator b)可以将流中的值反复结合起来,得到一个值。返回T
reduce(BinaryOperator b)可以将流中的值反复结合起来,得到一个值。返回Optional

例1:

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
     Integer sum = list.stream().reduce(0, (x, y) -> x + y);
     System.out.println(sum);

输出结果:55
例2:

 Optional<Double> op = employeeList.stream() //可能为空 所有返回的是Optional
                .map(Employee::getSalary)
                .reduce(Double::sum);
        System.out.println(op.get());

输出结果是所有员工的工资和

收集

方法描述
collect(Collector c)将流转换为其他形式。接收一个Collector接口的实现,用于给Stream中元素做汇总的方法

例:提取User对象的name,并将结合内的所有对象name收集到list1中

List<Employee> employeeList = Arrays.asList(
                new Employee("zhangsan", 18, 19999, Employee.Status.FREE),
                new Employee("lisi", 28, 29999, Employee.Status.BUSY),
                new Employee("wangwu", 38, 39999, Employee.Status.VOCATION),
                new Employee("zhaoliu", 18, 17999, Employee.Status.BUSY),
                new Employee("tianqi", 28, 12999, Employee.Status.FREE)       
        );
List<String> list1 =employeeList.stream().map(Employee::getName).collect(Collectors.toList()) ;
list1.forEach(System.out::println);

例:将结合内的所有对象name收集到set中

HashSet<String> set = employeeList.stream().map(Employee::getName).collect(Collectors.toCollection(HashSet::new));
set.forEach(System.out::println);

例:集合中元素总数

Long count = employeeList.stream().collect(Collectors.counting());
System.out.println(count);

例:平均年龄

double avAge=employeeList.stream().collect(Collectors.averagingInt(Employee::getAge));
System.out.println(avAge);  

例:总年龄

int toAge = employeeList.stream().collect(Collectors.summingInt(Employee::getAge));
System.out.println(toAge);

例:年龄最大值

Optional<Employee> u = employeeList.stream().collect(Collectors.maxBy((e1,e2)->Integer.compare(e1.getAge(),e2.getAge() )));
System.out.println(u);

例:平均年龄

IntSummaryStatistics collect = employeeList.stream().collect(Collectors.summarizingInt(Employee::getAge));
System.out.println(collect.getAverage());

例:按年龄分组

Map<Integer, List<Employee>> l= employeeList.stream().collect(Collectors.groupingBy(Employee::getAge));System.out.println(l);

输出 结果:

{18=[Employee{name='zhangsan', age=18, salary=19999.0, id=0, status=FREE}, Employee{name='zhaoliu', age=18, salary=17999.0, id=0, status=BUSY}], 38=[Employee{name='wangwu', age=38, salary=39999.0, id=0, status=VOCATION}], 28=[Employee{name='lisi', age=28, salary=29999.0, id=0, status=BUSY}, Employee{name='tianqi', age=28, salary=12999.0, id=0, status=FREE}]}

例:根据true和false进行分区,年龄大于18岁和小于18岁两个区

 Map<Boolean,List<Employee>> map=  employeeList.stream().collect(Collectors.partitioningBy((x) -> x.getAge()>18));
System.out.println(map);

输出结果:

 {false=[Employee{name='zhangsan', age=18, salary=19999.0, id=0, status=FREE}, Employee{name='zhaoliu', age=18, salary=17999.0, id=0, status=BUSY}], true=[Employee{name='lisi', age=28, salary=29999.0, id=0, status=BUSY}, Employee{name='wangwu', age=38, salary=39999.0, id=0, status=VOCATION}, Employee{name='tianqi', age=28, salary=12999.0, id=0, status=FREE}]}

例:连接字符串

String str = employeeList.stream().map(Employee::getName).collect(Collectors.joining(",","*","*"));
System.out.println(str);

输出结果:zhangsan,lisi,wangwu,zhaoliu,tianqi

并行流 : 就是把一个内容分成多个数据块,并用不同的线程分 别处理每个数据块的流。
Java 8 中将并行进行了优化,我们可以很容易的对数据进行并 行操作。Stream API 可以声明性地通过 parallel() 与 sequential() 在并行流与顺序流之间进行切换。

四、默认方法和静态方法

1、Java8允许接口中存在具体的实现方法,该方法称为默认方法,默认方法采用default关键字修饰

public interface A {
	default void Demo1(){
		System.out.println("这是默认方法");
	}
}

java8在接口中引入了默认方法,通过在方法前加上default关键字就可以在接口中写方法的默认实现,但是当多个接口或父类中有相同签名的方法时,会引发一些问题,经过实验得出如下结论:(参考:https://blog.csdn.net/travellersy/article/details/74537170)
(1)当继承的父类和实现的接口中有相同签名的方法时,优先使用父类的方法。
(2)当接口的父接口中也有同样的默认方法时,就近原则调用子接口的方法。
(3)当实现的多个接口中有相同签名的方法时,必须在实现类中通过重写方法解决冲突问题,否者无法通过编译,在重写的方法中可以通过 接口名.super.方法名(); 的方式显示调用需要的方法。
例:
有一个类和两个接口

public class A {
    public void methods(){
        System.out.println("Object A methods");    
}
}
public interface B {
    default void methods(){
        System.out.println("interface B methods");
    }
}
public interface C {
    default void methods(){
        System.out.println("interface C methods");
    }
}

下面通过一个类继承自A并且实现B接口:

public class Test extends A implements B{
    public static void main(String[] args){
        System.out.println("HelloWord!");
        Test test = new Test();
        test.methods();
    }
}

根据就近原则test将调用父类A的方法并输出"Object A methods"
如果一个类同时实现了接口B和C:

public class Test1 implements B, C {
    public static void main(String[] args){
        System.out.println("HelloWord!");
        Test1 test1 = new Test();
        test1.methods();
    }

@Override
    public void methods() {
        B.super.methods();
    }
}

通过使用Override重写的方式解决冲突问题,并且在Override方法中通过B.super.methods();的方式显示指定调用B接口中methods方法,最终输出结果为:“interface B methods”

2、Java中允许添加静态方法

public interface A {
	//抽象方法
	public String Demo(String str);
	default void Demo1(){
		System.out.println("这是默认方法");
	}
	static void show(){
		System.out.println("这是静态方法");
	}
}

五、时间和日期操作

相比Date,LoalDate更安全、更精确也更明确

System.out.println("------------------日期操作-------------------");
		//获取当前日期
		LocalDate today = LocalDate.now();
		System.out.println(today);
		//本月的第一天
		LocalDate firstDayOfThisMonth = today.with(TemporalAdjusters.firstDayOfMonth());
		System.out.println(firstDayOfThisMonth);
		//本月的第N天
		LocalDate NDayOfThisMonth = today.withDayOfMonth(30); // 2017-03-02
		System.out.println(NDayOfThisMonth);
		//后一天
		LocalDate firstDayOf2015 = today.plusDays(1);
		System.out.println(firstDayOf2015);
		//指定日期2015-02-10
		LocalDate ld=LocalDate.of(2015, 2, 10);
		System.out.println(ld);
		//指定日期2014-03-28
		LocalDate endOfFeb = LocalDate.parse("2014-03-28");
		System.out.println(endOfFeb);
		//两个日期之间相隔几天
		Period period = Period.between(today, today.plus(2, ChronoUnit.DAYS));
	    System.out.println(period.getDays());
		System.out.println("------------------时间操作-------------------");
		//获取当前时间
		LocalTime now = LocalTime.now();
		System.out.println(now);
		
		System.out.println("*********日期和时间************");
		//获取当前日期和时间
		LocalDateTime localDateTime = LocalDateTime.now();
		System.out.println(localDateTime);
		//获取当前月的天数
		System.out.println(localDateTime.getMonth().length(true));
	    //当月名称(英文)
	    System.out.println(localDateTime.getMonth().name());
	    //往后推迟两天
	    System.out.println(localDateTime.plus(2, ChronoUnit.DAYS));
	    //往前两天
	    System.out.println(localDateTime.minus(2, ChronoUnit.DAYS));

输出结果:
Mon Jul 16 11:01:04 CST 2018
------------------日期操作-------------------
2018-07-16
2018-07-01
2018-07-30
2018-07-17
2015-02-10
2014-03-28
2
------------------时间操作-------------------
11:01:05.084
日期和时间***
2018-07-16T11:01:05.087
31
JULY
2018-07-18T11:01:05.087
2018-07-14T11:01:05.087

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值