无论什么原因,我在我的抽象类中实现了以下
hashCode
。
@MappedSuperclass
abstract Some {
@Override
public boolean equals(final Object obj) {
// ...
}
@Override
public int hashCode() {
return getClass().hashCode(); // TODO: cache, maybe?
}
}
getClass().hashCode()
的值吗?
- 我可以缓存
的值吗?getClass().hashCode()
你可以,但是...
- 在 JVM 中运行时该值是否有可能被更改?
没有可能。 Java 对象的类无法更改,Java
Class
对象的哈希码(这是 getClass()
的结果类型)也无法更改。
- 这是不是过早的优化?
可能是1。
但是,从性能角度来看,使用对象类的哈希码作为对象的哈希码是一个非常糟糕的主意。
这样做意味着该类的所有实例(例如
Some
)将具有相同的哈希码2。这将导致无数的哈希冲突,并使大多数 HashSet
和 HashMap
操作成为 O(N)
或 O(logN)
(取决于您的 Java 版本)而不是 O(1)
。
1 - 我假设您没有做过一堆没有告诉我们的性能分析。如果您已经已经完成了分析,那么也许这不是一个过早的优化。
2 - 我假设
Some::hashCode
方法没有被 Some
的具体子类覆盖为更明智的东西。
Stephen C 的回答是一个很好的答案。然而,为了完整起见,我觉得有必要补充一点,如果一个类是由两个不同的类加载器加载的,那么对于该类
getClass().hashCode()
将返回两个不同的值。
为了验证这一点,我编写了一个程序并使用系统类加载器加载一个类,然后使用我自己的自定义类加载器加载一个类。两者返回不同的
hashCode()
:
首先是我从 https://www.digitalocean.com/community/tutorials/java-classloader:
复制的自定义类加载器的代码package com.journaldev.classloader;
import java.io.DataInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
/**
* Our Custom ClassLoader to load the classes. Any class in the
* com.journaldev package will be loaded using this ClassLoader.
* For other classes, it will delegate the request to its Parent
* ClassLoader.
*/
public class CCLoader extends ClassLoader {
/**
* This constructor is used to set the parent ClassLoader
*/
public CCLoader(ClassLoader parent) {
super(parent);
}
/**
* Loads the class from the file system. The class file should be located in
* the file system. The name should be relative to get the file location
*
* @param name Fully Classified name of the class, for example, com.journaldev.Foo
*/
private Class getClass(String name) throws ClassNotFoundException {
String file = name.replace('.', File.separatorChar) + ".class";
byte[] b = null;
try {
// This loads the byte code data from the file
b = loadClassFileData(file);
// defineClass is inherited from the ClassLoader class
// that converts byte array into a Class. defineClass is Final
// so we cannot override it
Class c = defineClass(name, b, 0, b.length);
resolveClass(c);
return c;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
/**
* Every request for a class passes through this method. If the class is in
* com.journaldev package, we will use this classloader or else delegate the
* request to parent classloader.
*
* @param name Full class name
*/
@Override
public Class loadClass(String name) throws ClassNotFoundException {
if (name.startsWith("com.journaldev.")) {
return getClass(name);
}
return super.loadClass(name);
}
/**
* Reads the file (.class) into a byte array. The file should be
* accessible as a resource and make sure that it's not in Classpath to avoid
* any confusion.
*
* @param name Filename
* @return Byte array read from the file
* @throws IOException if an exception comes in reading the file
*/
private byte[] loadClassFileData(String name) throws IOException {
InputStream stream = getClass().getClassLoader().getResourceAsStream(
name);
int size = stream.available();
byte buff[] = new byte[size];
DataInputStream in = new DataInputStream(stream);
in.readFully(buff);
in.close();
return buff;
}
}
然后是
Test
类和 main 方法,我们将使用它来使用不同的类加载器加载 Test
类:
package com.journaldev.test;
import com.journaldev.classloader.CCLoader;
public class Test {
public static void main(String[] args) throws Exception {
System.out.println(new CCLoader(ClassLoader.getSystemClassLoader())
.loadClass(Test.class.getCanonicalName()).hashCode());
System.out.println(ClassLoader.getSystemClassLoader()
.loadClass(Test.class.getCanonicalName()).hashCode());
}
}
输出显示使用不同类加载器加载的同一类的不同哈希码:
1554547125
1072591677