使用EasyPoi實(shí)現(xiàn)word文檔生成和段落循環(huán)
EasyPoi介紹
EasyPoi是一個(gè)Java的Excel和Word處理庫(kù),主要用于將Java對(duì)象轉(zhuǎn)換為Excel或Word文檔,并且可以從Excel或Word文檔中讀取數(shù)據(jù)到Java對(duì)象。本文將重點(diǎn)介紹如何使用EasyPoi寫(xiě)Word文檔。
1.引入EasyPoi依賴(lài)
<dependency> <groupId>cn.afterturn</groupId> <artifactId>easypoi-base</artifactId> <version>4.1.3</version> </dependency>
2.創(chuàng)建Word模板
可以使用Microsoft Word或其他能夠創(chuàng)建Word模板的軟件來(lái)設(shè)計(jì)Word文檔的模板文件。模板文件中可以使用占位符來(lái)標(biāo)記需要替換的內(nèi)容。例如,在Word文檔中可以插入占位符${name}表示需要替換的內(nèi)容。
3.創(chuàng)建Java對(duì)象
使用Java對(duì)象來(lái)存放需要寫(xiě)入Word文檔的數(shù)據(jù)。
@Data public class User { private String name; private int age; }
4.使用EasyPoi寫(xiě)入Word文檔
創(chuàng)建一個(gè)WordExportUtil對(duì)象,利用模板和Java對(duì)象生成Word文檔。
import cn.afterturn.easypoi.word.WordExportUtil; import org.apache.poi.xwpf.usermodel.XWPFDocument; import java.io.FileOutputStream; import java.util.HashMap; import java.util.Map; public class WriteWordDemo { public static void main(String[] args) throws Exception { // 創(chuàng)建模板和數(shù)據(jù)對(duì)象 User user = new User("Tom", 18); Map<String, Object> map = new HashMap<>(); map.put("name", user.getName()); map.put("age", user.getAge()); // 加載模板文件 XWPFDocument doc = WordExportUtil.exportWord07( "user_template.docx", map); // 輸出文件 FileOutputStream fos = new FileOutputStream("user.docx"); doc.write(fos); fos.close(); doc.close(); } }
使用WordExportUtil.exportWord07方法可以生成.docx格式的Word文檔,第一個(gè)參數(shù)為Word模板文件名,第二個(gè)參數(shù)是一個(gè)Map對(duì)象,其中包含了模板中所有的占位符和對(duì)應(yīng)的數(shù)據(jù)。
基于EasyPoi實(shí)現(xiàn)段落循環(huán)
EasyPoi寫(xiě)word模版導(dǎo)出比較方便。但是如果你希望在寫(xiě)word模版的時(shí)候?qū)⒍温溥M(jìn)行循環(huán),那就實(shí)現(xiàn)不了。
下面的方法基于EasyPoi實(shí)現(xiàn)了word段落的循環(huán)。
import cn.afterturn.easypoi.cache.WordCache; import cn.afterturn.easypoi.entity.ImageEntity; import cn.afterturn.easypoi.util.PoiElUtil; import cn.afterturn.easypoi.util.PoiPublicUtil; import cn.afterturn.easypoi.word.entity.MyXWPFDocument; import cn.afterturn.easypoi.word.entity.params.ExcelListEntity; import cn.afterturn.easypoi.word.parse.excel.ExcelMapParse; import cn.hutool.core.bean.BeanUtil; import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.StringUtils; import org.apache.poi.xwpf.usermodel.*; import org.apache.xmlbeans.XmlCursor; import org.apache.xmlbeans.XmlObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.io.FileOutputStream; import java.util.*; public class WordParagraphHolder { private static final Logger logger = LoggerFactory.getLogger(WordParagraphHolder1.class); private XWPFDocument sourceDocument; private String targetFileName; private Map<String, Object> data; public WordParagraphHolder(XWPFDocument sourceDocument, String targetFileName, Map<String, Object> data) { this.sourceDocument = sourceDocument; this.targetFileName = targetFileName; this.data = data; } public String execute() throws Exception { MyXWPFDocument second = null; FileOutputStream fos2 = null; try { // 循環(huán)生成段落 Map<String, Object> data = this.data; this.parseAllParagraphic(sourceDocument.getParagraphs(), data); File targetFile = new File(targetFileName); FileOutputStream fos = FileUtils.openOutputStream(targetFile); sourceDocument.write(fos); // 關(guān)閉流 fos.close(); sourceDocument.close(); // 循環(huán)段落賦值 second = WordCache.getXWPFDocument(targetFile.getPath()); this.evalAllParagraphic(second.getParagraphs(), data); fos2 = FileUtils.openOutputStream(targetFile); second.write(fos2); } catch (Exception e) { logger.error("生成word段落失敗:", e); return ""; } finally { // 關(guān)閉流 fos2.close(); second.close(); } return targetFileName; } private void evalAllParagraphic(List<XWPFParagraph> paragraphs, Map<String, Object> map) throws Exception { int size = paragraphs.size(); Map<String, Integer> indexMap = new HashMap<>(); for (int i = 0; i < size; ++i) { XWPFParagraph paragraph = (XWPFParagraph) paragraphs.get(i); logger.info("段落內(nèi)容:{}", paragraph.getText()); if (paragraph.getText().indexOf("(") != -1) { Object obj = checkThisParagraphNeedIterator(paragraph, map); String listKey = getParagraphListKey(paragraph); Integer index = indexMap.getOrDefault(listKey, 0); if (Objects.nonNull(obj) && obj instanceof List && index < ((List) obj).size()) { Object o = ((List) obj).get(index); parseParagraph(paragraph, o, listKey); index++; indexMap.put(listKey, index); } } } } private void parseAllParagraphic(List<XWPFParagraph> paragraphs, Map<String, Object> map) throws Exception { int size = paragraphs.size(); List<XWPFParagraph> paragraphList = new ArrayList<>(); // 循環(huán)找出需要復(fù)制的段落 for (int i = 0; i < size; ++i) { XWPFParagraph paragraph = (XWPFParagraph) paragraphs.get(i); if (paragraph.getText().indexOf("(") != -1) { Object obj = checkThisParagraphNeedIterator(paragraph, map); if (Objects.nonNull(obj) && obj instanceof List) { paragraphList.add(paragraph); } } } // 復(fù)制段落 for (XWPFParagraph paragraph : paragraphList) { System.out.println(paragraph.getText()); Object obj = checkThisParagraphNeedIterator(paragraph, map); addParagraph(paragraph, (List) obj); // 刪除模板段落 XWPFDocument document = paragraph.getDocument(); document.removeBodyElement(document.getPosOfParagraph(paragraph)); } } /** * 復(fù)制段落 * * @param source 原段落 * @param doc */ public XWPFParagraph createParagraph(XWPFParagraph source, XWPFDocument doc) { // 使用游標(biāo)創(chuàng)建一個(gè)新行 XmlCursor cursor = source.getCTP().newCursor(); XWPFParagraph newParagraph = doc.insertNewParagraph(cursor); newParagraph.getCTP().set(source.getCTP().copy()); return newParagraph; } private Object checkThisParagraphNeedIterator(XWPFParagraph paragraph, Map<String, Object> map) throws Exception { String text = paragraph.getText().trim(); if (text != null && text.contains("fe:") && text.startsWith("(")) { text = text.replace("!fe:", "").replace("$fe:", "").replace("fe:", "").replace("(", ""); String[] keys = text.replaceAll("\\s{1,}", " ").trim().split(" "); Object result = PoiPublicUtil.getParamsValue(keys[0], map); return Objects.nonNull(result) ? result : new ArrayList(0); } else { return null; } } private String getParagraphListKey(XWPFParagraph paragraph) throws Exception { String text = paragraph.getText().trim(); if (text != null && text.contains("fe:") && text.startsWith("(")) { text = text.replace("!fe:", "").replace("$fe:", "").replace("fe:", "").replace("(", ""); String[] keys = text.replaceAll("\\s{1,}", " ").trim().split(" "); return keys[0]; } else { return null; } } /** * 賦值段落 * * @param paragraph * @param obj * @throws Exception */ public void parseParagraph(XWPFParagraph paragraph, Object obj, String listKey) throws Exception { String listname = paragraph.getText().trim(); boolean contains = listname.contains("fe:"); if (!contains) { return; } Map<String, Object> objectMap = BeanUtil.beanToMap(obj); parseThisParagraph(paragraph, objectMap, listKey); } /** * 增加段落 * * @param paragraph * @param list * @throws Exception */ public void addParagraph(XWPFParagraph paragraph, List<Object> list) throws Exception { XWPFParagraph currentParagraph = paragraph; System.out.println("start for each data list :" + list.size()); Iterator var11 = list.iterator(); while (var11.hasNext()) { Object obj = var11.next(); this.createParagraph(currentParagraph, currentParagraph.getDocument()); } } /** * 遍歷段落賦值 * * @param paragraph * @param map * @throws Exception */ private void parseThisParagraph(XWPFParagraph paragraph, Map<String, Object> map, String listKey) throws Exception { XWPFRun currentRun = null; String currentText = ""; Boolean isfinde = false; List<Integer> runIndex = new ArrayList(); XWPFRun preRun = null; for (int i = 0; i < paragraph.getRuns().size(); ++i) { XWPFRun run = (XWPFRun) paragraph.getRuns().get(i); String text = run.getText(0); if (!StringUtils.isEmpty(text)) { if (isfinde) { currentText = currentText + text; if (currentText.indexOf("[") == -1) { isfinde = false; runIndex.clear(); } else { runIndex.add(i); } if (currentText.indexOf("]") != -1) { this.changeValues(paragraph, currentRun, currentText, runIndex, map); currentText = ""; isfinde = false; } } else if (text.indexOf("[") >= 0) { currentText = text; isfinde = true; currentRun = run; } else { currentText = ""; } if (currentText.indexOf("]") != -1) { this.changeValues(paragraph, currentRun, currentText, runIndex, map); isfinde = false; } } // 去除多余的字符串 if (!StringUtils.isEmpty(text)) { if (text.indexOf("(") != -1) { preRun = run; } else if (text.indexOf("$fe") != -1) { // run.setText("", 0); // 清除第一個(gè)( preRun.setText("", 0); // 防止自定義前綴未被拆分為多個(gè)run,根據(jù)自定義關(guān)鍵字刪除多余前綴。例如($fe:resultList if (text.indexOf(listKey) != -1) { String lastText = run.getText(0); if (StringUtils.isNotBlank(lastText) && text.indexOf(listKey) != -1) { String replace = lastText.substring(text.indexOf(listKey) + listKey.length()); run.setText(replace, 0); } } } // 清除最后一個(gè)) if (i == paragraph.getRuns().size() - 1) { if (text.indexOf(")") != -1) { if (text.length() >= 1) { String lastText = run.getText(0); String replace = lastText.replace(")", ""); run.setText(replace, 0); } } } } } } private void changeValues(XWPFParagraph paragraph, XWPFRun currentRun, String currentText, List<Integer> runIndex, Map<String, Object> map) throws Exception { Object obj = getRealValue(currentText, map); if (obj instanceof ImageEntity) { currentRun.setText("", 0); ExcelMapParse.addAnImage((ImageEntity) obj, currentRun); } else { currentText = obj.toString(); PoiPublicUtil.setWordText(currentRun, currentText); } for (int k = 0; k < runIndex.size(); ++k) { ((XWPFRun) paragraph.getRuns().get((Integer) runIndex.get(k))).setText("", 0); } runIndex.clear(); } public static Object getRealValue(String currentText, Map<String, Object> map) throws Exception { String params = ""; while (currentText.indexOf("[") != -1) { params = currentText.substring(currentText.indexOf("[") + 1, currentText.indexOf("]")); Object obj = PoiElUtil.eval(params.trim(), map); if (obj instanceof ImageEntity || obj instanceof List || obj instanceof ExcelListEntity) { return obj; } if (obj != null) { currentText = currentText.replace("[" + params + "]", obj.toString()); } else { currentText = currentText.replace("[" + params + "]", ""); } } return currentText; } /** * 復(fù)制表格段落 * * @param source 原段落 * @param cell */ public XWPFParagraph copyTableParagraph(XWPFParagraph source, XWPFTableCell cell) { // 使用游標(biāo)創(chuàng)建一個(gè)新行 XmlCursor cursor = source.getCTP().newCursor(); // 在游標(biāo)位置插入新段落 XWPFParagraph newParagraph = cell.insertNewParagraph(cursor); // 復(fù)制底層 XML 對(duì)象 XmlObject copy = source.getCTP().copy(); newParagraph.getCTP().set(copy); // 關(guān)閉游標(biāo) cursor.dispose(); return newParagraph; } /** * 表格段落復(fù)制 * * @param document * @param data */ private void createTableParagraph(XWPFDocument document, Map<String, Object> data) { Object resultList = data.get("resultList"); if (Objects.isNull(resultList)) { return; } List list = (List) resultList; List<XWPFTable> tables = document.getTables(); for (XWPFTable table : tables) { for (XWPFTableRow row : table.getRows()) { for (XWPFTableCell cell : row.getTableCells()) { XWPFParagraph xwpfParagraph = null; List<XWPFParagraph> paragraphs = cell.getParagraphs(); for (int i = 0; i < paragraphs.size(); i++) { XWPFParagraph paragraph = paragraphs.get(i); String listname = paragraph.getText().trim(); if (listname.startsWith("($fe")) { xwpfParagraph = paragraph; } } if (Objects.nonNull(xwpfParagraph)) { for (int i = 0; i < list.size() - 1; i++) { copyTableParagraph(xwpfParagraph, cell); } } } } } } /** * 表格段落賦值 * * @param document * @param date */ private void evalTableParagraph(XWPFDocument document, Map<String, Object> date) { List<XWPFTable> tables = document.getTables(); for (XWPFTable table : tables) { for (XWPFTableRow row : table.getRows()) { for (XWPFTableCell cell : row.getTableCells()) { List<XWPFParagraph> paragraphs = cell.getParagraphs(); List<XWPFParagraph> addList = new ArrayList<>(); for (int i = 0; i < paragraphs.size(); i++) { XWPFParagraph paragraph = paragraphs.get(i); String listname = paragraph.getText().trim(); if (listname.startsWith("($fe")) { addList.add(paragraph); } } try { evalAllParagraphic(addList, date); } catch (Exception e) { logger.error("單元格{}段落賦值異常", cell.getText(), e); } } } } } }
具體的調(diào)用邏輯是先執(zhí)行一次常規(guī)占位符的替換,然后再進(jìn)行段落的替換。WordParagraphHolder 中也是先生成段落,再進(jìn)行段落賦值。
代碼如下
public static File exportWord(Map<String, Object> params, File templateFile, File writeFile) { FileOutputStream fos = null; FileOutputStream outputStream = null; try { long start = System.currentTimeMillis(); logger.info("開(kāi)始寫(xiě)入word文件"); //獲取模板文檔 logger.info("開(kāi)始寫(xiě)入word模板文件路徑:{}", templateFile.getPath()); // 寫(xiě)入word常規(guī)替換 XWPFDocument doc = WordExportUtil.exportWord07(templateFile.getPath(), params); // 循環(huán)寫(xiě)段落 WordParagraphHolder paragraphHolder = new WordParagraphHolder(doc, writeFile.getPath(), params); String targetFileName = paragraphHolder.execute(); logger.info("寫(xiě)入word文件完成,耗時(shí){}", (System.currentTimeMillis() - start)); return new File(targetFileName); } catch (Exception e) { logger.error("寫(xiě)入word文件異常:", e); } finally { IOUtils.closeQuietly(fos); IOUtils.closeQuietly(outputStream); } return null; }
使用方法
新建word插入如下內(nèi)容:
($fe:resultList [createDate],我使用[number]元,購(gòu)買(mǎi)蘋(píng)果[amount]個(gè),截止日期[endDate],使用支付方式[type])
其中resultList 為入?yún)ist的名稱(chēng),可以自由更換但是要和代碼里的對(duì)應(yīng)上,因?yàn)槲覀円啥鄠€(gè)段落,所以參數(shù)要構(gòu)造為list的形式。
**[createDate]**內(nèi)createDate為list中實(shí)體的字段名稱(chēng),其他的為固定格式,不要修改,生成完成后會(huì)自動(dòng)刪除多余的字符串。
執(zhí)行下面的測(cè)試代碼:
public static void main(String[] args) throws Exception { Map<String, Object> date = createDate(); String sourceFile = "d:/temp/模版word.docx"; String targetFile = "d:/temp/輸出結(jié)果.docx"; MyXWPFDocument first = WordCache.getXWPFDocument(sourceFile); WordParagraphHolder test = new WordParagraphHolder(first, targetFile, date); test.execute(); } private static Map<String, Object> createDate() { //填充數(shù)據(jù) List<WordExportBatch> resultList = new ArrayList<>(); WordExportBatch wordExport = new WordExportBatch(); WordExportBatch wordExport1 = new WordExportBatch(); wordExport.setCreateDate("2022/9/30"); wordExport1.setCreateDate("2022/9/28"); wordExport.setNumber("11"); wordExport1.setNumber("15"); wordExport.setAmount("1234.5"); wordExport1.setAmount("2345.77"); wordExport.setEndDate("2022/12/31"); wordExport1.setEndDate("2022/11/30"); wordExport.setType("支付寶"); wordExport1.setType("微信"); resultList.add(wordExport); resultList.add(wordExport1); //準(zhǔn)備數(shù)據(jù) Map<String, Object> params = new HashMap<>(); params.put("resultList", resultList); return params; }
看到輸出如下就大功告成了。
補(bǔ)充pom依賴(lài)
有時(shí)候項(xiàng)目里poi的版本與easypoi李的poi會(huì)沖突,需要解決一下沖突。
<dependency> <groupId>cn.afterturn</groupId> <artifactId>easypoi-base</artifactId> <version>4.1.3</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml-schemas</artifactId> <version>4.1.2</version> </dependency> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.11.0</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> </dependency> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.7.20</version> </dependency> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.16.1</version> </dependency>
word表格里段落復(fù)制
奇怪的需求千千萬(wàn),遇到了需要在單元格內(nèi)進(jìn)行段落的循環(huán)復(fù)制。需要在生產(chǎn)段落的基礎(chǔ)上做一下調(diào)整。增加創(chuàng)建表格段落的方法,并修改execute()執(zhí)行方法。
public String execute() throws Exception { MyXWPFDocument second = null; FileOutputStream fos2 = null; try { Map<String, Object> data = this.data; // 循環(huán)生成段落 this.parseAllParagraphic(sourceDocument.getParagraphs(), data); // 循環(huán)生成表格段落 this.createTableParagraph(sourceDocument, data); File targetFile = new File(targetFileName); FileOutputStream fos = FileUtils.openOutputStream(targetFile); sourceDocument.write(fos); // 關(guān)閉流 fos.close(); sourceDocument.close(); second = WordCache.getXWPFDocument(targetFile.getPath()); // 循環(huán)段落賦值 this.evalAllParagraphic(second.getParagraphs(), data); // 循環(huán)表格段落賦值 this.evalTableParagraph(second, data); fos2 = FileUtils.openOutputStream(targetFile); second.write(fos2); } catch (Exception e) { logger.error("生成word段落失敗:", e); return ""; } finally { // 關(guān)閉流 fos2.close(); second.close(); } return targetFileName; } /** * 復(fù)制表格段落 * * @param source 原段落 * @param cell */ public XWPFParagraph copyTableParagraph(XWPFParagraph source, XWPFTableCell cell) { // 使用游標(biāo)創(chuàng)建一個(gè)新行 XmlCursor cursor = source.getCTP().newCursor(); // 在游標(biāo)位置插入新段落 XWPFParagraph newParagraph = cell.insertNewParagraph(cursor); // 復(fù)制底層 XML 對(duì)象 XmlObject copy = source.getCTP().copy(); newParagraph.getCTP().set(copy); // 關(guān)閉游標(biāo) cursor.dispose(); return newParagraph; } /** * 表格段落復(fù)制 * * @param document * @param data */ private void createTableParagraph(XWPFDocument document, Map<String, Object> data) { Object resultList = data.get("resultList"); if (Objects.isNull(resultList)) { return; } List list = (List) resultList; List<XWPFTable> tables = document.getTables(); for (XWPFTable table : tables) { for (XWPFTableRow row : table.getRows()) { for (XWPFTableCell cell : row.getTableCells()) { XWPFParagraph xwpfParagraph = null; List<XWPFParagraph> paragraphs = cell.getParagraphs(); for (int i = 0; i < paragraphs.size(); i++) { XWPFParagraph paragraph = paragraphs.get(i); String listname = paragraph.getText().trim(); if (listname.startsWith("($fe")) { xwpfParagraph = paragraph; } } if (Objects.nonNull(xwpfParagraph)) { for (int i = 0; i < list.size() - 1; i++) { copyTableParagraph(xwpfParagraph, cell); } } } } } } /** * 表格段落賦值 * * @param document * @param date */ private void evalTableParagraph(XWPFDocument document, Map<String, Object> date) { List<XWPFTable> tables = document.getTables(); for (XWPFTable table : tables) { for (XWPFTableRow row : table.getRows()) { for (XWPFTableCell cell : row.getTableCells()) { List<XWPFParagraph> paragraphs = cell.getParagraphs(); List<XWPFParagraph> addList = new ArrayList<>(); for (int i = 0; i < paragraphs.size(); i++) { XWPFParagraph paragraph = paragraphs.get(i); String listname = paragraph.getText().trim(); if (listname.startsWith("($fe")) { addList.add(paragraph); } } try { evalAllParagraphic(addList, date); } catch (Exception e) { logger.error("單元格{}段落賦值異常", cell.getText(), e); } } } } } public static void main(String[] args) throws Exception { Map<String, Object> date = createDate(); String sourceFile = "d:/temp/模版word1.docx"; String targetFile = "d:/temp/輸出結(jié)果1.docx"; MyXWPFDocument first = WordCache.getXWPFDocument(sourceFile); WordParagraphHolder1 test = new WordParagraphHolder1(first, targetFile, date); test.execute(); } private static Map<String, Object> createDate() { //填充數(shù)據(jù) List<WordExportBatch> resultList = new ArrayList<>(); WordExportBatch wordExport = new WordExportBatch(); WordExportBatch wordExport1 = new WordExportBatch(); wordExport.setCreateDate("2022/9/30"); wordExport1.setCreateDate("2022/9/28"); wordExport.setNumber("11"); wordExport1.setNumber("15"); wordExport.setAmount("1234.5"); wordExport1.setAmount("2345.77"); wordExport.setEndDate("2022/12/31"); wordExport1.setEndDate("2022/11/30"); wordExport.setType("支付寶"); wordExport1.setType("微信"); resultList.add(wordExport); resultList.add(wordExport1); //準(zhǔn)備數(shù)據(jù) Map<String, Object> params = new HashMap<>(); params.put("resultList", resultList); return params; }
($fe:resultList [createDate],我使用[number]元,購(gòu)買(mǎi)蘋(píng)果[amount]個(gè),截止日期[endDate],使用支付方式[type])
在word中新建表格,在對(duì)應(yīng)的單元格里寫(xiě)入上面需要循環(huán)的內(nèi)容。執(zhí)行測(cè)試代碼就可以看到輸出結(jié)果。
以上就是使用EasyPoi實(shí)現(xiàn)word文檔生成和段落循環(huán)的詳細(xì)內(nèi)容,更多關(guān)于EasyPoi word操作的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java JDBC自定義封裝工具類(lèi)的步驟和完整代碼
這篇文章主要給大家介紹了關(guān)于Java JDBC自定義封裝工具類(lèi)的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-02-02Java利用Dijkstra算法求解拓?fù)潢P(guān)系最短路徑
迪杰斯特拉算法(Dijkstra)是由荷蘭計(jì)算機(jī)科學(xué)迪家迪杰斯特拉于1959年提出的,因此又叫狄克斯特拉算法。本文將利用迪克斯特拉(Dijkstra)算法求拓?fù)潢P(guān)系最短路徑,感興趣的可以了解一下2022-07-07Java中的Monad設(shè)計(jì)模式及其實(shí)現(xiàn)過(guò)程
本文介紹了Java中的Monad設(shè)計(jì)模式及其在函數(shù)式編程中的應(yīng)用,雖然Java不是函數(shù)式編程語(yǔ)言,但可以通過(guò)接口和泛型模擬Monad的行為,實(shí)現(xiàn)鏈?zhǔn)秸{(diào)用和上下文管理,通過(guò)一個(gè)示例展示了如何使用OptionalMonad進(jìn)行鏈?zhǔn)秸{(diào)用,并解析了Monad接口和OptionalMonad的實(shí)現(xiàn)細(xì)節(jié)2025-03-03Java對(duì)接阿里云短信服務(wù)保姆級(jí)教程(新手秒會(huì))
這篇文章主要介紹了如何在阿里云上申請(qǐng)短信服務(wù)以及如何使用Java代碼進(jìn)行對(duì)接,包括申請(qǐng)資質(zhì)、簽名和模板,以及編寫(xiě)Java代碼整合成工具類(lèi)進(jìn)行調(diào)用的步驟,需要的朋友可以參考下2024-12-12Java微信公眾平臺(tái)開(kāi)發(fā)(11) 微信三大平臺(tái)的關(guān)聯(lián)
這篇文章主要介紹了Java微信公眾平臺(tái)開(kāi)發(fā)第十一步,微信開(kāi)發(fā)中微信公眾平臺(tái)、開(kāi)放平臺(tái)和商戶(hù)平臺(tái)的關(guān)聯(lián),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-04-04Java實(shí)現(xiàn)的簡(jiǎn)單音樂(lè)播放器功能示例
這篇文章主要介紹了Java實(shí)現(xiàn)的簡(jiǎn)單音樂(lè)播放器功能,涉及java針對(duì)多媒體文件相關(guān)載入、播放相關(guān)操作技巧,需要的朋友可以參考下2019-02-02Java內(nèi)部類(lèi)和匿名內(nèi)部類(lèi)的用法說(shuō)明
這篇文章主要介紹了Java內(nèi)部類(lèi)和匿名內(nèi)部類(lèi)的用法說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-08-08