模块化编程使人们能够将代码组织成独立的,有凝聚力的模块,这些模块可以组合在一起以实现所需的功能。

本文摘自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中创建一个简单的模块化应用教程相关推荐

  1. java qq ui界面_java swing 创建一个简单的QQ界面教程

    记录自己用java swing做的第一个简易界面. LoginAction.java package com.QQUI0819; import javax.swing.*; import java.a ...

  2. Java乐谱_如何在java中创建一个简单但结构良好的乐谱表(乐谱)?

    我正在使用非常基本的声音合成在我的游戏中创建音频和效果.基本上,我有一些方法可以发出一个频率和频率的声音.幅度和幅度持续时间. 对于短语和旋律,我想提出一个基本的符号,这样我就可以轻松地重写或添加新的 ...

  3. mysql求数值分位点_如何计算一组数据中任意一个数值的分位值?

    本篇补充,关于评论区@慢中取利 的提问. 分析:你的疑问是 想要逆推 算出percentile()方法的k值,也就是分位值对应的百分数. 逆推,最好的办法我认为是 找到Excel 关于percenti ...

  4. 用java画网状图_如何在背景中绘制一个带网格线的漂亮条形图?

    我是Matlab的新手 . 我做了一个实验 . 我需要帮助将结果绘制在2条形图(具有不同颜色)中,结果图形需要有网格线 . 有人建议使用非常matlap的代码输出一个漂亮的情节吗? 数据如下: x轴是 ...

  5. mysql auto_increment建表_在mysql表中创建一个ID auto_increment(在事实之后)

    小编典典 例如,这是一个具有主键但不是的表AUTO_INCREMENT: mysql> CREATE TABLE foo ( id INT NOT NULL, PRIMARY KEY (id) ...

  6. python制作一个计时器_如何在python中创建一个反应计时器作为GUI?

    我有一个反应计时器的问题.我想在我的程序中有一个按钮,它能告诉我从定时器开始按下按钮所花的时间.在 假设我打开我的程序,我想要一个按钮,如果我点击它,它将打印出计时器启动后我按下它所花的时间.然后在我 ...

  7. iar怎么新建立项目_请简述IAR中创建一个应用项目的步骤及配置过程。

    [多选题]公路工程中应用的石灰要求达到( ). [多选题]在石灰工业废渣稳定土施工前,取有代表性石灰样品进行下列试验( ). [单选题]在沥青混合料中掺加适量消石灰粉,可以有效提高沥青混合料的( ). ...

  8. docker mysql 蜂巢_在网易蜂巢中创建和管理Docker容器的教程

    创建容器点击左侧的导航菜单「容器管理」,进入容器管理列表页,通过点击容器列表左上角的「创建容器」按钮可进入创建容器页面,如下图所示: 其中带 * 为必填项. 选择镜像可选择的镜像分为「我的镜像」和「官 ...

  9. java项目----教务管理系统_基于Java的教务管理系统

    java项目----教务管理系统_基于Java的教务管理系统 2022-04-22 18:18·java基础 最近为客户开发了一套学校用教务管理系统,主要实现学生.课程.老师.选课等相关的信息化管理功 ...

最新文章

  1. HashMap实现原理
  2. 客户关系管理系统CRM(Customer Relationship Management)
  3. 比较两个文件夹图像相似度
  4. 面试腾讯算法:组合总和
  5. hdu 5570(数学期望)
  6. 简单阻容降压电路图_X2安规电容用于阻容降压容量衰减原因及对策
  7. 【音视频安卓开发 (零)】用 Android NDK 编译 FFmpeg 与 X264
  8. Angular 星级评分组件
  9. linux启动关闭脚本,Linux中启动/停止/重启/状态的startup脚本
  10. Xcode C++ and Objective-C refactoring
  11. 文字转语音软件真人发声(声音自然有感情)
  12. IT服务管理、IT运维管理、IT运营管理
  13. 架构师职位常见面试题
  14. Mysql基本知识1
  15. BLE4.0广播连接过程的底层剖析
  16. 【蓝桥备赛冲刺】2022年第十三届省赛模拟题题解C/C++
  17. 遥感影像语义分割数据集
  18. CA Server证书申请与颁发 Apache2 HTTPS
  19. NumPy的数组对象:ndarray
  20. ARM嵌入式系统开发:软件设计与优化--第二章ARM处理器基础

热门文章

  1. android layoutparams,Android LayoutParams用法解析
  2. FreeRTOS学习及移植笔记之一:开始FreeRTOS之旅
  3. 现代软件工程系列 学生读后感 梦断代码 DTSlob (2)
  4. 现代软件工程系列 学生的精彩文章 (3) 如何在Bug 不断的情况下还能保持平常心...
  5. hashmap扩容机制_图文并茂:HashMap经典详解!
  6. 中北大学计算机二级负责老师,导师信息#中北大学计算机与控制工程学院研究生导师介绍#秦品乐...
  7. sql android客户端,XSGManage: 学生成绩管理系统---客户端,基于Android+Django+sqlit3开发...
  8. vue和java实现页面增删改_SpringBoot-Vue实现增删改查及分页小DEMO
  9. python的py文件打包成exe_将python项目.py文件打包成.exe文件
  10. 运行catia_CATIA清除错误目录及防串链接