主頁 > 後端開發 > 萬字+28張圖帶你探秘小而美的規則引擎框架LiteFlow

萬字+28張圖帶你探秘小而美的規則引擎框架LiteFlow

2022-05-22 10:03:27 後端開發

大家好,今天給大家介紹一款輕量、快速、穩定可編排的組件式規則引擎框架LiteFlow,

一、LiteFlow的介紹

LiteFlow官方網站和代碼倉庫地址

官方網站:https://yomahub.com/liteflow

Gitee托管倉庫:https://gitee.com/dromara/liteFlow

Github托管倉庫:https://github.com/dromara/liteflow

前言

在每個公司的系統中,總有一些擁有復雜業務邏輯的系統,這些系統承載著核心業務邏輯,幾乎每個需求都和這些核心業務有關,這些核心業務業務邏輯冗長,涉及內部邏輯運算,快取操作,持久化操作,外部資源調取,內部其他系統RPC呼叫等等,時間一長,專案幾經易手,維護的成本就會越來越高,各種硬代碼判斷,分支條件越來越多,代碼的抽象,復用率也越來越低,各個模塊之間的耦合度很高,一小段邏輯的變動,會影響到其他模塊,需要進行完整回歸測驗來驗證,如要靈活改變業務流程的順序,則要進行代碼大改動進行抽象,重新寫方法,實時熱變更業務流程,幾乎很難實作,

LiteFlow框架的作用

LiteFlow就是為解耦復雜邏輯而生,如果你要對復雜業務邏輯進行新寫或者重構,用LiteFlow最合適不過,它是一個輕量,快速的組件式流程引擎框架,組件編排,幫助解耦業務代碼,讓每一個業務片段都是一個組件,并支持熱加載規則配置,實作即時修改,

使用LiteFlow,你需要去把復雜的業務邏輯按代碼片段拆分成一個個小組件,并定義一個規則流程配置,這樣,所有的組件,就能按照你的規則配置去進行復雜的流轉,

LiteFlow的設計原則

LiteFlow是基于作業臺模式進行設計的,何謂作業臺模式?

n個工人按照一定順序圍著一張作業臺,按順序各自生產零件,生產的零件最終能組裝成一個機器,每個工人只需要完成自己手中零件的生產,而無需知道其他工人生產的內容,每一個工人生產所需要的資源都從作業臺上拿取,如果作業臺上有生產所必須的資源,則就進行生產,若是沒有,就等到有這個資源,每個工人所做好的零件,也都放在作業臺上,

這個模式有幾個好處:

  • 每個工人無需和其他工人進行溝通,工人只需要關心自己的作業內容和作業臺上的資源,這樣就做到了每個工人之間的解耦和無差異性,
  • 即便是工人之間調換位置,工人的作業內容和關心的資源沒有任何變化,這樣就保證了每個工人的穩定性,
  • 如果是指派某個工人去其他的作業臺,工人的作業內容和需要的資源依舊沒有任何變化,這樣就做到了工人的可復用性,
  • 因為每個工人不需要和其他工人溝通,所以可以在生產任務進行時進行實時工位更改:替換,插入,撤掉一些工人,這樣生產任務也能實時地被更改,這樣就保證了整個生產任務的靈活性,

這個模式映射到LiteFlow框架里,工人就是組件,工人坐的順序就是流程配置,作業臺就是背景關系,資源就是引數,最終組裝的這個機器就是這個業務,正因為有這些特性,所以LiteFlow能做到統一解耦的組件和靈活的裝配,

二、LiteFlow的使用

1)非Spring環境下

引入pom依賴

<dependency>
   <groupId>com.yomahub</groupId>
   <artifactId>liteflow-core</artifactId>
   <version>2.6.13</version>
</dependency>

第一步構建自己的業務Node,也就是繼承NodeComponent,重寫process方法,業務執行的程序中,會呼叫process來執行節點的業務,

我這里寫了三個

然后撰寫xml檔案,直接放在resources底下

<nodes/>配置了每個業務的節點,這里配置了我們寫的那幾個,<chain/>標簽代表了每一個業務的執行流程,配置了<when/>和<then/>標簽,然后value標簽設定了上面配置的<node/>的id,至于為什么這么配置,后面會決議,

然后執行這個demo

構建了一個LiteflowConfig,傳入xml的路徑,然后構建FlowExecutor,最后呼叫FlowExecutor的execute2Resp,傳入需要執行的業務流程名字 chain1 ,就是xml中配置的,執行業務流程,

結果

如果業務變動,現在不需要執行B流程了,那么直接修改規則檔案就行了,如圖,

運行結果

這里發現B就沒執行了,

2)SpringBoot環境下

引入pom依賴

<dependency>
   <groupId>com.yomahub</groupId>
   <artifactId>liteflow-spring-boot-starter</artifactId>
   <version>2.6.13</version>
</dependency>

構建自己的業務Node,只不過在Spring的環境底下,可以不需要在xml配置<node/>標簽,直接使用@LiteflowComponent注解即可

xml中沒有宣告<node/>標簽

application.properties中配置xml檔案的路徑

測驗代碼

執行結果

跟非spring的環境結果一致,

如果有想要獲取demo的小伙伴在微信公眾號 三友的java日記 后臺回復 LiteFlow 即可獲取,

通過上面的例子我們可以看出,其實每個業務節點之間是沒有耦合的,用戶只需要按照一定的業務規則配置節點的執行順序,LiteFlow就能實作業務的執行,

三、LiteFlow核心組件講解

講解核心組件的時候如果有什么不是太明白的,可以繼續往下看,后面會有原始碼決議,

下圖為LiteFlow整體架構圖

1)Parser

這個組件的作用就是用來決議流程配置的規則,也就是將你配置的規則檔案決議成Java代碼來運行,支持的檔案格式有xml、json、yaml,其實不論是什么格式,只是形式的不同,用戶可根據自身配置的習慣來選擇規則檔案的格式,

同時,規則檔案的存盤目前官方支持基于zk或者本地檔案的形式,同時也支持自定義的形式,

對于xml來說,Parser會將<node/>標簽決議成Node物件,將<chain/>決議成Chain物件,將<chain/>內部的比如<when/>、<then/>等標簽都會決議成Condition物件,

如下圖所示,

  • Node其實就是代表了你具體業務執行的節點,就是真正的業務是在Node中執行的
  • Condition可以理解為一種條件,比如前置條件,后置條件,里面一個Condition可以包含許多需要執行的Node
  • Chain可以理解成整個業務執行的流程,按照一定的順序來執行Condition中的Node也就是業務節點

Condition和Node的關系

Condition分為以下幾種

  • PreCondition:在整個業務執行前執行,就是前置的作用
  • ThenCondition:內部的Node是串行執行的
  • WhenCondition:內部的Node是并行執行的
  • FinallyCondition:當前面的Condition中的Node都執行完成之后,就會執行這個Condition中的Node節點

Chain和Condition的關系

Chain內部其實就是封裝了一堆Condition,Chain的執行就是指從不同的Condition中拿出里面的Node來執行,首先會拿出來PreCondition中的Node節點來執行,執行完之后會執行ThenCondition和WhenCondition中的Node節點,最后執行完之后才會執行FinallyCondition中的Node節點,

2)FlowBus

這個組件主要是用來存盤上一步驟決議出來的Node和Chain的

3)FlowExecutor

這個其實是用來執行上面決議出來的業務流程,從FlowBus找到需要執行的業務流程Chain,然后執行Chain,也就是按照Condition的順序來分別執行每個Condition的Node,也就是業務節點,

4)Slot

Slot可以理解為業務的背景關系,在一個業務流程中,這個Slot是共享的,

Slot有個默認的實作DefaultSlot,DefaultSlot雖然可以用,但是在實際業務中,用這個會存在大量的弱型別,存取資料的時候都要進行強轉,頗為不方便,所以官方建議自己去實作自己的Slot,可以繼承AbsSlot,

5)DataBus

用來管理Slot的,從這里面可以獲取當前業務流程執行的Slot,

四、LiteFlow原始碼探究

說完核心的組件,接下來就來剖析一下原始碼,來看一看LiteFlow到底是如何實作規則編排的,

1)FlowExecutor的構造流程

我們這里就以非Spring環境的例子來說,因為在SpringBoot環境底下,FlowExecutor是由Spring創建的,但是創建的程序跟非Spring的例子是一樣的,

這里在構建FlowExecutor,傳入了一個規則的路徑flow.xml,也就是ruleSource屬性值,

進入loadInstance這個方法,其實就是直接new了一個FlowExecutor,

進入FlowExecutor構造方法,前面就是簡單的賦值操作,然后呼叫liteflowConfig.isParseOnStart(),這個方法默認是回傳true的,接下來會呼叫init方法,也就是在啟動時,就去決議規則檔案,保證運行時的效率,

接下來進入init方法,

init方法非常長,來一步一步決議

前面就是校驗,不用care

List<String> sourceRulePathList = Lists.newArrayList(liteflowConfig.getRuleSource().split(",|;"));

這行代碼的意思就是將我們傳入的規則檔案路徑進行分割成多個路徑,從這可以看出支持配置多個規則的檔案,對我們這個demo來說其實就是只有一個,那就是flow.xml,

分割完之后,就會遍歷每個路徑,然后判斷檔案的格式,比如xml、json、yaml,然后根據檔案格式找到對應的FlowParser,

隨后根據liteflowConfig.isSupportMultipleType()判斷是不是支持多型別的,什么叫多型別,就是指規則檔案配置了多個并且檔案的格式不同,如果支持的話,需要每個規則檔案單獨去決議,如果不支持,那就說明檔案的格式一定是相同的,相同可以在最后統一決議,決議是通過呼叫FlowParser的parseMain來決議的,

剖析完之后整個init方法就會結束,然后繼續呼叫DataBus的init方法,其實就是初始化DataBus,

到這其實構建FlowExecutor就完成了,從上面我們得出一個結論,那就是在構造FlowExecutor的時候會通過FlowParser的parseMain來處理對應規則檔案的路徑,所以接下來我們分析一下這個FlowParser是如何決議xml的,并且決議了之后干了什么,

2)FlowParser規則決議流程

接下來我們進入FlowParser來看看一個是如何決議規則的,

以本文的例子為例,因為是配置本地的xml檔案,找到的FlowParser的實作是LocalXmlFlowParser,

接下會呼叫parseMain方法,parseMain的方法的實作很簡單,首先根據PathContentParserHolder拿到一個PathContentParser來決議路徑,對上面案例來說,就是flow.xml路徑,拿到路徑對應檔案的內容,其實就是拿到了flow.xml內容,然后呼叫父類的parse方法來決議xml的內容,所以parse方法才是決議xml的核心方法,

這里有個細節說一下,PathContentParserHolder其實內部使用了Java的SPI機制來加載PathContentParser的實作,然后決議路徑,拿到內容,在Spring環境中默認基于Spring的實作的優先級高點,但是不論是怎么實作,作用都是一樣的,那就是拿到路徑對應的xml檔案的內容,這里就不繼續研究PathContentParser是如何加載檔案的原始碼了,

其實不光是PathContentParser,LiteFlow內部使用了很多SPI機制,但是基本上整合Spring的實作的優先級都高于框架本身的實作,

接下來我們就來看一下LocalXmlFlowParser父類中的parse方法的實作,

首先遍歷每個檔案中的內容,然后轉成Document,Document其實是dom4j的包,其實就是將xml轉成Java物件,這樣可以通過Java中的方法來獲取xml中每個標簽的資料,

將檔案都轉換成Document之后,呼叫parseDocument方法,

首先呼叫了ContextCmpInitHolder.loadContextCmpInit().initCmp() ,這行代碼也是通過SPI機制來加載ContextCmpInit,呼叫initCmp方法,框架本身對于initCmp的實作是空實作,但是在Spring環境中,主要是用來整合Spring中的Node節點的,將Node節點添加到FlowBus中,這也是為什么在Spring環境中的那個案例中不需要在xml檔案中配置<nodes/>的原因,因為LiteFlow會自動識別這些Node節點的Spring Bean,至于怎么整合Spring的,有興趣的同學可以看一下ComponentScanner類的實作,主要在Bean初始化之后進行判斷的,這里畫一張圖來總結一下initCmp方法的作用,

至于為什么需要先將Spring中的Node節點添加到FlowBus,其實很簡單,主要是因為構建Chain是需要Node,需要保證構建Chain之前,Spring中的Node節點都已經添加到了FlowBus中,

接下來就會繼續遍歷每個Document,也就是每個xml,然后拿到決議<nodes></nodes>中的每個<node></node>標簽,拿出每個node標簽中的屬性,通過LiteFlowNodeBuilder構建Node,然后放入到FlowBus中,至于如何放入到FlowBus中,可以看一下LiteFlowNodeBuilder的build方法的實作,

決議完Node之后,接下來就是決議<chain/>標簽,拿到每一個<chain/>標簽對應的Element之后,呼叫parseOneChain來決議<chain/>標簽的內容,

parseOneChain方法,先拿到<chain/>底下所有的標簽,然后判斷標簽型別,標簽的型別主要有四種型別:then、when、pre、finally,然后拿到每個標簽的值,構建對應的Condition,就是上文提到的ThenCondition、WhenCondition、PreCondition、FinallyCondition,然后加入到Chain中,至于如何將Node設定到Condition中,主要是通過LiteFlowConditionBuilder的setValue方法來實作的,setValue這個方式設定的值是條件標簽的value屬性值,然后決議value屬性值,然后從FlowBus中clone一個新的Node,加入到Condition中,至于為什么需要clone一下新的Node,因為同一個業務節點,可能在不同的執行鏈中,為了保證不同業務中的同一個業務節點不相互干擾,所以得重新clone一個新的Node物件,

構建好Condition之后,都設定到了對應的Chain中,最后將Chain添加到FlowBus中,

到這里,其實整個xml就決議完了,FlowParser的最主要的作用就是決議xml,根據配置構建Node、Condition和Chain物件,有了這些基礎的組件之后,后面才能運行業務流程,其實從這里也可以看出是如何流程編排的,其實就是根據配置,將一個個Node添加到Condition中,Condition再添加到Chain中,這樣相同的業務節點,可能分布在不同的Chain中,這樣就實作了業務代碼的復用和流程的編排,

3)Chain的執行流程

剖析完FlowParser的作用,也就是Node和Chain的構造流程之后,接下來看一下Chain是如何執行的,

流程執行是通過FlowExecutor來執行的,FlowExecutor執行的方法很多,我們以上面demo呼叫的execute2Resp為例,最侄訓走到如下圖的多載方法,

execute2Resp方法就會呼叫doExecute方法的實作,然后拿到Slot,封裝成一個LiteflowResponse回傳回去,所以從這里可以看出,doExecute是核心方法,

接下來看看doExecute方法的實作,

doExecute方法比較長,我截了兩張圖

首先從DataBus中獲取一個Slot,也就是當前業務執行的背景關系,之后從FlowBus中獲取需要執行的Chain,最后分別呼叫了Chain的executePre、execute、executeFinally方法,其實不用看也知道這些方法干了什么,其實就是呼叫不同的Condition中Node方法,

executePre和executeFinally方法

這兩個方法最后呼叫的是同一個方法,就是分別找到PreCondition和FinallyCondition,取出里面的Node節點,執行excute方法,

這里有重點說明一下,其實在Condition中存的不是直接的Node,而是Executable,Executable的有兩個實作,一個就是我們所說的Node,還有一個就是我們一直說的Chain,為了方便大家理解,我一直說的是Node,其實這里的Executable是有可能為Chain的,取決于規則的配置,當是一個Chain的時候,其實就是一個嵌套的子流程,也就是在一個流程中嵌套另一個流程的意思,大家注意一下就行了,其實不論怎么嵌套,流程執行到最后一定是Node,因為如果是Chain,那么還會繼續執行,不會停止,只有最后一個流程的Executable都是Node的時候流程才能執行完,

executePre和executeFinally方法說完之后,看一看execute方法的實作,

execute方法主要是判斷Condition的型別,然后判斷是ThenCondition還是WhenCondition,ThenCondition的話其實也就是拿出Node直接執行,如果是WhenCondition的話,其實就是并行執行每個Node節點,這也是ThenCondition和WhenCondition的主要區別,

畫圖總結一下Chain的執行流程

4)Node的執行流程

從上面我們可以看出,Chain的執行其實最終都是交給Node來執行的,只不過是不同階段呼叫不同的Node而已,其實最終也就是會呼叫Node的execute方法,所以我們就來著重看一下Node的execute方法,

instance就是NodeComponent物件,也就是我們自定義實作的節點物件,好家伙,終于要執行到業務了,有人可能好奇NodeComponent是如何設定到Node物件中的,其實就是在往FlowBus添加Node的時候設定的,不清楚的小伙伴可以翻一下那塊相關的原始碼,在決議xml那塊我有說過,

先呼叫NodeComponent的isAccess方法來判斷業務要不要執行,默認是true,你可以重寫這個方法,自己根據其它節點執行的情況來判斷當前業務的節點要不要執行,因為Slot是公共的,每個業務節點的執行結果可以放在Slot中,

隨后通過這個方法獲取了NodeExecutor,NodeExecutor可以通過execute方法來執行NodeComponent的,也就是來執行業務的,NodeExecutor默認是使用DefaultNodeExecutor子類的,當然你也可以自定義NodeExecutor來執行NodeComponent

NodeExecutor nodeExecutor = NodeExecutorHelper.loadInstance().buildNodeExecutor(instance.getNodeExecutorClass());

DefaultNodeExecutor的execute方法也是直接呼叫父類NodeExecutor的execute方法,接下來我們來看一下NodeExecutor的execute方法,

從這個方法的實作我們可以看出,LiteFlow對于業務的執行是支持重試功能的,但是不論怎么重試,最終一定呼叫的是NodeComponent的execute方法,

進入NodeComponent的execute方法

紅框圈出來的,就是核心代碼,self是一個變數,指的是當前這個NodeComponent物件,所以就直接呼叫當前這個NodeComponent的process方法,也就是用來執行業務的方法,

在執行NodeComponent的process方法前后其實有回呼的,也就是可以實作攔截的效果,在Spring環境中會生效,

至于這里為什么要使用self變數而不是直接使用this,其實原始碼也有注釋,簡單點說就是如果process方法被動態代理了,那么直接使用this的話,動態代理會不生效,所以為了防止動態代理不生效,就單獨使用了self變數來參考自己,至于為什么不生效,這是屬于Spring的范疇了,這里就不過多贅述了,

其實到這里,一個Node就執行完成了,Node的執行其實就是在執行NodeComponent,而NodeComponent其實最終是交給NodeExecutor來執行的,

每個Condition中的Node執行完之后,就將Slot回傳,這樣就能在呼叫方就能通過Slot拿到整個流程的執行結果了,

到這里,其實核心流程原始碼剖析就完成了,總的來說就是將規則組態檔翻譯成代碼,生成Node和Chain,然后通過呼叫Chain來執行業務流程,最終其實就是執行我們實作的NodeComponent的process方法,

最侄訓一張圖來總結整個核心原始碼,

圖中我省略了Condition的示意圖,因為Condition其實最終也是執行Node的,

以上就是本篇文章的全部內容,如果你有什么不懂或者想要交流的地方,可以關注我的個人的微信公眾號 三友的java日記 聯系我,我們下篇文章再見,

如果覺得這篇文章對你有所幫助,還請幫忙點贊、在看、轉發給更多的人,碼字不易,非常感謝!


往期熱門文章推薦

  • 7000字+24張圖帶你徹底弄懂執行緒池
  • 【SpringCloud原理】OpenFeign原來是這么基于Ribbon來實作負載均衡的
  • 【SpringCloud原理】Ribbon核心組件以及運行原理原始碼剖析
  • 【SpringCloud原理】OpenFeign之FeignClient動態代理生成原理
  • 為什么Java有了synchronized之后還造了Lock鎖這個輪子?
  • synchronized真的很重么?
  • 一文帶你看懂Java中的Lock鎖底層AQS到底是如何實作的

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

標籤:Java

上一篇:Jenkins Shared Library 添加第三方包支持

下一篇:我使用Spring AOP實作了用戶操作日志功能

標籤雲
其他(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