我在为测试目的更改字段值时遇到了麻烦。
尽管存在围绕单例的问题,但我想将其用于我的日志文件管理器。我确实考虑过是否应该重新设计,但很高兴我的决定得到了认可。
我想测试一下。特别是如果代码在windows和linux上运行,或者在其他我现在想不起来的地方。
这个想法是在当前目录下创建一个名为"testlogs"的测试目录,删除它并使用它。因此,我可以确保我至少在当前目录中具有写权限,或者我可以通知用户不写入日志文件,或者我可以提示他选择另一个位置等。
在下面的代码中,我一直得到我的"日志"目录。我希望能够省去真正的"日志"(如果它们存在的话)。
为什么我不能改变dirname
的值
package com.home.app.logger.LogsMngr;
public class LogsMngr {
private static class Holder {
public static String dirname = "logs";
public static final LogsMngr INSTANCE = new LogsMngr(dirname);
}
private LogsMngr(String dirname){
createDirectory(dirname);
}
private createDirectory(String dirname) {
// logic to create a directory on the filesystem
}
public static LogsMngr getInstance() {
return Holder.INSTANCE;
}
}
@RunWith(SeparateClassLoaderRunner.class)
public class Test {
LogsMngr lmngr;
@Test
public void test() {
try {
// Remove existing directory
TUtils.rmfDirectory("testlogs");
// Set directory name
String className = "com.home.app.logger.LogsMngr$Holder";
Class[] memberClasses = LogsMngr.class.getDeclaredClasses();
Field dirnamefield = null;
for(int i=0; i<memberClasses.length; i++) {
if(memberClasses[i].getName().equals(className)) {
dirnamefield = memberClasses[i].getField("dirname");
}
}
//TODO catch NullPointerException because of dirname?
// To prevent from IllegalAccessException:
dirnamefield.setAccessible(true);
// Set test directory
dirnamefield.set(null, "testlogs");
// Create
lmngr = LogsMngr.getInstance();
}
catch(Exception e) {
e.printStackTrace();
}
}
}
引用:
SeparateClassLoaderRunner.class
Java语言文档
使用JUnit测试带有私有方法的类的正确方法是什么?
我可以发现一个Java类的内部类声明使用反射?
The_solution_of_Bill_Pugh
解决方案:
package com.home.app.logger.LogsMngr;
public class LogsMngr {
private static String dirname = "logs"; <-- moved out of Holder
private static class Holder {
public static final LogsMngr INSTANCE = new LogsMngr(dirname);
}
private LogsMngr(String dirname){
createDirectory(dirname);
}
private createDirectory(String dirname) {
// logic to create a directory on the filesystem
}
public static LogsMngr getInstance() {
return Holder.INSTANCE;
}
}
@RunWith(SeparateClassLoaderRunner.class)
public class Test {
LogsMngr lmngr;
@Test
public void test() {
try {
// Remove existing directory
TUtils.rmfDirectory("testlogs");
Field dirnamefield = LogsMngr.class.getDeclaredField("dirname");
// To prevent from IllegalAccessException:
dirnamefield.setAccessible(true);
// Set test directory
dirnamefield.set(null, "testlogs");
// Create
lmngr = LogsMngr.getInstance();
}
catch(Exception e) {
e.printStackTrace();
}
}
}
这一行:
Class[] memberClasses = LogsMngr.class.getDeclaredClasses();
将导致LogsMngr.Holder
被初始化,导致INSTANCE
字段也被初始化,因此在此之后的任何时间更改dirname
都没有效果。
要做你想要完成的事情,你必须能够设置dirname
字段,而不首先初始化包含它的类,我认为这在Java中是不可能的,尽管我很想纠正这一点。然而,我相信你可以通过简单地将dirname
从Holder
移到LogsMngr
来获得你想要的效果。然后,在使用LogsMngr.Holder
之前,只需将LogsMngr.dirname
设置为您想要的任何值。