主頁 > 後端開發 > 這樣寫代碼,比直接使用 MyBatis 效率提高了 100 倍 !

這樣寫代碼,比直接使用 MyBatis 效率提高了 100 倍 !

2021-12-08 06:22:09 後端開發

作者:喬伊醬
鏈接:https://juejin.cn/post/7027733039299952676

對一個 Java 后端程式員來說,MyBatisHibernateData Jdbc 等都是我們常用的 ORM 框架,它們有時候很好用,比如簡單的 CRUD,事務的支持都非常棒,

但有時候用起來也非常繁瑣,比如接下來我們要聊到的一個常見的開發需求,而對這類需求,本文會給出一個比直接使用這些 ORM 開發效率至少會提高 100 倍的方法(絕無夸張),

首先資料庫有兩張表

用戶表(user):(簡單起見,假設只有 4 個欄位)

欄位名 型別 含義
id bitint 用戶 ID
name varchar(45) 用戶名
age int 年齡
role_id int 角色 ID

角色表(role):(簡單起見,假設只有 2 個欄位)

欄位名 型別 含義
id int 角色 ID
name varchar(45) 角色名

接下來我們要實作一個用戶查詢的功能

這個查詢有點復雜,它的要求如下:

  • 可按用戶名

    欄位查詢,要求:

    • 可精確匹配(等于某個值)
    • 可全模糊匹配(包含給定的值)
    • 可后模糊查詢(以...開頭)
    • 可前模糊查詢(以.. 結尾)
    • 可指定以上四種匹配是否可以忽略大小寫
  • 可按年齡

    欄位查詢,要求:

    • 可精確匹配(等于某個年齡)
    • 可大于匹配(大于某個值)
    • 可小于匹配(小于某個值)
    • 可區間匹配(某個區間范圍)
  • 可按角色ID查詢,要求:精確匹配

  • 可按用戶ID查詢,要求:同年齡欄位

  • 可指定只輸出哪些列(例如,只查詢 ID用戶名 列)

  • 支持分頁(每次查詢后,頁面都要顯示滿足條件的用戶總數)

  • 查詢時可選擇按 ID用戶名年齡 等任意欄位排序

后端介面該怎么寫呢?

試想一下,對于這種要求的查詢,后端介面里的代碼如果用 MyBatisHibernateData Jdbc 直接來寫的話,100 行代碼 能實作嗎?

反正我是沒這個信心,算了,我還是直接坦白,面對這種需求后端如何 只用一行代碼搞定 吧(有興趣的同學可以 MyBatis 等寫個試試,最后可以對比一下)

手把手:只一行代碼實作以上需求

首先,重點人物出場啦:Bean Searcher, 它就是專門來對付這種串列檢索的,無論簡單的還是復雜的,統統一行代碼搞定!而且它還非常輕量,Jar 包體積僅不到 100KB,無第三方依賴,

假設我們專案使用的框架是 Spring Boot(當然 Bean Searcher 對框架沒有要求,但在 Spring Boot 中使用更加方便)

Spring Boot 基礎就不介紹了,推薦下這個實戰教程:
https://github.com/javastacks/spring-boot-best-practice

添加依賴

Maven :

<dependency>
    <groupId>com.ejlchina</groupId>
    <artifactId>bean-searcher-boot-starter</artifactId>
    <version>3.1.2</version>
</dependency>

Gradle :

implementation 'com.ejlchina:bean-searcher-boot-starter:3.1.2'

然后寫個物體類來承載查詢的結果

@SearchBean(tables="user u, role r", joinCond="u.role_id = r.id", autoMapTo="u")
public class User {

    private Long id;		// 用戶ID(u.id)
    private String name;	// 用戶名(u.name)
    private int age;		// 年齡(u.age)
    private int roleId;		// 角色ID(u.role_id)
    @DbField("r.name")		// 指明這個屬性來自 role 表的 name 欄位
    private String role;        // 角色名(r.name)

    // Getter and Setter ...
}

接著就可以寫用戶查詢介面了

介面路徑就叫 /user/index 吧:

@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private MapSearcher mapSearcher;  // 注入檢索器(由 bean-searcher-boot-starter 提供)

    @GetMapping("/index")
    public SearchResult<Map<String, Object>> index(HttpServletRequest request) {
    	// 這里咱們只寫一行代碼
        return mapSearcher.search(User.class, MapUtils.flat(request.getParameterMap()));
    }

}

上述代碼中的 MapUtils 是 Bean Searcher 提供的一個工具類,MapUtils.flat(request.getParameterMap()) 只是為了把前端傳來的請求引數統一收集起來,然后剩下的,就全部交給 MapSearcher 檢索器了,

這樣就完了?那我們來測一下這個介面,看看效果吧

(1)無參請求

  • GET /user/index
  • 回傳結果:
{
    "dataList": [           // 用戶串列,默認回傳第 0 頁,默認分頁大小為 15 (可配置)
        { "id": 1, "name": "Jack", "age": 25, "roleId": 1, "role": "普通用戶" },
        { "id": 2, "name": "Tom", "age": 26, "roleId": 1, "role": "普通用戶" },
        ...
    ],
    "totalCount": 100       // 用戶總數
}

(2)分頁請求(page | size)

  • GET /user/index? page=2 & size=10
  • 回傳結果:結構同 (1)(只是每頁 10 條,回傳第 2 頁)

引數名 sizepage 可自定義, page 默認從 0 開始,同樣可自定義,并且可與其它引陣列合使用

(3)資料排序(sort | order)

  • GET /user/index? sort=age & order=desc
  • 回傳結果:結構同 (1)(只是 dataList 資料串列以 age 欄位降序輸出)

引數名 sortorder 可自定義,可與其它引陣列合使用

(4)指定(排除)欄位(onlySelect | selectExclude)

  • GET /user/index? onlySelect=id,name,role
  • GET /user/index? selectExclude=age,roleId
  • 回傳結果:( 串列只含 id,name 與 role 三個欄位)
{
    "dataList": [           // 用戶串列,默認回傳第 0 頁(只包含 id,name,role 欄位)
        { "id": 1, "name": "Jack", "role": "普通用戶" },
        { "id": 2, "name": "Tom", "role": "普通用戶" },
        ...
    ],
    "totalCount": 100       // 用戶總數
}

引數名 onlySelectselectExclude 可自定義,可與其它引陣列合使用

(5)欄位過濾(op = eq)

  • GET /user/index? age=20
  • GET /user/index? age=20 & age-op=eq
  • 回傳結果:結構同 (1)(但只回傳 age = 20 的資料)

引數 age-op = eq 表示 age欄位運算子eqEqual 的縮寫),表示引數 age 與引數值 20 之間的關系是 Equal,由于 Equal 是一個默認的關系,所以 age-op = eq 也可以省略

引數名 age-op 的后綴 -op 可自定義,且可與其它欄位引數 和 上文所列的引數(分頁、排序、指定欄位)組合使用,下文所列的欄位引數也是一樣,不再復述,

(6)欄位過濾(op = ne)

  • GET /user/index? age=20 & age-op=ne
  • 回傳結果:結構同 (1)(但只回傳 age != 20 的資料,neNotEqual 的縮寫)

(7)欄位過濾(op = ge)

  • GET /user/index? age=20 & age-op=ge
  • 回傳結果:結構同 (1)(但只回傳 age >= 20 的資料,geGreateEqual 的縮寫)

(8)欄位過濾(op = le)

  • GET /user/index? age=20 & age-op=le
  • 回傳結果:結構同 (1)(但只回傳 age <= 20 的資料,leLessEqual 的縮寫)

(9)欄位過濾(op = gt)

  • GET /user/index? age=20 & age-op=gt
  • 回傳結果:結構同 (1)(但只回傳 age > 20 的資料,gtGreateThan 的縮寫)

(10)欄位過濾(op = lt)

  • GET /user/index? age=20 & age-op=lt
  • 回傳結果:結構同 (1)(但只回傳 age < 20 的資料,ltLessThan 的縮寫)

(11)欄位過濾(op = bt)

  • GET /user/index? age-0=20 & age-1=30 & age-op=bt
  • GET /user/index? age=[20,30] & age-op=bt(簡化版,[20,30] 需要 UrlEncode, 參考下文)
  • 回傳結果:結構同 (1)(但只回傳 20 <= age <= 30 的資料,btBetween 的縮寫)

引數 age-0 = 20 表示 age 的第 0 個引數值是 20,上述提到的 age = 20 實際上是 age-0 = 20 的簡寫形式,另:引數名 age-0 age-1 中的連字符 - 可自定義,

(12)欄位過濾(op = mv)

  • GET /user/index? age-0=20 & age-1=30 & age-2=40 & age-op=mv
  • GET /user/index? age=[20,30,40] & age-op=mv(簡化版,[20,30,40] 需要 UrlEncode, 參考下文)
  • 回傳結果:結構同 (1)(但只回傳 age in (20, 30, 40) 的資料,mvMultiValue 的縮寫,表示有多個值的意思)

(13)欄位過濾(op = in)

  • GET /user/index? name=Jack & name-op=in
  • 回傳結果:結構同 (1)(但只回傳 name 包含 Jack 的資料,inInclude 的縮寫)

(14)欄位過濾(op = sw)

  • GET /user/index? name=Jack & name-op=sw
  • 回傳結果:結構同 (1)(但只回傳 name 以 Jack 開頭的資料,swStartWith 的縮寫)

(15)欄位過濾(op = ew)

  • GET /user/index? name=Jack & name-op=ew
  • 回傳結果:結構同 (1)(但只回傳 name 以 Jack 結尾的資料,swEndWith 的縮寫)

(16)欄位過濾(op = ey)

  • GET /user/index? name-op=ey
  • 回傳結果:結構同 (1)(但只回傳 name 為空 或為 null 的資料,eyEmpty 的縮寫)

(17)欄位過濾(op = ny)

  • GET /user/index? name-op=ny
  • 回傳結果:結構同 (1)(但只回傳 name 非空 的資料,nyNotEmpty 的縮寫)

(18)忽略大小寫(ic = true)

  • GET /user/index? name=Jack & name-ic=true
  • 回傳結果:結構同 (1)(但只回傳 name 等于 Jack (忽略大小寫) 的資料,icIgnoreCase 的縮寫)

引數名 name-ic 中的后綴 -ic 可自定義,該引數可與其它的引陣列合使用,比如這里檢索的是 name 等于 Jack 時忽略大小寫,但同樣適用于檢索 name 以 Jack 開頭或結尾時忽略大小寫,

當然,以上各種條件都可以組合,例如

查詢 name 以 Jack (忽略大小寫) 開頭,且 roleId = 1,結果以 id 欄位排序,每頁加載 10 條,查詢第 2 頁:

  • GET /user/index? name=Jack & name-op=sw & name-ic=true & roleId=1 & sort=id & size=10 & page=2
  • 回傳結果:結構同 (1)

OK,效果看完了,/user/index 介面里我們確實只寫了一行代碼,它便可以支持這么多種的檢索方式,有沒有覺得現在 你寫的一行代碼 就可以 干過別人的一百行 呢?

Bean Searcher

本例中,我們只使用了 Bean Searcher 提供的 MapSearcher 檢索器的一個檢索方法,其實,它還有很多檢索方法,

檢索方法

  • searchCount(Class<T> beanClass, Map<String, Object> params) 查詢指定條件下的資料 總條數
  • searchSum(Class<T> beanClass, Map<String, Object> params, String field) 查詢指定條件下的 某欄位統計值
  • searchSum(Class<T> beanClass, Map<String, Object> params, String[] fields) 查詢指定條件下的 多欄位統計值
  • search(Class<T> beanClass, Map<String, Object> params) 分頁 查詢指定條件下資料 串列總條數
  • search(Class<T> beanClass, Map<String, Object> params, String[] summaryFields) 同上 + 多欄位 統計
  • searchFirst(Class<T> beanClass, Map<String, Object> params) 查詢指定條件下的 第一條 資料
  • searchList(Class<T> beanClass, Map<String, Object> params) 分頁 查詢指定條件下資料 串列
  • searchAll(Class<T> beanClass, Map<String, Object> params) 查詢指定條件下 所有 資料 串列

MapSearcher 與 BeanSearcher

另外,Bean Searcher 除了提供了 MapSearcher 檢索器外,還提供了 BeanSearcher 檢索器,它同樣擁有 MapSearcher 所有的方法,只是它回傳的單條資料不是 Map,而是一個 泛型 物件,

引數構建工具

另外,如果你是在 Service 里使用 Bean Searcher,那么直接使用 Map<String, Object> 型別的引數可能不太優雅,為此, Bean Searcher 特意提供了一個引數構建工具,

例如,同樣查詢 name 以 Jack (忽略大小寫) 開頭,且 roleId = 1,結果以 id 欄位排序,每頁加載 10 條,加載第 2 頁,使用引數構建器,代碼可以這么寫:

Map<String, Object> params = MapUtils.builder()
        .field(User::getName, "Jack").op(Operator.StartWith).ic()
        .field(User::getRoleId, 1)
        .orderBy(User::getId, "asc")
        .page(2, 10)
        .build()
List<User> users = beanSearcher.searchList(User.class, params);

這里使用的是 BeanSearcher 檢索器,以及它的 searchList(Class<T> beanClass, Map<String, Object> params) 方法,

運算子約束

上文我們看到,Bean Searcher 對物體類中的每一個欄位,都直接支持了很多的檢索方式,

但某同學:哎呀!檢索方式太多了,我根本不需要這么多,我的資料量幾十億,用戶名欄位的前模糊查詢方式利用不到索引,萬一把我的資料庫查崩了怎么辦呀?

好辦,Bean Searcher 支持運算子的約束,物體類的用戶名 name 欄位只需要注解一下即可:

@SearchBean(tables="user u, role r", joinCond="u.role_id = r.id", autoMapTo="u")
public class User {

    @DbField(onlyOn = {Operator.Equal, Operator.StartWith})
    private String name;

    // 為減少篇幅,省略其它欄位...
}

如上,通過 @DbField 注解的 onlyOn 屬性,指定這個用戶名 name 只能適用與 精確匹配后模糊查詢,其它檢索方式它將直接忽略,

上面的代碼是限制了 name 只能有兩種檢索方式,如果再嚴格一點,只允許 精確匹配,那其實有兩種寫法,

(1)還是使用運算子約束:
@SearchBean(tables="user u, role r", joinCond="u.role_id = r.id", autoMapTo="u")
public class User {

    @DbField(onlyOn = Operator.Equal)
    private String name;

    // 為減少篇幅,省略其它欄位...
}

(2)在 Controller 的介面方法里把運算子引數覆寫:
@GetMapping("/index")
public SearchResult<Map<String, Object>> index(HttpServletRequest request) {
    Map<String, Object> params = MapUtils.flatBuilder(request.getParameterMap())
        .field(User::getName).op(Operator.Equal)   // 把 name 欄位的運算子直接覆寫為 Equal
        .build()
    return mapSearcher.search(User.class, params);
}

條件約束

該同學又:哎呀!我的資料量還是很大,age 欄位沒有索引,我不想讓它參與 where 條件,不然很可能就出現慢 SQL 啊!

不急,Bean Searcher 還支持條件的約束,讓這個欄位直接不能作為條件:

@SearchBean(tables="user u, role r", joinCond="u.role_id = r.id", autoMapTo="u")
public class User {

    @DbField(conditional = false)
    private int age;

    // 為減少篇幅,省略其它欄位...
}

如上,通過 @DbField 注解的 conditional 屬性, 就直接不允許 age 欄位參與條件了,無論前端怎么傳參,Bean Searcher 都不搭理,

引數過濾器

該同學仍:哎呀!哎呀 ...

別怕! Bean Searcher 還支持配置全域引數過濾器,可自定義任何引數過濾規則,在 Spring Boot 專案中,只需要配置一個 Bean:

@Bean
public ParamFilter myParamFilter() {
    return new ParamFilter() {
        @Override
        public <T> Map<String, Object> doFilter(BeanMeta<T> beanMeta, Map<String, Object> paraMap) {
            // beanMeta 是正在檢索的物體類的元資訊, paraMap 是當前的檢索引數
            // TODO: 這里可以寫一些自定義的引數過濾規則
            return paraMap;      // 回傳過濾后的檢索引數
        }
    };
}

某同學問

引數咋這么怪,這么多呢,和前端有仇么

  1. 引數名是否奇怪,這其實看個人喜好,如果你不喜歡中劃線 -,不喜歡 opic 后綴,完全可以自定義,參考這篇檔案:

searcher.ejlchina.com/guide/lates…

  1. 引數個數的多少,其實是和需求的復雜程度相關的,如果需求很簡單,那么很多引數沒必要讓前端傳,后端直接塞進去就好,比如:name 只要求后模糊匹配,age 只要求區間匹配,則可以:
@GetMapping("/index")
public SearchResult<Map<String, Object>> index(HttpServletRequest request) {
    Map<String, Object> params = MapUtils.flatBuilder(request.getParameterMap())
        .field(User::getName).op(Operator.StartWith)
        .field(User::getAge).op(Operator.Between)
        .build()
    return mapSearcher.search(User.class, params);
}

這樣前端就不用傳 name-opage-op 這兩個引數了,

其實還有一種更簡單的方法,那就是 運算子約束(當約束存在時,運算子默認就是 onlyOn 屬性中指定的第一個值,前端可以省略不傳):

@SearchBean(tables="user u, role r", joinCond="u.role_id = r.id", autoMapTo="u")
public class User {

    @DbField(onlyOn = Operator.StartWith)
    private String name;
    @DbField(onlyOn = Operator.Between)
    private String age;

    // 為減少篇幅,省略其它欄位...
}

  1. 對于 op=bt/mv 的多值引數傳遞,引數確實可以簡化,例如:
  • age-0=20 & age-1=30 & age-op=bt 簡化為 age=[20,30] & age-op=bt,
  • age-0=20 & age-1=30 & age-2=40 & age-op=mv 簡化為 age=[20,30,40] & age-op=mv

簡化方法:只需配置一個 ParamFilter(引數過濾器)即可,具體代碼可以參考這里:

https://github.com/ejlchina/bean-searcher/issues/10

入參是 request,我 swagger 檔案不好渲染了呀

其實,Bean Searcher 的檢索器只是需要一個 Map<String, Object> 型別的引數,至于這個引數是怎么來的,和 Bean Searcher 并沒有直接關系,前文之所以從 request 里取,只是因為這樣代碼看起來簡潔,如果你喜歡宣告引數,完全可以把代碼寫成這樣:

@GetMapping("/index")
public SearchResult<Map<String, Object>> index(Integer page, Integer size,
            String sort, String order, String name, Integer roleId,
            @RequestParam(value = "https://www.cnblogs.com/javastack/archive/2021/12/07/name-op", required = false) String name_op,
            @RequestParam(value = "https://www.cnblogs.com/javastack/archive/2021/12/07/name-ic", required = false) Boolean name_ic,
            @RequestParam(value = "https://www.cnblogs.com/javastack/archive/2021/12/07/age-0", required = false) Integer age_0,
            @RequestParam(value = "https://www.cnblogs.com/javastack/archive/2021/12/07/age-1", required = false) Integer age_1,
            @RequestParam(value = "https://www.cnblogs.com/javastack/archive/2021/12/07/age-op", required = false) String age_op) {
    Map<String, Object> params = MapUtils.builder()
        .field(Employee::getName, name).op(name_op).ic(name_ic)
        .field(Employee::getAge, age_0, age_1).op(age_op)
        .field(Employee::getRoleId, roleId)
        .orderBy(sort, order)
        .page(page, size)
        .build();
    return mapSearcher.search(User.class, params);
}

欄位引數之間的關系都是 “且” 呀,那 “或” 呢? “且” “或” 任意組合呢?

上文所述的欄位引數之間確是都是 "且" 的關系,至于 “或”,雖然這種使用場景不太多,但 Bean Searcher 也是支持的,詳細可以參考這篇文章:

https://github.com/ejlchina/bean-searcher/issues/8

這里就不再復述了,

開發效率真的提高 100 倍了嗎?

從本例其實可以看出,效率提升的程度依賴于檢索需求的復雜度,需求越復雜,則效率提高倍數越多,反之則越少,如果需求超級復雜,則提高 1000 倍都有可能,

但即使我們日常開發中沒有如此復雜的需求,開發效率只提升了 510 倍,那是不是也非常可觀呢?

結語

本文介紹了 Bean Searcher 在復雜串列檢索領域的超強能力,它之所以可以極大提高這類需求的研發效率,根本上歸功于它 獨創動態欄位運算子多表映射機制,這是傳統 ORM 框架所沒有的,但由于篇幅所限,它的特性本文不能盡述,比如它還:

  • 支持 聚合查詢
  • 支持 Select|Where|From子查詢
  • 支持 物體類嵌入引數
  • 支持 欄位轉換器
  • 支持 Sql 攔截器
  • 支持 資料庫 Dialect 擴展
  • 支持 多資料源
  • 支持 自定義注解
  • 等等

Bean Searcher 是我在作業中總結封裝出來的一個小工具,公司內部使用了 4 年,經歷大小專案三四十個,只是最近才著手完善檔案分享給大家,如果你喜歡,一定去點個 Star 哦 _

再奉上 Bean Searcher 的詳細檔案:searcher.ejlchina.com/

近期熱文推薦:

1.1,000+ 道 Java面試題及答案整理(2021最新版)

2.別在再滿屏的 if/ else 了,試試策略模式,真香!!

3.臥槽!Java 中的 xx ≠ null 是什么新語法?

4.Spring Boot 2.6 正式發布,一大波新特性,,

5.《Java開發手冊(嵩山版)》最新發布,速速下載!

覺得不錯,別忘了隨手點贊+轉發哦!

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/375738.html

標籤:其他

上一篇:如何進行excel資料分析之后的可視化資料寫入保存!

下一篇:mybatis配置決議

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more