目錄
-
-
- 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中都是預覽狀态、沒有正式釋出。