天天看點

8種開發工具,拒絕加班熬夜

作者:Java進階新手

前言

工欲善其事 必先利其器

身為一個程式員,每天面對的事情就是寫代碼和吹牛逼了。但是總是感覺自己這兩個事情沒有達到一個平衡點,總感覺每天寫代碼的時間太多了,都沒有多少讓自己吹的時間了。不知道大家有沒有這些問題和疑惑呢?

我們已知程式員是最會偷懶的生物!哎!那麼問題來了,那怎麼摸魚時間還是這麼少呢?難道是我們太菜了嗎?不不不,可不要小瞧自己,那會是啥原因嘞?

答案就是,當然是你還沒看這篇文章呗,本文切身感受程式員之痛苦,背後默默吐血整理了一篇文章,現在分享給大家,希望對大家有所幫助。

目錄

  1. 整體預覽圖
  2. JSON解析工具
  3. HTTP網絡請求工具
  4. 字元串處理工具
  5. 集合處理工具
  6. 檔案流處理工具
  7. 加解密工具
  8. JAVA bean 對象轉換工具
  9. 緩存和限流工具

開始上手

整體預覽圖

本文會從圖中分類觸發,介紹相關工具包,并簡單介紹使用。因為本文篇幅有限,是以隻當做是一個引子。具體細節還是都得大家在寫代碼的時候慢慢體會。

8種開發工具,拒絕加班熬夜

JSON 解析工具

json 解析工具在開發中有多常用相信不用我多說了吧,可以說是程式員天天用到的工具,這也是我将它放到第一個來說的原因,下面我們來一起看一下,概括和使用吧,GO! 筆者我用的比較多的是 Fastjson ,它是阿裡開源的一款進行 JSON 解析的工具,用法也是相當簡單。

8種開發工具,拒絕加班熬夜
  1. maven 導入 pom 坐标
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.83</version>
</dependency>           
  1. 下面看怎麼使用
  • JSON 字元串與實體對象互相轉化
// 字元串轉對象
Studen student = JSON.parseObject("{"name":"小明","age":18}", Student.class);
// 對象轉字元串
String str = JSON.toJSONString(student);           
  • JSON 字元串與 JSONObject 互相轉化
JSONObject隻是一種資料結構,可以了解為JSON格式的資料結構(key-value 結構),可以使用put方法給json對象添加元素。JSONObject可以很友善的轉換成字元串,也可以很友善的把其他對象轉換成JSONObject對象
// 字元串轉JSONObject對象
JSONObject jsonObject = JSONObject.parseObject("{"name":"小明","age":18}");
// JSONObject對象轉字元串
String str = jsonObject.toJSONString();           
  • JSON 字元串轉化為 集合類
// 定義解析字元串
String studentListStr = "[{"name":"小明","age":18},{"name":"小牛","age":24}]";
// 解析為 List<Student>
List<Student> studentList = JSON.parseArray(studentListStr, Student.class);
// 定義解析字元串
String studentMapStr = "{"name":"小明","age":18}";
// 解析為 Map<String,String>
Map<String, String> stringStringMap = 
JSONObject.parseObject(studentMapStr, new TypeReference<Map<String, String>>(){});           

fastjson 就介紹到這裡,這裡隻是介紹了簡單的使用,更加詳細的用法請參考官方的文檔,裡面還有更多的用法等着你的哦~~

HTTP 網絡請求工具

除了 JSON 工具,作為一個優秀的網際網路打勞工,不學會網絡請求,怎麼能夠在這個行業占有一席之地呢?HTTP 網絡請求工具你值得擁有~~ 根據我的個人意願,我簡單介紹 httpclient的用法,因為我對這個比較熟悉

8種開發工具,拒絕加班熬夜
  1. maven 導入 pom 坐标
  2. 如何使用
  • GET 請求(無參)
/**
* 無參的 GET 請求
*/
public static void noArgsGetHttp() throws IOException {
    // 定義 httpclient
    CloseableHttpClient httpClient = HttpClientBuilder.create().build();
    // 建立 httpGet
    HttpGet httpGet = new HttpGet("http://www.baidu.com");
    // 定義傳回結果
    CloseableHttpResponse execute = null;
    // 發送執行
    execute = httpClient.execute(httpGet);
    // 擷取傳回值
    HttpEntity entity = execute.getEntity();
    System.out.println("響應狀态為:" + execute.getStatusLine());
    if (Objects.nonNull(entity)) {
        System.out.println("響應内容長度為:" + entity.getContentLength());
        System.out.println("響應内容為:" + EntityUtils.toString(entity));
    }
    // 釋放資源
    if (httpClient != null) {
        httpClient.close();
    }
    if (execute != null) {
        execute.close();
    }
}           
響應狀态為:HTTP/1.1 200 OK 響應内容長度為:-1 響應内容為:
  • GET 請求(有參)
/**
* 有參的 GET 請求
*/
public static void haveArgsGetHttp() throws IOException, URISyntaxException {
    // 定義 httpclient
    CloseableHttpClient httpClient = HttpClientBuilder.create().build();
    // 建立參數清單
    List<NameValuePair> valueParamsList = new ArrayList<>();
    valueParamsList.add(new BasicNameValuePair("studentId","1"));
    // 建立對應請求 Uri
    URI uri = new URIBuilder()
        .setScheme("http")
        .setHost("localhost")
        .setPath("/getStudentInfo")
        .setParameters(valueParamsList)
        .build();
    // 根據 Uri 建立 httpGet
    HttpGet httpGet = new HttpGet(uri);
    // 定義傳回結果
    CloseableHttpResponse execute = null;
    // 發送執行
    execute = httpClient.execute(httpGet);
    // 擷取傳回值
    HttpEntity entity = execute.getEntity();
    System.out.println("響應狀态為:" + execute.getStatusLine());
    if (Objects.nonNull(entity)) {
        System.out.println("響應内容長度為:" + entity.getContentLength());
        System.out.println("響應内容為:" + EntityUtils.toString(entity));
    }
    // 釋放資源
    if (httpClient != null) {
        httpClient.close();
    }
    if (execute != null) {
        execute.close();
    }
}           
  • POST 請求(有參數)
/**
* post 有參數
*/
public static void haveArgsPosthttp() throws IOException {
    // 獲得Http用戶端(可以了解為:你得先有一個浏覽器;注意:實際上HttpClient與浏覽器是不一樣的)
    CloseableHttpClient httpClient = HttpClientBuilder.create().build();
    // 建立Post請求
    HttpPost httpPost = new HttpPost("http://localhost:12345/posttest");
    JSONUtil.Student student = new JSONUtil.Student();
    student.setName("潘曉婷");
    student.setAge(18);

    String jsonString = JSON.toJSONString(student);
    StringEntity entity = new StringEntity(jsonString, "UTF-8");
    // post請求是将參數放在請求體裡面傳過去的;這裡将entity放入post請求體中
    httpPost.setEntity(entity);
    httpPost.setHeader("Content-Type", "application/json;charset=utf8");
    // 響應模型
    CloseableHttpResponse response = httpClient.execute(httpPost);
    // 從響應模型中擷取響應實體
    HttpEntity responseEntity = response.getEntity();
    System.out.println("響應狀态為:" + response.getStatusLine());
    if (responseEntity != null) {
        System.out.println("響應内容長度為:" + responseEntity.getContentLength());
        System.out.println("響應内容為:" + EntityUtils.toString(responseEntity));
    }
    // 釋放資源
    if (httpClient != null) {
        httpClient.close();
    }
    if (response != null) {
        response.close();
    }
}           

啊!這樣一步一步總結下來好累啊,看到這裡的小夥伴們,點一下手裡的小贊。嘿嘿~~ 當然我隻是簡單介紹一下 httpClient 的使用,具體高深的使用方法和配置可以參考其他部落客的詳細介紹,讓我們介紹下一個常用的工具吧。

字元串處理工具

字元串使我們開發中最常見的類型,也是我們最常需要操作的類型了。如果不知道字元串的常用工具,那在寫代碼的時候簡直就是場災難!!!

字元串判空,截取字元串、轉換大小寫、分隔字元串、比較字元串、去掉多餘空格、拼接字元串、使用正規表達式等等。

StringUtils 給我們提供了非常豐富的選擇。我們着重以本類來介紹使用。

  1. 導入maven坐标
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.12.0</version>
</dependency>           
  1. 常用方法介紹
  • 字元串判空 ⭐⭐⭐

(isNotBlank 和 isBlanK)

String str = " ";
// 是否不為空
boolean res1 = StringUtils.isNotBlank(str);
// 是否為空
boolean res2 = StringUtils.isBlank(str);           

(isNotEmpty 和 isEmpty)

String str = " ";
// 是否不為空
boolean res1 = StringUtils.isNotEmpty(str);
// 是否為空
boolean res2 = StringUtils.isEmpty(str);           
優先推薦使用 isBlank 和 isNotBlank 方法,因為 它會把空字元串也考慮進去。
  • 分隔字元串 —— split —— ⭐⭐⭐
String str2 = "1,2,3";
String[] split = StringUtils.split(str2);
System.out.println(Arrays.toString(split));           
相較于 String 的 split 方法來說,StringUtils 的 split 方法不會有空指針異常
  • 判斷是否純數字 —— isNumeric —— ⭐⭐

給定一個字元串,判斷它是否為純數字 可以使用isNumeric方法

String str3 = "1";
boolean numeric = StringUtils.isNumeric(str3);
System.out.println(numeric);           

當然,這個工具類除了上面簡單的三個方法之外們還有其他很多對于我們來說很使用的方法,但是這裡就不一一舉例了, 有興趣的小夥伴們可以看源碼統計一下

集合相關處理工具

哦吼~~看完了字元串的常用工具,重中之重的集合它來了,如果說沒有字元串,我們的程式就無法運作,那麼沒有集合,我們将會是每天在加班的中度過了,出去後将會自豪的說,老闆的車輪胎我也是做了貢獻的。

既然集合工具這麼重要,那麼當然要重點介紹。學會相關工具的使用,真的是能讓我們事半功倍的,真的是能讓摸魚時間大大增加的,不信你看看。

8種開發工具,拒絕加班熬夜

Collections

它是 JAVA 的集合工具包,内部包括了很多我們常用的方法,下圖展示了其中一些,友善食用!

8種開發工具,拒絕加班熬夜
  • 排序—— sort—— ⭐⭐⭐

在我們日常開發工作中,經常會遇到一些集合排序的需求。sort 就可以很好的幫助我們做好這一點

List<Integer> list = new ArrayList<>();
list.add(2);
list.add(1);
list.add(3);
//升序
Collections.sort(list);
System.out.println(list);
//降序
Collections.reverse(list);
System.out.println(list);           
結果: [1, 2, 3] [3, 2, 1]
  • 擷取最大最小值 —— max / min ——⭐⭐⭐

最大值最小值是我們操作集合最常見的方法了吧!!Collections 就有現成的方法幫助我們實作

// 最大值最小值
List<Integer> intList = new ArrayList<>();
intList.add(1);
intList.add(2);
intList.add(3);

Integer max = Collections.max(intList);
Integer min = Collections.min(intList);

System.out.println("集合元素最大值:" + max);
System.out.println("集合元素最小值:" + min);           
結果:集合元素最大值:3 集合元素最小值:1
  • 轉換線程安全集合—— synchronizedList ——⭐⭐⭐

在多線程狀态下,普通集合會産生并發問題,比如 ArrayList 并發添加會産生空值情況,這時我們又不想改動我們之前的集合怎麼辦? 我們簡單的通過Collections 的線程安全轉化就可以做到了,簡簡單單一行代碼就可以做到!是不是友善的很!

List<Integer> synchronizedList = Collections.synchronizedList(intList);           

當然,Collections 還有很多有用和有趣的方法等着我們去探索,隻是隻是作為了一個抛轉引玉的效果,就不過多的贅述了。

CollectionUtils

我最常用的便是 CollecionUtils,它是 apache 開源的工具包。它的功能可以說是相當強大的,不信你可以往下看看,反正你能想到的集合操作基本上它都有。

8種開發工具,拒絕加班熬夜
  • 集合判空—— isNotEmpty——⭐⭐⭐

我們最常用的集合方法,沒有之一,必須掌握它!!

List<String> stringList = new ArrayList<>();
boolean notEmpty = CollectionUtils.isNotEmpty(stringList);
System.out.println("集合不是空的嗎?"+ notEmpty);           
集合不是空的嗎?false
  • 交集/并集/補集/差集——⭐⭐⭐

在開發中,經常需要将多集合進行交并補等的數學操作,不會還是傻傻的子集寫循環處理吧!那樣還能有摸魚的時間嗎?下面就是大大提升效率的工具!!!

List<Integer> list1 = new ArrayList<>();
list1.add(1);
list1.add(2);
list1.add(3);
List<Integer> list2 = new ArrayList<>();
list2.add(3);
list2.add(4);
// 擷取并集
Collection<Integer> union = CollectionUtils.union(list1, list2);
System.out.println(union);

// 擷取交集
Collection<Integer> intersection = CollectionUtils.intersection(list1, list2);
System.out.println(intersection);

//擷取交集的補集
Collection<Integer> disjunctionList = CollectionUtils.disjunction(list1, list2);
System.out.println(disjunctionList);

// 擷取差集
Collection<Integer> subtract = CollectionUtils.subtract(list1, list2);
System.out.println(subtract);           

兩集合并集: [1, 2, 3, 4]

兩集合交集: [3]

兩集合交集的補集:[1, 2, 4]

兩集合差集: [1, 2]

  • 判斷兩集合是否相等——isEqualCollection——⭐⭐⭐
// 判斷兩集合是否相等
List<Integer> list3 = new ArrayList<>();
list1.add(3);
list1.add(4);
List<Integer> list4 = new ArrayList<>();
list2.add(3);
list2.add(4);
boolean equalCollection = CollectionUtils.isEqualCollection(list3, list4);
System.out.println("兩集合相等嗎?:" + equalCollection);           
兩集合相等嗎?:true

Lists

最後在集合的工具類中再補充一個 google 官方的java包,裡面有很多我們想不到的超級友善的小工具,既然是說集合,我們說一下它裡面的 Lists 類,也是超級好用的!

8種開發工具,拒絕加班熬夜
  • 快速初始化集合——newArrayList——⭐⭐⭐

相信大家在開發中,都有過初始化集合的需要吧!那麼我們一般都是 建立一個 ArrayList 然後一個一個 add 進去,現在告訴大家,不用這麼麻煩,一個方法建立帶初始化全搞定,真香警告!!

// 快速初始化集合
ArrayList<Integer> integers1 = Lists.newArrayList(1, 2, 3);
System.out.println(integers1);           
[1, 2, 3]
  • 集合分頁——partition——⭐⭐⭐

有時候我們想将我們的大集合分散成為一些小集合,我們又不想手動操作怎麼辦?這些痛點肯定已經有人幫助我們想好了!!來來來,介紹一下!!

// 數組分頁
ArrayList<Integer> list7 = Lists.newArrayList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
List<List<Integer>> partition = Lists.partition(list7, 5);
System.out.println(partition);           
[ [1, 2, 3, 4, 5], [6, 7, 8, 9, 10] ]

介紹完了 集合相關的操作類,下面我們也要介紹一下檔案流相關操作,這個操作在開發中也是經常用到的。

檔案流處理工具

相信我們在工作已經厭煩了,經常寫** IO 流相關的類了吧。經常簡單的讀取和寫入一個檔案,我們需要大費周章的去定義一些 InputStream 和 OutputStream **等,感覺有一種殺雞用牛刀的錯覺。 介紹一個檔案操作工具類,省去大量操作和關閉行為,超級好用,平常我可是經常用的。

8種開發工具,拒絕加班熬夜
  • 将資訊寫入檔案——writeByteArrayToFile——⭐⭐⭐

最最常用的操作,怎麼樣很簡單吧,就隻用一行代碼,秒殺!

// 将 test 寫入 test.txt 檔案
FileUtils.writeByteArrayToFile(new File("C:\Users\test.txt"), "test".getBytes());           
  • 從檔案讀取資訊——readFileToByteArray——⭐⭐⭐

知道了怎麼往檔案裡寫東西,他的好兄弟讀取我們也得知道啊!

// 讀取 test.txt 檔案
byte[] bytes = FileUtils.readFileToByteArray(new File("D:\Users\test.txt"));
System.out.println("讀取到的文本為:" + new String(bytes));           
讀取到的文本為:test

API 很多,我也不能一一為大家介紹,深入的了解還需要大家去,熟練地運用起來,并且是不是的去看看官方的文檔,查漏補缺,相信你也可以見識到很多讓你歎為觀止的方法。

加解密工具類

平常我們經常會遇到比如對使用者的密碼進行加密 (MD5) ,校驗接口的簽名 (sha256) 加密等等 用到加密的場景雖然不是很多,但是有這樣的工具何樂而不為呢?

因為常用的加密方法并不多,這裡介紹兩個方法給大家,想知道其他更多用法的夥伴們,去自己探索吧

8種開發工具,拒絕加班熬夜
// MD5 加密
String MD5 = DigestUtils.md2Hex("123");
System.out.println("加密後的結果:" + MD5);

//  sha(安全雜湊演算法) 加密
String sha256Hex = DigestUtils.sha256Hex("123");
System.out.println("sha256加密後:" + sha256Hex);           

MD5加密後的結果:ef1fedf5d32ead6b7aaf687de4ed1b71

sha256加密後:a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e3

JAVA bean 對象轉換工具

說到這裡,我們來說一下對象轉化的工具,在開發中有些規範,比如DTO、DO、VO等等,之間,如果我們需要轉換,單純的我們要一個一個的 set 值,真是一項苦逼的活。

8種開發工具,拒絕加班熬夜

BeanUtils

java bean對象的相關轉化,我在這裡介紹兩個 ,一個 是大家都非常熟悉的 BeanUtils,還有一個就是我平常在開發中經常使用的 MapStruct 和 BeanUtils 最常用的莫過于對象的的拷貝了 。 不過面對需要深拷貝的對象大家要注意了,這裡并不推薦大家使用此工具去實作

Student student = new Student();
student.setName("小明");
student.setAge(18);
Student newStudent = new Student();
BeanUtils.copyProperties(student,newStudent);
System.out.println(newStudent);           
Student(name=小明, age=18)

MapStruct

下面我們重點說一下 MapStruct 這個轉化,BeanUtils 就是一個大老粗,隻能同屬性映射,或者在屬性相同的情況下,允許被映射的對象屬性少。

但當遇到被映射的屬性資料類型被修改或者被映射的字段名被修改,則會導緻映射失敗。而 mapstruct 就是一個巧媳婦兒了。

她心思細膩,把我們可能會遇到的情況都給考慮到了(要是我也能找一個這樣的媳婦兒該多好,内心笑出了豬聲)

  1. 首先啥都不用想上來我們先把該導的包導進去
<dependency>
	<groupId>org.mapstruct</groupId>
	<!-- jdk8以下就使用mapstruct -->
	<artifactId>mapstruct-jdk8</artifactId>
	<version>1.2.0.Final</version>
</dependency>
<dependency>
	<groupId>org.mapstruct</groupId>
	<artifactId>mapstruct-processor</artifactId>
	<version>1.2.0.Final</version>
</dependency>           
  1. 用一下
  • 對象之間字段完全相同

第一步:定義好我們的基礎類

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student{
	private String name;
	private Integer age ;
}

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Teacher{
	private String name;
	private Integer age ;
}           

第二步:接下來定義一個接口,但是注意不需要實作,他就呢能夠幫我們轉化很神奇的

@Mapper
public interface UserCovertToTeacher {
    UserCovertToTeacher INSTANCE = Mappers.getMapper(UserCovertToTeacher.class);
    Teacher toCovert(Student student);
}           

最後一步:在代碼中調用,聰明的小夥伴看下面代碼,一下就明白了,就是這麼簡單

Student student = new Student();
student.setName("小明");
student.setAge(18);
Teacher teacher = UserCovertToTeacher.INSTANCE.toCovert(student);
System.out.println(teacher);           
Teacher(name=小明, age=18)
  • 對象之間字段存在不相同情況

我們介紹了完全兩個類字段相同的情況,那麼,更加令我們頭疼的 有多個字段名字不同但是有對應關系應該怎麼搞呢?

我們進階介紹一下,如何處理這種參數名不同的對應關系 目前假設我們新定義一個微信類,我們的學生要注冊到微信上,我們就要将學生對象轉化為微信對象

@Data
@AllArgsConstructor
@NoArgsConstructor
static class WeiXin{
	private String username;
	private Integer age ;
}           

但是我們發現和我們上面的學生類,的名字參數名不同,怎麼對應過去的? 答案就是在對方法上配置

@Mapper 
public interface UserCovertToWeinxin {
    UserCovertToWeinxin INSTANCE = Mappers.getMapper(UserCovertToWeinxin.class);
	// 配置字段映射規則
    @Mapping(source = "name",target = "username")
    BeanUtilTest.WeiXin toCovert(BeanUtilTest.Student student);
}           
Student student = new Student();
student.setName("小明");
student.setAge(18);
WeiXin weiXin = UserCovertToWeinxin.INSTANCE.toCovert(student);
System.out.println(weiXin);           
WeiXin(username=小明, age=18)

這麼簡單的兩個小例子可包含不了 MapStruct這麼強大的功能,不管是日期格式化、還是表達式解析、還是深拷貝,都能一一搞定,隻是限于本篇文章,無法跟大家說了。想想都很傷心呢! 但是還是那句話,抛磚引玉麼!剩下的就交給聰明的小夥伴們了!

緩存和限流器工具

最後一小節,我給大家帶來我的珍藏,壓箱底的東西要拿出來了,大家還不快快點贊收藏,記好,錯過了,可就沒有下家店了。

我也是在 guava 中發現了很多好用的工具的 首先介紹緩存工具,開發中,我們常用的記憶體緩存,也就常常是定義一個 Map 去存放,但是單純的 Map 隻能存和取,确無法做到,緩存過期、緩存淘汰,和相關通知等等複雜操作。

我們有必要學習掌握一種工具,能夠 Cover 上面所有情況的緩存工具,有需求就有工具類,永遠記住。!!!,Cache 登場了,還是老規矩,先看看它怎麼用吧。

Cache

定義一個簡單定時過期的緩存

Cache<String, String> cache = CacheBuilder.newBuilder()
                .expireAfterWrite(10, TimeUnit.SECONDS)
                .build();
// 放入緩存
cache.put("小明","活着");
Thread.sleep(10000);
// 從緩存拿取值
System.out.println(cache.getIfPresent("小明"));           
null

看到沒,結果顯而易見,超過了緩存時間,就會自己釋放。嘿嘿。

定義一個緩存符合以下限制:

  • 限制通路并發
  • 設定初始化容量
  • 限制緩存數量上限
Cache<String, String> cache = CacheBuilder.newBuilder()、
		    // 最大容量,超過按照 LRU 淘汰
                .maximumSize(100)
		    // 初始容量
                .initialCapacity(10)
		    // 并發等級 10
                .concurrencyLevel(10)
                .expireAfterWrite(10, TimeUnit.SECONDS)
                .build();           

兩個小例子,大家看明白了沒有,真正的幹貨,還不趕緊用起來。

除了這個,一個限流器也是常常需要的,我們總不能自記去寫一個限流器吧,需要考慮的太多,性能還不行哎!那就用接下來介紹的這個工具

RateLimiter

限流器大家在并發場景下經常會遇到,最簡單的實作限流就是令牌桶算法,原理很簡單,但是具體實作是很複雜的,RateLimiter 幫助我們解決這一點,隻需要調用簡單的 API 就能實作并發限流

定義限流器,每1秒鐘通過 1 個請求

RateLimiter rateLimiter = RateLimiter.create(1,1,TimeUnit.SECONDS);           

并發兩個去擷取,看一下結果吧,是不是符合我們的預期

new Thread(()->{
	System.out.println("線程 1 擷取到執行權限了嗎?" + rateLimiter.tryAcquire());
}).start();

new Thread(()->{
	System.out.println("線程 2 擷取到執行權限了嗎?" + rateLimiter.tryAcquire());
}).start();           

線程 1 擷取到執行權限了嗎?true

線程 2 擷取到執行權限了嗎?false

怎麼樣,是不是隻能有一個通過,簡單例子說明問題。 具體用法還得大家在實際開發中具體體會,筆者在這裡就不多BB了!!留着時間大家趕快去練習吧。争取成為一個 API 調用高手。

修煉完成

經過上面這麼多的講解、案例和對知識的思考,相信大家已經初步掌握了線程池的使用方法和細節,以及對原理運作流程的掌握, 如果你覺得本文對你有一定的啟發,引起了你的思考。 點贊、轉發、收藏,下次你就能很快的找到我喽!

8種開發工具,拒絕加班熬夜