java - 扩展最终类或替换它
问题描述
“不,做不到”
已经讨论了很多次,唯一的回答是“不,做不到”。但是伙计们,我们做代码。理论上,只要有一段时间,一切都是可能的。
问题
基本上我正在尝试扩展最终类以更改单个行为。这不能以传统方式完成 - 在编译时使用extend
关键字。我已经寻找了其他一些方法:
- 创建一个代理 - 仅适用于接口;
- 编辑
Modifiers
类,删除final
修饰符并在运行时扩展类 - 可以为字段完成,但不能为类完成; - 使用自定义
ClassLoader
加载类的新版本 - 它不起作用,因为我想扩展的类是java
包的一部分。ClassLoader
幸运的是,由于SecurityException
. 否则,这会弄乱很多证券。
static class TestClassLoader extends ClassLoader {
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
if (name.equals("java.io.StringReader")) {
try {
InputStream is = Test.class.getClassLoader().getResourceAsStream("java/io/StringReader.class");
byte[] buf = new byte[10000];
int len = is.read(buf);
return defineClass(name, buf, 0, len); // This line throws the SecurityException :(
} catch (IOException e) {
throw new ClassNotFoundException("", e);
}
}
return getParent().loadClass(name);
}
}
所以我们在这里。有什么方法可以用来替换或扩展我的最终课程吗?
PS:我使用 Java 8,因为它SecurityManager
比新的 Java 版本更喜欢丑陋的东西 :)
语境
我有一个任务 - 呃,这在 SO 上从未受到好评:我必须为我无法更改的课程编写单元测试。这部分真的很简单,除了一个catch
块不能被访问:
Properties props = new Properties();
try
{
props.load(new StringReader(rules));
} catch (IOException e)
{
e.printStackTrace();
throw e;
}
我们老师发给我们的代码覆盖率报告显示他没有测试这个块,所以我们不需要。但我不信任 Java,我确信这个块按预期工作。那我得测试一下!
[注意:因为它不是必需的,它只是发现新的 Java hack 的一种手段,它甚至可能不会计入期末成绩。]
我可以访问的唯一变量是String
rules
. 然后我应该处理它以使load
调用抛出 IOException。load
调用抛出 IOException的唯一原因是 StringReader 的流关闭时。这是通过以下ensureOpen
方法检查的:
/** Check to make sure that the stream has not been closed */
private void ensureOpen() throws IOException {
if (str == null)
throw new IOException("Stream closed");
}
因此,只有 nullString
才能使此方法抛出IOException
. 然后很容易将 nullString
注入rules
. 坏消息是StringReader
构造函数:
public StringReader(String s) {
this.str = s;
this.length = s.length();
}
通过null
String
. 呃。
我的最后一个想法是更改 the 的值String
以使其内部value
字段null
- 在使用该字段时很容易Modifiers
。这不起作用,因为引用而不是值本身应该是null
;read
由于在以下位置调用方法,这只会引发 NPE StringReader
:
public int read() throws IOException {
synchronized (lock) {
ensureOpen();
if (next >= length)
return -1;
return str.charAt(next++);
}
}
你有什么主意吗?一个可以为我做这件事的框架?我可以探索一些路径来解决我无用的问题吗?
这个问题与代码测试无关。它是关于如何破解 Java 及其最终类。
解决方案
有Instrumentation API,它允许 Java 软件,即所谓的 Java 代理,在运行时修改类。这就是 Mocking 框架如何执行其工作的方式之一。将其用于单元测试并非不合理。
但请注意,您的代码注入的具体目标是错误的。在代码中
Properties props = new Properties();
try
{
props.load(new StringReader(rules));
} catch (IOException e)
{
e.printStackTrace();
throw e;
}
IOException
永远不会被抛出。正如您自己指出的那样,StringReader
' 的构造函数不会抛出它,这同样适用于它的read
方法。的load
方法Properties
声明它,因为它必须处理任意Reader
实现,但它自己不会产生这样的异常。
因此,在这种情况下,您从 a 中读取String
一个新构造的StringReader
,它永远不会被抛出,并且注入一个实际上不存在的行为来测试一个永远不会发生的场景是没有实际意义的。
正如您自己也注意到的,它可以抛出 , 的唯一点IOException
是 method ensureOpen()
,顾名思义,它检查阅读器是否仍然打开。因此,您可以通过简单地调用阅读器来强制抛出行为,close()
但是,如上所述,这与被测代码的实际行为不匹配,并且没有必要引发被测异常,这实际上永远不会发生。
推荐阅读
- ios - TestFlight 上的 API 调用错误(仅适用于 iPhone 11 Pro)
- java - 无法将 java.lang.String 类型的 url 中的值 https://test.com 转换为 JSONObject
- winapi - 为客户端生成远程调用现有服务器功能的 RPC 代码?
- java - 遍历 JSON 中收到的对象列表
- nginx - Nginx Ingress - 如果没有匹配的路由,如何设置重定向到外部站点?
- javascript - 有没有纯文本html标签之类的东西?
- scala - Scala HashMap#contains 没有任何期望
- c++ - C++ 运算符重载 << 与向量
- php - 输入值前的 Jquery 欧元符号
- javascript - 无法使用 Nativescript-drawingPad 在 iOS 上绘制签名