前言
马上国庆了,本来想着给自己放松一下,刷刷博客,慕然回首,自动拆装箱?equals?==?HashCode? instanceof? 似乎有点模糊了,那就大概看一下5道Java基础面试题吧。好记性不如烂键盘~
instanceof 关键字的作用
instanceof 严格来说是Java中的一个双目运算符,用来测试一个对象是否为一个类的实例,用法为:
if (object instanceof Json) {
Json json = (Json)object;
}
其中 object 为一个对象,Json表示一个类或者一个接口,当 object 为 Json的对象,或者是其直接
或间接子类,或者是其接口的实现类,结果result 都返回 true,否则返回false。
注意:编译器会检查 object 是否能转换成右边的Json类型,如果不能转换则直接报错,如果不能确定类型,则通过编译,具体看运行时定。
nt i = 0;
System.out.println(i instanceof Integer);//编译不通过 i必须是引用类型,不能是基本类型
System.out.println(i instanceof Object);//编译不通过
Integer integer = new Integer(1);
System.out.println(integer instanceof Integer);//true
System.out.println(null instanceof Object);//false ,在 JavaSE规范 中对 instanceof 运算符的规定就是:如果 obj 为 null,那么将返回 false。
Java自动装箱与拆箱
装箱就是自动将基本数据类型转换为包装器类型(int–>Integer);调用方法:Integer的
valueOf(int) 方法
拆箱就是自动将包装器类型转换为基本数据类型(Integer–>int)。调用方法:Integer的
intValue方
为什么会需要这功能呢?
在Java SE5之前,如果要生成一个数值为100的Integer对象,必须这样进行:
Integer i = new Integer(100);
而在从Java SE5开始就提供了自动装箱的特性,如果要生成一个数值为100的Integer对象,只需要这样就可以了:
Integer i = 100
面试题1:以下代码会输出什么?
public class Main {
public static void main(String[] args) {
Integer i1 = 100;
Integer i2 = 100;
Integer i3 = 200;
Integer i4 = 200;
System.out.println(i1==i2);
System.out.println(i3==i4);
}
}
true
false
为什么会出现这样的结果?时只需一看源码便知究竟
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
从这2段代码可以看出问题了吗?第一段代码,if判断,true拿缓存,false就new一个,是不是很熟悉
面试题2:以下代码输出什么
public class Main {
public static void main(String[] args) {
Double i1 = 100.0;
Double i2 = 100.0;
Double i3 = 200.0;
Double i4 = 200.0;
System.out.println(i1==i2); // false
System.out.println(i3==i4); // false
}
}
在某个范围内的整型数值的个数是有限的,而浮点数却没有,本身没做这数值的缓存
equals与==的区别
首先,先看定义
- == 比较的是变量(栈)内存中存放的对象的(堆)内存地址,用来判断两个对象的地址是否相同,即是否是指相同一个对象。比较的是真正意义上的指针操作
public static void main(String[] args) {
System.out.println(10L == 10); // true
}
当你使用 == 操作符比较这两个值时,Java会自动进行类型提升,将较小的数据类型(这里是 int)转换为较大的数据类型(这里是 long),以便进行比较。
- equals用来比较的是两个对象的内容是否相等,由于所有的类都是继承自java.lang.Object类的,所以适用于所有对象,如果没有对该方法进行覆盖的话(String就是很好的例子),调用的仍然是Object类中的方法,而Object中的equals方法返回的却是==的判断,
public class Object {
public boolean equals(Object obj) {
return (this == obj);
}
}
public class String{
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
}
Hashcode的作用
在Java中,集合框架主要分为两大类:List 和 Set。List 是有序且允许重复元素的集合,而 Set 则是无序且不允许重复元素的集合。
当我们向 Set 中插入元素时,需要确保该元素之前没有被添加过。这通常通过调用元素的 equals 方法来实现。然而,当集合中的元素数量非常大时,直接使用 equals 方法进行逐个比较会变得效率低下。
为了解决这个问题,引入了哈希算法。哈希算法能够提高查找、插入和删除操作的效率。每个对象都有一个 hashCode 方法,该方法返回一个整数值,这个值被称为哈希码。哈希码用于确定对象应该存储在集合内部的哪个位置(也称为桶或槽)。具体来说,哈希码可以用来快速定位到可能包含该对象的存储区域。
在实际操作中,当尝试向 Set 添加新元素时,首先计算该元素的哈希码,并根据哈希码找到对应的存储区域。如果该区域为空,则可以直接将新元素存入;如果该区域已包含其他元素,那么就需要进一步调用 equals 方法来确认这些元素是否与新元素相同。只有当 equals 方法返回 false 时,才会将新元素存入集合中(这里面就涉及到另一个面试题,hashcode相等的对象是否相等)。
通过这种方式,哈希码减少了直接使用 equals 方法进行比较的次数,从而大大提高了性能。理想情况下,即使集合中有很多元素,查找特定元素的操作也可以接近常数时间复杂度 O(1)。
需要注意的是,为了保证哈希表的高效运行,hashCode 方法的实现必须满足以下条件:
- 相等的对象必须具有相等的哈希码(即如果 x.equals(y) 返回 true,那么 x.hashCode() 必须等于 y.hashCode())。(hashcode相等的对象是否相等)
- 不相等的对象最好拥有不同的哈希码,以减少碰撞(不同对象具有相同的哈希码的情况),尽管这是不可能完全避免的。
因此,正确地重写 equals 和 hashCode 方法对于自定义类型的对象是非常重要的,这样可以确保它们能够在基于哈希的集合中正确工作。
重载和重写的区别
- 重载(Overloading)
- 定义:在同一个类中,允许存在多个方法名相同但参数列表不同的方法。
- 目的:提供一种方式来执行相似功能的方法,但是针对不同类型的输入或数量的参数。
public class Calculator {
public int add(int a, int b) { return a + b; }
public double add(double a, double b) { return a + b; } // 重载
}
- 重写(Overriding)
- 定义:子类重新定义从父类继承下来的方法,以实现特定的行为。
- 目的:当子类认为父类的方法不能满足需求时,可以覆盖该方法以实现新的行为,子类方法的访问级别不能比父类更严格(例如,如果父类方法是 public,那么子类方法也必须是 public),如果父类方法被声明为 final 或 private,则子类无法重写这些方法,使用 @Override 注解可以帮助编译器检查是否正确实现了重写
class Animal {
public void sound() {
System.out.println("Some generic animal sound");
}
}
class Dog extends Animal {
@Override
public void sound() { // 重写
System.out.println("Bark");
}
}