关于java:这18道Java面试题你能回答上几个附答案

10次阅读

共计 7315 个字符,预计需要花费 19 分钟才能阅读完成。

本文章转自:乐字节
文章次要解说:Java 面试题
获取更多 Java 相干常识能够关注公众号《乐字节》发送:999
1、Java 有哪些特点?

• 并发性的:你能够在其中执行许多语句,而不用一次执行它;

• 面向对象的:基于类和面向对象的编程语言;

• 独立性的:反对一次编写,到处运行的独立编程语言,即编译后的代码能够在反对 Java 的所有平台上运行。

2、Java 的个性

Java 的个性有如下这几点:

• 简略,Java 会让你的工作变得更加轻松,使你把关注点放在次要业务逻辑上,而不用关怀指针、运算符重载、内存回收等与次要业务无关的性能。

• 便携性,Java 是平台无关性的,这意味着在一个平台上编写的任何应用程序都能够轻松移植到另一个平台上。

• 安全性,编译后会将所有的代码转换为字节码,人类无奈读取。它使开发无病毒,无篡改的零碎 / 利用成为可能。

• 动态性,它具备适应一直变动的环境的能力,它可能反对动态内存调配,从而缩小了内存节约,进步了应用程序的性能。

• 分布式,Java 提供的性能有助于创立分布式应用。应用近程办法调用(RMI),程序能够通过网络调用另一个程序的办法并获取输入。您能够通过从互联网上的任何计算机上调用办法来拜访文件。这是革命性的一个特点,对于当今的互联网来说太重要了。

• 健壮性,Java 有弱小的内存治理性能,在编译和运行时查看代码,它有助于打消谬误。

• 高性能,Java 最黑的科技就是字节码编程,Java 代码编译成的字节码能够轻松转换为本地机器代码。通过 JIT 即时编译器来实现高性能。

• 解释性,Java 被编译成字节码,由 Java 运行时环境解释。

• 多线程性,Java 反对多个执行线程(也称为轻量级过程),包含一组同步原语。这使得应用线程编程更加容易,Java 通过管程模型来实现线程安全性。

3、形容一下值传递和援用传递的区别?

简略了解的话就是:

值传递是指在调用函数时将理论参数复制一份到函数中,这样的话如果函数对其传递过去的形式参数进行批改,将不会影响到理论参数

援用传递是指在调用函数时将对象的地址间接传递到函数中,如果在对形式参数进行批改,将影响到理论参数的值。

4、== 和 equals 区别是什么

== 是 Java 中一种操作符,它有两种比拟形式。

• 对于根本数据类型来说,== 判断的是两边的值是否相等;

public class DoubleCompareAndEquals {Person person1 = new Person(24,"boy");
    Person person2 = new Person(24,"girl");
    int c = 10;

    private void doubleCompare(){

        int a = 10;
        int b = 10;

        System.out.println(a == b);
        System.out.println(a == c);
        System.out.println(person1.getId() == person2.getId());

    }
}

• 对于援用类型来说,== 判断的是两边的援用是否相等,也就是判断两个对象是否指向了同一块内存区域。

private void equals(){

System.out.println(person1.getName().equals(person2.getName()));
}

equals 是 Java 中所有对象的父类,即 Object 类定义的一个办法。它只能比拟对象,它示意的是援用单方的值是否相等。所以记住,并不是说 == 比拟的就是援用是否相等,equals 比拟的就是值,这须要辨别来说的。

equals 用作对象之间的比拟具备如下个性:

• 自反性:对于任何非空援用 x 来说,x.equals(x) 应该返回 true。

• 对称性:对于任何非空援用 x 和 y 来说,若 x.equals(y)为 true,则 y.equals(x)也为 true。

• 传递性:对于任何非空援用的值来说,有三个值,x、y 和 z,如果 x.equals(y) 返回 true,y.equals(z) 返回 true,那么 x.equals(z) 也应该返回 true。

• 一致性:对于任何非空援用 x 和 y 来说,如果 x.equals(y) 相等的话,那么它们必须始终相等。

• 非空性:对于任何非空援用的值 x 来说,x.equals(null) 必须返回 false。

5、String 中的 equals 是如何重写的

String 代表的是 Java 中的字符串,String 类比拟非凡,它整个类都是被 final 润饰的,也就是说,String 不能被任何类继承,任何 批改 String 字符串的办法都是创立了一个新的字符串。

equals 办法是 Object 类定义的办法,Object 是所有类的父类,当然也包含 String,String 重写了 equals 办法,上面咱们来看看是怎么重写的:

• 首先会判断要比拟的两个字符串它们的援用是否相等。如果援用相等的话,间接返回 true,不相等的话持续上面的判断;

• 而后再判断被比拟的对象是否是 String 的实例,如果不是的话间接返回 false,如果是的话,再比拟两个字符串的长度是否相等,如果长度不想等的话也就没有比拟的必要了;长度如果雷同,会比拟字符串中的每个字符 是否相等,一旦有一个字符不相等,就会间接返回 false。

上面是它的流程图:

这里再提醒一下,你可能有纳闷什么时候是:

if (this == anObject) {
return true;
}

这个判断语句如何能力返回 true?因为都是字符串啊,字符串比拟的不都是堆空间吗,猛然一看发现如同永远也不会走,然而你遗记了 String.intern() 办法,它示意的概念在不同的 JDK 版本有不同的辨别。

在 JDK1.7 及当前调用 intern 办法是判断运行时常量池中是否有指定的字符串,如果没有的话,就把字符串增加到常量池中,并返回常量池中的对象。

验证过程如下:

private void StringOverrideEquals(){

  String s1 = "aaa";
  String s2 = "aa" + new String("a");
  String s3 = new String("aaa");

  System.out.println(s1.intern().equals(s1));
  System.out.println(s1.intern().equals(s2));
  System.out.println(s3.intern().equals(s1));

}

• 首先 s1.intern.equals(s1) 这个无论如何都返回 true,因为 s1 字符串创立进去就曾经在常量池中存在了。

• 而后第二条语句返回 false,因为 s1 返回的是常量池中的对象,而 s2 返回的是堆中的对象

• 第三条语句 s3.intern.equals(s1),返回 true,因为 s3 对象尽管在堆中创立了一个对象,然而 s3 中的 “aaa” 返回的是常量池中的对象。

6、为什么重写 equals 办法必须重写 hashcode 办法

equals 办法和 hashCode 都是 Object 中定义的办法,它们常常被一起重写。

equals 办法是用来比拟对象大小是否相等的办法,hashcode 办法是用来判断每个对象 hash 值的一种办法。如果只重写 equals 办法而不重写 hashcode 办法,很可能会造成两个不同的对象,它们的 hashcode 也相等,造成抵触。比方:

String str1 = “ 通话 ”;
String str2 = “ 重地 ”;

它们两个的 hashcode 相等,然而 equals 可不相等。

咱们来看一下 hashCode 官网的定义:

总结起来就是:

• 如果在 Java 运行期间对同一个对象调用 hashCode 办法后,无论调用多少次,都应该返回雷同的 hashCode,然而在不同的 Java 程序中,执行 hashCode 办法返回的值可能不统一;

• 如果两个对象的 equals 相等,那么 hashCode 必须雷同;

• 如果两个对象 equals 不相等,那么 hashCode 也有可能雷同,所以须要重写 hashCode 办法,因为你不晓得 hashCode 的底层结构(反正我是不晓得,有大牛能够传授传授),所以你须要重写 hashCode 办法,来为不同的对象生成不同的 hashCode 值,这样可能进步不同对象的访问速度;

• hashCode 通常是将地址转换为整数来实现的。

7、String s1 = new String(“abc”) 在内存中创立了几个对象?

一个或者两个,String s1 是申明了一个 String 类型的 s1 变量,它不是对象。应用 new 关键字会在堆中创立一个对象,另外一个对象是 abc,它会在常量池中创立,所以一共创立了两个对象;如果 abc 在常量池中曾经存在的话,那么就会创立一个对象。

8、String 为什么是不可变的、jdk 源码中的 String 如何定义的、为什么这么设计?

首先理解一下什么是不可变对象,不可变对象就是一经创立后,其对象的外部状态不能被批改,啥意思呢?也就是说不可变对象须要恪守上面几条准则:

• 不可变对象的外部属性都是 final 的;

• 不可变对象的外部属性都是 private 的;

• 不可变对象不能提供任何能够批改外部状态的办法、setter 办法也不行;

• 不可变对象不能被继承和扩大。

与其说问 String 为什么是不可变的,不如说如何把 String 设计成不可变的。

String 类是一种对象,它是独立于 Java 根本数据类型而存在的,String 你能够把它了解为字符串的汇合,String 被设计为 final 的,示意 String 对象一经创立后,它的值就不能再被批改,任何对 String 值进行批改的办法就是从新创立一个字符串。String 对象创立后会存在于运行时常量池中,运行时常量池是属于办法区的一部分,JDK1.7 后把它移到了堆中。

不可变对象不是真的不可变,能够通过反射来对其外部的属性和值进行批改,不过个别咱们不这样做。

9、static 关键字是干什么用的?谈谈你的了解。

static 是 Java 中十分重要的关键字,static 示意的概念是动态的,在 Java 中,static 次要用来:

• 润饰变量,static 润饰的变量称为动态变量、也称为类变量,类变量属于类所有,对于不同的类来说,static 变量只有一份,static 润饰的变量位于办法区中;static 润饰的变量可能间接通过 类名. 变量名 来进行拜访,不必通过实例化类再进行应用;

• 润饰办法,static 润饰的办法被称为静态方法,静态方法可能间接通过 类名. 办法名 来应用,在静态方法外部不能应用非动态属性和办法;

• static 能够润饰代码块,次要分为两种,一种间接定义在类中,应用 static{},这种被称为动态代码块,一种是在类中定义动态外部类,应用 static class xxx 来进行定义;

• static 能够用于动态导包,通过应用 import static xxx 来实现,这种形式个别不举荐应用;

• static 能够和单例模式一起应用,通过双重查看锁来实现线程平安的单例模式。

10、final 关键字是干什么用的?谈谈你的了解。

final 是 Java 中的关键字,它示意的意思是不可变的,在 Java 中,final 次要用来:

• 润饰类,final 润饰的类不能被继承,不能被继承的意思就是不能应用 extends 来继承被 final 润饰的类;

• 润饰变量,final 润饰的变量不能被改写,不能被改写的意思有两种,对于根本数据类型来说,final 润饰的变量,其值不能被扭转,final 润饰的对象,对象的援用不能被扭转,然而对象外部的属性能够被批改。final 润饰的变量在某种程度上起到了不可变的成果,所以,能够用来爱护只读数据,尤其是在并发编程中,因为明确的不能再为 final 变量进行赋值,有利于缩小额定的同步开销;

• 润饰办法,final 润饰的办法不能被重写;

• final 修饰符和 Java 程序性能优化没有必然联系。

11、抽象类和接口的区别是什么

抽象类和接口都是 Java 中的关键字,抽象类和接口中都容许进行办法的定义,而不必具体的办法实现。抽象类和接口都容许被继承,它们宽泛的利用于 JDK 和框架的源码中,来实现多态和不同的设计模式。

不同点在于:

• 形象级别不同:类、抽象类、接口其实是三种不同的形象级别,形象水平顺次是 接口 > 抽象类 > 类。在接口中,只容许进行办法的定义,不容许有办法的实现,抽象类中能够进行办法的定义和实现;而类中只容许进行办法的实现,我说的办法的定义是不容许在办法前面呈现 {}。

• 应用的关键字不同:类应用 class 来示意;抽象类应用 abstract class 来示意;接口应用 interface 来示意。

• 变量:接口中定义的变量只能是公共的动态常量,抽象类中的变量是一般变量。

12、重写和重载的区别

在 Java 中,重写和重载都是对同一办法的不同表现形式,上面咱们针对重写和重载做一下简略的辨别:

• 子父级关系不同,重写是针对子级和父级的不同表现形式,而重载是在同一类中的不同表现形式;

• 概念不同,子类重写父类的办法个别应用 @override 来示意;重写后的办法其办法的申明和参数类型、程序必须要与父类完全一致;重载是针对同一类中概念,它要求重载的办法必须满足上面任何一个要求:办法参数的程序,参数的个数,参数的类型任意一个放弃不同即可。

13、byte 的取值范畴是多少,怎么计算出来的

byte 的取值范畴是 -128 -> 127 之间,一共是 256。一个 byte 类型在计算机中占据一个字节,那么就是 8 bit,所以最大就是 2^7 = 1111 1111。

Java 中用补码来示意二进制数,补码的最高位是符号位,最高位用 0 示意负数,最高位 1 示意正数,负数的补码就是其自身,因为最高位是符号位,所以负数示意的就是 0111 1111,也就是 127。最大正数就是 1111 1111,这其中会波及到两个 0,一个 +0,一个 -0,+0 归为负数,也就是 0,-0 归为正数,也就是 -128,所以 byte 的范畴就是 -128 – 127。

14、HashMap 和 HashTable 的区别

相同点:

HashMap 和 HashTable 都是基于哈希表实现的,其外部每个元素都是 key-value 键值对,HashMap 和 HashTable 都实现了 Map、Cloneable、Serializable 接口。

不同点:

• 父类不同:HashMap 继承了 AbstractMap 类,而 HashTable 继承了 Dictionary 类:

• 空值不同:HashMap 容许空的 key 和 value 值,HashTable 不容许空的 key 和 value 值。HashMap 会把 Null key 当做一般的 key 看待。不容许 null key 反复。

• 线程安全性:HashMap 不是线程平安的,如果多个内部操作同时批改 HashMap 的数据结构比方 add 或者是 delete,必须进行同步操作,仅仅对 key 或者 value 的批改不是扭转数据结构的操作。能够抉择结构线程平安的 Map 比方 Collections.synchronizedMap 或者是 ConcurrentHashMap。而 HashTable 自身就是线程平安的容器。

• 性能方面:尽管 HashMap 和 HashTable 都是基于单链表的,然而 HashMap 进行 put 或者 get� 操作,能够达到常数工夫的性能;而 HashTable 的 put 和 get 操作都是加了 synchronized 锁的,所以效率很差。

• 初始容量不同:HashTable 的初始长度是 11,之后每次裁减容量变为之前的 2n+1(n 为上一次的长度)而 HashMap 的初始长度为 16,之后每次裁减变为原来的两倍。创立时,如果给定了容量初始值,那么 HashTable 会间接应用你给定的大小,而 HashMap 会将其裁减为 2 的幂次方大小。

15、HashMap 和 HashSet 的区别

HashSet 继承于 AbstractSet 接口,实现了 Set、Cloneable,、java.io.Serializable 接口。HashSet 不容许汇合中呈现反复的值。HashSet 底层其实就是 HashMap,所有对 HashSet 的操作其实就是对 HashMap 的操作。所以 HashSet 也不保障汇合的程序,也不是线程平安的容器。

16、HashMap 的底层构造

JDK1.7 中,HashMap 采纳位桶 + 链表的实现,即应用链表来解决抵触,同一 hash 值的链表都存储在一个数组中。然而当位于一个桶中的元素较多,即 hash 值相等的元素较多时,通过 key 值顺次查找的效率较低。

所以,与 JDK 1.7 相比,JDK 1.8 在底层构造方面做了一些扭转,当每个桶中元素大于 8 的时候,会转变为红黑树,目标就是优化查问效率。

17、HashMap 的长度为什么是 2 的幂次方?

这道题我想了几天,之前和群里小伙伴们探讨每日一题的时候,问他们为什么 length%hash == (n – 1) & hash,它们说相等的前提是 length 的长度 2 的幂次方,而后我回了一句难道 length 还能不是 2 的幂次方吗?其实是我没有搞懂因果关系,因为 HashMap 的长度是 2 的幂次方,所以应用余数来判断在桶中的下标。如果 length 的长度不是 2 的幂次方,小伙伴们能够举个例子来试试:

例如长度为 9 时候,3 & (9-1) = 0,2 & (9-1) = 0,都在 0 上,碰撞了;

这样会增大 HashMap 碰撞的几率。

18、HashMap 多线程操作导致死循环问题

HashMap 不是一个线程平安的容器,在高并发场景下,应该应用 ConcurrentHashMap,在多线程场景下应用 HashMap 会造成死循环问题(基于 JDK1.7),呈现问题的地位在 rehash 处,也就是:

do {
    Entry<K,V> next = e.next; // <-- 假如线程一执行到这里就被调度挂起了
    int i = indexFor(e.hash, newCapacity);
    e.next = newTable[i];
    newTable[i] = e;
    e = next;
} while (e != null);

这是 JDK1.7 的 rehash 代码片段,在并发的场景下会造成环。

JDK1.8 也会造成死循环问题。

感激大家的认同与反对,小编会继续转发《乐字节》优质文章

正文完
 0