百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术资源 > 正文

简单说说 java中的 模块系统 java 工程 模块 包关系

lipiwang 2024-10-29 15:55 9 浏览 0 评论

java中一般的包和类的封装系统已经不能满足我们对访问的控制。

因此有了Java9有了模块系统,有以下2个优点:

1.强封装性:我们可以控制哪些包是可访问的,并且无需区维护那些我们不想公开给大众的代码

2.可靠的配置:我们可以避免类重复或类丢失,这类常见的路径问题

对模块命名

模块是包的集合 ,并且模块名可以和包名重复。

模块名只模块声明中。在java类源文件中,永远不应该引用模块名

使用模块

模块文件在项目的根目录,模块文件叫 **module-info.java **。

如果我们编写这么一个类文件:

package mo_kuai;


import javax.swing.JOptionPane;

public class test1 {

public static void main(String[] args) {

JOptionPane.showMessageDialog(null, "Hello java");

//这个目的是用一个JOptionPane对象展示Hello java消息

}

}

这时,如果 module-info.java 为:

module mo_kuai {

}

mo_kuai模块里没有引入其他模块。那么编译会报错。因为javax.swing包在java.desktop模块中。我们的模块需要声明它依赖这个模块:

module mo_kuai {

requires java.desktop;

}

用requires 来需求模块。这样你的模块就可以用这个模块了;

注意:java.lang和java.io是默认需求在模块中的

我们的 mo_kuai模块只列出了自己的模块需求,它需要java.desktop模块,这样我们才能用javax.swing包。而java.desktop模块自身声明了3个它需要的模块。即java.datatransfer,java.prefs和java.xml

下面这个图就表示了模块之间的需求关系:



要注意的是:

模块中不能有环,即一个模块不能直接或间接的对自己产生依赖

模块不会自动把访问权限传递给其他模块,如上面的例子:mo_kuai模块需求java.desktop模块,java.desktop模块需求java.xml模块,这不会给予mo_kuai模块访问java.xml模块中包的权力。即按数学的术语来说,require不会是传递性的。 这使得需求必须明确化,是我们想要的行为

导出包

我们可以看到一个模块如果想要用其他模块中的包,就必须声明该模块。但并不会自动使所需模块中的所有包都可用。

模块可以用exports关键词来声明哪些包可用,如

module mo_kuai {

requires java.desktop;

exports 包1;

exports 包2;

.....

}

这个模块可以让导出的包可用,通过不导出的其他包来隐藏他们。没有导出的包在其自己的模块之外是不可访问的。

警告:模块没有作用域的概念,不能在不同的模块中放置2个相同名字的包。即使是隐藏的包(即没有导出的包)

模块和反射式访问

按照纯理论来说,破坏对象的封装并窥视其私有的成员是错误的。但是像对象-关系映射和XML/Json绑定这样的机制应用的十分广泛,使得模块系统也必须接纳 这种行为。

通过opens关键词,模块就可以打开包,从而启动对给定包的类的所有实例进行放射式访问。

module mo_kuai {

requires java.desktop;

opens 包;//声明自己模块的这个包 为open的

}

也可以像这样声明 Open:

open module mo_kuai {//这样就声明这个模块为开放模块

requires java.desktop;

}

开放的模块可以授权对所有的包的运行时访问,就像所有的包都用exports和opens声明过一样,但是,在运行时只有显式导出的包是可访问的。开发模块将模块系统编译时的安全性和经典的授权许可的运行时行为结合在一起。

JAR文件中除了类文件和清单外,还可以包含文件资源文件,它可以被Class.getResourceAsStream方法加载,现在还可以被Module.getResourceAsStream方法加载。如果资源文件在某个包里,那么这个包必须对调用者是开放的

自动模块

如果JAR文件没有进行模块化,那么我们有两种机制来完成 未模块化的JAR包与模块化的项目的适配:自动化模块和不具名模块

我们可以通过把任何JAR文件放置于模块路径的目录而不是类路径的目录中,实现将其转换为一个模块。模块路径上没有modul-info.class文件的JAR被称为自动模块。

模块隐式对其他所有模块 requires

所有的包都被导出且是开放的

如果JAR文件清单META-INF/MANIFEST.MF中具有 建为Automatic-Module-Name的项,那么它的值就会变成模块名

否则模块名从JAR的文件名获得,将文件尾部的版本号删除,将非字母 数字的字符都替换为.句点

如一个JAR包叫 jk-dk-1.5.jar 那么这个模块名叫 jk.dk

在将jar包放在模块路径前,请检查这个jar是否是已经模块化的

不具名模块

任何不在模块路径的类都是不具名模块的一部分。从计算上来说,可能有多个不具名的模块,但他们合起来就像是单个不具名模块。


与自动模块一样,不具名模块可以访问其他的模块,他们的所有包都会被导出,并且都是开放的。


但是,没有任何**明确模块**可以访问不具名模块(明确模块是指不是自动模块或不具名模块的模块)。换句话来说明确模块总是可以避免类路径的坑。

自动模块可以读取不具名模块,因此他们的依赖关系放在类路径中

传递的需求和静态的需求

之前说到模块之间的需求是不具有传递性的,但这样也会带来很多麻烦,比如声明所有需要的模块会十分的冗长。所以现在你可以这样做:

module mo_kuai {

requires transitive 模块1;

}

requires transitive 就是传递性需求。在这个例子中,你在mo_kuai模块中需求模块1,如果模块1也需求其他模块的话,你就不用再在mo_kuai中在声明需求 模块1需求的模块,它自动就帮你完成了。 这听起来有点绕。


我们回想起之前讲java.desktop模块时,我们说过java.desktop模块需求着其他3个模块,但我们事件上在我们自己的模块中只声明java.desktop这1个模块就可以使用java.desktop模块了。这是为什么呢? 因为在java.desktop模块中用的就是requires transitive来需求其他3个模块。这样我们自己的模块就相当于需求了这4个模块。

module java.desktop {

requires java.prefs;

requires transitive java.datatransfer;

requires transitive java.xml;

.....

}

还有一种不常见的 requires static声明方式。它声明一个模块必须在编译时出现,而在运行时是可选的。可用于访问位于不同模块的编译时注解。

限定导出和开放

限定导出就是 限定你导出的这个包只能由你指定的模块访问:

module test {

exports 包 to 模块1,模块2....;

}//限定导出

同理限定开放 就是 限定你导出的这个包只能由你指定的模块 通过反射访问私有数据:

module test {

opens 包 to 模块1,模块2....;

}//限定开放

服务加载器

我们先介绍一下服务加载器,java的jdk中带有简易的服务加载器类。用法是通过指定的 接口的Class对象,得到实现了该接口的实现类的对象。

这里给出一个例子:

public interface hello {

String hello();

}//这是hello接口

------------------------------------------

public class hello_1 implements hello{

@Override

public String hello() {

return "这是hello_1的问候";

}

}//这是一个hello接口的实现类

---------------------------------

public class hello_2 implements hello{

@Override

public String hello() {

return "这是hello_2的问候";

}

}

//这是另一个hello接口的实现类

----------------------------------------

public static void main(String[] args) {

//通过ServiceLoader类static的load(Class对象)方法得到一个 指定接口的

//实现类的对象合集,这要要求你的实现类有public的无参构造方法

ServiceLoader<hello> helloLoader=ServiceLoader.load(hello.class);

for(var i:helloLoader) {

//调用每个实现类对象的实现的hello方法

System.out.println(i.hello());

}

}//这是测试代码

----------------------------

//最终输出:

"这是hello_1的问候

这是hello_2的问候"

在过去实现类是放在jar包里的META-INF/services目录中提供的,

但现在可用模块声明这种实现类:

module npc {

exports npc;

provides npc.hello with npc.hello_1,npc.hello_2;

}//这样就相当于提供了hello接口npc.hello_1,npc.hello_2实现类

module mo_kuai {

requires npc;

uses npc.hello;

}//提供uses 接口 这样的格式来使用

provides和uses声明的效果,使得消费该服务的模块允许访问私有实现类

相关推荐

linux实例之设置时区的方式有哪些

linux系统下的时间管理是一个复杂但精细的功能,而时区又是时间管理非常重要的一个辅助功能。时区解决了本地时间和UTC时间的差异,从而确保了linux系统下时间戳和时间的准确性和一致性。比如文件的时间...

Linux set命令用法(linux cp命令的用法)

Linux中的set命令用于设置或显示系统环境变量。1.设置环境变量:-setVAR=value:设置环境变量VAR的值为value。-exportVAR:将已设置的环境变量VAR导出,使其...

python环境怎么搭建?小白看完就会!简简单单

很多小伙伴安装了python不会搭建环境,看完这个你就会了Python可应用于多平台包括Linux和MacOSX。你可以通过终端窗口输入"python"命令来查看本地是否...

Linux环境下如何设置多个交叉编译工具链?

常见的Linux操作系统都可以通过包管理器安装交叉编译工具链,比如Ubuntu环境下使用如下命令安装gcc交叉编译器:sudoapt-getinstallgcc-arm-linux-gnueab...

JMeter环境变量配置技巧与注意事项

通过给JMeter配置环境变量,可以快捷的打开JMeter:打开终端。执行jmeter。配置环境变量的方法如下。Mac和Linux系统在~/.bashrc中加如下内容:export...

C/C++|头文件、源文件分开写的源起及作用

1C/C++编译模式通常,在一个C++程序中,只包含两类文件——.cpp文件和.h文件。其中,.cpp文件被称作C++源文件,里面放的都是C++的源代码;而.h文件则被称...

linux中内部变量,环境变量,用户变量的区别

unixshell的变量分类在Shell中有三种变量:内部变量,环境变量,用户变量。内部变量:系统提供,不用定义,不能修改环境变量:系统提供,不用定义,可以修改,可以利用export将用户变量转为环...

在Linux中输入一行命令后究竟发生了什么?

Linux,这个开源的操作系统巨人,以其强大的命令行界面而闻名。无论你是初学者还是经验丰富的系统管理员,理解在Linux终端输入一条命令并按下回车后发生的事情,都是掌握Linux核心的关键。从表面上看...

Nodejs安装、配置与快速入门(node. js安装)

Nodejs是现代JavaScript语言产生革命性变化的一个主要框架,它使得JavaScript从一门浏览器语言成为可以在服务器端运行、开发各种各样应用的通用语言。在不同的平台下,Nodejs的安装...

Ollama使用指南【超全版】(olaplex使用方法图解)

一、Ollama快速入门Ollama是一个用于在本地运行大型语言模型的工具,下面将介绍如何在不同操作系统上安装和使用Ollama。官网:https://ollama.comGithub:http...

linux移植(linux移植lvgl)

1uboot移植l移植linux之前需要先移植一个bootlader代码,主要用于启动linux内核,lLinux系统包括u-boot、内核、根文件系统(rootfs)l引导程序的主要作用将...

Linux日常小技巧参数优化(linux参数调优)

Linux系统参数优化可以让系统更加稳定、高效、安全,提高系统的性能和使用体验。下面列出一些常见的Linux系统参数优化示例,包括修改默认配置、网络等多方面。1.修改默认配置1.1修改默认编辑器默...

Linux系统编程—条件变量(linux 条件变量开销)

条件变量是用来等待线程而不是上锁的,条件变量通常和互斥锁一起使用。条件变量之所以要和互斥锁一起使用,主要是因为互斥锁的一个明显的特点就是它只有两种状态:锁定和非锁定,而条件变量可以通过允许线程阻塞和等...

面试题-Linux系统优化进阶学习(linux系统的优化)

一.基础必备优化:1.关闭SElinux2.FirewalldCenetOS7Iptables(C6)安全组(阿里云)3.网络管理服务||NetworkManager|network...

嵌入式Linux开发教程:Linux Shell

本章重点介绍Linux的常用操作和命令。在介绍命令之前,先对Linux的Shell进行了简单介绍,然后按照大多数用户的使用习惯,对各种操作和相关命令进行了分类介绍。对相关命令的介绍都力求通俗易懂,都给...

取消回复欢迎 发表评论: