JDK1.8新特性總結(jié)
一、十大特性
- 1.
default
關(guān)鍵字 - 2.
Lambda
表達(dá)式 - 3.接口新增:默認(rèn)方法與靜態(tài)方法
- 4.方法引用,與
Lambda
表達(dá)式聯(lián)合使用 - 5.引入重復(fù)注解
- 6.類型注解
- 7.
Stream
函數(shù)式操作流元素集合 - 8.最新的
Date/Time API (JSR 310)
- 9.新增
base64
加解密API
- 10.數(shù)組并行(
parallel
)操作
- 1.
JVM
的PermGen
空間被移除:取代它的是Metaspace(JEP 122)
元空間
二、新特性
1、default關(guān)鍵字
在java里面猴抹,我們通常都是認(rèn)為接口里面是只能有抽象方法整陌,不能有任何方法的實(shí)現(xiàn)的划纽,那么在jdk1.8里面打破了這個(gè)規(guī)定脆侮,引入了新的關(guān)鍵字default
,通過(guò)使用default
修飾方法勇劣,可以讓我們?cè)诮涌诶锩娑x具體的方法實(shí)現(xiàn)靖避,如下。
public interface NewCharacter {
public void test1();
public default void test2(){
System.out.println("我是新特性1");
}
}
那這么定義一個(gè)方法的作用是什么呢比默?為什么不在接口的實(shí)現(xiàn)類里面再去實(shí)現(xiàn)方法呢幻捏?
其實(shí)這么定義一個(gè)方法的主要意義是定義一個(gè)默認(rèn)方法,也就是說(shuō)這個(gè)接口的實(shí)現(xiàn)類實(shí)現(xiàn)了這個(gè)接口之后命咐,不用管這個(gè)default
修飾的方法篡九,也可以直接調(diào)用,如下醋奠。
public class NewCharacterImpl implements NewCharacter{
@Override
public void test1() {
}
public static void main(String[] args) {
NewCharacter nca = new NewCharacterImpl();
nca.test2();
}
}
所以說(shuō)這個(gè)default
方法是所有的實(shí)現(xiàn)類都不需要去實(shí)現(xiàn)的就可以直接調(diào)用榛臼,那么比如說(shuō)jdk
的集合List
里面增加了一個(gè)sort
方法,那么如果定義為一個(gè)抽象方法钝域,其所有的實(shí)現(xiàn)類如arrayList,LinkedList
等都需要對(duì)其添加實(shí)現(xiàn)讽坏,那么現(xiàn)在用default
定義一個(gè)默認(rèn)的方法之后,其實(shí)現(xiàn)類可以直接使用這個(gè)方法了例证,這樣不管是開發(fā)還是維護(hù)項(xiàng)目,都會(huì)大大簡(jiǎn)化代碼量迷捧。
2织咧、Lambda 表達(dá)式
2.1、 Lambda表達(dá)式的寫法
Lambda表達(dá)式是jdk1.8里面的一個(gè)重要的更新漠秋,這意味著java也開始承認(rèn)了函數(shù)式編程笙蒙,并且嘗試引入其中。
-
Lambda 表達(dá)式
junit
測(cè)試代碼
public List<Integer> list = Lists.newArrayList(1,2,3,4,5,6,7,8,9,10);
/**
* 1.Lambda表達(dá)式
*/
@Test
public void testLambda(){
list.forEach(System.out::println);
list.forEach(e -> System.out.println("方式二:"+e));
}
首先庆锦,什么是函數(shù)式編程捅位,引用廖雪峰先生的教程里面的解釋就是說(shuō):
函數(shù)式編程就是一種抽象程度很高的編程范式,純粹的函數(shù)式編程語(yǔ)言編寫的函數(shù)沒有變量搂抒,因此艇搀,任意一個(gè)函數(shù),只要輸入是確定的求晶,輸出就是確定的焰雕,這種純函數(shù)我們稱之為沒有副作用。而允許使用變量的程序設(shè)計(jì)語(yǔ)言芳杏,由于函數(shù)內(nèi)部的變量狀態(tài)不確定矩屁,同樣的輸入辟宗,可能得到不同的輸出,因此吝秕,這種函數(shù)是有副作用的泊脐。函數(shù)式編程的一個(gè)特點(diǎn)就是,允許把函數(shù)本身作為參數(shù)傳入另一個(gè)函數(shù)烁峭,還允許返回一個(gè)函數(shù)容客!
簡(jiǎn)單的來(lái)說(shuō)就是,函數(shù)式編程語(yǔ)言里面函數(shù)也可以跟變量则剃,對(duì)象一樣使用了耘柱,也就是說(shuō)函數(shù)既可以作為參數(shù),也可以作為返回值了棍现,看一下下面這個(gè)例子调煎。
//這是常規(guī)的Collections的排序的寫法,需要對(duì)接口方法重寫
public void test1(){
List<String> list =Arrays.asList("aaa","fsa","ser","eere");
Collections.sort(list, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o2.compareTo(o1);
}
});
for (String string : list) {
System.out.println(string);
}
}
//這是帶參數(shù)類型的Lambda的寫法
public void testLamda1(){
List<String> list =Arrays.asList("aaa","fsa","ser","eere");
Collections.sort(list, (Comparator<? super String>) (String a,String b)->{
return b.compareTo(a);
}
);
for (String string : list) {
System.out.println(string);
}
}
//這是不帶參數(shù)的lambda的寫法
public void testLamda2(){
List<String> list =Arrays.asList("aaa","fsa","ser","eere");
Collections.sort(list, (a,b)->b.compareTo(a)
);
for (String string : list) {
System.out.println(string);
}
可以看到不帶參數(shù)的寫法一句話就搞定了排序的問題己肮,所以引入lambda表達(dá)式的一個(gè)最直觀的作用就是大大的簡(jiǎn)化了代碼的開發(fā)士袄,像其他一些編程語(yǔ)言Scala,Python等都是支持函數(shù)式的寫法的谎僻。當(dāng)然娄柳,不是所有的接口都可以通過(guò)這種方法來(lái)調(diào)用,只有函數(shù)式接口才行艘绍,jdk1.8里面定義了好多個(gè)函數(shù)式接口赤拒,我們也可以自己定義一個(gè)來(lái)調(diào)用,下面說(shuō)一下什么是函數(shù)式接口诱鞠。
2.2挎挖、函數(shù)式接口
定義:“函數(shù)式接口”是指僅僅只包含一個(gè)抽象方法的接口,每一個(gè)該類型的lambda表達(dá)式都會(huì)被匹配到這個(gè)抽象方法航夺。jdk1.8提供了一個(gè)@FunctionalInterface
注解來(lái)定義函數(shù)式接口蕉朵,如果我們定義的接口不符合函數(shù)式的規(guī)范便會(huì)報(bào)錯(cuò)。
@FunctionalInterface
public interface MyLamda {
public void test1(String y);
//這里如果繼續(xù)加一個(gè)抽象方法便會(huì)報(bào)錯(cuò)
// public void test1();
//default方法可以任意定義
default String test2(){
return "123";
}
default String test3(){
return "123";
}
//static方法也可以定義
static void test4(){
System.out.println("234");
}
}
看一下這個(gè)接口的調(diào)用阳掐,符合lambda表達(dá)式的調(diào)用方法始衅。
MyLamda m = y -> System.out.println("ss"+y);
2.3、方法與構(gòu)造函數(shù)引用
jdk1.8提供了另外一種調(diào)用方式::
缭保,當(dāng) 你 需 要使用 方 法 引用時(shí) 汛闸, 目 標(biāo)引用 放 在 分隔符::
前 ,方法 的 名 稱放在 后 面 涮俄,即ClassName :: methodName
蛉拙。例如 ,Apple::getWeight
就是引用了Apple類中定義的方法getWeight彻亲。請(qǐng)記住孕锄,不需要括號(hào)吮廉,因?yàn)槟銢]有實(shí)際調(diào)用這個(gè)方法。方法引用就是Lambda表達(dá)式(Apple a) -> a.getWeight()
的快捷寫法畸肆,如下示例宦芦。
//先定義一個(gè)函數(shù)式接口
@FunctionalInterface
public interface TestConverT<T, F> {
F convert(T t);
}
測(cè)試如下,可以以::
形式調(diào)用轴脐。
public void test(){
TestConverT<String, Integer> t = Integer::valueOf;
Integer i = t.convert("111");
System.out.println(i);
}
此外调卑,對(duì)于構(gòu)造方法也可以這么調(diào)用。
//實(shí)體類User和它的構(gòu)造方法
public class User {
private String name;
private String sex;
public User(String name, String sex) {
super();
this.name = name;
this.sex = sex;
}
}
//User工廠
public interface UserFactory {
User get(String name, String sex);
}
//測(cè)試類
UserFactory uf = User::new;
User u = uf.get("ww", "man");
這里的User::new
就是調(diào)用了User
的構(gòu)造方法大咱,Java編譯器會(huì)自動(dòng)根據(jù)UserFactory.get
方法的簽名來(lái)選擇合適的構(gòu)造函數(shù)恬涧。
-
方法引用,與Lambda表達(dá)式聯(lián)合使用
junit
測(cè)試代碼
/**
* 4.方法引用,與Lambda表達(dá)式聯(lián)合使用
*/
@Test
public void testMethodReference(){
//構(gòu)造器引用。語(yǔ)法是Class::new碴巾,或者更一般的Class< T >::new溯捆,要求構(gòu)造器方法是沒有參數(shù);
final Car car = Car.create( Car::new );
final List< Car > cars = Arrays.asList( car );
//靜態(tài)方法引用厦瓢。語(yǔ)法是Class::static_method提揍,要求接受一個(gè)Class類型的參數(shù);
cars.forEach( Car::collide );
//任意對(duì)象的方法引用煮仇。它的語(yǔ)法是Class::method劳跃。無(wú)參,所有元素調(diào)用浙垫;
cars.forEach( Car::repair );
//特定對(duì)象的方法引用刨仑,它的語(yǔ)法是instance::method。有參夹姥,在某個(gè)對(duì)象上調(diào)用方法贸人,將列表元素作為參數(shù)傳入;
final Car police = Car.create( Car::new );
cars.forEach( police::follow );
}
public static class Car {
public static Car create( final Supplier< Car > supplier ) {
return supplier.get();
}
public static void collide( final Car car ) {
System.out.println( "靜態(tài)方法引用 " + car.toString() );
}
public void repair() {
System.out.println( "任意對(duì)象的方法引用 " + this.toString() );
}
public void follow( final Car car ) {
System.out.println( "特定對(duì)象的方法引用 " + car.toString() );
}
}
2.4佃声、局部變量限制
Lambda
表達(dá)式也允許使用自由變量(不是參數(shù),而是在外層作用域中定義的變量)倘要,就像匿名類一樣圾亏。 它們被稱作捕獲Lambda
。 Lambda
可以沒有限制地捕獲(也就是在其主體中引用)實(shí)例變量和靜態(tài)變量封拧。但局部變量必須顯式聲明為final
志鹃,或事實(shí)上是final
。
為什么局部變量有這些限制泽西?
〔芰濉(1)實(shí)例變量和局部變量背后的實(shí)現(xiàn)有一個(gè)關(guān)鍵不同。實(shí)例變量都存儲(chǔ)在堆中捧杉,而局部變量則保存在棧上陕见。如果Lambda
可以直接訪問局部變量秘血,而且Lambda
是在一個(gè)線程中使用的,則使用Lambda
的線程评甜,可能會(huì)在分配該變量的線程將這個(gè)變量收回之后灰粮,去訪問該變量。因此忍坷, Java在訪問自由局部變量時(shí)粘舟,實(shí)際上是在訪問它的副本,而不是訪問原始變量佩研。如果局部變量?jī)H僅賦值一次那就沒有什么區(qū)別了——因此就有了這個(gè)限制柑肴。
(2)這一限制不鼓勵(lì)你使用改變外部變量的典型命令式編程模式旬薯。
final int num = 1;
Converter<Integer, String> stringConverter =
(from) -> String.valueOf(from + num);
stringConverter.convert(2);
3.接口新增:默認(rèn)方法與靜態(tài)方法
/**
* 3.接口新增:默認(rèn)方法與靜態(tài)方法
* default 接口默認(rèn)實(shí)現(xiàn)方法是為了讓集合類默認(rèn)實(shí)現(xiàn)這些函數(shù)式處理晰骑,而不用修改現(xiàn)有代碼
* (List繼承于Iterable<T>,接口默認(rèn)方法不必須實(shí)現(xiàn)default forEach方法)
*/
@Test
public void testDefaultFunctionInterface(){
//可以直接使用接口名.靜態(tài)方法來(lái)訪問接口中的靜態(tài)方法
JDK8Interface1.staticMethod();
//接口中的默認(rèn)方法必須通過(guò)它的實(shí)現(xiàn)類來(lái)調(diào)用
new JDK8InterfaceImpl1().defaultMethod();
//多實(shí)現(xiàn)類袍暴,默認(rèn)方法重名時(shí)必須復(fù)寫
new JDK8InterfaceImpl2().defaultMethod();
}
public class JDK8InterfaceImpl1 implements JDK8Interface1 {
//實(shí)現(xiàn)接口后些侍,因?yàn)槟J(rèn)方法不是抽象方法,重寫/不重寫都成政模!
// @Override
// public void defaultMethod(){
// System.out.println("接口中的默認(rèn)方法");
// }
}
public class JDK8InterfaceImpl2 implements JDK8Interface1,JDK8Interface2 {
//實(shí)現(xiàn)接口后岗宣,默認(rèn)方法名相同,必須復(fù)寫默認(rèn)方法
@Override
public void defaultMethod() {
//接口的
JDK8Interface1.super.defaultMethod();
System.out.println("實(shí)現(xiàn)類復(fù)寫重名默認(rèn)方法A苎:氖健!趁猴!");
}
}
public interface JDK8Interface1 {
//1.接口中可以定義靜態(tài)方法了
public static void staticMethod(){
System.out.println("接口中的靜態(tài)方法");
}
//2.使用default之后就可以定義普通方法的方法體了
public default void defaultMethod(){
System.out.println("接口中的默認(rèn)方法");
}
}
public interface JDK8Interface2 {
//1.接口中可以定義靜態(tài)方法了
public static void staticMethod(){
System.out.println("接口中的靜態(tài)方法");
}
//2.使用default之后就可以定義普通方法的方法體了
public default void defaultMethod(){
System.out.println("接口中的默認(rèn)方法");
}
}
4.方法引用,與Lambda表達(dá)式聯(lián)合使用
/**
* 4.方法引用,與Lambda表達(dá)式聯(lián)合使用
*/
@Test
public void testMethodReference(){
//構(gòu)造器引用刊咳。語(yǔ)法是Class::new,或者更一般的Class< T >::new儡司,要求構(gòu)造器方法是沒有參數(shù)娱挨;
final Car car = Car.create( Car::new );
final List< Car > cars = Arrays.asList( car );
//靜態(tài)方法引用。語(yǔ)法是Class::static_method捕犬,要求接受一個(gè)Class類型的參數(shù)跷坝;
cars.forEach( Car::collide );
//任意對(duì)象的方法引用。它的語(yǔ)法是Class::method碉碉。無(wú)參柴钻,所有元素調(diào)用;
cars.forEach( Car::repair );
//特定對(duì)象的方法引用垢粮,它的語(yǔ)法是instance::method贴届。有參,在某個(gè)對(duì)象上調(diào)用方法,將列表元素作為參數(shù)傳入毫蚓;
final Car police = Car.create( Car::new );
cars.forEach( police::follow );
}
public static class Car {
public static Car create( final Supplier< Car > supplier ) {
return supplier.get();
}
public static void collide( final Car car ) {
System.out.println( "靜態(tài)方法引用 " + car.toString() );
}
public void repair() {
System.out.println( "任意對(duì)象的方法引用 " + this.toString() );
}
public void follow( final Car car ) {
System.out.println( "特定對(duì)象的方法引用 " + car.toString() );
}
}
5.引入重復(fù)注解
/**
* 5.引入重復(fù)注解
* 1.@Repeatable
* 2.可以不用以前的“注解容器”寫法占键,直接寫2次相同注解即可
*
* Java 8在編譯器層做了優(yōu)化,相同注解會(huì)以集合的方式保存绍些,因此底層的原理并沒有變化捞慌。
*/
@Test
public void RepeatingAnnotations(){
RepeatingAnnotations.main(null);
}
package com.zgldoing.base.jdk8Features;
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.zgldoing.base.jdk8Features.RepeatingAnnotations.Filter;
import com.zgldoing.base.jdk8Features.RepeatingAnnotations.Filterable;
import com.zgldoing.base.jdk8Features.RepeatingAnnotations.Filters;
/**
* @ClassName:RepeatingAnnotations
* @Description:重復(fù)注解@Repeatable
* @author zgldo
*
*/
public class RepeatingAnnotations {
@Target( ElementType.TYPE )
@Retention( RetentionPolicy.RUNTIME )
public @interface Filters {
Filter[] value();
}
@Target( ElementType.TYPE )
@Retention( RetentionPolicy.RUNTIME )
@Repeatable( Filters.class )
public @interface Filter {
String value();
String value2();
};
@Filter( value="filter1",value2="111" )
@Filter( value="filter2", value2="222")
//@Filters({@Filter( value="filter1",value2="111" ),@Filter( value="filter2", value2="222")}).注意:JDK8之前:1.沒有@Repeatable2.采用本行“注解容器”寫法
public interface Filterable {
}
public static void main(String[] args) {
//獲取注解后遍歷打印值
for( Filter filter: Filterable.class.getAnnotationsByType( Filter.class ) ) {
System.out.println( filter.value() +filter.value2());
}
}
}
6.類型注解
/**
* 6.類型注解
* 新增類型注解:ElementType.TYPE_USE 和ElementType.TYPE_PARAMETER(在Target上)
*
*/
@Test
public void ElementType(){
Annotations.main(null);
}
package com.zgldoing.base.jdk8Features;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.ArrayList;
import java.util.Collection;
import com.zgldoing.base.jdk8Features.Annotations.Holder;
import com.zgldoing.base.jdk8Features.Annotations.NonEmpty;
/**
* @ClassName:Annotations
* @Description:新增類型注解:ElementType.TYPE_USE 和ElementType.TYPE_PARAMETER(在Target上)
* @author zgldo
*
*/
public class Annotations {
@Retention( RetentionPolicy.RUNTIME )
@Target( { ElementType.TYPE_USE, ElementType.TYPE_PARAMETER } )
public @interface NonEmpty {
}
public static class Holder< @NonEmpty T > extends @NonEmpty Object {
public void method() throws @NonEmpty Exception {
}
}
public static void main(String[] args) {
final Holder< String > holder = new @NonEmpty Holder< String >();
@NonEmpty Collection< @NonEmpty String > strings = new ArrayList<>();
}
}
7、Stream
函數(shù)式操作流元素
定義:流是Java API
的新成員柬批,它允許我們以聲明性方式處理數(shù)據(jù)集合(通過(guò)查詢語(yǔ)句來(lái)表達(dá)啸澡,而不是臨時(shí)編寫一個(gè)實(shí)現(xiàn))。就現(xiàn)在來(lái)說(shuō)氮帐,我們可以把它們看成遍歷數(shù)據(jù)集的高級(jí)迭代器嗅虏。此外,流還可以透明地并行處理上沐,也就是說(shuō)我們不用寫多線程代碼了皮服。
Stream
不是集合元素,它不是數(shù)據(jù)結(jié)構(gòu)并不保存數(shù)據(jù)参咙,它是有關(guān)算法和計(jì)算的龄广,它更像一個(gè)高級(jí)版本的Iterator
。原始版本的 Iterator
蕴侧,用戶只能顯式地一個(gè)一個(gè)遍歷元素并對(duì)其執(zhí)行某些操作择同;高級(jí)版本的 Stream
,用戶只要給出需要對(duì)其包含的元素執(zhí)行什么操作净宵,比如 “過(guò)濾掉長(zhǎng)度大于 10 的字符串”敲才、“獲取每個(gè)字符串的首字母”等,Stream
會(huì)隱式地在內(nèi)部進(jìn)行遍歷择葡,做出相應(yīng)的數(shù)據(jù)轉(zhuǎn)換紧武。
Stream
就如同一個(gè)迭代器(Iterator
),單向敏储,不可往復(fù)阻星,數(shù)據(jù)只能遍歷一次,遍歷過(guò)一次后即用盡了已添,就好比流水從面前流過(guò)迫横,一去不復(fù)返。而和迭代器又不同的是酝碳,Stream
可以并行化操作,迭代器只能命令式地恨狈、串行化操作疏哗。顧名思義,當(dāng)使用串行方式去遍歷時(shí)禾怠,每個(gè) item
讀完后再讀下一個(gè)item
返奉。而使用并行去遍歷時(shí)贝搁,數(shù)據(jù)會(huì)被分成多個(gè)段,其中每一個(gè)都在不同的線程中處理芽偏,然后將結(jié)果一起輸出雷逆。Stream
的并行操作依賴于 Java7 中引入的 Fork/Join 框架(JSR166y)
來(lái)拆分任務(wù)和加速處理過(guò)程。
流的操作類型分為兩種:
-
Intermediate
:一個(gè)流可以后面跟隨零個(gè)或多個(gè)intermediate
操作污尉。其目的主要是打開流膀哲,做出某種程度的數(shù)據(jù)映射/過(guò)濾,然后返回一個(gè)新的流被碗,交給下一個(gè)操作使用某宪。這類操作都是惰性化的(lazy
),就是說(shuō)锐朴,僅僅調(diào)用到這類方法兴喂,并沒有真正開始流的遍歷。 -
Terminal
:一個(gè)流只能有一個(gè)terminal
操作焚志,當(dāng)這個(gè)操作執(zhí)行后衣迷,流就被使用“光”了,無(wú)法再被操作酱酬。所以這必定是流的最后一個(gè)操作壶谒。Terminal
操作的執(zhí)行,才會(huì)真正開始流的遍歷岳悟,并且會(huì)生成一個(gè)結(jié)果佃迄,或者一個(gè)side effect
。
在對(duì)于一個(gè)Stream
進(jìn)行多次轉(zhuǎn)換操作 (Intermediate
操作)贵少,每次都對(duì) Stream
的每個(gè)元素進(jìn)行轉(zhuǎn)換呵俏,而且是執(zhí)行多次,這樣時(shí)間復(fù)雜度就是 N(轉(zhuǎn)換次數(shù))個(gè) for 循環(huán)里把所有操作都做掉的總和嗎滔灶?其實(shí)不是這樣的普碎,轉(zhuǎn)換操作都是 lazy 的,多個(gè)轉(zhuǎn)換操作只會(huì)在 Terminal 操作的時(shí)候融合起來(lái)录平,一次循環(huán)完成麻车。我們可以這樣簡(jiǎn)單的理解,Stream 里有個(gè)操作函數(shù)的集合斗这,每次轉(zhuǎn)換操作就是把轉(zhuǎn)換函數(shù)放入這個(gè)集合中动猬,在 Terminal 操作的時(shí)候循環(huán) Stream 對(duì)應(yīng)的集合,然后對(duì)每個(gè)元素執(zhí)行所有的函數(shù)表箭。
構(gòu)造流的幾種方式
// 1. Individual values
Stream stream = Stream.of("a", "b", "c");
// 2. Arrays
String [] strArray = new String[] {"a", "b", "c"};
stream = Stream.of(strArray);
stream = Arrays.stream(strArray);
// 3. Collections
List<String> list = Arrays.asList(strArray);
stream = list.stream();
- Stream函數(shù)式操作流元素集合
/**
* 2.Stream函數(shù)式操作流元素集合
*/
@Test
public void testStream(){
List<Integer> nums = Lists.newArrayList(1,1,null,2,3,4,null,5,6,7,8,9,10);
System.out.println("求和:"+nums
.stream()//轉(zhuǎn)成Stream
.filter(team -> team!=null)//過(guò)濾
.distinct()//去重
.mapToInt(num->num*2)//map操作
.skip(2)//跳過(guò)前2個(gè)元素
.limit(4)//限制取前4個(gè)元素
.peek(System.out::println)//流式處理對(duì)象函數(shù)
.sum());//
}
8赁咙、Date API更新
1.8之前JDK自帶的日期處理類非常不方便,我們處理的時(shí)候經(jīng)常是使用的第三方工具包,比如commons-lang
包等彼水。不過(guò)1.8出現(xiàn)之后這個(gè)改觀了很多崔拥,比如日期時(shí)間的創(chuàng)建、比較凤覆、調(diào)整链瓦、格式化、時(shí)間間隔等盯桦。這些類都在java.time
包下慈俯。比原來(lái)實(shí)用了很多。
8.1 LocalDate/LocalTime/LocalDateTime
LocalDate
為日期處理類俺附、LocalTime
為時(shí)間處理類肥卡、LocalDateTime
為日期時(shí)間處理類,方法都類似事镣,具體可以看API文檔或源碼步鉴,選取幾個(gè)代表性的方法做下介紹。
now
相關(guān)的方法可以獲取當(dāng)前日期或時(shí)間璃哟,of
方法可以創(chuàng)建對(duì)應(yīng)的日期或時(shí)間氛琢,parse
方法可以解析日期或時(shí)間,get
方法可以獲取日期或時(shí)間信息随闪,with
方法可以設(shè)置日期或時(shí)間信息阳似,plus
或minus
方法可以增減日期或時(shí)間信息;
8.2 TemporalAdjusters
這個(gè)類在日期調(diào)整時(shí)非常有用铐伴,比如得到當(dāng)月的第一天撮奏、最后一天,當(dāng)年的第一天当宴、最后一天畜吊,下一周或前一周的某天等。
8.3 DateTimeFormatter
以前日期格式化一般用SimpleDateFormat
類户矢,但是不怎么好用玲献,現(xiàn)在1.8引入了DateTimeFormatter
類,默認(rèn)定義了很多常量格式(ISO打頭的)梯浪,在使用的時(shí)候一般配合LocalDate/LocalTime/LocalDateTime
使用捌年,比如想把當(dāng)前日期格式化成yyyy-MM-dd hh:mm:ss
的形式:
LocalDateTime dt = LocalDateTime.now();
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss");
System.out.println(dtf.format(dt));
-
Date/Time API
junit
測(cè)試代碼
/**
* 7.最新的Date/Time API (JSR 310)
* UTC即為世界標(biāo)準(zhǔn)時(shí)間,世界統(tǒng)一時(shí)間挂洛,國(guó)際協(xié)調(diào)時(shí)間礼预,字母T是時(shí)間和日期的分隔符,T后面表示的即為時(shí)間虏劲,末尾的z表示UTC統(tǒng)一時(shí)間逆瑞。
*/
@Test
public void DateTime(){
//1.Clock
final Clock clock = Clock.systemUTC();
System.out.println( clock.instant() );//2018-12-27T13:25:18.636Z
System.out.println( clock.millis() );//1545917118662
//2. ISO-8601格式且無(wú)時(shí)區(qū)信息的日期部分
final LocalDate date = LocalDate.now();
final LocalDate dateFromClock = LocalDate.now( clock );
System.out.println( date );//2018-12-27
System.out.println( dateFromClock );//2018-12-27
// ISO-8601格式且無(wú)時(shí)區(qū)信息的時(shí)間部分
final LocalTime time = LocalTime.now();
final LocalTime timeFromClock = LocalTime.now( clock );
System.out.println( time ); //21:25:18.692
System.out.println( timeFromClock );//13:25:18.692
// 3.ISO-8601格式無(wú)時(shí)區(qū)信息的日期與時(shí)間
final LocalDateTime datetime = LocalDateTime.now();
final LocalDateTime datetimeFromClock = LocalDateTime.now( clock );
System.out.println( datetime ); //2018-12-27T21:25:18.692
System.out.println( datetimeFromClock );//2018-12-27T13:25:18.692
// 4.特定時(shí)區(qū)的日期/時(shí)間荠藤,
final ZonedDateTime zonedDatetime = ZonedDateTime.now();
final ZonedDateTime zonedDatetimeFromClock = ZonedDateTime.now( clock );
final ZonedDateTime zonedDatetimeFromZone = ZonedDateTime.now( ZoneId.of( "America/Los_Angeles" ) );
System.out.println( zonedDatetime ); //2018-12-27T21:25:18.694+08:00[Asia/Shanghai]
System.out.println( zonedDatetimeFromClock );//2018-12-27T13:25:18.694Z
System.out.println( zonedDatetimeFromZone ); //2018-12-27T05:25:18.706-08:00[America/Los_Angeles]
//5.在秒與納秒級(jí)別上的一段時(shí)間
final LocalDateTime from = LocalDateTime.of( 2014, Month.APRIL, 16, 0, 0, 0 );
final LocalDateTime to = LocalDateTime.of( 2015, Month.APRIL, 16, 23, 59, 59 );
final Duration duration = Duration.between( from, to );
System.out.println( "Duration in days: " + duration.toDays() ); //Duration in days: 365
System.out.println( "Duration in hours: " + duration.toHours() );//Duration in hours: 8783
}
9.新增base64加解密API
/**
* 8.新增base64加解密API
*/
@Test
public void testBase64(){
final String text = "就是要測(cè)試加解密!获高!abjdkhdkuasu!!@@@@";
String encoded = Base64.getEncoder()
.encodeToString( text.getBytes( StandardCharsets.UTF_8 ) );
System.out.println("加密后="+ encoded );
final String decoded = new String(
Base64.getDecoder().decode( encoded ),
StandardCharsets.UTF_8 );
System.out.println( "解密后="+decoded );
}
10.數(shù)組并行(parallel)操作
/**
* 9.數(shù)組并行(parallel)操作
*/
@Test
public void testParallel(){
long[] arrayOfLong = new long [ 20000 ];
//1.給數(shù)組隨機(jī)賦值
Arrays.parallelSetAll( arrayOfLong,
index -> ThreadLocalRandom.current().nextInt( 1000000 ) );
//2.打印出前10個(gè)元素
Arrays.stream( arrayOfLong ).limit( 10 ).forEach(
i -> System.out.print( i + " " ) );
System.out.println();
//3.數(shù)組排序
Arrays.parallelSort( arrayOfLong );
//4.打印排序后的前10個(gè)元素
Arrays.stream( arrayOfLong ).limit( 10 ).forEach(
i -> System.out.print( i + " " ) );
System.out.println();
}
11.JVM的PermGen空間被移除:取代它的是Metaspace(JEP 122)元空間
/**
* 10.JVM的PermGen空間被移除:取代它的是Metaspace(JEP 122)元空間
*/
@Test
public void testMetaspace(){
//-XX:MetaspaceSize初始空間大小,達(dá)到該值就會(huì)觸發(fā)垃圾收集進(jìn)行類型卸載吻育,同時(shí)GC會(huì)對(duì)該值進(jìn)行調(diào)整
//-XX:MaxMetaspaceSize最大空間念秧,默認(rèn)是沒有限制
//-XX:MinMetaspaceFreeRatio在GC之后,最小的Metaspace剩余空間容量的百分比布疼,減少為分配空間所導(dǎo)致的垃圾收集
//-XX:MaxMetaspaceFreeRatio在GC之后摊趾,最大的Metaspace剩余空間容量的百分比,減少為釋放空間所導(dǎo)致的垃圾收集
}
三游两、總結(jié)
總的來(lái)說(shuō)砾层,jdk1.8的一些新特性主要還是簡(jiǎn)化了代碼的寫法,減少了部分開發(fā)量贱案,但是需要一些時(shí)間來(lái)熟悉肛炮。很慚愧,1.8的新特性還不是很熟悉宝踪,所以還是要繼續(xù)努力侨糟,還需要多看些源碼。
四瘩燥、附錄 junit
測(cè)試代碼
1秕重、 JDK8_features.java
package com.zgldoing.base.jdk8Features;
import static org.junit.jupiter.api.Assertions.*;
import java.nio.charset.StandardCharsets;
import java.time.Clock;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.Month;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import java.util.function.Supplier;
import org.junit.jupiter.api.Test;
import com.google.common.collect.Lists;
/**
*
* @ClassName:JDK8_features
* @Description:JDK8新特性
*
*/
public class JDK8_features {
public List<Integer> list = Lists.newArrayList(1,2,3,4,5,6,7,8,9,10);
/**
* 1.Lambda表達(dá)式
*/
@Test
public void testLambda(){
list.forEach(System.out::println);
list.forEach(e -> System.out.println("方式二:"+e));
}
/**
* 2.Stream函數(shù)式操作流元素集合
*/
@Test
public void testStream(){
List<Integer> nums = Lists.newArrayList(1,1,null,2,3,4,null,5,6,7,8,9,10);
System.out.println("求和:"+nums
.stream()//轉(zhuǎn)成Stream
.filter(team -> team!=null)//過(guò)濾
.distinct()//去重
.mapToInt(num->num*2)//map操作
.skip(2)//跳過(guò)前2個(gè)元素
.limit(4)//限制取前4個(gè)元素
.peek(System.out::println)//流式處理對(duì)象函數(shù)
.sum());//
}
/**
* 3.接口新增:默認(rèn)方法與靜態(tài)方法
* default 接口默認(rèn)實(shí)現(xiàn)方法是為了讓集合類默認(rèn)實(shí)現(xiàn)這些函數(shù)式處理,而不用修改現(xiàn)有代碼
* (List繼承于Iterable<T>厉膀,接口默認(rèn)方法不必須實(shí)現(xiàn)default forEach方法)
*/
@Test
public void testDefaultFunctionInterface(){
//可以直接使用接口名.靜態(tài)方法來(lái)訪問接口中的靜態(tài)方法
JDK8Interface1.staticMethod();
//接口中的默認(rèn)方法必須通過(guò)它的實(shí)現(xiàn)類來(lái)調(diào)用
new JDK8InterfaceImpl1().defaultMethod();
//多實(shí)現(xiàn)類溶耘,默認(rèn)方法重名時(shí)必須復(fù)寫
new JDK8InterfaceImpl2().defaultMethod();
}
public class JDK8InterfaceImpl1 implements JDK8Interface1 {
//實(shí)現(xiàn)接口后,因?yàn)槟J(rèn)方法不是抽象方法服鹅,重寫/不重寫都成凳兵!
// @Override
// public void defaultMethod(){
// System.out.println("接口中的默認(rèn)方法");
// }
}
public class JDK8InterfaceImpl2 implements JDK8Interface1,JDK8Interface2 {
//實(shí)現(xiàn)接口后,默認(rèn)方法名相同菱魔,必須復(fù)寫默認(rèn)方法
@Override
public void defaultMethod() {
//接口的
JDK8Interface1.super.defaultMethod();
System.out.println("實(shí)現(xiàn)類復(fù)寫重名默認(rèn)方法A衾蟆!@骄搿聚蝶!");
}
}
public interface JDK8Interface1 {
//1.接口中可以定義靜態(tài)方法了
public static void staticMethod(){
System.out.println("接口中的靜態(tài)方法");
}
//2.使用default之后就可以定義普通方法的方法體了
public default void defaultMethod(){
System.out.println("接口中的默認(rèn)方法");
}
}
public interface JDK8Interface2 {
//1.接口中可以定義靜態(tài)方法了
public static void staticMethod(){
System.out.println("接口中的靜態(tài)方法");
}
//2.使用default之后就可以定義普通方法的方法體了
public default void defaultMethod(){
System.out.println("接口中的默認(rèn)方法");
}
}
/**
* 4.方法引用,與Lambda表達(dá)式聯(lián)合使用
*/
@Test
public void testMethodReference(){
//構(gòu)造器引用。語(yǔ)法是Class::new藻治,或者更一般的Class< T >::new碘勉,要求構(gòu)造器方法是沒有參數(shù);
final Car car = Car.create( Car::new );
final List< Car > cars = Arrays.asList( car );
//靜態(tài)方法引用桩卵。語(yǔ)法是Class::static_method验靡,要求接受一個(gè)Class類型的參數(shù)倍宾;
cars.forEach( Car::collide );
//任意對(duì)象的方法引用。它的語(yǔ)法是Class::method胜嗓。無(wú)參高职,所有元素調(diào)用;
cars.forEach( Car::repair );
//特定對(duì)象的方法引用辞州,它的語(yǔ)法是instance::method怔锌。有參,在某個(gè)對(duì)象上調(diào)用方法变过,將列表元素作為參數(shù)傳入埃元;
final Car police = Car.create( Car::new );
cars.forEach( police::follow );
}
public static class Car {
public static Car create( final Supplier< Car > supplier ) {
return supplier.get();
}
public static void collide( final Car car ) {
System.out.println( "靜態(tài)方法引用 " + car.toString() );
}
public void repair() {
System.out.println( "任意對(duì)象的方法引用 " + this.toString() );
}
public void follow( final Car car ) {
System.out.println( "特定對(duì)象的方法引用 " + car.toString() );
}
}
/**
* 5.引入重復(fù)注解
* 1.@Repeatable
* 2.可以不用以前的“注解容器”寫法,直接寫2次相同注解即可
*
* Java 8在編譯器層做了優(yōu)化媚狰,相同注解會(huì)以集合的方式保存岛杀,因此底層的原理并沒有變化。
*/
@Test
public void RepeatingAnnotations(){
RepeatingAnnotations.main(null);
}
/**
* 6.類型注解
* 新增類型注解:ElementType.TYPE_USE 和ElementType.TYPE_PARAMETER(在Target上)
*
*/
@Test
public void ElementType(){
Annotations.main(null);
}
/**
* 7.最新的Date/Time API (JSR 310)
* UTC即為世界標(biāo)準(zhǔn)時(shí)間崭孤,世界統(tǒng)一時(shí)間类嗤,國(guó)際協(xié)調(diào)時(shí)間,字母T是時(shí)間和日期的分隔符裳瘪,T后面表示的即為時(shí)間土浸,末尾的z表示UTC統(tǒng)一時(shí)間。
*/
@Test
public void DateTime(){
//1.Clock
final Clock clock = Clock.systemUTC();
System.out.println( clock.instant() );//2018-12-27T13:25:18.636Z
System.out.println( clock.millis() );//1545917118662
//2. ISO-8601格式且無(wú)時(shí)區(qū)信息的日期部分
final LocalDate date = LocalDate.now();
final LocalDate dateFromClock = LocalDate.now( clock );
System.out.println( date );//2018-12-27
System.out.println( dateFromClock );//2018-12-27
// ISO-8601格式且無(wú)時(shí)區(qū)信息的時(shí)間部分
final LocalTime time = LocalTime.now();
final LocalTime timeFromClock = LocalTime.now( clock );
System.out.println( time ); //21:25:18.692
System.out.println( timeFromClock );//13:25:18.692
// 3.ISO-8601格式無(wú)時(shí)區(qū)信息的日期與時(shí)間
final LocalDateTime datetime = LocalDateTime.now();
final LocalDateTime datetimeFromClock = LocalDateTime.now( clock );
System.out.println( datetime ); //2018-12-27T21:25:18.692
System.out.println( datetimeFromClock );//2018-12-27T13:25:18.692
// 4.特定時(shí)區(qū)的日期/時(shí)間彭羹,
final ZonedDateTime zonedDatetime = ZonedDateTime.now();
final ZonedDateTime zonedDatetimeFromClock = ZonedDateTime.now( clock );
final ZonedDateTime zonedDatetimeFromZone = ZonedDateTime.now( ZoneId.of( "America/Los_Angeles" ) );
System.out.println( zonedDatetime ); //2018-12-27T21:25:18.694+08:00[Asia/Shanghai]
System.out.println( zonedDatetimeFromClock );//2018-12-27T13:25:18.694Z
System.out.println( zonedDatetimeFromZone ); //2018-12-27T05:25:18.706-08:00[America/Los_Angeles]
//5.在秒與納秒級(jí)別上的一段時(shí)間
final LocalDateTime from = LocalDateTime.of( 2014, Month.APRIL, 16, 0, 0, 0 );
final LocalDateTime to = LocalDateTime.of( 2015, Month.APRIL, 16, 23, 59, 59 );
final Duration duration = Duration.between( from, to );
System.out.println( "Duration in days: " + duration.toDays() ); //Duration in days: 365
System.out.println( "Duration in hours: " + duration.toHours() );//Duration in hours: 8783
}
/**
* 8.新增base64加解密API
*/
@Test
public void testBase64(){
final String text = "就是要測(cè)試加解密;埔痢!abjdkhdkuasu!!@@@@";
String encoded = Base64.getEncoder()
.encodeToString( text.getBytes( StandardCharsets.UTF_8 ) );
System.out.println("加密后="+ encoded );
final String decoded = new String(
Base64.getDecoder().decode( encoded ),
StandardCharsets.UTF_8 );
System.out.println( "解密后="+decoded );
}
/**
* 9.數(shù)組并行(parallel)操作
*/
@Test
public void testParallel(){
long[] arrayOfLong = new long [ 20000 ];
//1.給數(shù)組隨機(jī)賦值
Arrays.parallelSetAll( arrayOfLong,
index -> ThreadLocalRandom.current().nextInt( 1000000 ) );
//2.打印出前10個(gè)元素
Arrays.stream( arrayOfLong ).limit( 10 ).forEach(
i -> System.out.print( i + " " ) );
System.out.println();
//3.數(shù)組排序
Arrays.parallelSort( arrayOfLong );
//4.打印排序后的前10個(gè)元素
Arrays.stream( arrayOfLong ).limit( 10 ).forEach(
i -> System.out.print( i + " " ) );
System.out.println();
}
/**
* 10.JVM的PermGen空間被移除:取代它的是Metaspace(JEP 122)元空間
*/
@Test
public void testMetaspace(){
//-XX:MetaspaceSize初始空間大小派殷,達(dá)到該值就會(huì)觸發(fā)垃圾收集進(jìn)行類型卸載还最,同時(shí)GC會(huì)對(duì)該值進(jìn)行調(diào)整
//-XX:MaxMetaspaceSize最大空間,默認(rèn)是沒有限制
//-XX:MinMetaspaceFreeRatio在GC之后毡惜,最小的Metaspace剩余空間容量的百分比拓轻,減少為分配空間所導(dǎo)致的垃圾收集
//-XX:MaxMetaspaceFreeRatio在GC之后,最大的Metaspace剩余空間容量的百分比经伙,減少為釋放空間所導(dǎo)致的垃圾收集
}
}
2扶叉、重復(fù)注解@Repeatable
package com.zgldoing.base.jdk8Features;
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.zgldoing.base.jdk8Features.RepeatingAnnotations.Filter;
import com.zgldoing.base.jdk8Features.RepeatingAnnotations.Filterable;
import com.zgldoing.base.jdk8Features.RepeatingAnnotations.Filters;
/**
* @ClassName:RepeatingAnnotations
* @Description:重復(fù)注解@Repeatable
* @author zgldo
*
*/
public class RepeatingAnnotations {
@Target( ElementType.TYPE )
@Retention( RetentionPolicy.RUNTIME )
public @interface Filters {
Filter[] value();
}
@Target( ElementType.TYPE )
@Retention( RetentionPolicy.RUNTIME )
@Repeatable( Filters.class )
public @interface Filter {
String value();
String value2();
};
@Filter( value="filter1",value2="111" )
@Filter( value="filter2", value2="222")
//@Filters({@Filter( value="filter1",value2="111" ),@Filter( value="filter2", value2="222")}).注意:JDK8之前:1.沒有@Repeatable2.采用本行“注解容器”寫法
public interface Filterable {
}
public static void main(String[] args) {
//獲取注解后遍歷打印值
for( Filter filter: Filterable.class.getAnnotationsByType( Filter.class ) ) {
System.out.println( filter.value() +filter.value2());
}
}
}
3、新增類型注解:ElementType.TYPE_USE 和ElementType.TYPE_PARAMETER(在Target上)
package com.zgldoing.base.jdk8Features;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.ArrayList;
import java.util.Collection;
import com.zgldoing.base.jdk8Features.Annotations.Holder;
import com.zgldoing.base.jdk8Features.Annotations.NonEmpty;
/**
* @ClassName:Annotations
* @Description:新增類型注解:ElementType.TYPE_USE 和ElementType.TYPE_PARAMETER(在Target上)
* @author zgldo
*
*/
public class Annotations {
@Retention( RetentionPolicy.RUNTIME )
@Target( { ElementType.TYPE_USE, ElementType.TYPE_PARAMETER } )
public @interface NonEmpty {
}
public static class Holder< @NonEmpty T > extends @NonEmpty Object {
public void method() throws @NonEmpty Exception {
}
}
public static void main(String[] args) {
final Holder< String > holder = new @NonEmpty Holder< String >();
@NonEmpty Collection< @NonEmpty String > strings = new ArrayList<>();
}
}