Java8 的一些常用场景

简介

Java 8 (又称为 jdk 1.8) 是 Java 语言开发的一个主要版本,有自己的独特编程风格,使代码更简洁化。

编程风格

import java.util.Collections;
import java.util.List;
import java.util.ArrayList;
import java.util.Comparator;
 
public class Java8Tester {
   public static void main(String args[]){
   
      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 ");
        
      Java8Tester tester = new Java8Tester();
      System.out.println("使用 Java 7 语法: ");
        
      tester.sortUsingJava7(names1);
      System.out.println("使用 Java 8 语法: ");
        
      tester.sortUsingJava8(names2);
   }
   
   // 使用 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));
   }
}

这是Java中的集合排序,使用Collections类提供的方法进行排序。从代码上就可以看出,Java 8的代码更简洁化。

Java 8 的新特性

1.Lambda 表达式

  • Lambda 表达式,也可称为闭包,它是推动 Java 8 发布的最重要新特性。
  • Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。
  • 使用 Lambda 表达式可以使代码变的更加简洁紧凑。

语法

语法格式:

       (parameters) -> expression

  或 (parameters) ->{ statements; }

语法特征:

  • 可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
  • 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。
  • 可选的大括号:如果主体包含了一个语句,就不需要使用大括号。
  • 可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值。

demo

// 1. 不需要参数,返回值为 5  
() -> 5  
  
// 2. 接收一个参数(数字类型),返回其2倍的值  
x -> 2 * x  
  
// 3. 接受2个参数(数字),并返回他们的差值  
(x, y) -> x – y  
  
// 4. 接收2个int型整数,返回他们的和  
(int x, int y) -> x + y  
  
// 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)  
(String s) -> System.out.print(s)

代码

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

说明:

Lambda表达式就是可以把函数当成方法参数传输,如果按照Java7的写法实现上述代码的效果,可以用多个类实现一个MathOperation 接口,或者用一个base类,然后多个子类重写父类的方法。一比较很显然,Java 8 的Lambda表达式使代码更简洁,耦合度也降低了很多。其实上边描述Java 8 编码风格的例子就是Lambda表达式。

注意:

lambda 表达式的变量作用域问题,必须是外部局部final变量,如果是内部局部变量,可以不声明final,但是必须保证变量是不可变的,否则会报错。

2.方法引用

  • 方法引用通过方法的名字来指向一个方法。
  • 方法引用可以使语言的构造更紧凑简洁,减少冗余代码。
  • 方法引用使用一对冒号 :: 

语法格式:

Class::method

俗语(个人理解):

原来调用方法需要通过 类名.方法名  java8可以 类名::方法名

例如:jdk自带转字符串的方法 String.ValueOf(ParmClass parmName);

           java8写法:String::ValueOf;

代码

函数式接口

 

public class StringToDo {
	
	public static String toDo(StringFun fun,String t){
		return fun.stringFun(t);
	}

}

配置函数式接口的通用调用

public class StringToDo {
	
	public static String toDo(StringFun fun,String t){
		return fun.stringFun(t);
	}
}

String字符串的一些处理方法

public class StringUtil {
	
	//去除 - 
	public static String replaceGang(String str){
		
		return str.replaceAll("-", "");
	}
	
	//反转字符串
    public static String strReverse(String str) {
        String result = "";
        for (int i = str.length() - 1; i >= 0; i--) {
            result += str.charAt(i);
        }
        return result;
    }
    
    //md5加密
    public static String md5(String str){
    	
    	return MD5.MD5Encode(str, "utf-8");
    }


}

demo

public class Java8Test {
	public static void main(String[] args) {
		String a = StringToDo.toDo(StringUtil::md5, "1-1-1");
		System.out.println("a:"+a);
	}

}

说明:

函数式接口后面会讲解,先无需关注。

方法引用就是改变了之前调用方法的一些方式,使代码更简洁。

这里可能有人问那我通过StringUtil.md5(参数)可以直接调用,干嘛要这么费劲,目前看来是这样,但是我在StringUtil里写的很多处理字符串的多种方法,后面还可再扩展,以后我处理的字符串情况只要在StringUtil里存在,那么我可以直接StringToDo.toDo(Class::方法名);扩展性高,封装的深, 同时也减少了很多不必要的内存对象的开销(因为有函数式接口的存在),如果单纯的使用Java8的方法引用,那么确实只是换了一种编码风格而已。

注意:

java8的方法引用并不是独立存在使用的,与Lambda 表达式、接口式函数使用时效果显著

3.接口式函数

  • 函数式接口(Functional Interface)就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。
  • 函数式接口可以被隐式转换为 lambda 表达式。

语法格式:

@FunctionalInterface
public interface StringFun {
	
	String stringFun(String str);
}

使用@FunctionalInterface注解表明接口式函数

 

代码:

@FunctionalInterface
public interface StringFun {
	
	String stringFun(String str);
}
public class Java8Test {
	public static void main(String[] args) {
		StringFun fun = str ->{
			return MD5.MD5Encode(str, "utf-8");
		};
        String md5str = fun.stringFun("passsword");

	}
}

说明:

函数式接口可以把接口当成参数进行传参

例如我写的StringToDo.toDo(StringUtil::md5, "1-1-1"); toDo方法的第一个参数就是StringFun(函数式接口)。

这样达到直接实现StringFun中的方法 ,无需通过implements去实现接口

 

函数式接口的实现可以通过与Lambda表达式结合来实现

例如 StringFun fun = str -> MD5.MD5Encode(str, "utf-8");

-> 后面的就是具体的实现 然后调用 fun.stringFun()方法 ,执行MD5加密,但是这种实现只是写在了方法内,若是其他方法或者类想调用,仍需重新编写,所以这种写法还需根据各种应用场景进行定制开发。

4.默认方法

  • 默认方法就是接口可以有实现方法,而且不需要实现类去实现其方法

语法:

public interface DefaultInterface {
	
	default void inPrint(){
		System.out.println("接口输入");
	}
	
}

在方法名前面加个 default 关键字即可实现默认方法

代码

接口默认方法

public interface DefaultInterface {
	
	default void inPrint(){
		System.out.println("接口输入");
	}
	
	default void outPrint(){
		System.out.println("接口输出");
	}
	
	static void staticPrint(){
		System.out.println("接口静态输入");
	}

}

接口实现类

public class DefaultInterfaceImpl implements DefaultInterface{
	
	public void inPrint(){
		System.out.println("实现类输入");
		DefaultInterface.super.inPrint();
		DefaultInterface.super.outPrint();
		DefaultInterface.staticPrint();
	}
}

demo

public class Java8Test {
	public static void main(String[] args) {
		
		DefaultInterface defaultInterfaceImpl = new DefaultInterfaceImpl();
		defaultInterfaceImpl.inPrint();
		DefaultInterface.staticPrint();

	}
}

说明:

默认方法就是可以将接口的实现方法直接在接口中体现,无需通过class类implements去实现接口

接口中可以写静态方法,直接通过Class.method进行调用。

如果实现类里重写了方法 ,可以通过Interface.super.method调用接口中的方法

5.Stream

  • Java 8 API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据。
  • Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。
  • Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。

语法格式:

List<Integer> transactionsIds = 
widgets.stream()
             .filter(b -> b.getColor() == RED)
             .sorted((x,y) -> x.getWeight() - y.getWeight())
             .mapToInt(Widget::getWeight)
             .sum();

什么是 Stream?

Stream(流)是一个来自数据源的元素队列并支持聚合操作

  • 元素是特定类型的对象,形成一个队列。 Java中的Stream并不会存储元素,而是按需计算。
  • 数据源 流的来源。 可以是集合,数组,I/O channel, 产生器generator 等。
  • 聚合操作 类似SQL语句一样的操作, 比如filter, map, reduce, find, match, sorted等。

和以前的Collection操作不同, Stream操作还有两个基础的特征:

  • Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。
  • 内部迭代: 以前对集合遍历都是通过Iterator或者For-Each的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream提供了内部迭代的方式, 通过访问者模式(Visitor)实现。

Stream 中的方法

  • forEach:与平时写的for(对象:名称 集合)类似
  • filter:过滤数据,返回为true的List/数组
  • limit:List分页
  • sorted:List排序
  • collectors:归集,将流进行转换/聚合

代码:

package test;

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


public class Java8Test {
	public static void main(String[] args) {
		
		List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10,11,12);
		
		list.stream().skip(2*2).limit(2).sorted(Comparator.reverseOrder()).forEach(System.out::println);
		
		Map<Integer, Integer> collect = list.stream().map(i -> i*i).collect(Collectors.toMap(a -> a, b -> b*b));
		
		System.out.println(collect.toString());
	}
	

}

说明:

Stream就是将之前一下繁琐的写法全部通过流的方式实现,并且增加了很多新功能,例如list转map,分页等,

Stream中的方法很多,在这里只介绍了常用的一些部分,并且很多方法可以一起结合使用,比如先排序后分页,或者先过滤数据然后将list转为map等等,省去了之前很多的if else写法,使代码更简洁高效。

6. Optional 类

  • Optional 类是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。
  • Optional 是个容器:它可以保存类型T的值,或者仅仅保存null。Optional提供很多有用的方法,这样我们就不用显式进行空值检测。
  • Optional 类的引入很好的解决空指针异常。

类方法

  • isPresent:如果值存在则方法会返回true,否则返回 false。
  • ofNullable:如果为非空,返回 Optional 描述的指定值,否则返回空的 Optional。
  • orElse:如果存在该值,返回值, 否则返回 other。
  • get:如果在这个Optional中包含这个值,返回值,否则抛出异常:NoSuchElementException
  • map:如果有值,则对其执行调用映射函数得到返回值。如果返回值不为 null,则创建包含映射返回值的Optional作为map方法返回值,否则返回空Optional。

Optional类的方法有很多,这里主要展示一些常用的操作方法

代码

public class Java8Test {
	public static void main(String[] args) {
		
		Integer a = 1;
		Optional<Integer> Opt = Optional.ofNullable(a);
		Opt.isPresent();
		Optional<Integer> map = Opt.map(as -> as+as);
		Integer orElse = map.orElse(111);
		System.out.println("--orElse--"+orElse);
	}
	

}

说明:

Optional 类主要是对null数据进行处理,防止经常出现空指针异常

例如:之前判断是否为空 data.equals 如果data为空,会引发空指针,如果用Optional 类,isPresent方法可以有效解决

7.日期时间 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)的操作。

代码

  // 获取当前的日期时间
      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);

    // 获取当前时间日期
      ZonedDateTime date12 = ZonedDateTime.parse("2015-12-03T10:15:30+05:30[Asia/Shanghai]");
      System.out.println("date12: " + date1);
        
      ZoneId id = ZoneId.of("Europe/Paris");
      System.out.println("ZoneId: " + id);
        
      ZoneId currentZone = ZoneId.systemDefault();
      System.out.println("当期时区: " + currentZone);

说明:

时间处理更简洁方便,parse也无需抛异常,如果有时间处理相关业务可以学习使用

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值