有一段看起来像这样的代码。问题是在启动期间,会发生2次初始化。 (1)某种方法对ForumRepository
&执行newInstance()纯粹是为了调用#setCacheEngine
。 (2)后面的另一种方法调用#start()
。我注意到#cache
成员变量的hashCode在某些奇怪的场景中有时会有所不同。由于只有1段代码调用#setCacheEngine
,hashCode如何在运行时更改(我假设不同的实例将具有不同的hashCode)。这个地方有错误吗?
public class ForumRepository implements Cacheable
{
private static CacheEngine cache;
private static ForumRepository instance;
public void setCacheEngine(CacheEngine engine) { cache = engine; }
public synchronized static void start()
{
instance = new ForumRepository();
}
public synchronized static void addForum( ... )
{
cache.add( .. );
System.out.println( cache.hashCode() );
// snipped
}
public synchronized static void getForum( ... )
{
... cache.get( .. );
System.out.println( cache.hashCode() );
// snipped
}
}
整个系统已接通电线。在servlet的init
方法中初始化。
init()方法在概念上看起来像这样:
// create an instance of the DefaultCacheEngine
cache = (CacheEngine)Class.forName( "com..DefaultCacheEngine" ).newInstance();
cache.init();
// init the ForumRepository's static member
Object o = Class.forName( "com.jforum....ForumRepository" ).newInstance();
if( o instanceof Cacheable )
((Cacheable)o).setCacheEngine(cache);
// Now start the ForumRepository
ForumRepository.start();
更新我没有写这段代码。它取自jforum
更新2 找到解决方案。我在下面添加了一个单独的评论来描述问题的原因。谢谢大家。
答案 0 :(得分:1)
您将不得不提供更多信息,但CacheEngine
可能是一种可变数据类型,更糟糕的是,它甚至可能被其他人共享。根据{{1}}定义其CacheEngine
的方式,这很可能会导致hashCode()
从aForumRepository
看到各种不同的哈希码。
对于同一个对象,如果它是可变的,在一段时间内改变它的cache
,只要它以一致的方式完成(完全是另一个主题),这是完全正常的。
Object.hashCode()
- 确保您了解合同的含义hashCode()
上cache
更多信息已经重新出现,我们现在知道有问题的对象虽然可变,却不会static
。但是,在@Override hashCode()
cache
static
类ForumRepository
字段的设计中,似乎存在严重的问题,其中非static
“ setter“setCacheEngine
(看起来由Cacheable
指定。)
这意味着,无论创建了多少个cache
实例,都只有ForumRepository
的化身!在某种程度上, <{strong> ForumRepository
的所有实例都“共享”相同的 cache
!
如果一个字段被声明为
static
,那么无论该类最终可以创建多少个实例(可能为零),都只存在该字段的一个化身。一个static
字段,有时称为类变量,在初始化类时实现。
我认为现在退后一步并提出这些问题非常重要:
cache
需要static
吗?这是有意的吗?
ForumRepository
的实例是否有自己的cache
?cache
?ForumRepository
个实例?
ForumRepository
应该是单身吗?setCacheEngine
个对象上可以ForumRepository
次调用多少次?
IllegalStateException
投掷setCacheEngine
的防御机制会受益吗?最佳建议取决于上述问题的答案。第三个要点是可以立即采取行动的,并且会显示是否多次调用ForumRepository
。即使它们仅针对每个cache
实例调用一次,它仍然在当前事态中实际上是多个“集合”,因为只有一个static
。
具有非static
setter的{{1}}字段是一项需要彻底重新审核的设计决策。
答案 1 :(得分:0)
您确定要比较的ForumRepository类是否相同。如果你正在做newInstance,你可能会遇到一个奇怪的类加载器问题。
答案 2 :(得分:0)
基于可变状态的hashCode
实现的可变对象几乎总是一个坏主意。例如,它们在基于散列的集合中表现得非常奇怪 - 如果将这样的对象插入HashSet
然后对其进行变异,则contains
方法将无法再找到它。 / p>
根据其身份自然区分的对象不应覆盖equals
和hashCode
。如果根据状态覆盖hashCode
,则该状态应为不可变状态。示例是String
和拳击类型。这些通常被称为“价值类”,因为它们的身份没有意义 - 区分new Integer(42)
的多个实例是毫无意义的。
答案 3 :(得分:0)
令我困惑的是这个问题:
为什么要查看CacheEngine实例的哈希码?
您的代码似乎将Forum
个对象放入缓存中并将其取回。因此,查看Forum
实例的哈希码值是有意义的。但是缓存本身的哈希码看起来完全不相关。
话虽如此,如果DefaultCacheEngine
类从hashcode
继承了java.lang.Object
的实现,那么该方法返回的值就是“身份”哈希码,这不能改变对象的生命周期。如果它确实发生了变化,这意味着您现在正在查看DefaultCacheEngine类的不同实例。
答案 4 :(得分:0)
我已经解决了我的问题,我想与你分享我学到或发现的东西。
错误的根本原因
jforum.war
webapp由Tomcat 6.x通过两个不同的虚拟主机加载两次。所以是的,CacheEngine
显示了两个不同的hashCodes,因为它们被加载到单独的webapp类加载器中。
<强>解决方案强>
我的快速解决方法是通过一个特定的虚拟主机地址限制jforum.war
中servlet的调用。