面试题 Java

& 和 && 的区别?

&运算符有两种用法:

  • 按位与
  • 逻辑与

&&运算符是短路与运算。逻辑与跟短路与的差别是很大的,虽然二者都要求运算符左右两端的布尔值都是true 整个表达式的值才是 true。
&&之所以称为短路运算是因为,如果&&左边的表达式的值是 false,右边的表达式会被直接短路掉,不会进行运算。
很多时候我们可能都需要用&&而不是&,例如在验证用户登录时判定用户名不是 null
且不是空字符串,应当写为:username != null &&!username.equals(“”),
二者的顺序不能交换,更不能用&运算符,因为第一个条件如果不成立,根本不能进行字符串的 equals 比较,否则会产生 NullPointerException 异常。

注意:逻辑或运算符(|)和短路或运算符(||)的差别也是如此。

==equals 的区别?

==equals 的主要区别是:

  • equals 和== 最大的区别是一个是方法,一个是运算符
  • == 常用于比较原生类型,而 equals() 用来比较方法两个对象的内容是否相等。
  • == 如果比较的对象是基本数据类型,则比较的是数值是否相等;如果比较的是引用数据类型,则比较的是对象的地址值是否相等。

如果 == 和 equals() 用于比较对象,当两个引用地址相同,== 返回 true。而 equals() 可以返回 true 或者 false 主要取决于重写实现。最常见的一个例子,字符串的比较,不同情况 == 和 equals() 返回不同的结果。

注意:equals 方法不能用于基本数据类型的变量,如果没有对 equals 方法进行重写,则比较的是引用类型的变量所指向的对象的地址。

在 Java 中,如何跳出当前的多重嵌套循环?

方案一:使用标记

  1. public static void main(String[] args) {
  2. System.out.println("标记前");
  3. ok:
  4. for (int i = 0; i < 10; i++) {
  5. for (int j = 0; j < 10; j++) {
  6. System.out.println("i=" + i + ",j=" + j);
  7. if (j == 5) {
  8. break ok;
  9. }
  10. }
  11. }
  12. System.out.println("标记后");
  13. }

运行结果:

  1. 标记前
  2. i=0,j=0
  3. i=0,j=1
  4. i=0,j=2
  5. i=0,j=3
  6. i=0,j=4
  7. i=0,j=5
  8. 标记后

方案二:使外层的循环条件表达式的结果可以受到里层循环体代码的控制

  1. public static void main(String[] args) {
  2. System.out.println("标记前");
  3. boolean flag = true;
  4. for (int i = 0; i < 10; i++) {
  5. for (int j = 0; j < 10 && flag; j++) {
  6. System.out.println("i=" + i + ",j=" + j);
  7. if (j == 5)
  8. flag = false;
  9. }
  10. }
  11. System.out.println("标记后");
  12. }

注:不推荐使用标记,因为它容易破坏代码的执行顺序

为什么重写 equals时必须重写 hashCode 方法?

hashCode() 的作用是获取哈希码,也称为散列码;它实际上是返回一个 int 整数。
这个哈希码的作用是确定该对象在哈希表中的索引位置。
hashCode() 定义在 JDK 的 Object.java 中,这就意味着 Java 中的任何类都包含有 hashCode() 函数。
散列表存储的是键值对(key-value),它的特点是:能根据“键”快速的检索出对应的“值”。这其中就利用到了散列码。(可以快速找到所需要的对象)
为什么要有 hashCode?
这里以“HashSet 如何检查重复” 为例子来说明为什么要有 hashCode :
当你把对象加入 HashSet 时,HashSet 会先计算对象的 hashcode 值来判断对象加入的位置,同时也会与其他已经加入的对象的 hashcode 值作比较,如果没有相符的 hashcode,HashSet 会假设对象没有重复出现。但是如果发现有相同 hashcode 值的对象,这时会调用 equals()方法来检查 hashcode 相等的对象是否真的相同。
如果两者相同,HashSet 就不会让其加入操作成功。
如果不同的话,就会重新散列到其他位置。
这样我们就大大减少了 equals 的次数,相应就大大提高了执行速度。
hashCode()equals()的相关规定:

  1. 如果两个对象相等,则 hashcode 一定也是相同的
  2. 两个对象相等,对两个对象分别调用 equals 方法都返回 true
  3. 两个对象有相同的 hashcode 值,它们也不一定是相等的
  4. 因此,equals 方法被覆盖过,则 hashCode 方法也必须被覆盖
  5. hashCode() 的默认行为是对堆上的对象产生独特值。如果没有重写hashCode(),则该 class 的两个对象无论如何都不会相等(即使这两个对象指向相同的数据)

    Java中的Math.round(-1.5)等于多少?

    等于-1
    在数轴上取值时,中间值(0.5)向右取整:
  • +0.5是往上取整
  • -0.5直接舍弃

    重载(overload)和重写(override)的区别?

    方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性,而后者实现的是运行时的多态性。
    重载发生在一个类中,同名的方法如果有不同的参数列表(参数类型不同、参数个数不同或者二者都不同)则视为重载;
    重写发生在子类与父类之间,重写要求子类被重写方法与父类被重写方法有相同的返回类型,比父类被重写方法更好访问,不能比父类被重写方法声明更多的异常(里氏代换原则)。
    重载对返回类型没有特殊的要求。

    方法重载的规则:

  • 方法名一致,参数列表中参数的顺序,类型,个数不同。

  • 重载与方法的返回值无关,存在于父类和子类,同类中。
  • 可以抛出不同的异常,可以有不同修饰符。

    方法重写的规则:

  • 参数列表必须完全与被重写方法的一致,返回类型必须完全与被重写方法的返回类型一致。

  • 构造方法不能被重写,声明为 final 的方法不能被重写,声明为 static 的方法不能被重写,但是能够被再次声明。
  • 访问权限不能比父类中被重写的方法的访问权限更低。
  • 重写的方法能够抛出任何非强制异常(UncheckedException,也叫非运行时异常),无论被重写的方法是否抛出异常。但是,重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更广泛的强制性异常,反之则可以。

    构造方法能不能重写?能不能重载?构造方法能不能显式调用?

    不可以重写,但可以重载
    不能显式调用,不能把构造方法当成普通方法调用,只有在创建对象的时候才会被系统调用

    阐述静态变量和实例变量的区别?

    静态变量:

    是被 static 修饰符修饰的变量,也称为类变量,它属于类,不属于类的任何一个对象,一个类不管创建多少个对象,静态变量在内存中有且仅有一个拷贝;

    实例变量:

    必须依存于某一实例,需要先创建对象然后通过对象才能访问到它。静态变量可以实现让多个对象共享内存。

    什么是隐式转换?什么是显式转换?

    显式转换就是强制类型转换,把一个大类型的数据强制赋值给小类型的数据;
    隐式转换就是自动类型转换,大范围的变量能够接受小范围的数据;

    什么是拆装箱?

    拆箱:把包装类型转化为基本数据类型
    装箱:把基本数据类型转化为包装类型

    break 和 continue 的区别?

    break 和 continue 都是用来控制循环的语句。
    break 用于完全结束一个循环,跳出循环体执行循环后面的语句。
    continue 用于跳过本次循环,执行下次循环。

    内部类与静态内部类的区别?

    内部类:

    1、内部类中的变量和方法不能声明为静态的。
    2、内部类实例化:B是A的内部类,实例化B:A.B b = new A().new B()
    3、内部类可以引用外部类的静态或者非静态属性及方法。

    静态内部类:

    1、静态内部类属性和方法可以声明为静态的或者非静态的。
    2、实例化静态内部类:B是A的静态内部类,A.B b = new A.B()
    3、静态内部类只能引用外部类的静态的属性及方法。
    二者比较:

  • 静态内部类相对于外部类是独立存在的,在静态内部类中无法直接访问外部类中的变量、方法。如果要访问,必须new一个外部类的对象,使用new出来的对象访问。但是可以直接访问静态的变量,调用静态的方法。

  • 普通内部类作为外部类的一个成员存在,在普通内部类中可以直接访问外部类的属性,调用外部类的方法。
  • 外部类要访问内部类的属性或者方法,需要创建一个内部类的对象,使用该对象去访问属性或者调用方法。
  • 其他类要访问普通内部类的属性或者方法,必须在外部类中创建一个普通内部类的对象作为属性,外部类可以通过该属性调用普通内部类的方法或者访问普通内部类的属性。
  • 其他类要访问静态内部类的属性或者方法,直接创建一个静态内部类对象即可。