java 应用分模块_在Java 11中创建一个简单的模块化应用教程
模塊化編程使人們能夠將代碼組織成獨立的,有凝聚力的模塊,這些模塊可以組合在一起以實現所需的功能。
本文摘自Nick Samoylov和Mohamed Sanaulla撰寫的一本名為Java 11 Cookbook - Second Edition的書。在本書中,您將學習如何使用Java?11中的類和接口實現面向對象的設計?。
可以在GitHub上找到本教程中顯示的示例的完整代碼。
您應該想知道這種模塊化是什么,以及如何使用Java創建模塊化應用程序?。在本文中,我們將通過一個簡單的示例來嘗試清除在Java中創建模塊化應用程序的困惑?。我們的目標是向您展示如何創建模塊化應用程序;?因此,我們選擇了一個簡單的例子,以便專注于我們的目標。
做什么
我們的示例是一個簡單的高級計算器,它檢查數字是否為素數,計算素數之和,檢查數字是否為偶數,并計算偶數和奇數之和。
做好準備
我們將應用程序分為兩個模塊:
math.util模塊包含用于執行數學計算的API
calculator模塊啟動了一個高級計算器
怎么做
1.?讓我們實現com.packt.math.MathUtil中的API,從isPrime(Integer number)API開始:public static Boolean isPrime(Integer number){
if ( number == 1 ) { return false; }
return IntStream.range(2,num).noneMatch(i -> num % i == 0 );
}
2. 實現sumOfFirstNPrimes(Integer count)
public static Integer sumOfFirstNPrimes(Integer count){
return IntStream.iterate(1,i -> i+1)
.filter(j -> isPrime(j))
.limit(count).sum();
}
3.?讓我們寫一個函數來檢查數字是否是偶數:
public static Boolean isEven(Integer number){
return number % 2 == 0;
}
4. 非isEven結果告訴我們這個數字是否是奇數。我們可以使用函數來查找前N個偶數和前N個奇數之和,如下所示:
public static Integer sumOfFirstNEvens(Integer count){
return IntStream.iterate(1,i -> i+1)
.filter(j -> isEven(j))
.limit(count).sum();
}
public static Integer sumOfFirstNOdds(Integer count){
return IntStream.iterate(1,i -> i+1) .filter(j -> !isEven(j)) .limit(count).sum();
}
我們可以在前面的API中看到重復以下操作:
從數字1開始的無限數字序列
根據某些條件過濾數字
將流的數量限制為給定的計數
找到由此獲得的數字之和
根據我們的觀察,我們可以重構前面的API并將這些操作提取到一個方法中,如下所示:
Integer computeFirstNSum(Integer count,
IntPredicate filter){
return IntStream.iterate(1,i ?- > i + 1)
.filter(filter)
.limit(count).sum();
}
這里??count是我們需要找到的總和的數量限制,并且??filter是選擇求和數的條件。
讓我們根據剛剛進行的重構重寫API:
public static Integer sumOfFirstNPrimes(Integer count){ return computeFirstNSum(count, (i -> isPrime(i))); }
public static Integer sumOfFirstNEvens(Integer count){ return computeFirstNSum(count, (i -> isEven(i))); } public static Integer sumOfFirstNOdds(Integer count){ return computeFirstNSum(count, (i -> !isEven(i)));
到目前為止,我們已經看到了一些圍繞數學計算的API。
開始正題
讓我們將這個小實用程序類作為名為的模塊的一部分??math.util。以下是我們用于創建模塊的一些約定:
將與模塊相關的所有代碼放在一個名為的目錄下math.util,并將其視為我們的模塊根目錄。
在根文件夾中,插入名為module-info.java.的文件
將包和代碼文件放在根目錄下。
module-info.java包含什么?
模塊的名稱
它導出的包,即可供其他模塊使用的包
它依賴的模塊
它使用的服務
它為其提供實施的服務
我們的math.util模塊不依賴于任何其他模塊(當然,java.base模塊除外)。但是,它使其API可用于其他模塊(如果沒有,那么這個模塊的存在是有問題的)。讓我們繼續把這個陳述放到代碼中:
module math.util {
exports com.packt.math;
}
我們告訴Java編譯器和運行時我們的math.util?模塊正在將com.packt.math包中的代碼導出到任何依賴的模塊math.util。
可以在以下位置找到此模塊的代碼??Chapter03/2_simple-modular-math-util/math.util。
現在,讓我們創建另一個使用該math.util模塊的模塊計算器。該模塊有一個Calculator類,其工作是接受用戶選擇執行哪個數學運算,然后執行操作所需的輸入。用戶可以從五種可用的數學運算中進行選擇:
素數檢查
偶數號檢查
N素數總和
N偶數總和
N奇數總和
我們在代碼中看到這個:
private static Integer acceptChoice(Scanner reader){
System.out.println("************Advanced Calculator************");
System.out.println("1. Prime Number check");
System.out.println("2. Even Number check");
System.out.println("3. Sum of N Primes");
System.out.println("4. Sum of N Evens");
System.out.println("5. Sum of N Odds");
System.out.println("6. Exit");
System.out.println("Enter the number to choose operation");
return reader.nextInt();
}
然后,對于每個選項,我們接受所需的輸入并調用相應的MathUtilAPI,如下所示:
switch(choice){
case 1:
System.out.println("Enter the number");
Integer number = reader.nextInt();
if (MathUtil.isPrime(number)){
System.out.println("The number "+ number +" is prime");
}else{
System.out.println("The number "+ number +" is not prime");
}
break;
case 2:
System.out.println("Enter the number");
Integer number = reader.nextInt();
if (MathUtil.isEven(number)){
System.out.println("The number "+ number +" is even");
}
break;
case 3:
System.out.println("How many primes?");
Integer count = reader.nextInt();
System.out.println(String.format("Sum of %d primes is %d",
count, MathUtil.sumOfFirstNPrimes(count)));
break;
case 4:
System.out.println("How many evens?");
Integer count = reader.nextInt();
System.out.println(String.format("Sum of %d evens is %d",
count, MathUtil.sumOfFirstNEvens(count)));
break;
case 5:
System.out.println("How many odds?");
Integer count = reader.nextInt();
System.out.println(String.format("Sum of %d odds is %d",
count, MathUtil.sumOfFirstNOdds(count)));
break;
}
讓我們calculator以與為模塊創建模塊相同的方式為模塊創建模塊定義math.util:
module calculator{
requires math.util;
}
在前面的模塊定義中,我們提到??calculator模塊依賴于??math.util模塊使用??required?關鍵字。
讓我們編譯代碼:
javac -d mods --module-source-path . $(find . -name "*.java")
--module-source-path?命令是??javac新的命令行選項,用于指定模塊源代碼的位置。
讓我們執行前面的代碼:
java --module-path mods -m calculator/com.packt.calculator.Calculator
--module-path?命令類似于--classpath,是新java的命令行選項???,指定已編譯模塊的位置。
運行上述命令后,您將看到計算器正在運行。
我們提供了腳本來測試Windows和Linux平臺上的代碼?。請使用run.bat用于Windows和run.sh用于?Linux的。
原理
現在您已經完成了示例,我們將了解如何對其進行概括,以便我們可以在所有模塊中應用相同的模式。我們遵循特定的約定來創建模塊:
| application_root_directory
| --module1_root
| ---- module-info.java
| ---- com
| ------ packt
| -------- sample
| --------- -MyClass.java
| --module2_root
| ---- module-info.java
| ---- com
| ------ packt
| -------- test
| ------- ---MyAnotherClass.java
我們將特定于模塊的代碼放在其文件夾中,并在文件夾module-info.java?的根目錄下放置相應的文件。這樣,代碼組織得很好。
{Annotation} [open] module ModuleName {{ModuleStatement}}
這是語法,解釋如下:
{Annotation}:這是表單的任何注釋@Annotation(2)。
open:此關鍵字是可選的。開放模塊通過反射在運行時訪問其所有組件。但是,在編譯時和運行時,只能訪問顯式導出的那些組件。
module:這是用于聲明模塊的關鍵字。
ModuleName:這是模塊的名稱,該模塊是有效的Java標識符,.在標識符名稱之間允許使用dot() - 類似于??math.util。
{ModuleStatement}:這是模塊定義中允許的語句的集合。讓我們接下來展開。
模塊語句具有以下形式:
ModuleStatement:
requires {RequiresModifier} ModuleName ;
exports PackageName [to ModuleName {, ModuleName}] ;
opens PackageName [to ModuleName {, ModuleName}] ;
uses TypeName ;
provides TypeName with TypeName {, TypeName} ;
模塊語句在這里被解碼:
requires:這用于聲明對模塊的依賴。{RequiresModifier}可以是傳遞的,靜態的,或兩者兼而有之。傳遞意味著依賴于給定模塊的任何模塊也隱式地依賴于給定模塊傳遞所需的模塊。靜態意味著模塊依賴在編譯時是必需的,但在運行時是可選的。一些例子是??requires math.util,requires transitive math.util和??requires static math.util。
exports:這用于使依賴模塊可以訪問給定的包。或者,我們可以通過指定模塊名稱來強制包對特定模塊的可訪問性,例如??exports com.package.math to claculator。
opens:這用于打開特定包。我們之前看到,我們可以通過open使用模塊聲明指定關鍵字來打開模塊。但這可能是限制性較小的。因此,為了使其更具限制性,我們可以使用openskeyword-?在運行時打開一個特定的反射訪問包opens com.packt.math。
uses:這用于聲明可通過可訪問的服務接口的依賴項java.util.ServiceLoader。服務接口可以位于當前模塊中,也可以位于當前模塊所依賴的任何模塊中。
provides:這用于聲明服務接口并為其提供至少一個實現。可以在當前模塊或任何其他相關模塊中聲明服務接口。但是,必須在同一模塊中提供服務實現;?否則,將發生編譯時錯誤。
我們將在使用服務中更詳細地查看uses和provides子句,??以在消費者和提供者模塊??配方之間創建松散耦合。
可以使用--module-source-path命令行選項一次編譯所有模塊的模塊源。這樣,所有模塊都將被編譯并放置在該-d選項提供的目錄下的相應目錄中。例如,??javac -d mods?--module-source-path . $(find . -name "*.java")?將當前目錄中的代碼編譯到mods?目錄中。
運行代碼同樣簡單。我們使用命令行選項指定編譯所有模塊的路徑??--module-path。然后,我們使用命令行選項提及模塊名稱以及完全限定的主類名稱??-m,例如??java --module-path mods?-m calculator/com.packt.calculator.Calculator。
總結
以上是生活随笔為你收集整理的java 应用分模块_在Java 11中创建一个简单的模块化应用教程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: css打印适应纸张_从生态平衡到打印机故
- 下一篇: Java集合之LinkedHashMap