JDK1.8新特性总结

一、引言

JDK1.8出来已经一段时间了,现在1.9也已经出来了,有部分公司不太愿意升级到高版本的JDK1.8,有些项目之前使用到是低版本JDK,如果升级维护老版本项目就比较麻烦,需要连带升级其他到框架。今天抽了一段时间把这些都总结一下

二、新特性

1、关键字 default 关键字

在JDK1.8之前,项目中我们定义的接口中一般都只有抽象的方法不能有方法体,其实则不然,接口里面可以定义静态的具体方法(使用static关键字定义方法),而JDK1.8之后可以通过关键字default定义具体方法,如下

public interface NewCharacter {
    /**
    *一般抽象方法没有实现体到方法
    */
    public void test1();
    /**
    *通过static修饰有方法体到方法
    */
    public static void test2() {
    	System.out.print("方法test2");
    }
    /**
    *JDK1.8引入到新特性定义到方法
    */
    public default void test3(){
        System.out.println("方法test3");
    }
}

那这么定义一个方法的作用是什么呢?为什么不在接口的实现类里面再去实现方法呢?

一般情况在接口里面定义到抽象方法,都需要子类重写父类到抽象方法,在接口里面如果定义到抽象方法是一个算法或者别的方法,需要子类都来重写,并且实现到方式都一样,这样就造成代码到冗余,而且如果修改每个实现类都需要修改,重复的工作量(实际开发中可以通过工具类的定义全局的,每个方法调用即可),这样很麻烦
  其实这么定义一个方法的主要意义是定义一个默认方法,也就是说这个接口的实现类实现了这个接口之后,不用管这个default修饰的方法,也可以直接调用,如下。

class NewCharacterImpl implements NewCharacter {

	public void test1() {
		System.out.println("test1");
	}
	
	@Override
	public void test3() {
		System.out.println("重写父类default方法test3");
	}
	
	public static void main(String[] args) {
        NewCharacter nca = new NewCharacterImpl();
        nca.test1();
        nca.test3();
    }
}

这样在接口里面定义方法目的是,当实现到方法中相同到代码,不需要再所有到子类中都去重复到写一遍,直接在接口中定义dufault 方法中编写具体代码子类就可以直接使用这个方法了,这样不管是开发还是维护项目,都会大大简化代码量。

2.lambda

Lambda可以将方法或者代码片段作为数据进行参数传递。
lambda运算符 " -> ",运算符将表达式分为两部分,左边指定输入参数,右边是lambda的主体(可以是表达式也可以是代码块,实现函数式接口中的方法)

 1.不接受参数,直接返回值1
    ()-> 1
 2.接收两个int类型到参数,返回两个参数到和
     (int a, int b) -> a + b
 3.接收a, b两个参数,JVM会根据上下文推断参数到类型,返回两个数到和
     (a, b) -> a + b
 4. 接收一个字符串参数,打印该字符串,没有返回值
    (String name) -> System.out.println(name)
 5.接收一个参数,JVM根据上下文推断参数到类型,打印该字符串,没有返回值
   (name) -> System.out.println(name)
 6.接收两个字符串,分别打印结果,没有返回值
   (name,address) -> {System.out.println(name);System.out.pirintln(address);7. 接收一个参数,返回它本身2倍
     x -> 2*x

案例测试

public class Java8Tester {
   public static void main(String args[]){
      Java8Tester tester = new Java8Tester();
        
      // 类型声明
      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");
   }
    
   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);
   }
}

执行以上脚本,输出结果为:

$ javac Java8Tester.java 
$ java Java8Tester
10 + 5 = 15
10 - 5 = 5
10 x 5 = 50
10 / 5 = 2
Hello Runoob
Hello Google

使用 Lambda 表达式需要注意以下两点:

  1. Lambda
    表达式主要用来定义行内执行的方法类型接口,例如,一个简单方法接口。在上面例子中,我们使用各种类型的Lambda表达式来定义MathOperation接口的方法。然后我们定义了sayMessage的执行。

  2. Lambda 表达式免去了使用匿名方法的麻烦,并且给予Java简单但是强大的函数化的编程能力。

3 . 函数式接口

函数式接口(Functional Interface)特点:

1.接口内有且只有一个抽象方法
2.可以有多个默认方法(default修饰的方法),即可以有多个非抽象方法的接口。
3.java.lang.Object的public方法不算
4.函数式接口可以被隐式转换为 lambda 表达式。

JDK 1.8 之前已有的函数式接口

 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

测试案例

import java.util.Arrays;
import java.util.List;
 public class LamdbaTest {
	 
	// 计算余数等于1的值
	public static void main(String[] args) {
		 List<Integer> list = Arrays.asList(1, 4, 2, 5, 7);
		 // 定义算法
		 Eval eval = a ->  a % 2 == 1;
		 // 调用方法执行
		 out(list, eval);
		 
	}
	public static void out(List<Integer> list, Eval eval) {
		for (Integer integer : list) {
			if(eval.bool(integer)) {
				System.out.println(integer);
			}
		}
	}
	
	interface Eval {
		boolean bool(Integer a);
	}
}

执行以上脚本,输出结果为

$ javac LamdbaTest.java 
$ java LamdbaTest
输出所有数据:
1
5
7

4. Java 8 Stream

Stream 是对集合(Collection)对象功能的增强,它专注于对集合对象进行各种非常便利、高效的聚合操作(aggregate operation),或者大批量数据操作 (bulk data operation)。这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。

import java.util.Arrays;
import java.util.Comparator;
import java.util.IntSummaryStatistics;
import java.util.List;
import java.util.stream.Collectors;

public class StreamList {

	public static void main(String[] args) {
		List<String> list = Arrays.asList("a", "", "c", "e", "", "f");
		// forEach 遍历集合数据
		list.forEach(str -> System.out.println(str));
		// or
		list.forEach(str -> {
			System.out.println(str);
		});

		// map 方法用于映射每个元素到对应的结果,以下代码片段使用 map 输出了元素对应的平方数
		List<Integer> list1 = Arrays.asList(1, 2, 5, 3, 4);
		// 把集合中的元素处理后赋值给新的集合
		List<Integer> collect = list1.stream().map(i -> i * i)
				.collect(Collectors.toList());
		collect.forEach(str -> System.out.println(str));

		// filter 方法用于通过设置的条件过滤出元素。以下代码片段使用 filter 方法过滤出空字符串:
		list = list.stream().filter(str -> !str.isEmpty())
				.collect(Collectors.toList());
		list.forEach(str -> System.out.println(str));

		// limit 方法用于获取指定数量的流。 以下代码片段使用 limit 方法打印出 10 条数据
		list.stream().limit(2).forEach(str -> System.out.println(str));

		// sorted 方法用于对流进行排序
		// 升序
		list1.stream().sorted().forEach(str -> System.out.println(str));
		// 降序
		list1.stream().sorted(Comparator.reverseOrder())
				.forEach(str -> System.out.println(str));

		List<Person> pList = Arrays.asList(new Person(1, 2), new Person(2, 1),
				new Person(1, 3));
		// 对象属性使用Comparator 来排序一个list
		pList.stream().sorted(Comparator.comparing(Person::getAge))
				.sorted(Comparator.comparing(Person::getNumber).reversed())
				.forEach(str -> System.out.println(str));
		//
		// 对象属性把上面的元素逆序
		pList.stream().sorted(Comparator.comparing(Person::getAge).reversed())
				.forEach(str -> System.out.println(str));

		// 大小写转换
		list.stream().map(String::toUpperCase)
				.forEach(str -> System.out.println(str));

		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());

	}

}

class Person {

	private Integer age;
	private Integer number;

	public Person(Integer age, Integer number) {
		super();
		this.age = age;
		this.number = number;
	}

	public Integer getAge() {
		return age;
	}

	public void setAge(Integer age) {
		this.age = age;
	}

	public Integer getNumber() {
		return number;
	}

	public void setNumber(Integer number) {
		this.number = number;
	}

	@Override
	public String toString() {
		return "Person [age=" + age + ", number=" + number + "]";
	}

}

5. Java 8 Date-Time

在旧版的 Java 中,日期时间 API 存在诸多问题,其中有:

 1. 非线程安全 − java.util.Date 是非线程安全的,所有的日期类都是可变的,这是Java日期类最大的问题之一。
 2. 设计很差 −Java的日期/时间类的定义并不一致,在java.util和java.sql的包中都有日期类,此外用于格式化和
 解析的类在java.text包中定义。java.util.Date同时包含日期和时间,而java.sql.Date仅包含日期,将其纳入
 java.sql包并不合理。另外这两个类都有相同的名字,这本身就是一个非常糟糕的设计。
 3. 时区处理麻烦 − 日期类并不提供国际化,没有时区支持,因此Java引入了java.util.Calendar和java.util.TimeZone类,
 但他们同样存在上述所有的问题。

Java 8 在 java.time 包下提供了很多新的 API

Local(本地) − 简化了日期时间的处理,没有时区的问题。

Zoned(时区) − 通过制定的时区处理日期时间。
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.LocalDateTime;
import java.time.Month;
 
public class Java8Tester {
   public static void main(String args[]){
      Java8Tester java8tester = new Java8Tester();
      java8tester.testLocalDateTime();
   }
    
   public void testLocalDateTime(){
    
      // 获取当前的日期时间
      LocalDateTime currentTime = LocalDateTime.now();
      System.out.println("当前时间: " + currentTime);
        
      LocalDate date1 = currentTime.toLocalDate();
      System.out.println("date1: " + date1);
        
      Month month = currentTime.getMonth();
      int day = currentTime.getDayOfMonth();
      int seconds = currentTime.getSecond();
        
      System.out.println("月: " + month +", 日: " + day +", 秒: " + seconds);
        
      LocalDateTime date2 = currentTime.withDayOfMonth(10).withYear(2012);
      System.out.println("date2: " + date2);
        
      // 12 december 2014
      LocalDate date3 = LocalDate.of(2014, Month.DECEMBER, 12);
      System.out.println("date3: " + date3);
        
      // 22 小时 15 分钟
      LocalTime date4 = LocalTime.of(22, 15);
      System.out.println("date4: " + date4);
        
      // 解析字符串
      LocalTime date5 = LocalTime.parse("20:15:30");
      System.out.println("date5: " + date5);
   }
}

执行以上脚本,输出结果为:

$ javac Java8Tester.java 
$ java Java8Tester
当前时间: 2016-04-15T16:55:48.668
date1: 2016-04-15
: APRIL, : 15, : 48
date2: 2012-04-10T16:55:48.668
date3: 2014-12-12
date4: 22:15
date5: 20:15:30

使用时区的日期时间API

import java.time.ZonedDateTime;
import java.time.ZoneId;
 
public class Java8Tester {
   public static void main(String args[]){
      Java8Tester java8tester = new Java8Tester();
      java8tester.testZonedDateTime();
   }
    
   public void testZonedDateTime(){
    
      // 获取当前时间日期
      ZonedDateTime date1 = ZonedDateTime.parse("2015-12-03T10:15:30+05:30[Asia/Shanghai]");
      System.out.println("date1: " + date1);
        
      ZoneId id = ZoneId.of("Europe/Paris");
      System.out.println("ZoneId: " + id);
        
      ZoneId currentZone = ZoneId.systemDefault();
      System.out.println("当期时区: " + currentZone);
   }
}

执行以上脚本,输出结果为:

$ javac Java8Tester.java 
$ java Java8Tester
date1: 2015-12-03T10:15:30+08:00[Asia/Shanghai]
ZoneId: Europe/Paris
当期时区: Asia/Shanghai
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值