- mongodb - 在 MongoDB mapreduce 中,如何展平值对象?
- javascript - 对象传播与 Object.assign
- html - 输入类型 ="submit"Vs 按钮标签它们可以互换吗?
- sql - 使用 MongoDB 而不是 MS SQL Server 的优缺点
请查看下面的修改
我正在尝试创建一个 JShell 实例,让我可以访问并与创建它的 JVM 中的对象进行交互。这适用于在编译时可用但对于动态加载的类失败的类动态。
public class Main {
public static final int A = 1;
public static Main M;
public static void main(String[] args) throws Exception {
M = new Main();
ClassLoader cl = new URLClassLoader(new URL[]{new File("Example.jar").toURL()}, Main.class.getClassLoader());
Class<?> bc = cl.loadClass("com.example.test.Dynamic");//Works
JShell shell = JShell.builder()
.executionEngine(new ExecutionControlProvider() {
@Override
public String name() {
return "direct";
}
@Override
public ExecutionControl generate(ExecutionEnv ee, Map<String, String> map) throws Throwable {
return new DirectExecutionControl();
}
}, null)
.build();
shell.eval("System.out.println(com.example.test.Main.A);");//Always works
shell.eval("System.out.println(com.example.test.Main.M);");//Fails (is null) if executionEngine is not set
shell.eval("System.out.println(com.example.test.Dynamic.class);");//Always fails
}
}
此外,将 DirectExecutionControl
与 LocalExecutionControl
交换会得到相同的结果,但我不明白这两个类之间的区别。
如何使 运行时 加载的类可用于此 JShell 实例?
编辑:这个问题的第一部分已经解决,下面是更新的源代码来演示问题的第二部分
public class Main {
public static void main(String[] args) throws Exception {
ClassLoader cl = new URLClassLoader(new URL[]{new File("Example.jar").toURL()}, Main.class.getClassLoader());
Class<?> c = cl.loadClass("com.example.test.C");
c.getDeclaredField("C").set(null, "initial");
JShell shell = JShell.builder()
.executionEngine(new ExecutionControlProvider() {
@Override
public String name() {
return "direct";
}
@Override
public ExecutionControl generate(ExecutionEnv ee, Map<String, String> map) throws Throwable {
return new DirectExecutionControl();
}
}, null)
.build();
shell.addToClasspath("Example.jar");
shell.eval("import com.example.test.C;");
shell.eval("System.out.println(C.C)"); //null
shell.eval("C.C = \"modified\";");
shell.eval("System.out.println(C.C)"); //"modified"
System.out.println(c.getDeclaredField("C").get(null)); //"initial"
}
}
这是预期的输出,如果 JVM 和 JShell 实例 不共享任何内存,但是添加 com.example.test.C
直接加载到项目中,而不是动态加载它,结果如下:
shell.eval("import com.example.test.C;");
shell.eval("System.out.println(C.C)"); //"initial"
shell.eval("C.C = \"modified\";");
shell.eval("System.out.println(C.C)"); //"modified"
System.out.println(c.getDeclaredField("C").get(null)); //"modified"
为什么在运行时加载的类不共享 JVM 和 JShell 实例 之间的内存?
编辑 2:问题似乎是由不同的类加载器引起的
在上述示例的上下文中执行以下代码:
System.out.println(c.getClassLoader()); //java.net.URLClassLoader
shell.eval("System.out.println(C.class.getClassLoader())"); //jdk.jshell.execution.DefaultLoaderDelegate$RemoteClassLoader
shell.eval("System.out.println(com.example.test.Main.class.getClassLoader())"); //jdk.internal.loader.ClassLoaders$AppClassLoader
这表明,同一个类 com.example.test.C
由两个不同的类加载器加载。是否可以将类添加到 JShell 实例而不再次加载它?如果不是,为什么静态加载的类已经加载了?
最佳答案
解决方案是创建一个自定义 LoaderDelegate
实现,它提供已加载类的实例,而不是再次加载它们。一个简单的例子是使用默认实现 DefaultLoaderDelegate
( source ) 并覆盖其内部 RemoteClassLoader
findClass
方法
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] b = classObjects.get(name);
if (b == null) {
Class<?> c = null;
try {
c = Class.forName(name);//Use a custom way to load the class
} catch(ClassNotFoundException e) {
}
if(c == null) {
return super.findClass(name);
}
return c;
}
return super.defineClass(name, b, 0, b.length, (CodeSource) null);
}
要创建一个有效的 JShell 实例,请使用以下代码
JShell shell = JShell.builder()
.executionEngine(new ExecutionControlProvider() {
@Override
public String name() {
return "name";
}
@Override
public ExecutionControl generate(ExecutionEnv ee, Map<String, String> map) throws Throwable {
return new DirectExecutionControl(new CustomLoaderDelegate());
}
}, null)
.build();
shell.addToClasspath("Example.jar");//Add custom classes to Classpath, otherwise they can not be referenced in the JShell
关于java - 与 JShell 实例共享动态加载的类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48896013/
我正在尝试创建自定义 JShell 提示符并希望直接从文件加载它。 但是当从文件加载设置提示时它没有显示自定义提示,但我能够在 JShell 上编写相同的命令。 例如:我有一个文件“jshell_pr
从 jShell 脚本内部,是否可以访问或注册在创建 JShell 的代码中定义的变量? 目前似乎没有机制可以访问或注册一个变量到 Shell 实例,或者从 JShell 内部返回无字符串类型(如对象
从 jShell 脚本内部,是否可以访问或注册在创建 JShell 的代码中定义的变量? 目前似乎没有机制可以访问或向 Shell 实例注册变量,或者从 JShell 内部返回任何字符串类型(如对象或
我已经下载并“手动”安装了适用于 Linux 64 位版本的 JDK 10(适用于 Ubuntu 16.04), Oracle Java SE Development Kit(注意:我想下载 Java
我正在使用 JDK9 的 jshell。 我刚刚创建了一个最终变量并为其分配了一个值。 在下一行中,我只是修改了值。令我惊讶的是,修改最终变量时没有错误。 以下是代码片段: jshell> final
我是 java 9 的新手,只是在 jshell 上练习一些代码片段 jshell> System.out.println("hello"); hello jshell> System.out.
有人知道 JShell 历史记录存储在哪里(Windows-10 上的 Java-11/Open JDK,但任何配置都可能有用)?我一直在$HOME($USERPROFILE,$APPDATA,...
我想像这样运行 jshell $ jshell --execute 'System.out.println("Hello World")' Hello World $ 因此它执行 java 单行代码并
我看了几个 JShell 教程,但我仍然不明白最重要的部分:我为什么要使用 JShell? 甲骨文是这样说的: "You can test individual statements, try out
tl;dr:如何在不替换此类的情况下向现有类添加方法? 描述: 当我在 JShell 中运行以下命令时: public class TestClass {} 打印出以下输出: created clas
如何在 JShell 中查找变量或表达式结果的类型。 在 Java 中试验按位运算符 jshell> byte b=5 5 -2147483648 jshell> 有没有像 Python type(5
我在我的机器上使用 'jshell 命令,它无法被识别。但是 java 命令工作正常。 jdk 10 中的jshell 是否有任何环境设置 C:\Users\Kannan λ jshell 'jshe
当我在 JShell (9.0.1) 中输入一个表达式时,它返回: $22 -> 22 从哪里来,$1 到 $21 发生了什么? (它们是未定义的。) 我似乎隐约记得(当我开始使用 Java 9.0
我找到了 this问题,这个 other ,如此有趣以至于引出了几个问题,至少对我而言: 相当开放式的问题,但是 jshell 被限制 在哪里?显然,GUI 应用程序不属于 jshell 解决方案或
如何指示 jshell 在脚本末尾终止,类似于其他语言的解释器,例如 python3 或 node? 以下命令 ./jshell -q /tmp/shell.java 带有脚本/tmp/shell.j
我该怎么做 /exit jshell具有非零错误代码的 session ? /exit产量:进程完成,退出代码 0 /exit 1产量:进程完成,退出代码 0 throw new Error("1")
我正在发现 JShell,并且发现默认添加的导入: jshell> /imports | import java.io.* | import java.math.* | import
为什么我的 jshell 实例(JDK 版本 9-ea)无法识别 printf() 语句?以下是我观察到的错误, jshell> printf("Print number one - %d",1) |
关注帖子Is there a way to remove imports in JShell? .我想知道是否还有办法摆脱默认导入。 jshell> /imports | import java
根据文档, There’s also the option to load a script on startup, including some special predefined options
我是一名优秀的程序员,十分优秀!