题1:什么是自动装箱?什么是自动拆箱?
自动装箱是指将基本数据类型重新转化为对象。
public class Test {
public static void main(String[] args) {
Integer num = 9;
}
}
num = 9的值是属于基本数据类型,原则上不能直接赋值给对象Integer。但是在JDK1.5版本后就可以进行这样的声明自动将基本数据类型转化为对应的封装类型,成为对象后可以调用对象所声明的方法。
自动拆箱是指将对象重新转化为基本数据类型。
public class Test {
public static void main(String[] args) {
// 声明Integer对象
Integer num = 9;
// 隐含自动拆箱
System.out.print(num--);
}
}
由于对象不能直接进行运算,而是需要转化为基本数据类型后才能进行加减乘除。
// 装箱
Integer num = 10;
// 拆箱
int num1 = num;
题2:JDK1.8 中 ConcurrentHashMap 不支持空键值吗?
首先明确一点HashMap是支持空键值对的,也就是null键和null值,而ConcurrentHashMap是不支持空键值对的。
查看一下JDK1.8源码,HashMap类部分源码,代码如下:
public V get(Object key) {
Node<K,V> e;
return (e = getNode(hash(key), key)) == null ? null : e.value;
}
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
HashMap在调用put()方法存储数据时会调用hash()方法来计算key的hashcode值,可以从hash()方法上得出当key==null时返回值是0,这意思就是key值是null时,hash()方法返回值是0,不会再调用key.hashcode()方法。
ConcurrentHashMap类部分源码,代码如下:
public V put(K key, V value) {
return putVal(key, value, false);
}
/** Implementation for put and putIfAbsent */
final V putVal(K key, V value, boolean onlyIfAbsent) {
if (key == null || value == null) throw new NullPointerException();
int hash = spread(key.hashCode());
int binCount = 0;
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh;
if (tab == null || (n = tab.length) == 0)
tab = initTable();
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
if (casTabAt(tab, i, null,
new Node<K,V>(hash, key, value, null)))
break; // no lock when adding to empty bin
}
else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f);
else {
V oldVal = null;
synchronized (f) {
if (tabAt(tab, i) == f) {
if (fh >= 0) {
binCount = 1;
for (Node<K,V> e = f;; ++binCount) {
K ek;
if (e.hash == hash &&
((ek = e.key) == key ||
(ek != null && key.equals(ek)))) {
oldVal = e.val;
if (!onlyIfAbsent)
e.val = value;
break;
}
Node<K,V> pred = e;
if ((e = e.next) == null) {
pred.next = new Node<K,V>(hash, key,
value, null);
break;
}
}
}
else if (f instanceof TreeBin) {
Node<K,V> p;
binCount = 2;
if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
value)) != null) {
oldVal = p.val;
if (!onlyIfAbsent)
p.val = value;
}
}
}
}
if (binCount != 0) {
if (binCount >= TREEIFY_THRESHOLD)
treeifyBin(tab, i);
if (oldVal != null)
return oldVal;
break;
}
}
}
addCount(1L, binCount);
return null;
}
ConcurrentHashmap在调用put()方法时调用了putVal()方法,而在该方法中判断key为null或value为null时抛出空指针异常NullPointerException。
ConcurrentHashmap是支持并发的,当通过get()方法获取对应的value值时,如果指定的键为null,则为NullPointerException,这主要是因为获取到的是null值,无法分辨是key没找到null还是有key值为null。
题3:父类中静态方法能否被子类重写?
父类中静态方法不能被子类重写。
重写只适用于实例方法,不能用于静态方法,而且子类当中含有和父类相同签名的静态方法,一般称之为隐藏。
public class A {
public static String a = "这是父类静态属性";
public static String getA() {
return "这是父类静态方法";
}
}
public class B extends A{
public static String a = "这是子类静态属性";
public static String getA() {
return "这是子类静态方法";
}
public static void main(String[] args) {
A a = new B();
System.out.println(a.getA());
}
}
如上述代码所示,如果能够被重写,则输出的应该是“这是子类静态方法”。与此类似的是,静态变量也不能被重写。如果想要调用父类的静态方法,应该使用类来直接调用。