Java 基础面试题

Java 基础面试题,第1张

Java 基础面试题 重写和重载 不同点重载重写发生范围同一个类子类参数列表必须修改不可修改返回类型随便修改子类要比父类小(void 不可改)抛出异常随便修改子类要比父类小修饰符随便修改可降低限制发生阶段编译器运行期 泛型擦除

泛型提供了编译期间类型安全检测机制,该机制允许程序员在编译期间检测到非法的类型。

Java 的泛型是伪泛型,在编译后的字节码,所有的泛型信息都会被擦掉。但是为了反编译时识别泛型 LocalVariableTable 中的 Signature 会保留泛型信息。

List l1 = new ArrayList<>();
List l2 = l1;
l2.add('a');

上述代码编译后再反编译可得下面的代码

List l1 = new ArrayList();
l1.add('a');

扩展阅读

深入探索Java泛型的本质 | 泛型

== 和 equals

基本数据类型:== 比值

引用数据类型:== 比地址

对于没有重写的类,其 equals 继承自 Object,而 Object 的 equals 用的是 ==

hashCode 和 equals

对于 HashMap、HashSet 这类容器而言,需要先使用 hashCode 判断在容器中是否存在 hashCode 相同的元素,如果存在再使用 equals 比较是否是相同元素。

hashCode 相同,可能是因为 hash 碰撞导致的。所以 equals 判断相等的两个对象,其 hashCode 也相等。而 hashCode 相等的对象,equals 判断未必会相等。

hashCode 的作用是降低所需要比较的成本。如果不使用基于 Hash 的容器,无需重写 hashCode。

扩展阅读

Java hashCode() 和 equals()的若干问题解答

包装类常量池

Byte、Short、Integer、Long 常量池中缓存了 − 128 ~ 127 -128 ~ 127 −128~127 ,而 Character 缓存了 0 ~ 127 0~127 0~127。

Boolean、Float、Double 无常量池。

自动装拆箱

装箱:将基本类型用它们对应的引用类型包装起来。调用包装类的 valueOf 方法拆箱:将包装类型转换为基本数据类型。调用了 xxxValue 方法

频繁的自动装拆箱会严重影响性能。

构造方法能否被重写(override)

构造方法不能重写。因为重写必须发生在继承的基础上,而构造方法又无法被继承,所以不能重写。

虽然构造方法不能重写,但可以重载。

被 final 修饰的方法同理,还有被 private 修饰的方法会隐式加上 final。

String、Stirng Builder 和 String Buffer

String 不可变

String 不可变指的是对 String 二次赋值不是在原内存地址上修改数据,而是重新指向一个新对象。

    底层字符数组是 final,导致数组引用不可变,但数组元素是可变的。底层字符数组是 private,也没提供暴露出去修改的方法。所以就杜绝了数组元素可变的情况。上述两点足以保证 String 不可变,但是仍然在类上加了 final 完全杜绝被子类破坏。

不可变性带来的好处

    多线程安全:在多线程环境下,只有写 *** 作才会引起安全问题,然而 String 是不可写的所以在多线程下是安全的。字符串常量池:字符串不可变,所以可以利用常量池公用一个串,从而节省内存空间。

扩展:反射改变 Stirng 底层字符数组

    所有引用相同字符串常量池的 String,都会发生变化String 的 hashCode 只会计算一次,所以不会出现改变前后 hashCode 对不上的问题。

不可变性带来的弊端

由于 String 不可变,每次对 String 类型进行改变的时候,都会生成一个新的 String 对象,然后将指针指向新的 String 对象,开销较大。

从而引入了 StringBuilder 和 StringBuffer 来解决频繁修改字符串时带来的开销问题。StringBuilder 是线程不安全的,StringBuffer 是线程安全的。

综上所述:

    对字符串修改不频繁,使用 String对字符串修改频繁且在单线程环境下,使用 StirngBuilder对字符串修改频繁且在多线程环境下,使用 StringBuffer

扩展阅读

如何理解 String 类型值的不可变? - 知乎提问

反射能否改变 final 字段

final 修饰的字段代表该字段引用不能改变,但引用对象本体可以改变,所以 final 能够被反射改变。

但对于基本数据类型和 String 来说,在编译时会做内联优化,所以从结果上来说就失效了。

public class A {
    final String s = "a";
    final int i = 1;
    public String getS() {
        return  s;
    }

    public int getI() {
        return i;
    }
}

编译后再反编译可得

public class A {
    final String s = "a";
    final int i = 1;

    public A() {
    }

    public String getS() {
        return "a";
    }

    public int getI() {
        return 1;
    }
}
深拷贝、潜拷贝、引用拷贝

深拷贝:堆内新创建的对象里的所有字段都是新创建的。

潜拷贝:堆内新创建的对象里的所有字段都是引用旧对象的字段。

引用拷贝:压根没在堆内创建新对象,而是直接引用了老对象。

这里所说的字段是引用类型

欢迎分享,转载请注明来源:内存溢出

原文地址: https://www.outofmemory.cn/zaji/5717196.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-18
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存