天天看點

JDK7~13的新特性

目錄

      • jdk7
        • 新的資源關閉方式 try-with-resources
      • jdk8
        • 接口新增的預設方法、靜态方法
        • 新增的base64加解密api
        • 新增的時間日期類、時間日期處理類
        • 新增的NPE處理類Optional
        • 新增的函數式接口、lambda表達式
          • 函數式接口
          • lambda表達式
          • jdk8内置的函數式接口
        • 新增的方法引用方式::
        • 新增的集合操作方式Stream
          • stream的基本使用
          • map()
          • filter()
          • sorted()
          • limit()
          • allMatch()、anyMatch()
          • max()、min()
          • reduce()
          • foreach()
          • collect()
          • paralleStream 并行流
        • 新的記憶體空間Matespace
      • jdk9
        • 接口新增的私有方法
        • 增強的try-with-resource
        • 新增的建立隻讀集合的api
        • 子產品化
      • jdk10
        • 新增的建立隻讀集合的api
        • 新增的局部變量類型推斷var
      • jdk11
        • 新增的http用戶端
        • javac、java指令的優化
      • jdk13

jdk7

新的資源關閉方式 try-with-resources

以前的資源關閉方式 try…finally

OutputStream os = null;
try {
    os = new FileOutputStream("C:/Users/chy/Desktop/1.txt");
    os.write("hello".getBytes());
} catch (FileNotFoundException e) {
    //...
} catch (IOException e) {
    //...
} finally {
    if (os != null) {
        try {
            os.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
           

需要在finally中關閉,且在finally中還可能需要嵌套try,代碼冗雜;如果需要關閉多個資源,關閉順序還可能寫錯。

jdk7新增的資源關閉方式 try-with-resources

/**
 * 在try()中定義要關閉的資源,try代碼塊執行完畢後會自動關閉該資源,無需手動關閉。關閉時會自動flush刷出資料,無需手動調用flush()。
 * 相當于自動提供了一個關閉資源的finally代碼塊,如果執行try代碼塊時發生異常,資源也能正常關閉。
 * 如果有手動寫的finally代碼塊,會在手動寫的finally代碼塊之前執行。
 */
try (OutputStream os = new FileOutputStream("C:/Users/chy/Desktop/1.txt")) {
    os.write("hello".getBytes());
} catch (FileNotFoundException e) {
    //...
} catch (IOException e) {
    //...
}


/**
 * 如果有多個要關閉的資源,分号隔開。關閉順序與聲明順序相反,後聲明的先關閉。
 * 隻要實作了AutoCloseable接口的類,都可以使用此種方式關閉資源。
 * IO流的4個抽象基類InputStream、OutputStream、Reader、Writer都實作了AutoCloseable接口,IO流基本都可以使用此方式關閉
 */
try (
    OutputStream os1 = new FileOutputStream("C:/Users/chy/Desktop/1.txt");
    OutputStream os2 = new FileOutputStream("C:/Users/chy/Desktop/2.txt")
) {
    os1.write("hello".getBytes());
} catch (FileNotFoundException e) {
    //...
} catch (IOException e) {
    //...
}
           

自動關閉資源,友善,代碼也簡潔,盡量用try-with-resources代替try…finally來關閉資源。

jdk8

接口新增的預設方法、靜态方法

public interface Animal {

    void run();

    void eat();

    default void breath() {
        System.out.println("預設方法實作...");
    }

    static void test() {
        System.out.println("靜态⽅法...");
    }
    
}
           

jdk8以前:接口中的方法隻能是抽象方法,不能有方法體(實作)

jdk8

  • 接口新增了預設方法:方法使用default修飾(default并不是通路權限,接口中的方法都隻能是public),必須提供方法體(實作)。實作類會繼承預設方法,可根據需要重寫預設方法,實作類調用預設方法時,如果進行了重寫則預設調用本類中的預設方法,否則調用接口中的預設方法。
  • 接口新增了靜态方法:方法使用static修飾,必須提供方法體(實作),通過接口名調用。關于靜态方法,注意:子類會繼承父類的靜态方法,但子接口、實作類不會繼承父接口的靜态方法。

接口新增了預設方法、靜态方法,但和抽象類相比,接口依然不能表達對象狀态的變化(接口中不能定義對象的成員字段),接口不能完全替代抽象類。

新增的base64加解密api

base64常用于資料的加解密,比如登入使用http+密碼明文很不安全,最好使用https,如果堅持要用http,最好加密敏感資料後再進行傳輸。

jdk7的base64加解密方式

//原文本
String text = "年年有魚";
//編碼後的文本
String encodedText;

//編碼
BASE64Encoder encoder = new BASE64Encoder();
encodedText = encoder.encode(text.getBytes("UTF-8"));
System.out.println("編碼後得到的文本:" + encodedText);

//解碼
BASE64Decoder decoder = new BASE64Decoder();
text = new String(decoder.decodeBuffer(encodedText), "UTF-8");
System.out.println("解碼後得到的原文本:" + text);
           

編解碼性能差,據說會在将來的版本中删除。

jdk8提供了性能更好的base64加解密方式

//原文本
String text = "年年有魚";
//編碼後的文本
String encodedText;

//編碼
Base64.Encoder encoder = Base64.getEncoder();
encodedText = encoder.encodeToString(text.getBytes("UTF-8"));
System.out.println("編碼後得到的文本:" + encodedText);

//解碼
Base64.Decoder decoder = Base64.getDecoder();
text = new String(decoder.decode(encodedText), "UTF8");
System.out.println("解碼後得到的原文本:" + text);
           

新增的時間日期類、時間日期處理類

  • jdk7的時間日期類:Date、Calendar,非線程安全,api設計差、不好使用。
  • jdk8新增的時間日期類:LocalDate 日期、LocalTime 時間、LocalDateTime 日期+時間,api衆多,操作友善。
  • jdk7的時間日期處理類:DateFormat、SimpleDateFormat,SimpleDateFormat簡單易用,但非線程安全
  • jdk8新增的時間日期處理類:DateTimeFormatter,同樣好用,但線程安全
//新增的三個時間日期類,建立對象的方法都是靜态方法,通過類名直接調用
LocalDateTime today = LocalDateTime.now();
LocalDateTime dt = LocalDateTime.of(2020, 1, 1, 1, 1, 1);

//使用新增的時間日期處理類進行格式化
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String formatDT = dtf.format(today);
System.out.println("格式化後得到的時間日期:" + formatDT);

//計算2個時間日期的內插補點。第二個參數減第一個參數
Duration duration = Duration.between(dt, today);
//Duration包含了多種內插補點表示方式
System.out.println("dt距today多少天:" + duration.toDays());
System.out.println("dt距today多少小時:" + duration.toHours());
           

新增的NPE處理類Optional

使用大段大段的if…else…判空,可讀性差,可以用Optional替代對象成員變量的if判空。

User user = null;


//user可以為null
Optional<User> optional = Optional.ofNullable(user);

//user不能為null,為null時會發生NPE
Optional<User> optional = Optional.of(user);


//Optional可以看做對對象的包裝,判斷内部對象是否為null
boolean present = optional.isPresent();

//擷取内部對象,内部對象為null時會發生NPE
User user = optional.get();



//可以對optional中的對象進行過濾,滿足條件才保留在optional中,以新對象的形式傳回
//user可以為null,為null時不會發生NPE
optional = optional.filter(user -> StringUtils.isBlank(user.getUsername()) && user.getUsername().length() < 10);


String defaultHeadImg = "/xxx/xxx.png";

//user對象的headImg屬性不為null,則傳回headImg,為null則傳回設定的預設值
String headImg = optional.map(user -> user.getHeadImg()).orElse(defaultHeadImg);

//可以把lambda表達式的傳回值作為預設值
String headImg = optional.map(user -> user.getHeadImg()).orElseGet(() -> defaultHeadImg);

//也可以直接抛出異常
String headImg = optional.map(user -> user.getHeadImg()).orElseThrow(() -> new RuntimeException("未選擇頭像"));

//以上三句代碼,user為null時也不會發生NPE


//也可以先進行判斷,内部對象不為null才進行操作
optional.ifPresent(user -> {
    //傳入的是對象位址,對形參的操作就是對實參的操作
    //...
});
           
  • 建立Optional對象,對内部對象進行判空,再進行操作,這樣使用Optional沒有意義,比原先的if判空還寫得複雜。
  • Optional适合在對象可能為null(需要判空)時單次擷取對象的屬性,不是每種判空都适合使用Optional,比如對包裝類、String這些簡單對象的判空,比如要多次擷取對象的(多個)屬性進行操作。高頻使用map()方法會顯得很繁瑣,最好先對對象進行判空,再進行後續操作,有時候使用if直接判空更好。

新增的函數式接口、lambda表達式

函數式接口

jdk8新增了函數式接口,以支援函數式程式設計

@FunctionalInterface  //辨別為函數式接口
public interface MyStringUtils {

    String join(String str1, String str2);

}
           

函數式接口中的抽象方法有且隻能有1個,可以有靜态方法、預設實作的方法,但抽象方法必須有、且隻能有1個。

lambda表達式

lambda表達式可以實作隻有一個抽象方法的接口,不局限于函數式接口,隻要接口中隻有一個抽象方法,就可以使用lambda表達式實作。

//原來的寫法:使用匿名内部類
MyStringUtils myStringUtils1 = new MyStringUtils() {
    @Override
    public String join(String str1, String str2) {
        return str1 + str2;
    }
};


//使用lambda表達式。lambda表達式實質是使用匿名内部類,文法糖,代碼更加簡潔
MyStringUtils myStringUtils2 = (str1, str2) -> str1 + str2;


//相比于匿名内部類,lambda表達式除了簡潔之外,還可以直接通路外部變量,但隻能通路、不能修改
String username = "chy";
userlist.forEach(user -> {
	//可以直接使用外部變量username
    if (username.equals(user.getUsername())) {
        System.out.println(user);
    }
});
           
  • 隻有一個參數時,可以省略()
  • 方法體中隻有一條語句時,可以省略{ }
  • 方法體中隻有一條語句,且方法有傳回值時,可以預設return關鍵字
jdk8内置的函數式接口

jdk、各種架構都内置了多個函數式接口,jdk8常見的内置函數式接口如下

  • Consumer:void accept(T t); 消費型接口,有入參、無傳回值,用于處理傳入的參數。
  • Supplier:T get(); 供給型接口,無入參、有傳回值,用于提供某個類型的執行個體。
  • Function<T, R>:R apply(T t); 函數型接口,有入參、有傳回值,用于處理傳入的資料,并傳回結果。
  • BiFunction:R apply(T t, U u); Function隻能接收⼀個參數,BiFunction可以接收2個參數
  • Predicate: boolean test(T t); 斷⾔型接口,有入參、有傳回值,傳回值為布爾類型,用于校驗傳入的資料。

示例

public class Test {

    /**
     * 過濾list中的元素,傳回滿足條件的元素
     */
    public static <T> List<T> filterList(List<T> list, Predicate<T> predicate) {
        List<T> resultList = new ArrayList<>();
        for (T ele : list) {
            //調用predicate的test()進行判斷
            if (predicate.test(ele)) {
                resultList.add(ele);
            }
        }
        return resultList;
    }

    public static void main(String[] args) {
        List<String> list = Arrays.asList("zhangsan", "lisi", "wangwu");
        //條件:字元串長度>5
        List<String> resultList = filterList(list, ele -> ele.length() > 5);
        System.out.println("滿足條件的元素:" + resultList);
    }

}
           

新增的方法引用方式::

在lambda表達式中,可以使用雙冒号引用方法

User user = new User();
Optional<User> optional = Optional.ofNullable(user);

//原來的調用方式:u.getHeadImg()
String headImg1 = optional.map(u->u.getHeadImg()).orElse("/xxx/defaultHeadImg.png");
//新的調用方式:User::getHeadImg
String headImg2 = optional.map(User::getHeadImg).orElse("/xxx/defaultHeadImg.png");
           

這種方法引用方式隻能在lambda表達式中使用

新增的集合操作方式Stream

stream 流,可以将集合轉換為流,進而對集合中的元素進行⼀系列的操作,比如排序、過濾、聚合等。

stream的基本使用

eg. 把手機号的中間4位替換為****

List<String> list = Arrays.asList("13712798761", "13712798762", "13712798763");

//stream()是轉換為對應的流對象 Stream<T>
List<String> resultList = list.stream()
        //map()傳回的也是流對象 Stream
        .map(ele -> new StringBuilder(ele).replace(3, 7, "****").toString())
        //将Stream轉回List
        .collect(Collectors.toList());

System.out.println(resultList);
           
//可以使用集合對象的.stream()方法建立流,也可以使用Stream的靜态方法建立流,比如
Stream<Integer> stream = Stream.of(1, 2, 3);

//jdk也封裝了一些常見類型的流,比如IntStream、LongStream、DoubleStream
IntStream intStream = IntStream.of(1, 2, 3);
//這些流根據自身的元素類型,有一些額外的方法,比如sum()、average()
int sum = intStream.sum();
OptionalDouble average = intStream.average();

//Optional也封裝一些常見類型,比如OptionalInt、OptionalLong、OptionalDouble
           
map()

map()可以将流中的元素映射為指定類型,類似于類型轉換。

.map(ele -> new StringBuilder(ele).replace(3, 7, “****”).toString())

流中的元素原本是String類型,lambda表達式的方法體預設了return關鍵字,方法體其實是 return new StringBuilder(ele).replace(3, 7, “****”).toString()); 傳回String類型的元素。

eg. 把List<User>轉換為List<UserDTO>傳給其它服務處理

List<User> userlist = Arrays.asList(
        new User(1, "chy1", "abcd1", "13723456781"),
        new User(2, "chy2", "abcd2", "13723456782"),
        new User(3, "chy3", "abcd2", "13723456783")
);

List<UserDTO> userDTOList = userlist.stream()
        .map(user -> {
        	//增删改一些字段,比如去除密碼之類的敏感資訊
            String tel = new StringBuilder(user.getTel()).replace(3, 7, "****").toString();
            return new UserDTO(user.getId(), user.getUsername(), tel);
        })
        .collect(Collectors.toList());

System.out.println(userDTOList);
           
filter()

filter()用于篩選流中滿足條件的元素,隻保留滿足條件的元素。

List<User> resultList = userlist.stream()
               // filter()的參數是函數式斷言接口 Predicate,傳回布爾值即可,true表示保留
               // .filter(user->user.getUsername().startsWith("ch") && user.getUsername().endsWith("2"))
               .filter(user -> {
                   String username = user.getUsername();
                   if (username.startsWith("ch") && username.endsWith("2")) {
                       return true;
                   }
                   return false;
               })
               .collect(Collectors.toList());
           
sorted()

sorted()可以對流中的元素排序

List<User> resultList = userlist.stream()
              // 按id的大小升序排列
              .sorted(Comparator.comparing(user -> user.getId()))

              // 按username的長度升序排列
              // .sorted(Comparator.comparing(user -> user.getUsername().length()))

              // lambda方法體有2個參數,第一個指定用來排序的字段,第二個指定升|降序,預設第二個時預設Comparator.naturalOrder() 升序
              // .sorted(Comparator.comparing(user -> user.getId(), Comparator.naturalOrder()))

              // 降序方式
              // .sorted(Comparator.comparing(user -> user.getId(), Comparator.reverseOrder()))
              // .sorted(Comparator.comparing(user -> user.getUsername().length(), Comparator.reverseOrder()))

              .collect(Collectors.toList());
           
limit()

limit()用于指定流中要保留的元素個數。

List<User> resultList = userlist.stream()
        //隻保留2個元素
        .limit(2)
        .collect(Collectors.toList());
           

很多方法都可以搭配使用,可以多次使用同一個方法。

allMatch()、anyMatch()

allMatch()用于判斷流中的元素是否都滿足指定條件,anyMatch()用于判斷流中是否有元素滿足指定條件(有一個元素滿足即可)。

//參數都是斷言接口Predicate
boolean b1 = userlist.stream().anyMatch(user -> StringUtils.isNotBlank(user.getUsername()));
boolean b2 = userlist.stream().allMatch(user -> StringUtils.isNotBlank(user.getUsername()));
           
max()、min()

max()、min()用于擷取流中指定字段的值最大、最小的元素。

//lambda表達式的參數是2個元素,Integer.compare()的參數是要比較的2個值,實質是指定要比較的字段,元素、值要對應
Optional<User> max2 = userlist.stream().max((user1, user2) -> Integer.compare(user1.getId(), user2.getId()));

//可以簡寫如下
Optional<User> max1 = userlist.stream().max(Comparator.comparingInt(user -> user.getId()));
           

傳回值是Optional類型,包含了整個元素。

reduce()

reduce()可以逐漸将2個相鄰元素替換為1個元素,減少流中的元素數,直到流中隻剩下一個元素。

eg. 求元素之和、之積

//lambda的參數表示2個相鄰元素
int sum1 = Stream.of(1, 2, 3, 4, 5).reduce((ele1, ele2) -> ele1 + ele2).get();

//可以設定一個初始值,相當于在流中的第一個元素之前暫時插入一個新元素參與計算
int sum2 = Stream.of(1, 2, 3, 4, 5).reduce(100, (ele1, ele2) -> ele1 + ele2);
           
  • 1,2,3,4,5
  • 3,3,4,5
  • 6,4,5
  • 10,5
  • 15

IntStream之類的數值型流有sum()方法可以求和,但很多流中的元素是OrderItem之類的複雜類型,需要使用reduce()來操作元素的成員變量。

eg. 求最大、最小值

int sum1 = Stream.of(1, 2, 3, 4, 5).reduce((ele1, ele2) -> ele1 > ele2 ? ele1 : ele2).get();

int sum2 = Stream.of(1, 2, 3, 4, 5).reduce((ele1, ele2) -> Integer.max(ele1, ele2)).get();
           

也可以使用流的max()、min()方法實作

foreach()

集合、stream都有foreach()方法,用于周遊元素

//方法體即循環體
userlist.stream().forEach(user -> System.out.println(user));
userlist.forEach(user -> System.out.println(user));
           

注意點

  • 循環體中不能使用break、continue
  • 循環體中可以使用return,但return無效
  • 不管是使用疊代器、增強for循環,還是使用foreach周遊集合,不要在循環體中增删目前周遊的集合|stream中的元素,增删元素會導緻周遊出錯
collect()

collect()是收集器,可以将流中的元素收集到指定容器中。

//收集到list中,使用的是ArrayList
List<Integer> collect1 = Stream.of(1, 2, 3).collect(Collectors.toList());

//收集到set中,使用的是HashSet
Set<Integer> collect2 = Stream.of(1, 2, 3).collect(Collectors.toSet());


//可以指定目标集合類型
LinkedList<Integer> collect3 = Stream.of(1, 2, 3).collect(Collectors.toCollection(LinkedList::new));
CopyOnWriteArrayList<Integer> collect4 = Stream.of(1, 2, 3).collect(Collectors.toCollection(CopyOnWriteArrayList::new));


//可以轉換為map:toMap()、toConcurrentMap()
           

如果元素是字元串,可以拼接(收集)元素到一個字元串中

//直接拼接,不使用分隔符(即使用空串作為分隔符)
String str1 = Stream.of("hello", "word").collect(Collectors.joining());

//可以指定分隔符
String str2 = Stream.of("hello", "word").collect(Collectors.joining(" "));

//可以指定分隔符、字首、字尾
String str3 = Stream.of("hello", "word").collect(Collectors.joining(" ", "", "!"));
           

流的分組統計

//将滿足條件(true)的分為一組,将不滿足條件(false)的分為一組,2個key分别是true、false
Map<Boolean, List<Integer>> map1 = Stream.of(1, 2, 3, 4, 5).collect(Collectors.partitioningBy(ele -> ele > 3));


//按指定字段進行分組,一組就是一個List<Student>
//eg.統計員工中各個省份的有哪些,統計訂單中各個狀态的訂單有哪些
Map<String, List<Student>> map2 = studentList.stream().collect(Collectors.groupingBy(student -> student.getProvince()));


//lambda表達式的方法體多了一個參數,按指定字段分組統計元素個數
//eg.統計員工中各個省份的人數,統計訂單中各個狀态的訂單數
Map<String, Long> map3 = studentList.stream().collect(Collectors.groupingBy(student -> student.getProvince(), Collectors.counting()));
           

對于數值型字段,可以聚合統計流的多個名額

//summarizingInt包含了統計字段的資料類型,支援Int、Long、Double
IntSummaryStatistics intSummaryStatistics = studentList.stream().collect(Collectors.summarizingInt(student -> student.getAge()));

long count = intSummaryStatistics.getCount();
double average = intSummaryStatistics.getAverage();
int max = intSummaryStatistics.getMax();
int min = intSummaryStatistics.getMin();
long sum = intSummaryStatistics.getSum();
           
paralleStream 并行流
//以下2種方式建構的都是串行流
Stream<Integer> stream1 = Stream.of(1, 2);
Stream<User> stream2 = userlist.stream();

//以下2種方式建構的是并行流。調用parallel()方法,可以将串行流轉換為并行流
Stream<Integer> parallelStream1 = Stream.of(1, 2).parallel();
Stream<User> parallelStream2 = userlist.parallelStream();
           

并行流的使用方式和串行流相同,隻是串行流是單線程執行流操作,并行流使用多線程執行流操作。

涉及多線程時,注意2點

  • 多線程不一定比單線程快:多線程有配置設定線程資源、切換線程上下文的時間開銷,如果是執行時間長的大任務,多線程往往比單線程快;如果是執行時間很短的小任務,單線程可能比多線程快。周遊集合時是否要使用多線程要看元素個數,幾十個元素的小集合一般使用單線程即可,幾千上萬個元素的大集合、循環體又有點耗時的,可以使用多線程。
  • 使用多線程時一定要注意線程安全問題,對于線程共享資源,該使用線程安全類的使用線程安全類,該加鎖的加鎖。

一般使用串行流即可,如果元素較多,可以使用并行流,使用并行流時要注意線程安全問題。

新的記憶體空間Matespace

jdk、jvm都隻是一個标準、規範,有多種實作,此處以主流的jvm實作Hotspot為準。

jdk7用永久代(permanent generation)來此存儲類的中繼資料,永久代使用的是jvm的記憶體空間,可能發生OOM。

jdk8使用元空間(Metaspace)代替永久代,元空間使用本地記憶體,受制于機器實體記憶體的大小,基本不會發生OOM。

jdk9

接口新增的私有方法

public interface Animal {

    private void m1() {
        System.out.println("私有方法");
    }

    default void m2() {
        System.out.println("預設方法");
        m1();
    }

    static void m3() {
        System.out.println("靜态方法");
    }

}
           

私有方法使用private修飾,自然不會被繼承,隻能在目前接口中調用,且必須提供方法體(實作),不然沒有意義。

說明

  • 接口中的私有方法、預設方法、靜态方法都必須提供方法體(實作)。
  • 私有方法不能被繼承,預設方法可以被繼承,接口中的靜态方法不能被繼承,但類中的靜态方法可以被繼承。

增強的try-with-resource

OutputStream os1 = new FileOutputStream("C:/Users/chy/Desktop/1.txt");
OutputStream os2 = new FileOutputStream("C:/Users/chy/Desktop/2.txt");

//可以将資源聲明放在try()外面,()中指定資源變量即可,有多個時分号隔開 try (os1; os2)
try (os1) {
    os1.write("hello".getBytes());
} catch (FileNotFoundException e) {
    e.printStackTrace();
} catch (IOException e) {
    e.printStackTrace();
}
           

新增的建立隻讀集合的api

之前建立隻讀集合的方式

List<String> list = new ArrayList<>();
list.add("chy");
//設定為隻讀集合。原集合不變,傳回一個隻讀集合
list = Collections.unmodifiableList(list);


Set<String> set = new HashSet<>();
set.add("chy");
//設定為隻讀集合
set = Collections.unmodifiableSet(set);


Map<String, String> map = new HashMap<>();
map.put("name", "chy");
//設定為隻讀集合
map = Collections.unmodifiableMap(map);


//Arrays.asList()建立的也是隻讀集合
List<String> list = Arrays.asList("chy1", "chy2");
           

jdk9新增的方式

List<String> list = List.of("chy1", "chy2");

Set<String> set = Set.of("chy1", "chy2");

Map<String, Object> map = Map.of("name", "chy", "age", 20);
           

子產品化

jdk9将把jdk、jre、jar等分成較小的子產品,使用時無需全部引入,隻引入所需的子產品即可,子產品化降低了代碼耦合度、大大縮小了應用體積。

jdk10

新增的建立隻讀集合的api

ArrayList<String> list1 = new ArrayList<>();
list1.add("chy");

//jdk10的List、Set、Map都提供了靜态方法copyOf()建立對應的隻讀集合,原集合不變,以新集合的方式傳回
List<String> list2 = List.copyOf(list1);
           

jdk9、10的這2個api實質都是調用 ImmutableCollections 類的方法建立隻讀集合。immutable 隻讀的、不可變更的。

新增的局部變量類型推斷var

//聲明時可以聲明為var類型,聲明時就必須初始化,初始化時不能賦null,會自動根據賦的值推斷變量類型
var a = 1;
var b = 2;
System.out.println(a + b);
           

var隻能用于聲明局部變量,不能用于聲明方法形參,不能作為方法的傳回值類型。

jdk11

新增的http用戶端

基本使用

//要請求的uri
URI uri = URI.create("http://www.baidu.com");


//建立httpClient。可以HttpClient.newHttpClient()直接建立HttpClient對象,但連接配接最好都設定逾時時間
HttpClient httpClient = HttpClient.newBuilder()
        .connectTimeout(Duration.ofMillis(5000))
        .build();


//建立請求。get請求方式
HttpRequest request = HttpRequest.newBuilder()
        .timeout(Duration.ofMillis(5000))
        .uri(uri)
        // 預設就是get方式,可省略
        // .GET()
        // 配置項
        .header("key1", "value1")
        .header("key2", "value2")
        .build();


//執行請求
try {
    HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
    //使用響應中的資料
    System.out.println(response.body());
} catch (IOException e) {
    e.printStackTrace();
} catch (InterruptedException e) {
    e.printStackTrace();
}
           

發送post方式的請求

//post請求方式傳遞表單資料
String formData = "username=chy&password=1234";
HttpRequest request1 = HttpRequest.newBuilder()
        .uri(uri)
        .header("Content-Type", "application/x-www-formurlencoded")
        .POST(HttpRequest.BodyPublishers.ofString(formData))
        .build();


//post方式傳遞json資料
String jsonData = "{\"username\":\"chy\",\"password\":\"abcd\"}";
HttpRequest request2 = HttpRequest.newBuilder()
        .uri(uri)
        .header("Content-Type", "application/json")
        .POST(HttpRequest.BodyPublishers.ofString(jsonData))
        .build();
           

發送異步請求

// 同步請求
// HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
// System.out.println(response.body());


// 異步請求
CompletableFuture<String> completableFuture = httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString())
        .thenApply(response -> response.body());

//要執行的其它代碼
//......

//使用響應。completableFuture.get()會阻塞目前線程,直到請求完成傳回了響應,或者逾時
String resposeBody = completableFuture.get();
System.out.println(resposeBody);
           

發送http2請求

HttpRequest request = HttpRequest.newBuilder()
        .timeout(Duration.ofMillis(5000))
        .uri(uri)
        //指定httpClient使用的http協定版本
        .version(HttpClient.Version.HTTP_2)
        .header("key1", "value1")
        .header("key2", "value2")
        .build();
           

目前http協定的主流版本是http1.1,http2是下一代版本。http2強制使用https,需要伺服器支援http2。

javac、java指令的優化

以前編譯執行:先javac編譯,再java執行,本地會生成.class檔案。

jdk11:無需javac編譯,直接java執行即可,本地不會生成.class檔案。

jdk13

jdk13新增了文本塊、增強了switch表達式,但這2個特性在jdk13中都是預覽狀态、沒有正式釋出。