jdk8新功能
一、接口
之前的接口是个双刃剑,好处是面向抽象而不是面向具体编程,缺陷是,当需要修改接口时候,需要修改全部实现该接口的类,目前的java 8之前的集合框架没有foreach方法,通常能想到的解决办法是在JDK里给相关的接口添加新的方法及实现。然而,对于已经发布的版本,是没法在给接口添加新方法的同时不影响已有的实现。所以引进的默认方法。他们的目的是为了解决接口的修改与现有的实现不兼容的问题。
public static void main(String[] args) throws Exception {
A.show("静态方法");
B b = new B();
b.print();
}
interface A{
//默认方法
default void print(){
System.out.println("this is A");
}
//静态方法
static void show(String str){
System.out.println(str);
}
}
class B implements A{
//可以重写也可以不重写
public void print(){
System.out.println("this is B");
}
}
二、base64
Java 8 内置了 Base64 编码的编码器和解码器。Base64工具类提供了一套静态方法获取下面三种BASE64编解码器:
- 基本:输出被映射到一组字符A-Za-z0-9+/,编码不添加任何行标,输出的解码仅支持A-Za-z0-9+/。
- URL:输出映射到一组字符A-Za-z0-9+_,输出是URL和文件。
- MIME:输出隐射到MIME友好格式。输出每行不超过76字符,并且使用’\r’并跟随’\n’作为分割。编码输出最后没有行分割。
String base64encodedString = Base64.getEncoder().encodeToString("jdk8".getBytes("utf-8"));
System.out.println(base64encodedString);//打印:amRrOA==
byte[] bs = Base64.getDecoder().decode(base64encodedString);
System.out.println(new String(bs, "utf-8"));//打印:jdk8
三、时间api
在旧版的 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,涵盖了所有处理日期,时间,日期/时间,时区,时刻(instants),过程(during)与时钟(clock)的操作。以下为两个比较重要的 API:
- Local(本地) − 简化了日期时间的处理,没有时区的问题。
- Zoned(时区) − 通过制定的时区处理日期时间。
//获取当前日期
LocalDate date = LocalDate.now();
System.out.println(date);//打印:2018-04-20
//获取当前时间
LocalTime time = LocalTime.now();//打印:14:39:31.839
System.out.println(time);
//获取当前日期和时间
LocalDateTime dateTime = LocalDateTime.now();
System.out.println(dateTime);//打印:2018-04-20T14:39:31.839
//操作时间对象
int year = dateTime.getYear();
//month使用比较特殊,但是会相对方便,不需要在月份上加1
Month month = dateTime.getMonth();
int monthInt = month.getValue();
int day = dateTime.getDayOfMonth();
int minute = dateTime.getMinute();
//传入参数,获得实践对象
Month inMonth = Month.of(1);//或者Month.JANUARY
LocalDateTime inDateTime = LocalDateTime.of(1992, inMonth, 1, 12, 12, 12);
System.out.println(inDateTime);//打印:1992-01-01T12:12:12
LocalDateTime inDateTime2 = LocalDateTime.parse("1992-01-01T12:12:12");
//本机时间
LocalDateTime localnow = LocalDateTime.now();
//将本地时间的时区设置为哈尔滨时区
ZonedDateTime localHarbin = ZonedDateTime.parse(localnow + "+08:00[Asia/Harbin]");
//将本地时间的时区设置为洛杉矶时区
ZonedDateTime localLos = ZonedDateTime.parse(localnow + "-07:00[America/Los_Angeles]");
System.out.println(localHarbin);//打印:2018-04-20T13:56:41.227+08:00[Asia/Harbin]
System.out.println(localLos);//打印:2018-04-20T13:56:41.227-07:00[America/Los_Angeles]
//获取洛杉矶时区id
ZoneId id = ZoneId.of("America/Los_Angeles");
//获取当前时区的本地时间
ZonedDateTime losnow = ZonedDateTime.now(id);
System.out.println(losnow);//打印:2018-04-19T22:31:49.882-07:00[America/Los_Angeles]
//比较
System.out.println(localHarbin.compareTo(losnow));//打印:-18000000
System.out.println(localHarbin.compareTo(localLos));//打印:-18000000
System.out.println(localLos.compareTo(losnow));//打印:1
//localHarbin的时间是哈尔滨时间,与losnow的洛杉矶时间只是时区差别,时间其实是一样的,所以输出的结果只是程序执行所花费的时间18毫秒
//localLos的时间由哈尔滨,也就是本地时间转为洛杉矶时间的,与losnow的洛杉矶时间就会相差7+8两个时区的时间
注意:Local和Zoned获取当前时间都是获取本机的系统时间,如果电脑的系统时间改变,获得的时间对象也会改变。
四、Optional
Optional 类是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。
Optional 是个容器:它可以保存类型T的值,或者仅仅保存null。Optional提供很多有用的方法,这样我们就不用显式进行空值检测。Optional 类的引入很好的解决空指针异常。
Integer value1 = null;
Integer value2 = new Integer(10);
//允许传递为 null 参数
Optional<Integer> optional1 = Optional.ofNullable(value1);
boolean b = optional1.isPresent();//false
//如果传递的参数是 null,抛出异常 NullPointerException
Optional<Integer> optional2 = Optional.of(value2);
Integer integer = optional2.get();//10
五、Lambda表达式
public static void main(String[] args) throws Exception {
//有参数、有返回值
MathOperation1 m1 = (a,b) -> a < b ? a : b;
int min = m1.getMin(20, 30);
System.out.println(min);
//有参数、无返回值
MathOperation2 m2 = (a, b) -> System.out.println(a < b ? a : b);
m2.showMin(20, 30);
//无参数、无返回值
String str = "hello";
MathOperation3 m3 = () -> System.out.println(str);//不能在 lambda 内部修改定义在域外的局部变量,否则会编译错误。
m3.printHello();
}
interface MathOperation1{
int getMin(int a, int b);
}
interface MathOperation2{
void showMin(int a, int b);
}
interface MathOperation3{
void printHello();
}
要使用lambda表达式实现接口,要求只能有一个抽象方法,否则会报错。但java8的新特性允许实现如下写法:
interface MathOperation1{
int getMin(int a, int b);
default int getSum(int a, int b){
return a + b;
}
static int getMax(int a, int b){
return a < b ? b : a;
}
}
六、方法引用
以下demo都使用的实体类:
public static void main(String[] args) throws Exception {
Student[] stus = new Student[3];
stus[0] = new Student(20, "A");
stus[1] = new Student(40, "B");
stus[2] = new Student(30, "C");
}
class Student{
private int age;
private String name;
public Student() {
super();
}
public Student(int age, String name) {
super();
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public String getName() {
return name;
}
public String toString() {
return "Student [age=" + age + ", name=" + name + "]";
}
//静态比较方法
public static int staticMethodCompareByAge(Student s1, Student s2){
return s1.age - s2.age;
}
//实例比较方法,不需要放在本类中
public int instanceMethodCompareByAge(Student s1,Student s2){
return s1.getAge() - s2.getAge();
}
//实例比较方法
public int methodCompareByAge(Student s){
return this.age - s.age;
}
//创建本类的实体对象
public static Student getInstance(Supplier<Student> supplier){
return supplier.get();
}
}
1、内部类
//使用匿名内部类
Arrays.sort(stus, new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
return s1.getAge() - s2.getAge();
}
});
//使用lambda表达式
Arrays.sort(stus, (s1, s2) -> s1.getAge() - s2.getAge());
2、引用静态方法
ContainingClass::staticMethodName
//使用lambda表达式
Arrays.sort(stus, (s1, s2) -> Student.staticMethodCompareByAge(s1, s2));
//引用静态方法
Arrays.sort(stus, Student::staticMethodCompareByAge);
3、引用某个对象的实例方法
containingObject::instanceMethodName
//创建Student只是为了使用这个类里面的比较方法,也可以是别的类,里面有比较两个Student类的方法也可以,这里只是为了书写方便。
Student s = new Student();
//使用lambda表达式
Arrays.sort(stus, (s1, s2) -> s.instanceMethodCompareByAge(s1, s2));
//引用对象的实例方法
Arrays.sort(stus, s::instanceMethodCompareByAge);
4、引用某个类型的任意对象的实例方法
ContainingType::methodName
//使用lambda表达式
Arrays.sort(stus, (s1, s2) -> s1.methodCompareByAge(s2));
//引用类型对象的实例方法
Arrays.sort(stus, Student::methodCompareByAge);
5、引用构造方法
ClassName::new
Student student = Student.getInstance(Student::new);
七、Stream
java 8 API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据。它有以下几个特点:
- stream不存储数据
- stream不改变源数据
- stream的延迟执行特性
通常我们在数组或集合的基础上创建stream,stream不会专门存储数据,对stream的操作也不会影响到创建它的数组和集合,对于stream的聚合、消费或收集操作只能进行一次。
List<String> list = Arrays.asList("a", "b", "c");
1、生成流
Stream<String> stream = list.stream();
Stream<String> stream2 = list.parallelStream();
2、forEach,迭代流中的每个数据
list.stream().forEach(System.out::println);
3、map,映射每个元素到对应的结果
list.stream().map((str) -> "这是" + str).forEach(System.out::println);
4、filter,通过设置的条件过滤出元素
list.stream().filter((str) -> str.equals("c")).forEach(System.out::println);
5、limit,获取指定n个数据;skip,跳过前n个数据
list.stream().limit(2).forEach(System.out::println);
list.stream().skip(1).forEach(System.out::println);
6、sorted,对流进行排序
list.stream().sorted((s1, s2) -> s1.hashCode() - s2.hashCode()).forEach(System.out::println);
7、去重
list.stream().distinct().forEach(System.out::println);
8、合并流
Stream<List<String>> stream2 = Stream.of(list, list);
Stream<List<String>> stream3 = Stream.of(list, list);
Stream.concat(stream2,stream3).distinct().forEach(System.out::print);
9、聚合操作
//最小值,如果有值
list.stream().min((s1, s2) -> s1.hashCode() - s2.hashCode()).ifPresent(System.out::println);//a
//个数
System.out.println(list.stream().count());//3
10、Collectors
Collectors 类实现了很多归约操作,例如将流转换成集合和聚合元素。Collectors 可用于返回列表或字符串:
Set<String> set = list.stream().collect(Collectors.toSet());//[a, b, c]
String string = list.stream().collect(Collectors.joining("-"));//b-c-a
11、将普通流转换成数值流
StreamAPI提供了三种数值流:IntStream、DoubleStream、LongStream,也提供了将普通流转换成数值流的三种方法:mapToInt、mapToDouble、mapToLong。
IntSummaryStatistics stats = list.stream().mapToInt(x -> x.hashCode()).summaryStatistics();
int max = stats.getMax();//99
int min = stats.getMin();//97
八、function包的使用
这几个接口属于java.util.function包使用方法,使用方法大同小异,后面都是跟着Lambda表达式或者方法引用。
1、Predicate
判断使用,返回的都是boolean类型数据。
//Lambda表达式
Predicate<String> predicate1 = (str) -> str.length() > 0;
boolean b1 = predicate1.test("");//false
//方法引用
Predicate<String> predicate2 = Objects::nonNull;
boolean b2 = predicate2.test(null);//false
//negate获得与传入的Predicate相反的Predicate,即(t) -> !test(t)
boolean b3 = predicate1.negate().test("");
//or、and方法是把两个测试条件组合在一起,or的结果是test(t) || other.test(t),and结果是test(t) && other.test(t);
Predicate<String> predicate3 = predicate1.or(predicate2);
Predicate<String> predicate4 = predicate1.and(predicate2);
boolean b4 = predicate3.test("");//true
boolean b5 = predicate4.test("");//false
2、Function
执行特定的函数,Function【T, R】传入的是T类型,输出的是R类型。
//返回值是Integer
Function<String, Integer> function1 = Integer::parseInt;
Integer i1 = function1.apply("10");//10
System.out.println(i1);
//返回值是String
Function<Integer, String> function2 = (i) -> (i+20) + "";
String s1 = function2.apply(10);//30
//先执行function1,String转为Integer,再执行function2,转回String
Function<String, String> function3 = function1.andThen(function2);
String s2 = function3.apply("10");//30
//先执行function2,Integer转为String,再执行function1,转回Integer
Function<Integer, Integer> function5 = function1.compose(function2);
Integer i2 = function5.apply(10);//30
3、Consumer
表示一个接受单个输入参数并且没有返回值的操作。不像其他函数式接口,Consumer接口期望执行带有副作用的操作(译者注:Consumer的操作可能会更改输入参数的内部状态)。
Consumer<List<String>> consumer = (list) -> list.forEach(System.out::println);
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
consumer.accept(list);
4、Supplier
与方法引用一起使用创建一个新的空对象。
Supplier<Student> supplier = Student::new;
Student student = supplier.get();
Supplier<ArrayList<String>> supplier2 = ArrayList<String>::new;
ArrayList<String> list = supplier2.get();
九、杂项改进
String string = String.join("-", "first","second");
通过一个分隔符将字符串连接起来,相当于split的相反方法。
int i = Integer.sum(1, 2);
long l = Long.max(20L, 30L);
Short、Integer、Long、Float、Double五种包装类型数据提供了sum、max、min方法;同样Boolean也提供了logicalXor、logicalOr、logicalAnd方法。
long l = Math.multiplyExact(200000000L, 20000000L);
准确的计算出200000000*200000000的值。
int i = Math.toIntExact(20000L);
将long类型数值转化为相应的int类型数值,不超过int取值范围。
double d = Math.nextDown(2F);
取到无线接近于2的浮点数字。
上一篇: 三星Galaxy Fold折叠屏手机开箱图赏:实力拉风
下一篇: Android6.0权限问题解析