Java基础(面试必备)

151 阅读10分钟

Java基本功

一、语法

Java有几种基本数据类型?

有8种,基本数据类型都有默认初始值,引用数据类型默认都是null。

6 种数字类型 :byte、short、int、long、float、double
1 种字符类型:char
1 种布尔型:boolean

这八种基本类型都有对应的包装类分别为:Byte、Short、Integer、Long、Float、Double、Character、Boolean

continue、break、和 return 的区别是什么?

在循环结构中,当循环条件不满足或者循环次数达到要求时,循环会正常结束。但是,有时候可能需要在循环的过程中,当发生了某种条件之后 ,提前终止循环,这就需要用到下面几个关键词:

continue :指跳出当前的这一次循环,继续下一次循环。
break :指跳出整个循环体,继续执行循环下面的语句。
return 用于跳出所在方法,结束该方法的运行。return 一般有两种用法:

return; :直接使用 return 结束方法执行,用于没有返回值函数的方法
return value; :return 一个特定值,用于有返回值函数的方法

Java 泛型了解么?什么是类型擦除?介绍一下常用的通配符?

泛型是参数化类型,所操作的数据类型被指定为一个参数。
常用的通配符为: T,E,K,V,?
? 表示不确定的 java 类型
T (type) 表示具体的一个 java 类型
K V (key value) 分别代表 java 键值中的 Key Value
E (element) 代表 Element

==和 equals 的区别?

  1. == : 它的作用是判断两个对象的地址是不是相等。即判断两个对象是不是同一个对象。(基本数据类型==比较的是值,引用数据类型==比较的是内存地址)因为 Java 只有值传递,所以,对于 == 来说,不管是比较基本数据类型,还是引用数据类型的变量,其本质比较的都是值,只是引用类型变量存的值是对象的地址
  2. equals() : 它的作用也是判断两个对象是否相等,它不能用于比较基本数据类型的变量。equals()方法存在于Object类中,而Object类是所有类的直接或间接父类。
    Object类的equals()方法源码:
public boolean equals(Object obj) {
     return (this == obj);
}

equals() 方法存在两种使用情况:
情况 1:类没有覆盖 equals()方法。则通过equals()比较该类的两个对象时,等价于通过“==”比较这两个对象。使用的默认是 Object类equals()方法。
情况 2:类覆盖了 equals()方法。一般,我们都覆盖 equals()方法来两个对象的内容相等;若它们的内容相等,则返回 true(即,认为这两个对象相等)。

hashCode()与 equals()

你重写过hashcode和equals么,为什么重写equals时必须重写hashCode方法
hashCode()的作用是获取哈希码,也称为散列码;它实际上是返回一个 int 整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。hashCode()定义在 JDK 的 Object 类中,这就意味着 Java 中的任何类都包含有 hashCode() 函数。另外需要注意的是: Object 的 hashcode 方法是本地方法,也就是用 c 语言或 c++ 实现的,该方法通常用来将对象的 内存地址 转换为整数之后返回。

public native int hashCode();

散列表存储的是键值对(key-value),它的特点是:能根据“键”快速的检索出对应的“值”。这其中就利用到了散列码!(可以快速找到所需要的对象)

为什么要有 hashCode?

我们以“HashSet 如何检查重复”为例子来说明为什么要有 hashCode?

当你把对象加入 HashSet 时,HashSet 会先计算对象的 hashcode 值来判断对象加入的位置,同时也会与其他已经加入的对象的 hashcode 值作比较,如果没有相符的 hashcode,HashSet 会假设对象没有重复出现。但是如果发现有相同 hashcode 值的对象,这时会调用 equals() 方法来检查 hashcode 相等的对象是否真的相同。如果两者相同,HashSet 就不会让其加入操作成功。如果不同的话,就会重新散列到其他位置。(摘自我的 Java 启蒙书《Head First Java》第二版)。这样我们就大大减少了 equals 的次数,相应就大大提高了执行速度。

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

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

二、方法(函数)

为什么java中只有值传递?

按值调用表示方法接收的是调用者提供的值,而按引用调用表示方法接收的是调用者提供的变量地址。Java总是采用按值调用。方法得到的是所有参数值的一个拷贝,也就是说,方法不能修改传递给它的任何参数变量的内容。
一个方法不能修改一个基本数据类型的参数,而对象引用作为参数就不一样

    public static void main(String[] args) {
       int[] arr = { 1, 2, 3, 4, 5 };
       System.out.println(arr[0]);
       change(arr);
       System.out.println(arr[0]);
   }

   public static void change(int[] array) {
       // 将数组的第一个元素变为0
       array[0] = 0;
   }
   // 结果是
   1
   0

array 被初始化 arr 的拷贝也就是一个对象的引用,也就是说 array 和 arr 指向的是同一个数组对象。 因此,外部对引用对象的改变会反映到所对应的对象上。

重载和重写的区别

重载:体现在同一个类中,出现多个同名的方法,但是参数不同,返回值也可以不同。 重写:当子类继承父类时,可以重新再子类里重写(定义)一次父类的方法,相当于覆盖了父类的方法。
如果父类方法访问修饰符为 private/final/static 则子类就不能重写该方法,但是被 static 修饰的方法能够被再次声明。

深拷贝,浅拷贝

  1. 浅拷贝:对基本数据类型进行值传递,对引用数据类型进行引用传递般的拷贝,此为浅拷贝。
  2. 深拷贝:对基本数据类型进行值传递,对引用数据类型,创建一个新的对象,并复制其内容,此为深拷贝。

三、其他面试点

String为什么是不可变的?

String 类中使用 final 关键字修饰字符数组来保存字符串

private final char value[]

所以String 对象是不可变的。

String,StringBuffer和StringBuilder的区别是什么?

而 StringBuilder 与 StringBuffer 都继承自 AbstractStringBuilder 类,在 AbstractStringBuilder 中也是使用字符数组保存字符串char[]value 但是没有用 final 关键字修饰,所以这两种对象都是可变的。

/**
 * Constructs a string builder with no characters in it and an
 * initial capacity of 16 characters.
 */
public StringBuilder() {
    super(16);
}

通过源码得知,StringBuilder对象初始大小是16,父类创建的一个长度为16的char数组。

线程安全

String 中的对象是不可变的,也就可以理解为常量,线程安全
AbstractStringBuilder 是 StringBuilder 与 StringBuffer 的公共父类,定义了一些字符串的基本操作,如 expandCapacity、append、insert、indexOf 等公共方法。StringBuffer 对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。
StringBuilder 并没有对方法进行加同步锁,所以是非线程安全的。

@Override
public synchronized StringBuffer append(Object obj) {
    toStringCache = null;
    super.append(String.valueOf(obj));
    return this;
}

StringBuffer源码可以看出方法名前面有synchronized关键字。表示加锁。

性能

每次对 String 类型进行改变的时候,都会生成一个新的 String 对象,然后将指针指向新的 String 对象。
StringBuffer 每次都会对 StringBuffer 对象本身进行操作,而不是生成新的对象并改变对象引用。相同情况下使用 StringBuilder 相比使用 StringBuffer 仅能获得 10%~15% 左右的性能提升,但是在多线程中是不安全的。

对于三者使用的总结:

操作少量的数据: 适用 String
单线程操作字符串缓冲区下操作大量数据: 适用 StringBuilder
多线程操作字符串缓冲区下操作大量数据: 适用 StringBuffer

Object类

它是一个所以类的祖先,所有类都继承了Object。它提供了一下方法:

//native方法,返回当前运行时对象的Class对象,final关键字修饰,不允许子类重写。
public final native Class<?> getClass()
//native方法,用于返回对象的哈希码,主要使用在哈希表中,比如JDK中的HashMap。
public native int hashCode() 
//用于比较2个对象的内存地址是否相等,String类对该方法进行了重写用户比较字符串的值是否相等。
public boolean equals(Object obj)
//naitive方法,用于创建并返回当前对象的一份拷贝。
//一般情况下,对于任何对象 x,表达式 x.clone() != x 为true,x.clone().getClass() == x.getClass() 为true。
protected native Object clone() throws CloneNotSupportedException
//返回类的名字@实例的哈希码的16进制的字符串。建议Object所有的子类都重写这个方法。
public String toString()
//native方法,并且不能重写。唤醒一个在此对象监视器上等待的线程(监视器相当于就是锁的概念)。有多个线程在等待只会任意唤醒一个。
public final native void notify()
//native方法,并且不能重写。跟notify差不多,唤醒在此对象监视器上等待的所有线程,所有!
public final native void notifyAll()
//native方法,并且不能重写。暂停线程的执行。
//注意:sleep方法没有释放锁,而wait方法释放了锁 。timeout是等待时间。
public final native void wait(long timeout) throws InterruptedException
//nanos参数设置超时时间,以毫微秒为单位,范围是 0-999999。
public final void wait(long timeout, int nanos) throws InterruptedException
//跟之前的2个wait方法一样,只不过该方法一直等待,没有超时时间这个概念
public final void wait() throws InterruptedException
//实例被垃圾回收器回收的时候触发的操作
protected void finalize() throws Throwable { }

四、Java核心技术

反射机制

JAVA 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为 java 语言的反射机制。

优缺点

优点:运行期类型的判断,动态加载类,提高代码灵活度。 缺点

  1. 性能问题,反射相当于一系列解释操作,通知JVM要做的事情,性能比直接的Java代码要慢很多。
  2. 安全问题,让我们可以动态操作改变类的属性,同时增加了类的安全性。

静态编译和动态编译

静态编译:在编译时确定类型,绑定对象
动态编译:运行时确定类型,绑定对象

反射应用场景

一般框架设计中使用反射。
我们在使用 JDBC 连接数据库时使用 Class.forName()通过反射加载数据库的驱动程序。
Spring 框架的 IOC(动态加载管理 Bean)创建对象以及 AOP(动态代理)功能都和反射有联系。

异常

概念

异常本质上是程序上的错误,包括程序逻辑错误和系统错误。比如使用空的引用、数组下标越界、内存溢出错误等,这些都是意外的情况,背离我们程序本身的意图。真实工作中我们需要考虑异常处理,这样可以保证你的代码质量。

异常分类

异常处理

try,catch,finally,throws,throw

层次结构图

图片来自:https://simplesnippets.tech/exception-handling-in-java-part-1/ 图片来自:simplesnippets.tech/exception-h…

多线程