java 模块设计模式_Java9模块化学习笔记二之模块设计模式

模块设计的原则:

1、防止出现编译时循环依赖(主要是编译器不支持),但运行时是允许循环依赖的,比如GUI应用

2、明确模块的边界

几种模块设计:

API模块,聚合模块(比如java.base)

可选依赖

两种方式:

1、可选的编译时依赖(类似于maven的provided scope)声明: requires static , requires transitive static

2、使用services模式,缺点就是需要使用侵入性的ServiceLoader API

使用编译时可选依赖

module framework {

requires static fastjsonlib;

}

public static void main(String... args) {

try {

Class> clazz = Class.forName("javamodularity.fastjsonlib.FastJson");

FastJson instance =

(FastJson) clazz.getConstructor().newInstance();

System.out.println("Using FastJson");

} catch (ReflectiveOperationException e) {

System.out.println("Oops, we need a fallback!");

}

}

注意,通过requires static声明后,运行时,即使fastjsonlib模块在模块路径中,仍然会跑到异常块中,因为requies static声明的模块不会出现在模块解析路径上。除非你通过jlink打包时,加入--add-modules fastjsonlib选项来显式将其添加到模块解析路径(通过--add-modules也是作为一个root module).

使用Services模式的可选依赖

请参考之前的对于Services的探讨

Versioned Modules

jar命令打包时可以通过 --module-version=选项支持将版本添加到module-info.class中作为一个属性。但是对于模块解析而言,版本是没有意义的,模块解析过程中,只看模块名,不支持版本。

所以如果需要版本化,还是得借助于Maven,Gradle之类的打包工具。

资源封装

分模块内资源访问、模块间资源访问

模块内资源访问

firstresourcemodule/

├── javamodularity

│   └── firstresourcemodule

│   ├── ResourcesInModule.java

│   ├── ResourcesOtherModule.java

│   └── resource_in_package.txt 包内资源

├── module-info.java

└── top_level_resource.txt 与module-info.java平级的资源

访问方式有几种,见下面代码:

public class ResourcesInModule {

public static void main(String... args) throws Exception {

Class clazz = ResourcesInModule.class;

InputStream cz_pkg = clazz.getResourceAsStream("resource_in_package.txt"); //<1>

URL cz_tl = clazz.getResource("/top_level_resource.txt"); //<2>

Module m = clazz.getModule(); //<3>

InputStream m_pkg = m.getResourceAsStream(

"javamodularity/firstresourcemodule/resource_in_package.txt"); //<4>

InputStream m_tl = m.getResourceAsStream("top_level_resource.txt"); //<5>

assert Stream.of(cz_pkg, cz_tl, m_pkg, m_tl)

.noneMatch(Objects::isNull);

}

}

在模块化中,不推荐使用ClassLoder::getResource*

注意上面代码中用到了Module API

跨模块资源访问

.

├── firstresourcemodule

│   ├── javamodularity

│   │   └── firstresourcemodule

│   │   ├── ResourcesInModule.java

│   │   ├── ResourcesOtherModule.java

│   │   └── resource_in_package.txt

│   ├── module-info.java

│   └── top_level_resource.txt

└── secondresourcemodule

├── META-INF

│   └── resource_in_metainf.txt

├── foo

│   └── foo.txt

├── javamodularity

│   └── secondresourcemodule

│   ├── A.java

│   └── resource_in_package2.txt

├── module-info.java

└── top_level_resource2.txt

注意,下面代码的前提是两个模块的包都没暴露给对方

public class ResourcesOtherModule {

public static void main(String... args) throws Exception {

Optional otherModule = ModuleLayer.boot().findModule("secondresourcemodule"); //<1>

otherModule.ifPresent(other -> {

try {

InputStream m_tl = other.getResourceAsStream("top_level_resource2.txt"); //<2>

InputStream m_pkg = other.getResourceAsStream(

"javamodularity/secondresourcemodule/resource_in_package2.txt"); //<3>

InputStream m_class = other.getResourceAsStream(

"javamodularity/secondresourcemodule/A.class"); //<4>

InputStream m_meta = other.getResourceAsStream("META-INF/resource_in_metainf.txt"); //<5>

InputStream cz_pkg =

Class.forName("javamodularity.secondresourcemodule.A")

.getResourceAsStream("resource_in_package2.txt"); //<6>

assert Stream.of(m_tl, m_class, m_meta)

.noneMatch(Objects::isNull);

assert Stream.of(m_pkg, cz_pkg)

.allMatch(Objects::isNull);

} catch (Exception e) {

throw new RuntimeException(e);

}

});

}

}

请注意<1>中的ModuleLayer.boot() API

<2>说明了模块中的top-level资源总是可以被其他模块访问的

<3>将得到null,因为模块2的包没有开放给模块1,模块包中的资源访问遵循模块的封装原则

<4>将返回结果,上面提到资源访问遵循模块封装原则,但对于.class文件除外。(想想也是,因为是允许运行时获取到别的模块封装的Class对象,只是不允许反射调用相关方法)

<5>由于META-INF不是一个包,所以其不会遵循模块封装原则,换言之,也像top-level资源一样,是可以被其他模块访问的。

<6>Class.forName会正常调用,不过接着调用的.getResourceAsStream会返回null,就像<3>说明的一样。

记住一个原则:资源封装只针对包下的(除.class外,包下的.class文件也可以被其他模块访问),其余的不会有封装。

那么问题来了,如果我真的很想公开包下的资源给其他模块呢?

使用open module或者opens 包名,比如:

open module aaa{

...

}

module aaa{

opens a.b.c

}

ResourceBundle

我们知道jdk有个i18n资源加载API: ResourceBundle。它的行为是扫描classpath中的所有资源,只要符合baseName和Local即可加载到。

但是java9模块化当中,无法扫描classpath,只有模块中可以使用ResourceBundle::getBundle

有两种解决方案:

1、定义一个专门的i18n资源模块,并open module

2、使用java9提供的ResourceBundleProvider接口,实现它,并将这个实现注册为服务。

Deep Reflection 与 三方框架

深度反射与浅反射的区别:浅反射只是获取基本的类信息,比如字段名,方法上的注解等,而深度反射会进行字段赋值,方法调用等。

模块化强封装带来的问题就是,我们没法使用深度反射,比如对一个exports包中的某个公开类的private域进行反射调用,field.setAccessible(true)之类的就会出现异常;对非exports包中的类进行任何深度反射都是非法的。

那么我们熟悉的ORM框架,IOC框架等都广泛地使用了深度反射。这就会导致问题。如何解决?使用Services肯定是不行的,因为框架本身改动成本就会很大,没几个愿意这么改。

有两种方式: 1、使用open module或opens 包名, opens 包名 to 模块名;2、使用Module::addOpens运行时动态open。

java9还为反射类添加了canAccess方法、trySetAccessible方法

f82f9d0c30151ed51b03590c90702192.png

使用open module或opens 包名

open允许对open的模块或包进行深度反射

310bd1c0300838eb08b2ad87805dbbbd.png

还有个问题,假如我们想对三方提供的模块进行深度反射,那该怎么办呢,总不能去拿到别人的代码改module-info.java声明吧。这个时候就要用到java命令行参数 --add-opens /=. 比如我想深度反射java.base中的java.lang包,那么可以 --add-opens java.base/java.lang=mymodule,但是如果我不使用模块化,而只是使用classpath-based,那么我们可以使用--add-opens java.base/java.lang=ALL_UNNAMED,指定想未命名ALL_UNNAMED的代码开放。

反射的替代方案:

java9基于JEP193提供了反射的替代方案用于访问非public元素MethodHandles (始于java7),VarHandles(始于java9)

示例:

src

├── application

│ ├── javamodularity

│ │ └── application

│ │ ├── Book.java

│ │ └── Main.java

│ └── module-info.java

└── ormframework

├── javamodularity

│ └── ormframework

│ └── OrmFramework.java

└── module-info.java

Book是一个POJO,里面有个private title字段

OrmFramework是一个模拟orm行为的demo,内容如下:

ublic class OrmFramework {

private Lookup lookup;

public OrmFramework(Lookup lookup) { this.lookup = lookup; }

public T loadfromDatabase(String query, Class clazz) {

try {

MethodHandle ctor = lookup.findConstructor(clazz, MethodType.methodType(void.class));

T entity = (T) ctor.invoke();

Lookup privateLookup = MethodHandles.privateLookupIn​(clazz, lookup);

VarHandle title = privateLookup.findVarHandle(clazz, "title", String.class); // Name/type presumably found in some orm mapping config

title.set(entity, "Loaded from database!");

return entity;

} catch(Throwable e) {

throw new RuntimeException(e);

}

}

Main类内容如下:

public static void main(String... args) {

Lookup lookup = MethodHandles.lookup();

OrmFramework ormFramework = new OrmFramework(lookup);

Book book = ormFramework.loadfromDatabase("/* query */", Book.class);

System.out.println(book.getTitle());

}

你可能要问,为什么OrmFramework需要传入Lookup,因为只有application模块的Lookup才能有权限访问那个模块的非public元素,而OrmFramework模块自己生成的Lookup是没有权限访问的。

所以使用MethodHandles与VarHandles时需要注意Lookup的权限

利用module相关api进行反射

java.lang.module提供了三种类型的能力:1、查询模块属性(主要基于module-info.java的内容);2、运行时动态修改模块的行为;3、模块内资源访问

类图:

ff3821995a7891c8f4d4b0d91b5dcf24.png

1、查询模块属性(主要基于module-info.java的内容)

public class Introspection {

public static void main(String... args) {

Module module = String.class.getModule();

String name1 = module.getName(); // Name as defined in module-info.java

System.out.println("Module name: " + name1);

Set packages1 = module.getPackages(); // Lists all packages in the module

System.out.println("Packages in module: " + packages1);

// The methods above are convenience methods that return

// information from the Module's ModuleDescriptor:

ModuleDescriptor descriptor = module.getDescriptor();

String name2 = descriptor.name(); // Same as module.getName();

System.out.println("Module name from descriptor: " + name2);

Set packages2 = descriptor.packages(); // Same as module.getPackages();

System.out.println("Packages from descriptor: " + packages2);

// Through ModuleDescriptor, all information from module-info.java is exposed:

Set exports = descriptor.exports(); // All exports, possibly qualified

System.out.println("Exports: " + exports);

Set uses = descriptor.uses(); // All services used by this module

System.out.println("Uses: " + uses);

}

}

2、运行时动态修改模块的行为

比如动态exports

Module target=...

Module current=getClass().getModule();

current.addExports("com.test.in.Hello",target);

看了这段代码,你可能要问,第二行,假如我是在别的模块中调用,那么是不是任何模块都可以修改其他模块的exports,opens等属性呢,非也,JVM运行时会判断Module对象的调用上下文,如果检测到调用时非当前模块,那么就会出现异常。这种行为叫做Caller Sensitive

Caller Sensitive

jdk定义了很多caller sensitive的方法,只要是caller sensitive的方法都会被注解@CallerSensitive标注,比如刚刚提到的Module::addExports,Field::setAccessible

Module API中可修改运行时行为的几个方法:

addExports(String pkgName, Module other)

addOpens(String pkgName, Module other)

addReads(Module other)

模块上也可以加注解

@Deprecated

module m{

}

你也可以自定义模块注解

注意:@Target(value={PACKAGE, MODULE})

@Retention(RetentionPolicy.RUNTIME)

@Target(value={PACKAGE, MODULE})

public @interface CustomAnnotation {

}

容器应用模式

Layers And Configurations

ModuleLayer API、boot layer、layer的父子关系、一个layer可以有多个父layer

一个layer包含了当前root模块的解析图(module resolution graph),一个应用中可以有多个layer,但是只有一个boot layer,启动时的boot layer是java给你自动创建的,你也可以手动创建layer,那么这个创建的layer的parent就是boot layer。 只有boot layer才能解析platform module,但children layer可以共享boot layer中的Platform module,但是如果boot layer中没有加载到的platform module,children module是无法使用的。

675cf371c772cb9e5eb1c5ca7c19e47c.png

public static void main(String... args) {

Driver driver = null; // We reference java.sql.Driver to see 'java.sql' gets resolved

ModuleLayer.boot().modules().forEach(m -> System.out.println(m.getName() + ", loader: " + m.getClassLoader()));

System.out.println("System classloader: " + ClassLoader.getSystemClassLoader());

}

创建ModuleLayer的示例:

ModuleFinder finder=ModuleFinder.of(Paths.get("../modules"));

ModuleLayer bootLayer=ModuleLayer.boot();

//第二个Finder参数是在第一个finder中找不到模块时才会去第二个finder中找,还有个resolveAndBind方法,区别在于,后者还会解析services provides/uses

Configuration config=bootLayer.configuration().resolve(finder,ModuleFinder.of(), Set.of("rootmodule"));

ClassLoader cl=ClassLoader.getSystemClassLoader();

ModuleLayer newLayer=bootLayer.defineModulesWithOneLoader(config,cl);

上面的Configuration除了resolve方法外,还有个resolveAndBind方法,区别在于,后者还会解析services provides/uses

be5bb41a6ff41b23b7d030ade36dbb0d.png

ClassLoaders in Layer

c78f9069941ad7b013bd0e9251bba8ac.png

引入模块化以后,去掉了之前的ExtClassLoader,引入了PlatformClassLoader

如果我们为每个layer都传入不同的ClassLoader,那么则允许不同layer中存在相同的全限定类,这样可以做到隔离与相互不干扰。

Plug-in 架构

比如Eclipse,IDEA都是基于插件的应用

在Java9中,我们有两种方式来实现插件化:1、仍然利用以前的Services能力;2、结合ModuleLayer+Services实现封装性更强的插件

public class PluginHostMain {

public static void main(String... args) {

if (args.length < 1) {

System.out.println("Please provide plugin directories");

return;

}

System.out.println("Loading plugins from " + Arrays.toString(args));

Stream pluginLayers = Stream

.of(args)

.map(dir -> createPluginLayer(dir)); //<1>

pluginLayers

.flatMap(layer -> toStream(ServiceLoader.load(layer, Plugin.class))) // <2>

.forEach(plugin -> {

System.out.println("Invoking " + plugin.getName());

plugin.doWork(); // <3>

});

}

static ModuleLayer createPluginLayer(String dir) {

ModuleFinder finder = ModuleFinder.of(Paths.get(dir));

Set pluginModuleRefs = finder.findAll();

Set pluginRoots = pluginModuleRefs.stream()

.map(ref -> ref.descriptor().name())

.filter(name -> name.startsWith("plugin")) // <1>

.collect(Collectors.toSet());

ModuleLayer parent = ModuleLayer.boot();

Configuration cf = parent.configuration()

.resolve(finder, ModuleFinder.of(), pluginRoots); // <2>

ClassLoader scl = ClassLoader.getSystemClassLoader();

ModuleLayer layer = parent.defineModulesWithOneLoader(cf, scl); // <3>

return layer;

}

static Stream toStream(Iterable iterable) {

return StreamSupport.stream(iterable.spliterator(), false);

}

}

7765d2fbeac6b1fa1d610bc7d45bbd49.png

Container架构

比如tomcat,Jetty就是基于Container的应用,支持运行时动态depoy和undeploy应用。

819f636708d9df554186077a47f7d42a.png

与Plugin-in架构的区别:1、Container支持运行时deploy和undeploy;2、Plugin-in是用的是Services思路,而Container模式不应该使用Services。这种情况下,就需要使用模块的open功能,但是我们又不应该强制应用open,那么这就需要用到ModuleLayer.Controller::addOpens了,与Module::addOpens是Caller Sensitive不同,它可以实现跨模块调用来修改模块属性。然后利用Deep reflection来实例化应用类

private static void deployApp(int appNo) {

AppDescriptor appDescr = apps[appNo];//AppDescriptor是自定义的类

System.out.println("Deploying " + appDescr);

ModuleLayer.Controller appLayerCtrl = createAppLayer(appDescr);

Module appModule = appLayerCtrl.layer()

.findModule(appDescr.rootmodule)

.orElseThrow(() -> new IllegalStateException(appDescr.rootmodule + " missing"));

appLayerCtrl.addOpens(appModule, appDescr.appClassPkg,

Launcher.class.getModule());

ContainerApplication app = instantiateApp(appModule, appDescr.appClass);

deployedApps[appNo] = app;

app.startApp();

}

private static ModuleLayer.Controller createAppLayer(AppDescriptor appDescr) {

ModuleFinder finder = ModuleFinder.of(Paths.get(appDescr.appDir));

ModuleLayer parent = ModuleLayer.boot();

Configuration cf = parent.configuration()

.resolve(finder, ModuleFinder.of(), Set.of(appDescr.rootmodule));

ClassLoader scl = ClassLoader.getSystemClassLoader();

ModuleLayer.Controller layerCtrl =

ModuleLayer.defineModulesWithOneLoader(cf, List.of(parent), scl);

return layerCtrl;

}

private static ContainerApplication instantiateApp(Module appModule, String appClassName) {

try {

ClassLoader cl = appModule.getClassLoader();

Class> appClass = cl.loadClass(appClassName);

if(ContainerApplication.class.isAssignableFrom(appClass)) {

return ((Class) appClass).getConstructor().newInstance();

} else {

System.out.println("WARNING: " + appClassName + " doesn't implement ContainerApplication, cannot be started");

}

} catch (ReflectiveOperationException roe) {

System.out.println("Could not start " + appClassName);

roe.printStackTrace();

}

注意点:只有jvm启动时的boot layer才能解析platform module,在这里就是Container的root layer,但children layer可以共享boot layer中的Platform module,但是如果boot layer中没有加载到的platform module,children module是无法使用的。所以Container启动时可以指定参数--add-modules ALL-SYSTEM这样便可以解析所有的platform module到layer module graph中

总之:不管是Plugin-in还是Container模式,我们都需要适应新的ModuleLayer API就像以前的ClassLoader API一样

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/440528.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【HDU - 5627】Clarke and MST(最大生成树,与运算性质,最小生成树MST变形)

题干&#xff1a; Clarke is a patient with multiple personality disorder. One day he turned into a learner of graph theory. He learned some algorithms of minimum spanning tree. Then he had a good idea, he wanted to find the maximum spanning tree with bit …

java爬虫获取div内容_Java爬虫-简单解析网页内容

获取百度新闻中所有的中国新闻的标题时间来源1 获取网页2 public static String getContent(String str) throwsClientProtocolException, IOException {3 CloseableHttpClient closeableHttpClientHttpClients.createDefault(); //创建实例4 HttpGet httpGetnewHttpGet(str);5…

【HDU - 4635】Strongly connected(缩点,新图性质,建图,Tarjan求强连通分量)

题干&#xff1a; Give a simple directed graph with N nodes and M edges. Please tell me the maximum number of the edges you can add that the graph is still a simple directed graph. Also, after you add these edges, this graph must NOT be strongly connected.…

python bind sock_python 在bind端口之后创建的socket如果不关闭的话会被回收吗?

在进行tcpsocket编程的时候&#xff0c;遇到一个问题&#xff1a;我创建一个Asocket&#xff0c;在进行bind之后&#xff0c;这个socket为 在进行tcpsocket编程的时候&#xff0c;遇到一个问题&#xff1a; 我创建一个Asocket&#xff0c;在进行bind之后&#xff0c;这个socket…

【ZOJ - 2955】Interesting Dart Game(背包,结论,裴蜀定理,数论)

题干&#xff1a; Recently, Dearboy buys a dart for his dormitory, but neither Dearboy nor his roommate knows how to play it. So they decide to make a new rule in the dormitory, which goes as follows: Given a number N, the person whose scores accumulate e…

接口测试 java_接口测试--Java

1&#xff1a;interface :定义接口接口用于 模块与模块之间的连接 或者系统与系统间的连接2&#xff1a;软件系统UI层&#xff1a;程序的界面&#xff0c;主要为用户提供交互和操作--查看&#xff0c;输入&#xff0c;点击&#xff0c;等。业务逻辑层&#xff1a;进行复杂的业…

【ZOJ - 2949】Coins of Luck (概率dp,期望)

题干&#xff1a; 给你两种泡面 各N包&#xff0c;然后你扔硬币&#xff0c;正面朝上吃第一种&#xff0c;反面呢 吃第二种&#xff0c;有任意一种吃完时 就不需要抛硬币了&#xff0c;求停止抛硬币的期望。 Sample Input 2 1 2Sample Output 1.00 2.50 解题报告&#xff…

java perl_在Java中调用Perl脚本

有两种方法&#xff0c;第一种是直接 Runtime.getRuntime().exec("...");这里推荐第二种&#xff0c;可以得到输出和返回值&#xff1a;源代码如下&#xff1a;import java.io.BufferedReader;import java.io.InputStream;import java.io.InputStreamReader;public c…

*【HDU - 6201】transaction transaction transaction(树形dp 或 spfa最长路 或 网络流)

题干&#xff1a; 题目大意&#xff1a; 给出一棵n个顶点的树&#xff0c;每个点有一个权值&#xff0c;代表商品的售价&#xff0c;树上每一条边上也有一个权值&#xff0c;代表从这条边经过所需要的花费。现在需要你在树上选择两个点&#xff0c;一个作为买入商品的点&#…

java 手写签名_Android 自定义View手写签名并保存图片

GIF压缩有问题&#xff0c;运行很顺滑&#xff01;&#xff01;&#xff01;1.自定义View——支持设置画笔颜色&#xff0c;画笔宽度&#xff0c;画板颜色&#xff0c;清除画板&#xff0c;检查是否有签名&#xff0c;保存画板图片(复制粘贴可直接使用)/*** Created by YyyyQ o…

【2019第十届蓝桥杯省赛C/C++B组题解】(非官方题解)

A。 数数题。 答案&#xff1a;490 B。 26进制模拟。 答案&#xff1a;BYQ C。 类似fib数列求值&#xff0c;递推一下就好。 答案&#xff1a;4659 D。 注意两个坑点&#xff0c;一个是正整数&#xff0c;所以枚举要从1开始。第二个坑点是互不相同的&#xff0c;为了达到这…

java对象模型 指令_深入理解多线程(二)—— Java的对象模型

上一篇文章中简单介绍过synchronized关键字的方式&#xff0c;其中&#xff0c;同步代码块使用monitorenter和monitorexit两个指令实现&#xff0c;同步方法使用ACC_SYNCHRONIZED标记符实现。后面几篇文章会从JVM源码的角度更加深入&#xff0c;层层剥开synchronized的面纱。在…

【POJ - 1651】Multiplication Puzzle(区间dp)

题干&#xff1a; The multiplication puzzle is played with a row of cards, each containing a single positive integer. During the move player takes one card out of the row and scores the number of points equal to the product of the number on the card taken …

java hive demo_java 操作hive通过jdbc

直接代码吧&#xff1a;记得要开启hive jdbc服务hive --service hiveserverpackage hive;import java.sql.Connection;import java.sql.DriverManager;import java.sql.ResultSet;import java.sql.Statement;public class HiveDemo {static{//注册jdbc驱动try {Class.forName(&…

【牛客 - 551D】CSL 的字符串(单调栈,思维)

题干&#xff1a; CSL 以前不会字符串算法&#xff0c;经过一年的训练&#xff0c;他还是不会……于是他打算向你求助。 给定一个字符串&#xff0c;只含有可打印字符&#xff0c;通过删除若干字符得到新字符串&#xff0c;新字符串必须满足两个条件&#xff1a; 原字符串中…

java 学生课程成绩_Java课设--学生成绩管理系统一

写在前面这个项目是Java课程的课设&#xff0c;一共花了5天的时间去完成它&#xff0c;在这期间感谢一些博主的帮助&#xff0c;让我了解到了一些新的技术知识&#xff0c;所以打算写这一系列博客来介绍一整个课设项目&#xff0c;也为了帮助之后的人&#xff0c;如有错误&…

【牛客 - 551E】CSL 的魔法(贪心,思维,STLmap,分块)

题干&#xff1a; 链接&#xff1a;https://ac.nowcoder.com/acm/contest/551/E 来源&#xff1a;牛客网 有两个长度为 n 的序列&#xff0c;a0,a1,…,an−1a0,a1,…,an−1和 b0,b1,…,bn−1b0,b1,…,bn−1。CSL 有一种魔法&#xff0c;每执行一次魔法&#xff0c;可以任意挑…

java调用cplex案例_【CPLEX教程03】java调用cplex求解一个TSP问题模型

前面我们已经搭建好cplex的java环境了&#xff0c;相信大家已经跃跃欲试&#xff0c;想动手写几个模型了。今天就来拿一个TSP的问题模型来给大家演示一下吧~CPLEX系列教程可以关注我们的公众号哦&#xff01;获取更多精彩消息&#xff01;01 TSP寤烘ā关于TSP建模&#xff0c;就…

【牛客 - 551C】CSL 的密码(后缀数组,后缀自动机,随机算法)

题干&#xff1a; 链接&#xff1a;https://ac.nowcoder.com/acm/contest/551/C 来源&#xff1a;牛客网 为了改变这一点&#xff0c;他决定重新设定一个密码。于是他随机生成了一个很长很长的字符串&#xff0c;并打算选择一个子串作为新密码。他认为安全的密码长度至少为…

java在一个类里实现存款_用Java编写一个简单的存款

package desposit.money;public class DespositMoney {public static void main(String[] args) {Customer c1 new Customer("第一个顾客",3);Customer c2 new Customer("第二个顾客",10);Customer c3 new Customer("第三个顾客",5);c1.start…