Activiti工作流引擎的使用

前些天發現了一個巨牛的人工智能學習網站,通俗易懂,風趣幽默,忍不住分享一下給大家。點擊跳轉到教程。

Activiti工作流引擎使用

1.簡單介工作流引擎與Activiti

對于工作流引擎的解釋請參考百度百科:工作流引擎

1.1 我與工作流引擎

在第一家公司工作的時候主要任務就是開發OA系統,當然基本都是有工作流的支持,不過當時使用的工作流引擎是公司一些牛人開發的(據說是用一個開源的引擎修改的),名稱叫CoreFlow;功能相對Activiti來說比較弱,但是能滿足日常的使用,當然也有不少的問題所以后來我們只能修改引擎的代碼打補丁。

現在是我工作的第二家公司,因為要開發ERP、OA等系統需要使用工作流,在項目調研階段我先搜索資料選擇使用哪個開源工作流引擎,最終確定了Activiti5并基于公司的架構做了一些DEMO。

1.2 Activiti與JBPM5?

對于Activiti、jBPM4、jBPM5我們應該如何選擇,在InfoQ上有一篇文章寫的很好,從大的層面比較各個引擎之間的差異,請參考文章:縱觀jBPM:從jBPM3到jBPM5以及Activiti5

1.3 Activiti資料

  • 官網:http://www.activiti.org/

  • 下載:http://www.activiti.org/download.html

  • 版本:Activiti的版本是從5開始的,因為Activiti是使用jBPM4的源碼;版本發布:兩個月發布一次。

  • Eclipse Plugin:?http://activiti.org/designer/update/

  • Activit中文群:5435716

2.初次使用遇到問題收集

因為Activiti剛剛退出不久所以資料比較空缺,中文資料更是少的可憐,所以開始的時候一頭霧水(雖然之前用過工作流,但是感覺差距很多),而且官方的手冊還不是很全面;所以我把我在學習使用的過程遇到的一些疑問都羅列出來分享給大家;以下幾點是我遇到和想到的,如果你還有什么疑問可以在評論中和我交流再補充。

2.1 部署流程圖后中文亂碼

亂碼是一直纏繞著國人的問題,之前各個技術、工具出現亂碼的問題寫過很多文章,這里也不例外……,Activiti的亂碼問題在流程圖中。

流程圖的亂碼如下圖所示:

Activiti工作流引擎使用

解決辦法有兩種:

2.1.1 修改源代碼方式

修改源碼

org.activiti.engine.impl.bpmn.diagram.ProcessDiagramCanvas

?

在構造方法

public ProcessDiagramCanvas(int width, int height)
中有一行代碼是設置字體的,默認是用?Arial?字體,這就是亂碼產生的原因,把字改為本地的中文字體即可,例如:

?

Font font = new Font("WenQuanYi Micro Hei", Font.BOLD, 11);

當然如果你有配置文件讀取工具那么可以設置在*.properties文件中,我就是這么做的:

Font font = new Font(PropertyFileUtil.get("activiti.diagram.canvas.font"), Font.BOLD, 11);

2.1.2 使用壓縮包方式部署

Activiti支持部署*.bpmn20.xml、bar、zip格式的流程定義。

使用Activit Deisigner工具設計流程圖的時候會有三個類型的文件:

  • .activiti設計工具使用的文件

  • .bpmn20.xml設計工具自動根據.activiti文件生成的xml文件

  • .png流程圖圖片

解決辦法就是把xml文件和圖片文件同時部署,因為在單獨部署xml文件的時候Activiti會自動生成一張流程圖的圖片文件,但是這樣在使用的時候坐標和圖片對應不起來……

所以把xml和圖片同時部署的時候Activiti自動關聯xml和圖片,當需要獲取圖片的時候直接返回部署時壓縮包里面的圖片文件,而不是Activiti自動生成的圖片文件

2.1.2.1 使用工具打包Bar文件

右鍵項目名稱然后點擊“Create deployment artifacts”,會在src目錄中創建deployment文件夾,里面包含*.bar文件.

2.1.2.2 使用Ant腳本打包Zip文件

這也是我們采用的辦法,你可以手動選擇xml和png打包成zip格式的文件,也可以像我們一樣采用ant target的方式打包這兩個文件。

?

<?xml version="1.0" encoding="UTF-8"?>
<project name="foo"><property name="workflow.definition" value="foo-common-core/src/main/resources/diagrams" /><property name="workflow.deployments" value="foo-common-core/src/main/resources/deployments" /><target name="workflow.package.oa.leave"><echo>打包流程定義及流程圖::OA-請假</echo><zip destfile="${workflow.deployments}/oa/leave.zip" basedir="${workflow.definition}/oa/leave" update="true"includes="*.xml,*.png" /></target>
</project>
?

?

這樣當修改流程定義文件后只要運行ant命令就可以打包了:

ant workflow.package.oa.leave

?

現在部署bar或者zip文件查看流程圖圖片就不是亂碼了,而是你的壓縮包里面的png文件。

2.2 使用引擎提供的Form還是自定義業務Form

2.2.1 引擎提供的Form

定義表單的方式在每個Task標簽中定義extensionElementsactiviti:formProperty即可,到達這個節點的時候可以通過API讀取表單元素。

Activiti官方的例子使用的就是在流程定義中設置每一個節點顯示什么樣的表單哪些字段需要顯示、哪些字段只讀、哪些字段必填。

但是這種方式僅僅適用于比較簡單的流程,對于稍微復雜或者頁面需要業務邏輯的判斷的情況就不適用了。

對于數據的保存都是在引擎的表中,不利于和其他表的關聯、對整個系統的規劃也不利!

2.2.2 自定義業務Form

這種方式應該是大家用的最多的了,因為一般的業務系統業務邏輯都會比較復雜,而且數據庫中很多表都會有依賴關系,表單中有很多狀態判斷。

例如我們的系統適用jQuery UI作為UI,有很多javascript代碼,頁面的很多操作需要特殊處理(例如:多個選項的互斥、每個節點根據類型和操作人顯示不同的按鈕);基本每個公司都有一套自己的UI風格,要保持多個系統的操作習慣一致只能使用自定義表單才能滿足。

2.3 業務和流程的關聯方式

這個問題在群里面很多人都問過,這也是我剛剛開始迷惑的地方;

后來看了以下API發現RuntimeService有兩個方法:

2.3.1 startProcessInstanceByKey

javadoc對其說明:

?

startProcessInstanceByKey(String processDefinitionKey, Map variabes) Starts a new process instance in the latest version of the process definition with the given key
?

?

其中businessKey就是業務ID,例如要申請請假,那么先填寫登記信息,然后(保存+啟動流程),因為請假是單獨設計的數據表,所以保存后得到實體ID就可以把它傳給processInstanceBusinessKey方法啟動流程。當需要根據businessKey查詢流程的時候就可以通過API查詢:

?

runtimeService.createProcessInstanceQuery().processInstanceBusinessKey(processInstanceBusinessKey, processDefinitionKey);
?

?

議數據庫冗余設計:在業務表設計的時候添加一列:PROCESS_INSTANCE_ID varchar2(64),在流程啟動之后把流程ID更新到業務表中,這樣不管從業務還是流程都可以查詢到對方!

特別說明:?此方法啟動時自動選擇最新版本的流程定義。

2.3.2 startProcessInstanceById

javadoc對其說明:

?

startProcessInstanceById(String processDefinitionId, String businessKey, Map variables) Starts a new process instance in the exactly specified version of the process definition with the given id.
?

?

processDefinitionId:這個參數的值可以通過repositoryService.createProcessDefinitionQuery()方法查詢,對應數據庫:ACT_RE_PROCDEF;每次部署一次流程定義就會添加一條數據,同名的版本號累加。

特別說明:?此可以指定不同版本的流程定義,讓用戶多一層選擇。

2.3.3 如何選擇

建議使用startProcessInstanceByKey,特殊情況需要使用以往的版本選擇使用startProcessInstanceById

2.4 同步用戶數據

這個問題也是比較多的人詢問過,Activiti支持對任務分配到:指定人、指定組、兩者組合,而這些人和組的信息都保存在ACT_ID..表中,有自己的用戶和組(角色)管理讓很多人不知所措了;原因是因為每個系統都會存在一個權限管理模塊(維護:用戶、部門、角色、授權),不知道該怎么和Activiti同步。

2.4.1 建議處理方式

Activiti有一個IdentityService接口,通過這個接口可以操控Activiti的ACT_ID_*表的數據,一般的做法是用業務系統的權限管理模塊維護用戶數據,當進行CRUD操作的時候在原有業務邏輯后面添加同步到Activiti的代碼;例如添加一個用戶時同步Activiti User的代碼片段:

?

/*** 保存用戶信息 并且同步用戶信息到activiti的identity.User,同時設置角色* @param user* @param roleIds*/
public void saveUser(User user, List<Long> roleIds, boolean synToActiviti) {accountManager.saveEntity(user);String userId = user.getId().toString();if (synToActiviti) {List<org.activiti.engine.identity.User> activitiUsers = identityService.createUserQuery().userId(userId).list();if (activitiUsers.size() == 1) {//更新信息org.activiti.engine.identity.User activitiUser = activitiUsers.get(0);activitiUser.setFirstName(user.getName());activitiUser.setLastName("");activitiUser.setPassword(user.getPassword());activitiUser.setEmail(user.getEmail());identityService.saveUser(activitiUser);// 刪除用戶的membershipList<Group> activitiGroups = identityService.createGroupQuery().groupMember(userId).list();for (Group group : activitiGroups) {identityService.deleteMembership(userId, group.getId());}// 添加membershipfor (Long roleId : roleIds) {Role role = roleManager.getEntity(roleId);identityService.createMembership(userId, role.getEnName());}} else {org.activiti.engine.identity.User newUser = identityService.newUser(userId);newUser.setFirstName(user.getName());newUser.setLastName("");newUser.setPassword(user.getPassword());newUser.setEmail(user.getEmail());identityService.saveUser(newUser);// 添加membershipfor (Long roleId : roleIds) {Role role = roleManager.getEntity(roleId);identityService.createMembership(userId, role.getEnName());}}}}
?

?

刪除操作也和這個類似!

不管從業務系統維護用戶還是從Activiti維護,肯定要確定一方,然后CRUD的時候同步到對方,如果需要同步多個子系統那么可以再調用WebService實現。

2.5 流程圖設計工具用什么

Activiti提供了兩個流程設計工具,但是面向對象不同。

  • Activiti Modeler,面向業務人員,使用開源的BPMN設計工具Signavio,使用BPMN描述業務流程圖

  • Eclipse Designer,面向開發人員,Eclipse的插件,可以讓開發人員定制每個節點的屬性(ID、Name、Listener、Attr等)

2.5.1 我們的方式

可能你會驚訝,因為我們沒有使用Activiti Modeler,我們認為用Viso已經能表達流程圖的意思了,而且項目經理也是技術出身,和開發人員也容易溝通。

目前這個項目是第一個使用Activiti的,開始我們在需求調研階段使用Viso設計流程圖,利用泳道流程圖設計和客戶溝通,確定后由負責流程的開發人員用Eclipse Designer設計得到bpmn20.xml,最后部署。

2.6 Eclipse Designer存在的問題

這個插件有一個很討厭的Bug一直未修復,安裝了插件后Eclipse的復制和粘帖快捷鍵會被更換為(Ctrl+Insert、Shift+Insert);Bug描述請見:

  • Activit Forums中報告的Bug

  • Jira的登記

所以最后我們只能單獨開一個安裝了Eclipse Designer的Eclipse專門用來設計流程圖,這樣就不影響正常使用Eclipse JAVAEE了。

3.配置

3.1 集成Spring

對于和Spring的集成Activiti做的不錯,簡單配置一些Bean代理即可實現,但是有兩個和事務相關的地方要提示:

  • 配置processEngineConfiguration的時候屬性transactionManager要使用和業務功能的同一個事務管理Bean,否則事務不同步。

  • 對于實現了org.activiti.engine.delegate包中的接口的類需要被事務控制的實現類需要被Spring代理,并且添加事務的Annotation或者在xml中配置,例如:

    ?

    /*** 創建繳費流程的時候自動創建實體** @author HenryYan*/
    @Service
    @Transactional
    publicclass CreatePaymentProcessListener implementsExecutionListener {....
    }
    ?

    ?

?

4.使用單元測試

單元測試均使用Spring的AbstractTransactionalJUnit4SpringContextTests作為SuperClass,并且在測試類添加:

?

@ContextConfiguration(locations = { "/applicationContext-test.xml"})
@RunWith(SpringJUnit4ClassRunner.class)

?

?

雖然Activiti也提供了測試的一些超類,但是感覺不好用,所以自己封裝了一些方法。

代碼請轉移:https://gist.github.com/2182847

4.1 驗證流程圖設計是否正確

代碼請轉移:https://gist.github.com/2182869

4.2 業務對象和流程關聯測試

代碼請轉移:https://gist.github.com/2182973

5.各種狀態的任務查詢以及和業務對象關聯

我們目前分為4中狀態:未簽收、辦理中、運行中、已完成。

查詢到任務或者流程實例后要顯示在頁面,這個時候需要添加業務數據,最終結果就是業務和流程的并集,請參考6.2

5.1 未簽收(Task)

此類任務針對于把Task分配給一個角色時,例如部門領導,因為部門領導角色可以指定多個人所以需要先簽收再辦理,術語:搶占式

對應的API查詢:

?

/*** 獲取未簽收的任務查詢對象* @param userId    用戶ID*/
@Transactional(readOnly = true)
publicTaskQuery createUnsignedTaskQuery(String userId) {TaskQuery taskCandidateUserQuery = taskService.createTaskQuery().processDefinitionKey(getProcessDefKey()).taskCandidateUser(userId);returntaskCandidateUserQuery;
}

?

?

5.2 辦理中(Task)

此類任務數據類源有兩種:

  • 簽收后的,5.1中簽收后就應該為辦理中狀態

  • 節點指定的是具體到一個人,而不是角色

對應的API查詢:

?

/*** 獲取正在處理的任務查詢對象* @param userId    用戶ID*/
@Transactional(readOnly = true)
publicTaskQuery createTodoTaskQuery(String userId) {TaskQuery taskAssigneeQuery = taskService.createTaskQuery().processDefinitionKey(getProcessDefKey()).taskAssignee(userId);returntaskAssigneeQuery;
}

?

?

5.3 運行中(ProcessInstance)

說白了就是沒有結束的流程,所有參與過的人都應該可以看到這個實例,但是Activiti的API沒有可以通過用戶查詢的方法,這個只能自己用hack的方式處理了,我目前還沒有處理。

從表ACT_RU_EXECUTION中查詢數據。

對應的API查詢:

?

/*** 獲取未經完成的流程實例查詢對象* @param userId    用戶ID*/
@Transactional(readOnly = true)
publicProcessInstanceQuery createUnFinishedProcessInstanceQuery(String userId) {ProcessInstanceQuery unfinishedQuery = runtimeService.createProcessInstanceQuery().processDefinitionKey(getProcessDefKey()).active();returnunfinishedQuery;
}

?

?

5.4 已完成(HistoricProcessInstance)

已經結束的流程實例。

從表ACT_HI_PROCINST中查詢數據。

?

/*** 獲取已經完成的流程實例查詢對象* @param userId    用戶ID*/
@Transactional(readOnly = true)
publicHistoricProcessInstanceQuery createFinishedProcessInstanceQuery(String userId) {HistoricProcessInstanceQuery finishedQuery = historyService.createHistoricProcessInstanceQuery().processDefinitionKey(getProcessDefKey()).finished();returnfinishedQuery;
}
?

?

?

5.5 查詢時和業務關聯

提示:之前在業務對象添加了PROCESS_INSTANCE_ID字段

思路:現在可以利用這個字段查詢了,不管是Task還是ProcessInstance都可以得到流程實例ID,可以根據流程實例ID查詢實體然后把流程對象設置到實體的一個屬性中由Action或者Controller輸出到前臺。

代碼請參考:https://gist.github.com/2183557

6.UI及截圖

結合實際業務描述一個業務從開始到結束的過程,對于迷惑的同學看完豁然開朗了;這里使用請假作為例子。

6.1 單獨一個列表負責申請

這樣的好處是申請和流程辦理分離開處理,列表顯示未啟動流程的請假記錄(數據庫PROCESS_INSTANCE_ID為空)。

申請界面的截圖:

Activiti工作流引擎使用

6.2 流程狀態

Activiti工作流引擎使用

6.3 流程跟蹤

圖片方式顯示當前節點:

Activiti工作流引擎使用

列表形式顯示流程流轉過程:

Activiti工作流引擎使用

6.3.1 當前節點定位JS

Java代碼請移步:https://gist.github.com/2183712

Javascript思路:先通過Ajax獲取當前節點的坐標,在指定位置添加紅色邊框,然后加載圖片。

代碼移步:https://gist.github.com/2183804

7.開啟Logger

  1. 添加log4j的jar
  2. 設置log4j.logger.java.sql=DEBUG

8.結束

之前就想寫這篇文章,現在終于完成了,花費了幾個小時,希望能節省你幾天的時間。

請讀者仔細閱讀Activiti的用戶手冊和Javadoc。

?

?

?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/451284.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/451284.shtml
英文地址,請注明出處:http://en.pswp.cn/news/451284.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Flurry調查報告:圖片和視頻應用增長速度最快

據外國媒體報道&#xff0c;移動數據分析公司Flurry近期對應用分類的歡迎度進行研究后發現&#xff0c;就用戶使用的時間而言&#xff0c;“圖片和視頻”應用已成為增長最快的移動應用。 Flurry對各種應用分類的800多萬移動用戶進行了調查&#xff0c;并利用其分析工具跟蹤了1…

JS內存管理與垃圾回收

javascript的內存管理Javascript 是那些被稱作垃圾回收語言當中的一員。垃圾回收語言通過周期性地檢查那些之前被分配出去的內存是否可以從應用的其他部分訪問來幫助開發者管理內存。換句話說&#xff0c;當計算機發現有的內存已經不能被訪問到了&#xff0c;就會把它們標記為垃…

牛客網分糖果

題解&#xff1a; 考試的時候沒有想出來。。。 剛開始想了個比較錯誤的dp 后來想到了容斥。。 但是沒有想到怎么去維護這個東西。。 按照一般的套路 至少有一個相鄰相等的-至少有兩個相鄰相等的 但是這道題里這樣并不好維護 我們考慮用dp來算這個東西 f[i]f[j]*min(a[j].....a[…

HTTP 503 錯誤 – 服務不可用 (Service unavailable)

介紹因暫時超載或臨時維護&#xff0c;您的 Web 服務器目前無法處理 HTTP 請求。 其含義是&#xff0c; 這是一個暫時情況&#xff0c;會有一些延誤&#xff0c; 過 后將會得到緩解。 有些服務器在這種情況下也許干脆拒絕套接字&#xff08;socket&#xff09; 連接&#xff0c…

SHA 加密是什么( sha1 和 MD5 的區別 )

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 SHA 安全哈希算法&#xff08;Secure Hash Algorithm&#xff09;主要適用于數字簽名標準&#xff08;Digital Signature Standard DSS…

10個管理工作時間的小技巧

導讀&#xff1a;如何才能穩步追隨社會的變遷&#xff0c;如何才能留住飛逝的時間。俗話說&#xff1a;“一寸光陰一寸金&#xff0c;寸金難買寸光陰。”珍惜現在的時光&#xff0c;將來才不會為年輕時揮霍的時間而感到悔恨。三位作者Joe Mathews&#xff0c;Don Debolt&#x…

12-思科防火墻:ASA會話超時

一、實驗拓撲&#xff1a;二、實驗要求&#xff1a;DCD&#xff1a;死亡檢測時間&#xff0c;默認R2 Telnet上去保持時間為1個小時&#xff0c;超時就會被清除掉&#xff0c;這里要求變為4小時&#xff0c;每15s就會發送5個DCD檢測包&#xff0c;如沒回應就剔除掉&#xff0c;回…

orCAD使用Orcad Capture CIS按Room擺放

http://www.sig007.com/EDAguide/176.html 37 使用Orcad Capture CIS按Room擺放 &#xff08;1&#xff09;打開原理圖某一工程頁 &#xff08;2&#xff09;按Ctrl鍵依次選中需要建Room的元件&#xff0c;右擊&#xff0c;選擇Edit Properties 在Filter by 下拉菜單中選擇Cad…

503錯誤的原因和解決方法

如果出現“Service Unavailable”的提示&#xff0c;刷新幾下又可以訪問。 出現這種情況是由于您的網站超過了iis限制造成的 由于2003的操作系統在提示IIS過多時并非像2000系統提示“鏈接人數過多”&#xff0c;而是提示"Service Unavailable",出現這種情況是由于網…

Java中十六進制轉換 Integer.toHexString()

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 用例&#xff1a; Long.toHexString(number) 其中 number 是一個long類型參數。 描述&#xff1a; java.lang.Integer.toHexString(…

細數25個硅谷最熱創業公司 尋找創業靈感

【CSDN編譯報道】硅谷堪稱高科技產業之鄉&#xff0c;每當提起就能聯想到影響全球產業的各大著名企業Mircosoft、Google、Adobe、Oracle、Yahoo、SAP、intel等。而硅谷也被譽為創業公司的天堂&#xff0c;多如繁星的初創公司Karma、99Dresses、Getaround、Omada Health、Social…

數據特征分析:2.對比分析

對比分析 兩個相互聯系的數&#xff08;指標&#xff09;進行比較&#xff08;絕對數比較、相對數比較&#xff09; 對比分析 對比分析 → 兩個互相聯系的指標進行比較 絕對數比較&#xff08;相減&#xff09; / 相對數比較&#xff08;相除&#xff09;結構分析、比例分析、…

Java基礎-Java中的內存分配與回收機制

Java基礎-Java中的內存分配與回收機制 作者&#xff1a;尹正杰 版權聲明&#xff1a;原創作品&#xff0c;謝絕轉載&#xff01;否則將追究法律責任。 一. 二.轉載于:https://www.cnblogs.com/yinzhengjie/p/9279601.html

離散數學中關于自反與反自反的通俗解釋

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 設R是A上的二元關系&#xff0c;二元關系自反&#xff1a;任取一個A中的元素x&#xff0c;如果都有<x,x>在R中&#xff0c;那么就…

[Educational Round 5][Codeforces 616F. Expensive Strings]

這題調得我心疲力竭...Educational Round 5就過一段時間再發了_(:з」∠)_ 先后找了三份AC代碼對拍&#xff0c;結果有兩份都會在某些數據上出點問題...這場的數據有點水啊_(:з」∠)_【然而卡掉本弱還是輕輕松松的】 題目鏈接&#xff1a;616F - Expensive Strings 題目大意&a…

Redis自增計數

INCR key 將 key 中儲存的數字值增一。 如果 key 不存在&#xff0c;那么 key 的值會先被初始化為 0 &#xff0c;然后再執行 INCR 操作。 如果值包含錯誤的類型&#xff0c;或字符串類型的值不能表示為數字&#xff0c;那么返回一個錯誤。 本操作的值限制在 64 位(bit)有符號數…

android布局中使用include及需注意點

在android布局中&#xff0c;使用include&#xff0c;將另一個xml文件引入&#xff0c;可作為布局的一部分&#xff0c;但在使用include時&#xff0c;需注意以下問題&#xff1a;一、使用include引入如現有標題欄布局block_header.xml&#xff0c;代碼如下&#xff1a;<Rel…

周鴻祎回顧IPO一周年:保持創業心態 看好無線

奇虎360董事長兼CEO周鴻祎 3月19日晚間消息&#xff0c;在奇虎360上市接近一周年之際&#xff0c;奇虎360董事長兼CEO周鴻祎與媒體及個人投資者進行溝通&#xff0c;他表示這一年壓力比以前更大&#xff0c;因為在上市光環下依然需要保持創業心態&#xff0c;同時他強調無線和…

《Effective Java》 第二講:對于所有對象都通用的方法

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 上接《Effective Java》 第一講&#xff1a;創建和銷毀對象 八、覆蓋 equals 時請遵守通用約定 1. 自反性&#xff1a;對于任何非空的引…

linux刪除文件操作

linux刪除文件夾命令 在用Linux的時候&#xff0c;有時候要刪除一個文件夾&#xff0c;往往會提示次此文件非空&#xff0c;沒法刪除&#xff0c;這個時候&#xff0c;必須使用rm -rf命令。 實例一&#xff1a; rm -rf /var/log/httpd/access 將會刪除/var/log/httpd/access目錄…