## 導讀
唐宋八大家之一歐陽修在《賣油翁》中寫道:
> 翁取一葫蘆置于地,以錢覆其口,徐以杓酌油瀝之,自錢孔入,而錢不濕。因曰:我亦無他,唯手熟爾。
編寫代碼的”老司機”也是如此,”老司機”之所以被稱為”老司機”,來由也是”無他,唯手熟爾”。編碼過程中踩過的坑多了,牟取的編碼經歷也就多了,結算的編碼技能也就更多了。結算的編碼技能多了,凡事又或許舉一反三,編碼的速度天然就上來了。筆者從數據組織的角度,收拾了一些Java編程技能,以供大家吸取參考。
## 1.採用HashSet判斷主鍵是否存在
HashSet實現Set接口,由哈希表(實質上是HashMap)支持,但不擔保set 的迭代次序,并許可採用null元素。HashSet的時間復雜度跟HashMap一致,假如沒有哈希沖突則時間復雜度為O(1),假如存在哈希沖突則時間復雜度不過份O(n)。所以,在日常編碼中,可以採用HashSet判斷主鍵是否存在。
**案例:**給定一個字符串(不一定全為字母),請回去第一個重復顯露的字符。
“`java
** 查找第一個重復字符 *
public static Character findFirstRepeatedChar(String string) {
查驗空字符串
if (Objects.isNull(string) || string.isEmpty()) {
return null;
}
查找重復字符
char[] charArray = string.toCharArray();
Set charSet = ne HashSet(charArray.length);
for (char ch charArray) {
if (charSet.contains(ch)) {
return ch;
}
charSet.add(ch);
}
默認返回為空
return null;
}
“`
其中,由于Set的add函數有個特性——如果添加的元素已經再集合中存在,則會返回false。可以簡化代碼為:
“`java
if (!charSet.add(ch)) {
return ch;
}
“`
## 2.使用HashMap存取鍵值映射關系
簡單來說,HashMap由數組和鏈表組成的,數組是HashMap的主體,鏈表則是主要為了解決哈希沖突而存在的。如果定位到的數組位置不含鏈表,那么查找、添加等操作很快,僅需一次尋址即可,其時間復雜度為O(1);如果定位到的數組包含鏈表,對于添加操作,其時間復雜度為O(n)——首先遍歷鏈表,存在即覆蓋,不存在則新增;對于查找操作來講,仍需要遍歷鏈表,然后通過key對象的equals方法逐一對比查找。從性能上考慮,HashMap中的鏈表出現越少,即哈希沖突越少,性能也就越好。所以,在日常編碼中,可以使用HashMap存取鍵值映射關系。
**案例:**給定菜單記錄列表,每條菜單記錄中包含父菜單標識(根菜單的父菜單標識為null),構建出整個菜單樹。
“`java
** 菜單DO類 *
Setter
Getter
ToString
public static class MenuDO {
** 菜單標識 *
private Long ;
** 菜單父標識 *
private Long parentId;
** 菜單名稱 *
private String name;
** 菜單鏈接 *
private String url;
}
** 菜單VO類 *
Setter
Getter
ToString
public static class MenuVO {
** 菜單標識 *
private Long ;
** 菜單名稱 *
private String name;
** 菜單鏈接 *
private String url;
** 子菜單列表 *
private List childList;
}
** 構建菜單樹函數 *
public static List buildMenuTree(List menuList) {
檢查列表為空
if (CollectionUtils.isEmpty(menuList)) {
return Collections.emptyList();
}
依次處理菜單
int menuSize = menuList.size();
List rootList = ne ArrayList(menuSize);
Map menuMap = ne HashMap(menuSize);
for (MenuDO menuDO menuList) {
賦值菜單對象
Long menuId = menuDO.getId();
MenuVO menu = menuMap.get(menuId);
if (Objects.isNull(menu)) {
menu = ne MenuVO();
menu.setChildList(ne ArrayList());
menuMap.put(menuId, menu);
}
menu.setId(menuDO.getId());
menu.setName(menuDO.getName());
menu.setUrl(menuDO.getUrl());
依據父標識處置
Long parentId = menuDO.getParentId();
if (Objects.nonNull(parentId)) {
構建父菜單對象
MenuVO parentMenu = menuMap.get(parentId);
if (Objects.isNull(parentMenu)) {
parentMenu = ne MenuVO();
parentMenu.setId(parentId);
parentMenu.setChildList(ne ArrayList());
menuMap.put(parentId, parentMenu);
}
添加子菜單對象
parentMenu.getChildList().add(menu);
} else {
添加根菜單對象
rootList.add(menu);
}
}
返回根菜單列表
return rootList;
}
“`
## 3.使用ThreadLocal存儲線程專有對象
ThreadLocal提供了線程專有對象,可以在整個線程生命周期中隨時取用,極大地方便了一些邏輯的實現。
常見的ThreadLocal用法主要有兩種:
1. 保存線程上下文對象,避免多層級參數傳遞;
2. 保存非線程安全對象,避免多線程并發調用。
### 3.1.保存線程上下文對象,避免多層級參數傳遞
這里,以PageHelper插件的源代碼中的分頁參數設置與使用為例說明。
**設置分頁參數代碼:**
“`java
** 分頁方法類 *
public abstract class PageMethod {
** 本地分頁 *
protected static final ThreadLocal LOCAL_PAGE = ne ThreadLocal();
** 設置分頁參數 *
protected static vo setLocalPage(Page page) {
LOCAL_PAGE.set(page);
}
** 獲取分頁參數 *
public static Page getLocalPage() {
return LOCAL_PAGE.get();
}
** 開始分頁 *
public static Page startPage(int pageNum, int pageSize, boolean count, Boolean reasonable, Boolean pageSizeZero) {
Page page = ne Page(pageNum, pageSize, count);
page.setReasonable(reasonable);
page.setPageSizeZero(pageSizeZero);
Page oldPage = getLocalPage();
if (oldPage != null
}
setLocalPage(page);
return page;
}
}
“`
**使用分頁參數代碼:**
“`java
** 虛輔助方言類 *
public abstract class AbstractHelperDialect extends AbstractDialect implements Constant {
** 獲取本地分頁 *
public Page getLocalPage() {
return PageHelper.getLocalPage();
}
** 獲取分頁SQL *
Overre
public String getPageSql(MappedStatement ms, BoundSql boundSql, Object parameterObject, RoBounds roBounds, CacheKey pageKey) {
String sql = boundSql.getSql();
Page page = getLocalPage();
String orderBy = page.getOrderBy();
if (StringUtil.isNotEmpty(orderBy)) {
pageKey.update(orderBy);
sql = OrderByParser.converToOrderBySql(sql, orderBy);
}
if (page.isOrderByOnly()) {
return sql;
}
return getPageSql(sql, page, pageKey);
日棒和局 運彩 }
…
}
“`
**採用分頁插件代碼:**
“`java
** 查詢用戶函數 *
public PageInfo queryUser(UserQuery userQuery, int pageNum, int pageSize) {
PageHelper.startPage(pageNum, pageSize);
List userList = userDAO.queryUser(userQuery);
PageInfo pageInfo = ne PageInfo(userList);
return pageInfo;
}
“`
如果要把分頁參數通過函數參數逐級傳給查詢語句,除非修改MyBatis相關接口函數,否則是不可能實現的。
### 3.2.保存非線程安全對象,避免多線程并發調用
在寫日期格式化工具函數時,首先想到的寫法如下:
“`java
** 日期模式 *
private static final String DATE_PATTERN = “yyyy-MM-dd”;
** 格式化日期函數 *
public static String formatDate(Date date) {
return ne SimpleDateFormat(DATE_PATTERN).format(date);
}
“`
其中,每次調用都要初始化DateFormat導致性能較低,把DateFormat定義成常量后的寫法如下:
“`java
** 日期格式 *
private static final DateFormat DATE_FORMAT = ne SimpleDateFormat(“yyyy-MM-dd”);
** 格式化日期函數 *
public static String formatDate(Date date) {
return DATE_FORMAT.format(date);
}
“`
由于SimpleDateFormat是非線程安全的,當多線程同時調用formatDate函數時,會導致返回結果與預期不一致。如果采用ThreadLocal定義線程專有對象,優化后的代碼如下:
“`java
** 本地日期格式 *
private static final ThreadLocal LOCAL_DATE_FORMAT = ne ThreadLocal() {
Overre
protected DateFormat initialValue() {
return ne SimpleDateFormat(“yyyy-MM-dd”);
}
};
** 格式化日期函數 *
public static String formatDate(Date date) {
return LOCAL_DATE_FORMAT.get().format(date);
}
“`
這是在沒有線程安全的日期格式化工具類之前的實現方法。在JDK8以后,建議使用DateTimeFormatter代替SimpleDateFormat,因為SimpleDateFormat是線程不安全的,而DateTimeFormatter是線程安全的。當然,也可以采用第三方提供的線程安全日期格式化函數,比如apache的DateFormatUtils工具類。
**注意:**ThreadLocal有一定的內存泄露的風險,盡量在業務代碼結束前調用remove函數進行數據清除。
## 4.使用Pair實現成對結果的返回
在CC++語言中,Pair(對)是將兩個數據類型組成一個數據類型的容器,例如stdpair。
Pair重要有兩種用處:
1. 把key和value放在一起成對處置,重要用于Map中回去名值對,例如Map中的Entry類;
2. 當一個函數需求回去兩個結局時,可以採用Pair來避免定義過多的數據模子類。
運彩走地第一種用處對照常見,這里重要說明第二種用處。
### 4.1.定義模子類實現成對結局的回去
**函數實今世碼:**
“`java
** 點和間隔類 *
Setter
Getter
ToString
AllArgsConstructor
public static class PointAndDistance {
** 點 *
private Point point;
** 間隔 *
private Double distance;
}
** 獲取近期點和間隔 *
public static PointAndDistance getNearestPointAndDis運動投注tance(Point point, Point[] points) {
查驗點數組為空
if (ArrayUtils.isEmpty(points)) {
return null;
}
獲取近期點和間隔
Point nearestPoint = points;
double nearestDistance = getDistance(point, points);
for (int i = 1; i < points.length; i++) {
double distance = getDistance(point, point);
if (distance < nearestDistance) {
nearestDistance = distance;
nearestPoint = point;
}
}
返回最近點和距離
return ne PointAndDistance(nearestPoint, nearestDistance);
}
“`
**函數使用案例:**
“`java
Point point = …;
Point[] points = …;
PointAndDistance pointAndDistance = getNearestPointAndDistance(point, points);
if (Objects.nonNull(pointAndDistance)) {
Point point = pointAndDistance.getPoint();
Double distance = pointAndDistance.getDistance();
…
}
“`
### 4.2.使用Pair類實現成對結果的返回
在JDK中,沒有提供原生的Pair數據結構,也可以使用MapEntry代替。不過,Apache的mons-lang3包中的Pair類更為好用,下面便以Pair類進行舉例說明。
**函數實現代碼:**
“`java
** 獲取最近點和距離 *
public static Pair getNearestPointAndDistance(Point point, Point[] points) {
檢查點數組為空
if (ArrayUtils.isEmpty(points)) {
return null;
}
獲取最近點和距離
Point nearestPoint = points;
double nearestDistance = getDistance(point, points);
for (int i = 1; i < points.length; i++) {
double distance = getDistance(point, point);
if (distance < nearestDistance) {
nearestDistance = distance;
nearestPoint = point;
}
}
返回最近點和距離
return Pair.of(nearestPoint, nearestDistance);
}
“`
**函數使用案例:**
“`java
Point point = …;
Point[] points = …;
Pair pair = getNearestPointAndDistance(point, points);
if (Objects.nonNull(pair)) {
Point point = pair.getLeft();
Double distance = pair.getRight();
…
}
“`
## 5.定義Enum類實現取值和描述
在C++、Java等計算機編程語言中,枚舉類型(Enum)是一種特殊數據類型,能夠為一個變量定義一組預定義的常量。在使用枚舉類型的時候,枚舉類型變量取值必須為其預定義的取值之一。
### 5.1.用class關鍵字實現的枚舉類型
在JDK5之前,Java語言不支持枚舉類型,只能用類(class)來模擬實現枚舉類型。
“`java
** 訂單狀態枚舉 *
public final class OrderStatus {
** 屬性相關 *
** 狀態取值 *
private final int value;
** 狀態描述 *
private final String description;
** 常量相關 *
** 已創建(1) *
public static final OrderStatus CREATED = ne OrderStatus(1, "已創建");
** 進行中(2) *
public static final OrderStatus PROCESSING = ne OrderStatus(2, "進行中");
** 已完成(3) *
public static final OrderStatus FINISHED = ne OrderStatus(3, "已完成");
** 構造函數 *
private OrderStatus(int value, String description) {
this.value = value;
this.description = description;
}
** 獲取狀態取值 *
public int getValue() {
return value;
}
** 獲取狀態描述 *
public String getDescription() {
return description;
}
}
“`
### 5.2.用enum關鍵字實現的枚舉類型
JDK5提供了一種新的類型——Java的枚舉類型,關鍵字enum可以將一組具名的值的有限集合創建為一種新的類型,而這些具名的值可以作為常量使用,這是一種非常有用的功能。
“`java
** 訂單狀態枚舉 *
public enum OrderStatus {
** 常量相關 *
** 已創建(1) *
CREATED(1, "已創建"),
** 進行中(2) *
PROCESSING(2, "進行中"),
** 已完成(3) *
FINISHED(3, "已完成");
** 屬性相關 *
** 狀態取值 *
private final int value;
** 狀態描述 *
private final String description;
** 構造函數 *
private OrderStatus(int value, String description) {
this.value = value;
this.description = description;
}
** 獲取狀態取值 *
public int getValue() {
return value;
}
** 獲取狀態描述 *
public String getDescription() {
return description;
}
}
“`
其實,Enum類型就是一個語法糖,編譯器幫我們做了語法的解析和編譯。通過反編譯,可以看到Java枚舉編譯后實際上是生成了一個類,該類繼承了 java.lang.Enum,并增添了values()、valueOf()等枚舉類型通用想法。
## 6.定義Holder類實現參數的輸出
在許多語言中,函數的參數都有輸入(in)、輸出(out)和輸入輸出(inout)之分。在CC++語言中,可以用對象的引用(
** 組成函數 *
public LongHolder() {}
** 組成函數 *
public LongHolder(long value) {
this.value = value;
}
}
“`
**Holder類採用案例:**
“`java
** 靜態常量 *
** 頁面數目 *
private static final int PAGE_COUNT = 100;
** 最大數目 *
private static final int MAX_COUNT = 1000;
** 處置過時訂單 *
public vo handleExpiredOrder() {
LongHolder minIdHolder = ne LongHolder(0L);
for (int pageIndex = 0; pageIndex = PAGE_SIZE;
}
“`
實在,可以實現一個泛型支撐類,實用于更多的數據類型。
## 7.定義Union類實現數據體的共存
在CC++語言中,聯盟體(union),又稱共用體,相似組織體(struct)的一種數據組織。聯盟體(union)和組織體(struct)一樣,可以涵蓋許多種數據類型和變量,兩者區別如下:
1. **組織體(struct)**中所有變量是共存的,同時所有變量都生效,各個變量佔領差異的內存空間;
2. **聯盟體(union)**中是各變量是互斥的,同時只有一個變量生效,所有變量佔領同一塊內存空間。
當多個數據需求共享內存或者多個數據每次只取其一時,可以采用聯盟體(union)。
在Java語言中,沒有聯盟體(union)和組織體(struct)概念,只有類(class)的概念。眾所眾知,組織體(struct)可以用類(class)來實現。實在,聯盟體(union)也可以用類(class)來實現。不過,這個類不具備多個數據需求共享內存的性能,只具備多個數據每次只取其一的性能。
這里,以協議的客戶動靜為例說明。依據我長年來的接口協議封裝經歷,重要有以下兩種實現方式。
### 7.1.採用函數方式實現Union
**Union類實現**
“`java
** 客戶動靜類 *
ToString
public class CustomerMessage {
** 屬性關連 *
** 動靜類型 *
private String msgType;
** 目的用戶 *
private String toUser;
** 共用體關連 *
** 報導內容 *
private Nes nes;
…
** 常量關連 *
** 報導動靜 *
public static final String MSG_TYPE_NEWS = “nes”;
…
** 組成函數 *
public CustomerMessage() {}
** 組成函數 *
public CustomerMessage(String toUser) {
this.toUser = toUser;
}
** 組成函數 *
public CustomerMessage(String toUser, Nes nes) {
this.toUser = toUser;
this.msgType = MSG_TYPE_NEWS;
this.nes = nes;
}
** 清理動靜內容 *
private vo removeMsgContent() {
查驗動靜類型
if (Objects.isNull(msgType)) {
return;
}
清理動靜內容
if (MSG_TYPE_NEWS.equals(msgType)) {
nes = null;
} else if (…) {
…
}
msgType = null;
}
** 查驗動靜類型 *
private vo checkMsgType(Stri運彩場中賽事ng msgType) {
查驗動靜類型
if (Objects.isNull(msgType)) {
thro ne IllegalArgumentException(“動靜類型為空”);
}
對照動靜類型
if (!Objects.equals(msgType, this.msgType)) {
thro ne IllegalArgumentException(“動靜類型不匹配”);
}
}
** 建置動靜類型函數 *
public vo setMsgType(String msgType) {
清理動靜內容
removeMsgContent();
查驗動靜類型
if (Objects.isNull(msgType)) {
thro ne IllegalArgumentException(“動靜類型為空”);
}
賦值動靜內容
this.msgType = msgType;
if (MSG_TYPE_NEWS.equals(msgType)) {
nes = ne Nes();
} else if (…) {
…
} else {
thro ne IllegalArgumentException(“動靜類型不支持”);
}
}
** 獲中止息類型 *
public String getMsgType() {
查驗動靜類型
if (Objects.isNull(msgType)) {
thro ne IllegalArgumentException(“動靜類型無效”);
}
回去動靜類型
return this.msgType;
}
** 建置報導 *
public vo setNes(Nes nes) {
清理動靜內容
removeMsgContent();
賦值動靜內容
this.msgType = MSG_TYPE_NEWS;
this.nes = nes;
}
** 獲取報導 *
public Nes getNes() {
查驗動靜類型
checkMsgType(MSG_TYPE_NEWS);
回去動靜內容
return this.nes;
}
…
}
“`
**Union類採用**
“`java
String accessToken = …;
String toUser = …;
List articleList = …;
Nes nes = ne Nes(articleList);
CustomerMessage customerMessage = ne CustomerMessage(toUser, nes);
echatApi.sendCustomerMessage(accessToken, customerMessage);
“`
**重要優瑕疵**
– 好處:更貼身CC++語言的聯盟體(union);
– 瑕疵:實現邏輯較為復雜,參數類型驗證較多。
### 7.2.採用接管方式實現Union
**Union類實現**
“`java
** 客戶動靜類 *
Getter
Setter
ToString
public abstract class CustomerMessage {
** 屬性關連 *
** 動靜類型 *
private String msgType;
** 目的用戶 *
private String toUser;
** 常量關連 *
** 報導動靜 *
public static final String MSG_TYPE_NEWS = “nes”;
…
** 組成函數 *
public CustomerMessage(String msgType) {
this.msgType = msgType;
}
** 組成函數 *
public CustomerMessage(String msgType, String toUser) {
this.msgType = msgType;
this.toUser = toUser;
}
}
** 報導客戶動靜類 *
Getter
Setter
ToString(callSuper = true)
public class NesCustomerMessage extends CustomerMessage {
** 屬性關連 *
** 報導內容 *
private Nes nes;
** 組成函數 *
public NesCustomerMessage() {
super(MSG_TYPE_NEWS);
}
** 組成函數 *
public NesCustomerMessage(String toUser, Nes nes) {
super(MSG_TYPE_NEWS, toUser);
this.nes = nes;
}
}
“`
**Union類採用**
“`java
String accessToken = …;
String toUser = …;
List articleList = …;
Nes nes = ne Nes(articleList);
CustomerMessage customerMessage = ne NesCustomerMessage(toUser, nes);
echatApi.sendCustomerMessage(accessToken, customerMessage);
“`
**重要優瑕疵**
– 好處:採用虛基類和子類進行拆分,各個子類對象的概念領會;
– 瑕疵:與CC++語言的聯盟體(union)分別大,不過性能上大體一致。
在CC++語言中,聯盟體并不包含有聯盟體當前的數據類型。但在上面實現的Ja賭足球va聯盟體中,已經涵蓋了聯盟體對應的數據類型。所以,從嚴峻意義上說,Java聯盟體并不是真正的聯盟體,只是一個具備多個數據每次只取其一性能的類。
## 8.採用泛型屏蔽類型的不同性
在C++語言中,有個很好用的**模板(template)**性能,可以編寫帶有參數化類型的通用版本,讓編譯器主動生成針對差異類型的具體版本。而在Java語言中,也有一個相似的性能叫**泛型(generic)**。在編寫類和想法的時候,通常採用的是具體的類型,而用泛型可以使類型參數化,這樣就可以編寫更通用的代碼。
很多人都以為,C++模板(template)和Java泛型(generic)兩個概念是等價的,實在實現機制是徹底差異的。C++模板是一套宏指令集,編譯器會針對每一種類型創造一份模板代碼副本;Java泛型的實現基于”類型擦除”概念,本性上是一種進行類型限制的語法糖。
### 8.1.泛型類
以支撐類為例,定義泛型的通用支撐類:
“`java
** 通用支撐類 *
Getter
Setter
ToString
public class GenericHolder {
** 通用取值 *
private T value;
** 組成函數 *
public GenericHolder() {}
** 組成函數 *
public GenericHolder(T value) {
this.value = value;
}
}
“`
### 8.2.泛型接口
定義泛型的數據提供者接口:
“`java
** 數據提供者接口 *
public interface DataProver {
** 獲取數據函數 *
public T getData();
}
“`
### 8.3.泛型想法
定義泛型的淺拷貝函數:
“`java
** 淺拷貝函數 *
public static T shalloCopy(Object source, Class clazz) thros BeansException {
判斷源對象
if (Objects.isNull(source)) {
return null;
}
新建目的對象
T target;
try {
target = clazz.neInstance();
} catch (Exception e) {
thro ne BeansException(“新建類實例反常”, e);
}
拷貝對象屬性
BeanUtils.copyProperties(source, target);
回去目的對象
return target;
}
“`
### 8.4.泛型通配符
泛型通配符通常是採用”?”取代具體的類型實參,可以把”?”當作所有類型的父類。當具體類型不確認的時候,可以採用泛型通配符 “?”;當不需求採用類型的具體性能,只採用Object類中的性能時,可以採用泛型通配符 “?”。
“`java
** 打印取值函數 *
public static vo printValue(GenericHolder holder) {
System.out.println(holder.getValue());
}
** 主函數 *
public static vo main(String[] args) {
printValue(ne GenericHolder(12345));
printValue(ne GenericHolder(“abcde”));
}
“`
在Java規范中,不建議採用泛型通配符”?”,上面函數可以改為:
“`java
** 打印取值函數 *
public static vo printValue(GenericHolder holder) {
System.out.println(holder.getValue());
}
“`
### 8.5.泛型高下界
在採用泛型的時候,我們還可認為傳入的泛型類型實參進行高下界的限制,如:類型實參只準傳入某種類型的父類或某種類型的子類。泛型高下界的宣示,必要與泛型的宣示放在一起 。
**上界通配符(extends):**
上界通配符為extends,可以承受其指定類型或其子類作為泛參。其還有一種不同凡響的格式,可以指定其不光要是指定類型的子類,並且還要實現某些接口。比如:List表示這是A某個具體子類的List,保留的對象必要是A或A的子類。對于List列表,不可增添A或A的子類對象,只能獲取A的對象。
**下界通配符(super):**
下界通配符為super,可以承受其指定類型或其父類作為泛參。比如:List表示這是A某個具體父類的List,保留的對象必要是A或A的超類。對于List列表,或許增添A或A的子類對象,但只能獲取Object的對象。
**PECS(Producer Extends Consumer Super)原理:**
作為生產者提供數據(往外讀取)時,合適用上界通配符(extends);
作為花費者花費數據(往里寫入)時,合適用下界通配符(super)。
在日常編碼中,對照常用的是**上界通配符(extends)**,用于限定泛型類型的父類。範例代碼如下:
“`java
** 數字支撐類 *
Getter
Setter
ToString
public class NumberHolder {
** 通用取值 *
private T value;
** 組成函數 *
public NumberHolder() {}
** 組成函數 *
public NumberHolder(T value) {
this.value = value;
}
}
** 打印取值函數 *
public static vo printValue(GenericHolder holder) {
System.out.println(holder.getValue());
}
“`
## 后記
筆者曾在通訊產業從業十余年,接入了各類管和器材的北向接口協議上百余種,涉及到傳輸、互換、接入、電源、環境等技術,接觸了CORBA、HTTPHTTPS、WebService、Socket TCPUDP、串口RS232485等接口,結算出一套接口協議封裝的”想法論”。此中,把接口協議文檔中的數據形式幻化為Java的枚舉、組織體、聯盟體等數據組織,是接口協議封裝中極其主要的一步。
**本文作者:**陳昌毅,花名常意,高德地圖專業專家,2024年參加阿里巴巴,一直從事地圖數據采集的關連任務。