一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術(shù)|正則表達(dá)式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務(wù)器之家 - 編程語言 - Java教程 - Java8深入學(xué)習(xí)系列(三)你可能忽略了的新特性

Java8深入學(xué)習(xí)系列(三)你可能忽略了的新特性

2020-12-13 20:07王爵nice Java教程

一提到Java 8就只能聽到lambda,但這不過是其中的一個(gè)而已,Java 8還有許多新的特性,有一些功能強(qiáng)大的新類或者新的用法,還有一些功能則是早就應(yīng)該加到Java里了,所以下面這篇文章主要給大家介紹了關(guān)于Java8中大家可能忽略了的一

前言

我們之前已經(jīng)介紹了關(guān)于java8中lambda函數(shù)式編程的相關(guān)內(nèi)容,雖然我們開始了Java8的旅程,但是很多人直接從java6上手了java8, 也許有一些JDK7的特性你還不知道,在本章節(jié)中帶你回顧一下我們忘記了的那些特性。 盡管我們不能講所有特性都講一遍,挑出常用的核心特性拎出來一起學(xué)習(xí)。

Java8深入學(xué)習(xí)系列(三)你可能忽略了的新特性

異常改進(jìn)

try-with-resources

這個(gè)特性是在JDK7種出現(xiàn)的,我們在之前操作一個(gè)流對象的時(shí)候大概是這樣的:

?
1
2
3
4
5
6
7
8
9
10
11
12
try {
 // 使用流對象
 stream.read();
 stream.write();
} catch(Exception e){
 // 處理異常
} finally {
 // 關(guān)閉流資源
 if(stream != null){
 stream.close();
 }
}

這樣無疑有些繁瑣,而且finally塊還有可能拋出異常。在JDK7種提出了try-with-resources機(jī)制, 它規(guī)定你操作的類只要是實(shí)現(xiàn)了AutoCloseable接口就可以在try語句塊退出的時(shí)候自動調(diào)用close 方法關(guān)閉流資源。

?
1
2
3
4
5
6
public static void tryWithResources() throws IOException {
 try( InputStream ins = new FileInputStream("/home/biezhi/a.txt") ){
 char charStr = (char) ins.read();
 System.out.print(charStr);
 }
}

使用多個(gè)資源

?
1
2
3
4
5
6
try ( InputStream is = new FileInputStream("/home/biezhi/a.txt");
 OutputStream os = new FileOutputStream("/home/biezhi/b.txt")
) {
 char charStr = (char) is.read();
 os.write(charStr);
}

當(dāng)然如果你使用的是非標(biāo)準(zhǔn)庫的類也可以自定義AutoCloseable,只要實(shí)現(xiàn)其close方法即可。

捕獲多個(gè)Exception

當(dāng)我們在操作一個(gè)對象的時(shí)候,有時(shí)候它會拋出多個(gè)異常,像這樣:

?
1
2
3
4
5
6
7
8
try {
 Thread.sleep(20000);
 FileInputStream fis = new FileInputStream("/a/b.txt");
} catch (InterruptedException e) {
 e.printStackTrace();
} catch (IOException e) {
 e.printStackTrace();
}

這樣代碼寫起來要捕獲很多異常,不是很優(yōu)雅,JDK7種允許你捕獲多個(gè)異常:

?
1
2
3
4
5
6
try {
 Thread.sleep(20000);
 FileInputStream fis = new FileInputStream("/a/b.txt");
} catch (InterruptedException | IOException e) {
 e.printStackTrace();
}

并且catch語句后面的異常參數(shù)是final的,不可以再修改/復(fù)制。

處理反射異常

使用過反射的同學(xué)可能知道我們有時(shí)候操作反射方法的時(shí)候會拋出很多不相關(guān)的檢查異常,例如:

?
1
2
3
4
5
6
7
8
9
10
try {
 Class<?> clazz = Class.forName("com.biezhi.apple.User");
 clazz.getMethods()[0].invoke(object);
} catch (IllegalAccessException e) {
 e.printStackTrace();
} catch (InvocationTargetException e) {
 e.printStackTrace();
} catch (ClassNotFoundException e) {
 e.printStackTrace();
}

盡管你可以使用catch多個(gè)異常的方法將上述異常都捕獲,但這也讓人感到痛苦。 JDK7修復(fù)了這個(gè)缺陷,引入了一個(gè)新類ReflectiveOperationException可以幫你捕獲這些反射異常:

?
1
2
3
4
5
6
try {
 Class<?> clazz = Class.forName("com.biezhi.apple.User");
 clazz.getMethods()[0].invoke(object);
} catch (ReflectiveOperationException e){
 e.printStackTrace();
}

文件操作

我們知道在JDK6甚至之前的時(shí)候,我們想要讀取一個(gè)文本文件也是非常麻煩的一件事,而現(xiàn)在他們都變得簡單了, 這要?dú)w功于NIO2,我們先看看之前的做法:

讀取一個(gè)文本文件

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
BufferedReader br = null;
try {
 new BufferedReader(new FileReader("file.txt"));
 StringBuilder sb = new StringBuilder();
 String line = br.readLine();
 while (line != null) {
 sb.append(line);
 sb.append(System.lineSeparator());
 line = br.readLine();
 }
 String everything = sb.toString();
} catch (Exception e){
 e.printStackTrace();
} finally {
 try {
 br.close();
 } catch (IOException e) {
 e.printStackTrace();
 }
}

大家對這樣的一段代碼一定不陌生,但這樣太繁瑣了,我只想讀取一個(gè)文本文件,要寫這么多代碼還要 處理讓人頭大的一堆異常,怪不得別人吐槽Java臃腫,是在下輸了。。。

下面我要介紹在JDK7中是如何改善這些問題的。

Path

Path用于來表示文件路徑和文件,和File對象類似,Path對象并不一定要對應(yīng)一個(gè)實(shí)際存在的文件, 它只是一個(gè)路徑的抽象序列。

要創(chuàng)建一個(gè)Path對象有多種方法,首先是final類Paths的兩個(gè)static方法,如何從一個(gè)路徑字符串來構(gòu)造Path對象:

?
1
2
3
4
Path path1 = Paths.get("/home/biezhi", "a.txt");
Path path2 = Paths.get("/home/biezhi/a.txt");
URI u = URI.create("file:////home/biezhi/a.txt");
Path pathURI = Paths.get(u);

通過FileSystems構(gòu)造

?
1
Path filePath = FileSystems.getDefault().getPath("/home/biezhi", "a.txt");

Path、URI、File之間的轉(zhuǎn)換

?
1
2
3
4
File file = new File("/home/biezhi/a.txt");
Path p1 = file.toPath();
p1.toFile();
file.toURI();

讀寫文件

你可以使用Files類快速實(shí)現(xiàn)文件操作,例如讀取文件內(nèi)容:

?
1
2
byte[] data = Files.readAllBytes(Paths.get("/home/biezhi/a.txt"));
String content = new String(data, StandardCharsets.UTF_8);

如果希望按照行讀取文件,可以調(diào)用

?
1
List<String> lines = Files.readAllLines(Paths.get("/home/biezhi/a.txt"));

反之你想將字符串寫入到文件可以調(diào)用

?
1
Files.write(Paths.get("/home/biezhi/b.txt"), "Hello JDK7!".getBytes());

你也可以按照行寫入文件,F(xiàn)iles.write方法的參數(shù)中支持傳遞一個(gè)實(shí)現(xiàn)Iterable接口的類實(shí)例。 將內(nèi)容追加到指定文件可以使用write方法的第三個(gè)參數(shù)OpenOption:

?
1
2
Files.write(Paths.get("/home/biezhi/b.txt"), "Hello JDK7!".getBytes(),
 StandardOpenOption.APPEND);

默認(rèn)情況Files類中的所有方法都會使用UTF-8編碼進(jìn)行操作,當(dāng)你不愿意這么干的時(shí)候可以傳遞Charset參數(shù)進(jìn)去變更。

當(dāng)然Files還有一些其他的常用方法:

?
1
2
3
4
InputStream ins = Files.newInputStream(path);
OutputStream ops = Files.newOutputStream(path);
Reader reader = Files.newBufferedReader(path);
Writer writer = Files.newBufferedWriter(path);

創(chuàng)建、移動、刪除

創(chuàng)建文件、目錄

?
1
2
3
4
if (!Files.exists(path)) {
 Files.createFile(path);
 Files.createDirectory(path);
}

Files還提供了一些方法讓我們創(chuàng)建臨時(shí)文件/臨時(shí)目錄:

?
1
2
3
4
Files.createTempFile(dir, prefix, suffix);
Files.createTempFile(prefix, suffix);
Files.createTempDirectory(dir, prefix);
Files.createTempDirectory(prefix);

這里的dir是一個(gè)Path對象,并且字符串prefix和suffix都可能為null。 例如調(diào)用Files.createTempFile(null, ".txt")會返回一個(gè)類似/tmp/21238719283331124678.txt

讀取一個(gè)目錄下的文件請使用Files.listFiles.walk方法

復(fù)制、移動一個(gè)文件內(nèi)容到某個(gè)路徑

?
1
2
Files.copy(in, path);
Files.move(path, path);

刪除一個(gè)文件

?
1
Files.delete(path);

小的改進(jìn)

Java8是一個(gè)較大改變的版本,包含了API和庫方面的修正,它還對我們常用的API進(jìn)行很多微小的調(diào)整, 下面我會帶你了解字符串、集合、注解等新方法。

字符串

使用過JavaScript語言的人可能會知道當(dāng)我們將一個(gè)數(shù)組中的元素組合起來變成字符串有一個(gè)方法join, 例如我們經(jīng)常用到將數(shù)組中的字符串拼接成用逗號分隔的一長串,這在Java中是要寫for循環(huán)來完成的。

Java8種添加了join方法幫你搞定這一切:

?
1
String str = String.join(",", "a", "b", "c");

第一個(gè)參數(shù)是分隔符,后面接收一個(gè)CharSequence類型的可變參數(shù)數(shù)組或一個(gè)Iterable。

集合

集合改變中最大的當(dāng)屬前面章節(jié)中提到的Stream API,除此之外還有一些小的改動。

 

類/接口 新方法
Iterable foreach
Collection removeIf
List replaceAll, sort
Map forEach, replace, replaceAll, remove(key, value),
putIfAbsent, compute, computeIf, merge
Iterator forEachRemaining
BitSet stream

 

  • Map中的很多方法對并發(fā)訪問十分重要,我們將在后面的章節(jié)中介紹
  • Iterator提供forEachRemaining將剩余的元素傳遞給一個(gè)函數(shù)
  • BitSet可以產(chǎn)生一個(gè)Stream對象

通用目標(biāo)類型判斷

Java8對泛型參數(shù)的推斷進(jìn)行了增強(qiáng)。相信你對Java8之前版本中的類型推斷已經(jīng)比較熟悉了。 比如,Collections中的方法emptyList方法定義如下:

?
1
static <T> List<T> emptyList();

emptyList方法使用了類型參數(shù)T進(jìn)行參數(shù)化。 你可以像下面這樣為該類型參數(shù)提供一個(gè)顯式的類型進(jìn)行函數(shù)調(diào)用:

?
1
List<Person> persons = Collections.<Person>emptyList();

不過編譯器也可以推斷泛型參數(shù)的類型,上面的代碼和下面這段代碼是等價(jià)的:

?
1
List<Person> persons = Collections.emptyList();

我還是習(xí)慣于這樣書寫。

注解

Java 8在兩個(gè)方面對注解機(jī)制進(jìn)行了改進(jìn),分別為:

  • 可以定義重復(fù)注解
  • 可以為任何類型添加注解

重復(fù)注解

之前版本的Java禁止對同樣的注解類型聲明多次。由于這個(gè)原因,下面的第二句代碼是無效的:

?
1
2
3
4
5
6
@interface Basic {
 String name();
}
@Basic(name="fix")
@Basic(name="todo")
class Person{ }

我們之前可能會通過數(shù)組的做法繞過這一限制:

?
1
2
3
4
5
6
7
8
@interface Basic {
 String name();
}
@interface Basics {
 Basic[] value();
}
@Basics( { @Basic(name="fix") , @Basic(name="todo") } )
class Person{ }

Book類的嵌套注解相當(dāng)難看。這就是Java8想要從根本上移除這一限制的原因,去掉這一限制后, 代碼的可讀性會好很多。現(xiàn)在,如果你的配置允許重復(fù)注解,你可以毫無顧慮地一次聲明多個(gè)同一種類型的注解。 它目前還不是默認(rèn)行為,你需要顯式地要求進(jìn)行重復(fù)注解。

創(chuàng)建一個(gè)重復(fù)注解

如果一個(gè)注解在設(shè)計(jì)之初就是可重復(fù)的,你可以直接使用它。但是,如果你提供的注解是為用戶提供的, 那么就需要做一些工作,說明該注解可以重復(fù)。下面是你需要執(zhí)行的兩個(gè)步驟:

  • 將注解標(biāo)記為@Repeatable
  • 提供一個(gè)注解的容器下面的例子展示了如何將@Basic注解修改為可重復(fù)注解
?
1
2
3
4
5
6
7
8
@Repeatable(Basics.class)
@interface Basic {
 String name();
}
@Retention(RetentionPolicy.RUNTIME)
@interface Basics {
 Basic[] value();
}

完成了這樣的定義之后,Person類可以通過多個(gè)@Basic注解進(jìn)行注釋,如下所示:

?
1
2
3
@Basic(name="fix")
@Basic(name="todo")
class Person{ }

編譯時(shí), Person 會被認(rèn)為使用了 @Basics( { @Basic(name="fix") , @Basic(name="todo")} ) 這樣的形式進(jìn)行了注解,所以,你可以把這種新的機(jī)制看成是一種語法糖, 它提供了程序員之前利用的慣用法類似的功能。為了確保與反射方法在行為上的一致性, 注解會被封裝到一個(gè)容器中。 Java API中的getAnnotation(Class<T> annotationClass)方法會為注解元素返回類型為T的注解。 如果實(shí)際情況有多個(gè)類型為T的注解,該方法的返回到底是哪一個(gè)呢?

我們不希望一下子就陷入細(xì)節(jié)的魔咒,類Class提供了一個(gè)新的getAnnotationsByType方法, 它可以幫助我們更好地使用重復(fù)注解。比如,你可以像下面這樣打印輸出Person類的所有Basic注解:

返回一個(gè)由重復(fù)注解Basic組成的數(shù)組

?
1
2
3
4
5
6
public static void main(String[] args) {
 Basic[] basics = Person.class.getAnnotationsByType(Basic.class);
 Arrays.asList(basics).forEach(a -> {
 System.out.println(a.name());
 });
}

Null檢查

Objects類添加了兩個(gè)靜態(tài)方法isNull和nonNull,在使用流的時(shí)候非常有用。

例如獲取一個(gè)流的所有不為null的對象:

?
1
2
3
Stream.of("a", "c", null, "d")
 .filter(Objects::nonNull)
 .forEach(System.out::println);

Optional

空指針異常一直是困擾Java程序員的問題,也是我們必須要考慮的。當(dāng)業(yè)務(wù)代碼中充滿了if else判斷null 的時(shí)候程序變得不再優(yōu)雅,在Java8中提供了Optional類為我們解決NullPointerException。

我們先來看看這段代碼有什么問題?

?
1
2
3
4
5
6
7
8
9
class User {
 String name;
 public String getName() {
 return name;
 }
}
public static String getUserName(User user){
 return user.getName();
}

這段代碼看起來很正常,每個(gè)User都會有一個(gè)名字。所以調(diào)用getUserName方法會發(fā)生什么呢? 實(shí)際這是不健壯的程序代碼,當(dāng)User對象為null的時(shí)候會拋出一個(gè)空指針異常。

我們普遍的做法是通過判斷user != null然后獲取名稱

?
1
2
3
4
5
6
public static String getUserName(User user){
 if(user != null){
 return user.getName();
 }
 return null;
}

但是如果對象嵌套的層次比較深的時(shí)候這樣的判斷我們需要編寫多少次呢?難以想象

處理空指針

使用Optional優(yōu)化代碼

?
1
2
3
4
public static String getUserNameByOptional(User user) {
 Optional<String> userName = Optional.ofNullable(user).map(User::getName);
 return userName.orElse(null);
}

當(dāng)user為null的時(shí)候我們設(shè)置UserName的值為null,否則返回getName的返回值,但此時(shí)不會拋出空指針。

在之前的代碼片段中是我們最熟悉的命令式編程思維,寫下的代碼可以描述程序的執(zhí)行邏輯,得到什么樣的結(jié)果。 后面的這種方式是函數(shù)式思維方式,在函數(shù)式的思維方式里,結(jié)果比過程更重要,不需要關(guān)注執(zhí)行的細(xì)節(jié)。程序的具體執(zhí)行由編譯器來決定。 這種情況下提高程序的性能是一個(gè)不容易的事情。

我們再次了解下Optional中的一些使用方法

Optional方法

創(chuàng)建 Optional 對象

你可以通過靜態(tài)工廠方法Optional.empty,創(chuàng)建一個(gè)空的Optional對象:

?
1
Optional<User> emptyUser = Optional.empty();

創(chuàng)建一個(gè)非空值的Optional

?
1
Optional<User> userOptional = Optional.of(user);

如果user是一個(gè)null,這段代碼會立即拋出一個(gè)NullPointerException,而不是等到你試圖訪問user的屬性值時(shí)才返回一個(gè)錯(cuò)誤。

可接受null的Optional

?
1
Optional<User> ofNullOptional = Optional.ofNullable(user);

使用靜態(tài)工廠方法Optional.ofNullable,你可以創(chuàng)建一個(gè)允許null值的Optional對象。

如果user是null,那么得到的Optional對象就是個(gè)空對象,但不會讓你導(dǎo)致空指針。

使用map從Optional對象中提取和轉(zhuǎn)換值

?
1
2
Optional<User> ofNullOptional = Optional.ofNullable(user);
Optional<String> userName = ofNullOptional.map(User::getName);

這種操作就像我們之前在操作Stream是一樣的,獲取的只是User中的一個(gè)屬性。

默認(rèn)行為及解引用Optional對象

我們決定采用orElse方法讀取這個(gè)變量的值,使用這種方式你還可以定義一個(gè)默認(rèn)值, 遭遇空的Optional變量時(shí),默認(rèn)值會作為該方法的調(diào)用返回值。 Optional類提供了多種方法讀取 Optional實(shí)例中的變量值。

  • get()是這些方法中最簡單但又最不安全的方法。如果變量存在,它直接返回封裝的變量 值,否則就拋出一個(gè)NoSuchElementException異常。所以,除非你非常確定Optional 變量一定包含值,否則使用這個(gè)方法是個(gè)相當(dāng)糟糕的主意。此外,這種方式即便相對于 嵌套式的null檢查,也并未體現(xiàn)出多大的改進(jìn)。
  • orElse(T other)是我們在代碼清單10-5中使用的方法,正如之前提到的,它允許你在 Optional對象不包含值時(shí)提供一個(gè)默認(rèn)值。
  • orElseGet(Supplier<? extends T> other)是orElse方法的延遲調(diào)用版,Supplier 方法只有在Optional對象不含值時(shí)才執(zhí)行調(diào)用。如果創(chuàng)建默認(rèn)值是件耗時(shí)費(fèi)力的工作, 你應(yīng)該考慮采用這種方式(借此提升程序的性能),或者你需要非常確定某個(gè)方法僅在 Optional為空時(shí)才進(jìn)行調(diào)用,也可以考慮該方式(這種情況有嚴(yán)格的限制條件)。
  • orElseThrow(Supplier<? extends X> exceptionSupplier)和get方法非常類似, 它們遭遇Optional對象為空時(shí)都會拋出一個(gè)異常,但是使用orElseThrow你可以定制希 望拋出的異常類型。
  • ifPresent(Consumer<? super T>)讓你能在變量值存在時(shí)執(zhí)行一個(gè)作為參數(shù)傳入的 方法,否則就不進(jìn)行任何操作。

當(dāng)前除了這些Optional類也具備一些和Stream類似的API,我們先看看Optional類方法:

 

方法 描述
empty 返回一個(gè)空的 Optional 實(shí)例
get 如果該值存在,將該值用Optional包裝返回,否則拋出一個(gè)NoSuchElementException異常
ifPresent 如果值存在,就執(zhí)行使用該值的方法調(diào)用,否則什么也不做
isPresent 如果值存在就返回true,否則返回false
filter 如果值存在并且滿足提供的謂詞,就返回包含該值的 Optional 對象;
否則返回一個(gè)空的Optional對象
map 如果值存在,就對該值執(zhí)行提供的 mapping 函數(shù)調(diào)用
flatMap 如果值存在,就對該值執(zhí)行提供的 mapping 函數(shù)調(diào)用,
返回一個(gè) Optional 類型的值,否則就返 回一個(gè)空的Optional對象
of 將指定值用 Optional 封裝之后返回,如果該值為null,則拋出一個(gè)NullPointerException異常
ofNullable 將指定值用 Optional 封裝之后返回,如果該值為 null,則返回一個(gè)空的Optional對象
orElse 如果有值則將其返回,否則返回一個(gè)默認(rèn)值
orElseGet 如果有值則將其返回,否則返回一個(gè)由指定的 Supplier 接口生成的值
orElseThrow 如果有值則將其返回,否則拋出一個(gè)由指定的 Supplier 接口生成的異常

 

用Optional封裝可能為null的值

目前我們寫的大部分Java代碼都會使用返回NULL的方式來表示不存在值,比如Map中通過Key獲取值, 當(dāng)不存在該值會返回一個(gè)null。 但是,正如我們之前介紹的,大多數(shù)情況下,你可能希望這些方法能返回一個(gè)Optional對象。 你無法修改這些方法的簽名,但是你很容易用Optional對這些方法的返回值進(jìn)行封裝。

我們接著用Map做例子,假設(shè)你有一個(gè)Map<String, Object>類型的map,訪問由key的值時(shí), 如果map中沒有與key關(guān)聯(lián)的值,該次調(diào)用就會返回一個(gè)null。

?
1
Object value = map.get("key");

使用Optional封裝map的返回值,你可以對這段代碼進(jìn)行優(yōu)化。要達(dá)到這個(gè)目的有兩種方式: 你可以使用笨拙的if-then-else判斷語句,毫無疑問這種方式會增加代碼的復(fù)雜度; 或者你可以采用Optional.ofNullable方法

?
1
Optional<Object> value = Optional.ofNullable(map.get("key"));

每次你希望安全地對潛在為null的對象進(jìn)行轉(zhuǎn)換,將其替換為Optional對象時(shí),都可以考慮使用這種方法。

總結(jié)

以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者使用java8能帶來一定的幫助,如果有疑問大家可以留言交流,謝謝大家對服務(wù)器之家的支持。

參考資料:Java文件IO操作應(yīng)該拋棄File擁抱Paths和Files

原文鏈接:http://biezhi.me/2017/07/21/keep-up-with-java8-features.html

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 久久精品亚洲牛牛影视 | 色倩网站| 欧美老少 | 国产精品成 | 亚洲AV无码国产精品午夜久久 | 精彩国产萝视频在线 | 国产精品怡红院永久免费 | 草逼的视频 | 久久AV国产麻豆HD真实 | 亚洲国产AV无码综合在线 | 国产无限 | 国产成人高清精品免费5388密 | 九九精品视频在线观看 | 国产3p在线| 国内自拍成人网在线视频 | 国产日产国无高清码2020 | 国产久热香蕉在线观看 | 单亲乱l仑在线观看免费观看 | 亚洲天堂免费 | 欧美四级无删版影片 | 国产精品林美惠子在线观看 | 国产视频一区二 | 毛片网站免费观看 | brazzersxxx欧美 | 欧美聚众性派对hdsex | 三级视频中文字幕 | 果冻传媒在线播放观看228集 | 久草在线精彩免费视频 | 精品国产乱码久久久久久人妻 | 福利视频免费 | 天堂伊人| 日本道色综合久久影院 | 亚洲第一色网站 | 亚洲av欧美在我 | 四虎国产欧美成人影院 | 99热国产这里只有精品99 | 无码国产成人777爽死 | 青柠影视在线播放观看高清 | 我强进了老师身体在线观看 | 欧美亚洲国产一区二区三区 | 色网在线视频 |