熬夜总结的2022java面试题

Posted 野生java研究僧

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了熬夜总结的2022java面试题相关的知识,希望对你有一定的参考价值。

java面试宝典

前言

面试不但要有工作经验,还得会点八股文,不然别人问起来,答不上来也是异常的尴尬,特别是应届毕业生,也是需要背点八股文,注重基础知识,才能在面试中得心应手,以下是我收集的面试题,以及我个人遇到的一些问题,做一些总结希望能帮助到大家。打了 * 号的都是我个人真实遇到过的。 答案只做参考,不喜勿喷,因为什么问题都没有标准的答案,只有你真正的明白了这个问题,你才能毫不费劲给别说出来。

java基础

什么是面向对象?

面向对象是模型化的,你只需抽象出一个类,这是一个抽象模板,在这里你拥有数据也拥有解决问题的方法。我们只需要操作这个对象,就可以使用里面的数据和方法。

面向对象的三大特征:封装,继承,多态

封装:在于明确标识出允许外部使用的所有成员函数和数据项,内部细节对外部隐藏,外部无需修改或者关心内部实现。对内部数据进行保护。

继承:继承父类的方法,子类和父类公用一些方法和属性,可以达到功能的扩展和代码复用。

多态:基于对象所属类不同,外部对同一个方法的调用,实际执行逻辑不同。继承特点:方法重写,父类引用指向子类对象,编译看左边,运行看右边

值传递和引用传递?*

Java中都是值传递

参数类型

  • 形参:方法被调用时需要传递进来的参数,如:func(int a)中的a,它只有在func被调用期间a才有意义,也就是会被分配内存空间,在方法fun执行完成后, a 就会被销毁释放空间,也就是不存在了

  • 实参:方法被调用时是传入的实际值,它在方法被调用前就已经被初始化并旦在方法被调用时传入。

值传递与引用传递

  • 值传递:在方法被调用时,实参通过形参把它的内容副本传入方法内部,此时形参接收到的内容是实参值的一个拷贝,因此在方法内对形参的任何操作,都仅仅是对这个副本的操作,不影响原始值的内容。值传递传递的是真实内容的一个副本,对副本的操作不影响原内容,也就是形参怎么变化,不会影响实参对应的内容。

  • 引用传递:"引用"也就是指向真实内容的地址值,在方法调用时,实参的地址通过方法调用被传递给相应的形参,在方法体内,形参和实参指向同一块内存地址,对形参的操作会影响的真实内容。(java中只有值传递)

Java中都是值传递

Object类型,除了这种不可变的类以外,你传到另外一个方法中去修改其里面的属性时,原有的对象中的值也会发生,原因是形参拷贝了实参的地址作为副本,他们俩共用一个地址,所以会改掉堆中的那个对象中的属性值 (叫传递引用)

传递值

当我们用这种基本数据类型、不可变类型( String, Integer,Long)之类的做形参, 他们的实参值不会改变

==和equals的区别是什么?

什么是==?

== 等于比较运算符,如果进行比较的两个操作数都是数值类型,即使他们的数据类型不相同,只要他们的值相等,也都将返回true.如果两个操作数都是引用类型,那么只有当两个引用变量的类型具有父子关系时才可以比较,而且这两个引用必须指向同一个对象,才会返回true.(在这里我们可以理解成==比较的是两个变量的内存地址)

什么是equals()?

equals()方法是Object类的方法,在Object类中的equals()方法体内实际上返回的就是使用==进行比较的结果.但是我们知道所有的类都继承Object,而且Object中的equals()方法没有使用final关键字修饰,那么当我们使用equals()方法进行比较的时候,我们需要关注的就是这个类有没有重写Object中的equals()方法. 如果重写了equals方法就按照自己的重写的规则进行比较,如果没有重写就继承自Object类的equals方法进行比较内存地址(只有引用类型的对象才可以调用equals方法)。

重载和重写的区别?

1.重写(Override)

重写就是重新写一遍的意思。其实就是在子类中把父类本身有的方法重新写一遍。子类继承了父类原有的方法,但有时子类并不想原封不动的继承父类中的某个方法,所以在方法名,参数列表,返回类型(除过子类中方法的返回值是父类中方法返回值的子类时)都相同的情况下, 对方法体进行修改或重写,这就是重写。但要注意子类函数的访问修饰权限不能少于父类的。 方法重写又叫方法覆盖。

重写总结:
1.发生在父类与子类之间
2.方法名,参数列表,返回类型(除过子类中方法的返回类型是父类中返回类型的子类)必须相同
3.访问修饰符的限制一定要大于被重写方法的访问修饰符(public>protected>default>private)
4.重写方法一定不能抛出新的检查异常或者比被重写方法申明更加宽泛的检查型异常
5.父类的静态方法是不能被重写的。

2.重载(Overload)

在一个类中,同名的方法如果有不同的参数列表(参数类型不同、参数个数不同甚至是参数顺序不同)则视为重载。同时,重载对返回类型没有要求,可以相同也可以不同,但不能通过返回类型是否相同来判断重载

重载总结:
1.重载Overload是一个类中多态性的一种表现
2.重载要求同名方法的参数列表不同(参数类型,参数个数甚至是参数顺序)
3.重载的时候,返回值类型可以相同也可以不相同。无法以返回型别作为重载函数的区分标准

区别总结:

1、重写实现的是运行时的多态,而重载实现的是编译时的多态。

2、重写的方法参数列表必须相同;而重载的方法参数列表必须不同。

3、重写的方法的返回值类型只能是父类类型或者父类类型的子类,而重载的方法对返回值类型没有要求。

4、重写方法一定不能抛出新的检查异常或者比被重写方法申明更加宽泛的检查型异常,重载不做限制

5、重写对访问修饰符的限制一定要大于等于被重写方法的访问修饰符,重载不做限制

抽象类和接口的区别 *

类型abstract classInterface
定义abstract class关键字Interface关键字
继承抽象类只可以继承一个类接口可以继承接口(一个或多个接口)
访问修饰符抽象方法可以有publicprotecteddefault这些修饰符接口方法默认修饰符是public。你不可以使用其它修饰符
方法实现可定义构造方法,可以有抽象方法和具体方法,可以没有抽象方法,但是抽象方法只能再抽象类中接口完全是抽象的,没构造方法,且方法都是抽象的,不存在方法的实现jdk8后有默认实现
作用抽象类是对一种事物的抽象接口是对行为的抽象
成员变量抽象类中可以有成员变量public static final修饰的常量

抽象类是对一种事物的抽象,即对类抽象,而接口是对行为的抽象。抽象类是对整个类整体进行抽象,包括属性、行为,但是接口却是对类行为进行抽象。

构造器 Constructor 是否可被 override?

构造器不能被@Override(重写),构造器只能被overload(重载)。

java静态变量、代码块、和静态方法的执行顺序是什么?

基本上代码块分为三种:Static静态代码块、构造代码块、普通代码块

代码块执行顺序静态代码块——> 构造代码块 ——> 构造函数——> 普通代码块

继承中代码块执行顺序:父类静态块——>子类静态块——>父类代码块——>父类构造器——>子类代码块——>子类构造器

想要深入了解,可以参考这篇文章 :https://juejin.cn/post/6844903986475040781

break ,continue ,return 的区别及作用?

  • break 跳出当前包含break的循环,不再执行循环(结束当前的循环体)
  • continue 跳出本次循环,继续执行下次循环(结束正在执行的循环 进入下一个循环条件)
  • return 程序返回,不再执行下面的代码(结束当前的方法 直接返回)

final和finally以及finalize区别?

final修饰符:可以修饰类、变量、方法,修饰类表示该类不能被继承、修饰方法表示该方法不能被重写、
修饰变量表示该变量是一个常量不能被重新赋值;

finally代码块中:一般作用在try-catch代码块中,在处理异常时通常将一定要执行的代码方法放在finally代码块中,表示不管是否出现异常,该代码块都会执行,一般用来存放一些关闭资源的代码。

注:有些情况不会执行finally

  • 只有与finally对应的try语句块得到执行的情况下,finally语句块才会执行。如果在执行try语句块之前已经返回或抛出异常,那么try对应的finally语句并没有执行
  • 我们在try语句块中执行了System.exit (0) 语句,终止了Java虚拟机的运行;
  • 如果在try-catch-finally语句中执行return语句,finally语句在该代码中一定会执行,因为finally用法特殊会撤销之前的return语句,继续执行最后的finally块中的代码;

finalize一个方法:属于所有类的父类Object类的一个方法,也就是说每一个对象都有这么个方法;Java中允许使用finalize()方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作;调用super.finalize();

这个方法在GC启动该对象被回收的时候被调用。GC可以回收大部分的对象(凡是new出来的对象GC都能搞定,一般情况下我们又不会用new以外的方式去创建对象),所以一般是不需要程序员去实现finalize的。
特殊情况下,需要程序员实现finalize,当对象被回收的时候释放一些资源,比如:一个socket链接,在对象初始化时创建,整个生命周期内有效,那么就需要实现finalize,关闭这个链接。

谈谈你对多态的理解 *

同一个对象,在不同时刻体现出来的不同状态

  • 多态的关键是每个子类都要重写方法,实现了继承了同样的方法名称但是又有每个的特点,就像龙生九子,每个不一样,有两个好处,一个是类关系清晰,另一个是方法调用方便,只要有传入实参就行。

  • 多态是Java面向对象三个特征中的一个也是做主要的一个,所谓多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量倒底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。因为在程序运行时才确定具体的类,这样,不用修改源程序代码,就可以让引用变量绑定到各种不同的类实现上,从而导致该引用调用的具体方法随之改变,即不修改程序代码就可以改变程序运行时所绑定的具体代码,让程序可以选择多个运行状态,这就是多态性。
    多态分为编译时多态和运行时多态。其中编译时多态是静态的,主要是指方法的重载,它是根据参数列表的不同来区分不同的函数,通过编辑之后会变成两个不同的函数,在运行时谈不上多态。而运行时多态是动态的,它是通过动态绑定来实现的,也就是我们所说的多态性。

多态的实现

Java实现多态有三个必要条件:继承、重写、向上转型。

  • 继承:在多态中必须存在有继承关系的子类和父类(实现关系接口)。
  • 重写:子类对父类中某些方法进行重新定义,在调用这些方法时就会调用子类的方法。
  • 向上转型:在多态中需要将父类或者父接口引用指向子类Fu f= new Zi(),只有这样该引用才能够具备技能调用父类的方法和子类的方法。

只有满足了这三个条件,我们才能够在同一个继承结构中使用统一的逻辑实现代码处理不同的对象,从而达到执行不同的行为。

向上转型

父类对象通过子类对象去实例化,实际上就是对象的向上转型。向上转型是不需要进行强制类型转换的,但是向上转型会丢失精度。

向下转型

所谓向下转型,也就是说父类的对象可以转换为子类对象,但是需要注意的是,这时则必须要进行强制的类型转换。

多态的好处

  • 可替换性:多态对已存在代码具有可替换性。例如,多态对圆Circle类工作,对其他任何圆形几何体,如圆环,也同样工作。
  • 可扩充性:多态对代码具有可扩充性。增加新的子类不影响已存在类的多态性、继承性,以及其他特性的运行和操作。实际上新加子类更容易获得多态功能。例如,在实现了圆锥、半圆锥以及半球体的多态基础上,很容易增添球体类的多态性。
  • 接口性:多态是超类通过方法签名,向子类提供了一个共同接口,由子类来完善或者覆盖它而实现的。超类Shape规定了两个实现多态的接口方法,computeArea()以及computeVolume()。子类,如Circle和Sphere为了实现多态,完善或者覆盖这两个接口方法。
  • 灵活性:它在应用中体现了灵活多样的操作,提高了使用效率。
  • 简化性:多态简化对应用软件的代码编写和修改过程,尤其在处理大量对象的运算和操作时,这个特点尤为突出和重要。

对象的序列化和反序列化

  • Serialization(序列化)是一种将对象以一连串的字节描述的过程;反序列化deserialization是一种将这些字节重建成一个对象的过程;

  • 序列化是将对象状态转换为可保持或传输的格式的过程。与序列化相对的是反序列化,它将流转换为对象。这两个过程结合起来,就使得数据能够被轻松地存储和传输。

  • Java序列化是指把Java对象转换为字节序列的过程;而Java反序列化是指把字节序列恢复为Java对象的过程;

1.序列化是干什么的?

  • 简单说就是为了保存在内存中的各种对象的状态,并且可以把保存的对象状态再读出来。虽然你可以用自己的各种方法来保存Object states, 但是Java给你提供一种应该比你自己好的保存对象状态的机制、那就是序列化。

2.什么情况下需要序列化?

  • 当你想把的内存中的对象保存到一个文件或者数据库中时候;
  • 当你想用套接字在网络上传送对象的时候;
  • 当你想通过RMI传输对象的时候(RMI->Remote Method Invocation 远程方法调用)

简述java继承

继承可以降低代码编写的冗余度,提高编程的效率。通过继承,子类获得了父类的成员变量和方法。

**继承的作用:**通过继承可以快速创建新的类,实现代码的重用,提高程序的可维护性,节省大量创建新类的时间,提高开发效率和开发质量。

Java不支持多重继承,但一个类可以实现多个接口,从而克服单继承的缺点;

构造方法不会被子类继承,但可以从子类中调用父类的构造方法。

子类变量访问顺序:先找局部变量,局部变量就找当前类的成员变量,再找不到就找父类的成员变量

子类无法继承父类中私有的内容(可以继承但是没有访问权限)

可以在子类声明父类已有的方法和属性,从而隐藏父类的属性和方法

子类可以直接使用从父类继承过来的属性和方法,可以使用super关键字调用,也可以隐式调用

继承的优点

1.继承过来的字段和方法,可以像任何其他字段和方法一样被直接使用;
2.在子类中可以声明一个与父类中同名的新字段或静态方法,从而“隐藏”父类中的字段或方法;
3.可以在子类中声明一个在父类中没有的新字段和方法;
4.可以在子类中编写一个父类当中具有相同名的新实例方法,这称为“方法重写”或“方法覆盖”;
5.可以在子类中编写一个调用父类构造方法的子类构造方法,既可以隐式地实现,也可以通过使 用关键字super来实现。

重写父类方法

子类继承了父类中的所有成员及方法,但在某种情况下,子类中该方法所表示的行为与其父类中该方法所表示的行为不完全相同.

当一个子类中的一个实例方法具有与其父类中的一个实例方法相同的名称,相同的参数列表和返回值时,称子类中的方法“重写”了父类的方法。

隐藏父类中的方法

如果一个子类定义了一个静态类方法,而这个类方法与其父类的一个类方法具有相同的签名(指名称、参数格式和类型)和返回值,则称在子类中的这个类方法“隐藏”了父类中的该类方法。

使用super关键字

使用super调用父类中重写的方法、访问父类中被隐藏的字段

当使用无参数的super()时,父类的无参数构造方法就会被调用;

当使用带有参数的super()方法时,父类的有参数构造方法就会被调用。

super 可以调用父类的方法和成员变量

在子类创建对象时会默认调用父类的无参构造方法。

this访问本类的成员,super访问父类的成员

final关键字

final关键字可用于修饰类、变量和方法,它有“无法改变”或者“最终”的含义,因此被final修饰的类、变量和方法将具有以下特性:

final可以修饰类,方法,变量

final修饰的类不可以被继承

final修饰的方法不可以被覆盖

final修饰的变量是一个常量,只能被赋值一次

为什么要用final修饰变量,其实,在程序中如果一个数据是固定的。那么直接使用这个数据就可以了,但是这种阅读性差,所以应该给数据起个名称。而且这个变量名称的值不能变化,所以加上final固定写法规范:常量所有字母都大写,多个单词,中间用_连接。

JDK和JRE的区别?

  1. JDK 和 JRE 有什么区别?

    JDK:Java Development Kit 的简称,java 开发工具包,提供了 java 的开发环境和运行环境。
    JRE:Java Runtime Environment 的简称,java 运行环境,为 java 的运行提供了所需环境。

具体来说 JDK 其实包含了 JRE,同时还包含了编译 java 源码的编译器 javac,还包含了很多 java 程序调试和分析的工具。简单来说:如果你需要运行 java 程序,只需安装 JRE 就可以了,如果你需要编写 java 程序,需要安装 JDK。

两个对象的 hashCode()相同,则 equals()也一定为 true,对吗?

不对,两个对象的 hashCode()相同,equals()不一定 true。

    public static void main(String[] args) 
        String str1 = "通话";
        String str2 = "重地";
        System.out.println(String.format("str1=%d ; str2=%d",  str1.hashCode(),str2.hashCode()));
        System.out.println(str1.equals(str2));
    

很显然“通话”和“重地”的 hashCode() 相同,然而 equals() 则为 false,因为在散列表中,hashCode()相等即两个键值对的哈希值相等,然而哈希值相等,并不一定能得出键值对相等。

什么是反射机制?

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

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

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

反射机制优缺点

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

缺点: 性能瓶颈:反射相当于一系列解释操作,通知JVM要做的事情,性能比直接的Java代码要慢很多;

String 属于基础的数据类型吗?

不是,String是属于引用数据类型,也就是对象类型。

基本数据类型只有八种:byte、boolean、char、short、int、float、long、double

对应的包装类型:Byte、Short、Integer、Long、Float、Double、Boolean、Character

是否可以继承String类?

String类是final类,不可以被继承。

java 中操作字符串都有哪些类?它们之间有什么区别?

操作字符串的类有:String、StringBuffer、StringBuilder。

String 和 StringBuffer、StringBuilder 的区别在于 String 声明的是不可变的对象,每次操作都会生成新的 String 对象,然后将指针指向新的 String 对象,而 StringBuffer、StringBuilder 可以在原有对象的基础上进行操作,所以在经常改变字符串内容的情况下最好不要使用 String。

StringBuffer 和 StringBuilder 最大的区别在于,StringBuffer 是线程安全的,而 StringBuilder 是非线程安全的,但 StringBuilder 的性能却高于 StringBuffer,所以在单线程环境下推荐使用 StringBuilder,多线程环境下推荐使用 StringBuffer。

如何将字符串反转?

使用 StringBuilder 或者 stringBuffer 的 reverse() 方法。

String 类的常用方法都有那些?

indexOf():返回指定字符的索引。
charAt():返回指定索引处的字符。
replace():字符串替换。
trim():去除字符串两端空白。
split():分割字符串,返回一个分割后的字符串数组。
getBytes():返回字符串的 byte 类型数组。
length():返回字符串长度。
toLowerCase():将字符串转成小写字母。
toUpperCase():将字符串转成大写字符。
substring():截取字符串。
equals():字符串比较。

抽象类和普通类的区别?

普通类不能包含抽象方法,抽象类可以包含抽象方法。

抽象类不能直接实例化,普通类可以直接实例化。

java 中 IO 流分为几种? *

按功能来分:输入流(input)、输出流(output)。

按类型来分:字节流和字符流。

字节流和字符流的区别是:字节流按 8 位传输以字节为单位输入输出数据,字符流按 16 位传输,以字符为单位输入输出数据。

BIO、NIO、AIO 有什么区别?

①BIO是同步阻塞的,数据的读写会阻塞在一个线程中,适用于连接数目比较小且固定的架构,对服务器资源要求高,JDK1.4前的唯一选择。

②NIO是同步非阻塞的,通过Selector监听Channel上事件的变化,在Channel上有数据发生变化时通知该线程进行读写操作。适用于连接数目比较多且连接比较短的架构,如聊天服务器,从 JDK1.4开始支持。

③AIO是异步非阻塞的,异步是指服务端线程接收到客户端管道后就交给底层处理IO通信,自己可以做其他事情。适用于连接数目比较多且连接比较长的架构,从JDK1.7开始支持。

14.Files的常用方法都有哪些?

Files.exists():检测文件路径是否存在。
Files.createFile():创建文件。
Files.createDirectory():创建文件夹。
Files.delete():删除一个文件或目录。
Files.copy():复制文件。
Files.move():移动文件。
Files.size():查看文件个数。
Files.read():读取文件。
Files.write():写入文件。

java集合哪些?

①主要有两个接口Collection和Map,其中Collection又包括List、Set和Queue。

②List是有序的,主要包括ArrayList,LinkedList和Vector,ArrayList底层通过数组实现,线程不安全,Vector是线程安全的ArrayList,但效率较低,LinkedList底层通过双向链表实现,与ArrayList相比增删快查询慢。

③Set是唯一且无序的,主要包括HashSet,LinkedHashSet和TreeSet。HashSet底层其实就是HashMap,利用了key来保证元素的唯一性。LinkedHashSet可以按照key的操作顺序排序,TreeSet支持按照默认或指定的排序规则排序。

④Queue是队列结构,主要有ArrayBlockingQueue基于数组的阻塞队列、LinkedBlockingQueue基于链表的阻塞队列等。

⑤Map以key-value键值对的形式存储元素,主要包括HashMap、LinkedHashMap和TreeMap。HashMap底层通过数组+链表/红黑树实现,LinkedHashMap可以按照key的操作顺序对集合排序,TreeMap可以按照默认或指定的排序规则对集合排序。

在使用foreach循环遍历集合元素时能否添加或删除元素?

使用foreach循环遍历元素集合时不能修改或删除元素,通过java -c查看字节码可以发现foreach循环实际上是用Iterator迭代器实现的,如果进行添加或删除元素会抛出ConcurrentModificationException异常,因为添加或删除元素会改变modCount的值,modCount是集合类的一个成员变量,代表集合的修改次数,当modCount的值和预期的exceptedModCount值不一致时就会抛出ConcurrentModificationException异常。

Collection 和 Collections 有什么区别?

java.util.Collection 集合类的一个顶级接口。它提供了对集合对象进行基本操作的通用接口方法。Collection接口在Java 类库中有很多具体的实现。Collection接口的意义是为各种具体的集合提供了最大化的统一操作方式,其直接继承接口有List与Set。
Collections则是集合类的一个工具类/帮助类,其中提供了一系列静态方法,用于对集合中元素进行排序、搜索以及线程安全等各种操作。

List、Set、Map 之间的区别是什么?

List:有序集合、元素可重复;ArrayList基于数组实现的有序集合;LinkedList基于链表实现的有序集合。可以用itertor取出所有元素,也可以根据索引取出元素

Set:无序集合、元素不可重复,最多只能允许有一个null;LinkedHashSet按照插入排序,SortSet可排序,HashSet无序。取出元素的时候只能Itertor取出所有元素在逐个遍历,不能根据索引取出元素

Map:键值对集合、储存键、值和之间的映射,Key无序,唯一,最多允许一个null;Value不要求有序,允许重复。

HashMap 和 Hashtable 有什么区别?

hashMap去掉了HashTable 的contains方法,但是加上了containsValue()和containsKey()方法。

hashTable线程安全的,HashMap是线程不安全的,HashMap的效率比HashTable要高

hashMap允许空键值,hashTable不允许空键值。

如何决定使用 HashMap 还是 TreeMap

对于在HashMap中插入、删除和定位元素这类操作,HashMap是最好的选择。假如你需要对一个有序的key集合进行遍历,TreeMap是更好的选择。基于你的collection的大小,也许向HashMap中添加元素会更快,将HashMap换为TreeMap进行有序key的遍历。TreeMap中的父接口中定义了comparator()方法,所以TreeMap在添加的时候key可以按照顺序进行添加。

HashMap实现原理?*

hashMap是一个key-value的键值对集合,key无序不可重复,key可以为null,但是只能允许有一个null,key如果重复value会被替换掉。HashMap1.8是由数组+链表+红黑树构成

  1. HashMap底层维护了Node类型的数组table,默认为null
  2. 当创建对象时,将加载因子(loadfactor)设置为默认值为0.75,可以通过构造器传入手动设置加载因子和初始容量
  3. 当往map中添加数据时,根据key计算出哈希索引值,得到在table中的下标位置,然后判断该索引位置是否有元素
    1. 如果没有,构建一个Node进行添加
    2. 如果该索引位置有元素,继续判断该元素的key和准备加入的元素的key是否相同,如果相同,则替换value
    3. 如果不相同,需要判断是数结构,还是链表还是红黑树,做出相应的处理,如果添加时,发现容量不够,则需要扩容。
  4. 第一次添加需要扩容table的容量为16,临界值为12 【0.75 x 16】临界值=加载因子*当前tableLength
  5. table以后再次扩容,就是原本容量的2倍进行扩容,如果链表中的元素超过了8个,并且table的大小超过64就会转换为红黑树

Jdk 1.8中对HashMap的实现做了优化,当链表中的节点数据超过八个之后,该链表会转为红黑树来提高查询效率,从原来的O(n)到O(logn)

说一下 HashSet 的实现原理?

HashSet:可以存放null值,但是只能允许有一个null,不保证元素顺序是有序的,取决于hash过后,得到的索引进行确定位置,不能有重复的元素,如果添加重复值,会被新的值替换掉。HashSet底层是HashMap,HashSet的value是HashMap的key,这个map的value部分是添加的是一个Object类型的常量值,因为在移除元素和添加元素的时候会根据返回值来判断是否成功。

思考为什么HashSet的底层的map对象的value部分为什么不放null?

答:我们都知道HashSet底层的使用的HashMap 的key作为HashSet的Value,但是为什么不放一个null呢?而是直接放一个常量类型的Object对象,原因是因为HashSet在调用remove()的时候调用的是map.remove(),HashSet.remove() 需要返回一个布尔值,而HashMap的remove的时候需要判断remove的元素是否为空来进行判断是否移除成功,如果放的都是null,那就不能进行判断是否移除成功了。

ArrayList 和 LinkedList 的区别是什么?

• 数据结构实现:
ArrayList 是动态数组的数据结构实现,而 LinkedList 是双向链表的数据结构实现。
• 随机访问效率:
ArrayList 比 LinkedList 在随机访问的时候效率要高,
因为 LinkedList 是线性的数据存储方式,所以需要移动指针从前往后依次查找。
• 增加和删除效率:
在非首尾的增加和删除操作,LinkedList 要比 ArrayList 效率要高,
因为 ArrayList 增删操作要影响数组内的其他数据的下标。
• 综合来说:
在需要频繁读取集合中的元素时,更推荐使用 ArrayList,
而在插入和删除操作较多时,更推荐使用 LinkedList。ArrayList 的时间复杂度是 O(1),而 LinkedList 是 O(n)。

如何实现数组和 List 之间的转换?

List转换成为数组:调用ArrayList的toArray方法。

数组转换成为List:调用Arrays的asList方法。

ArrayList 和 Vector 的区别是什么?

同步性:
Vector是线程安全的,也就是说它的方法是线程同步的,而ArrayList是线程不安全的,它的方法之间是线程不同步的如果只有一个线程去访问集合那么使用ArrayList,他不考虑线程安全的问题,所以效率会高一些,如果是多个线程去访问集合,那么使用Vector。

数据增长性:

ArrayList和Vector集合都有一个初始容量的大小,当元素的个数超过存储容量是,就需要增加ArrayList和Vector的存储空间,每次增加不是增加一个而是增加多个,Vector是增加原来的两倍,从源码中可以看出ArrayList增长原来的1.5倍,ArrayList和Vector可以设置初始的存储空间的大小,Vector还以设置增长空间大小,而ArrayList不可以。

Array 和 ArrayList 有何区别?

Array可以容纳基本类型和对象,而ArrayList只能容纳对象。
Array是指定大小的,而ArrayList大小是固定的。
Array没有提供ArrayList那么多功能,比如addAll、removeAll和iterator等。

在 Queue 中 poll()和 remove()有什么区别?

poll() 和 remove() 都是从队列中取出一个元素,但是 poll() 在获取元素失败的时候会返回空,但是 remove() 失败的时候会抛出异常。

迭代器 Iterator 是什么?

它是 Java 中常用的设计模式之一。用于顺序访问集合对象的元素,无需知道集合对象的底层实现。

Iterator 是可以遍历集合的对象,为各种容器提供了公共的操作接口,隔离对容器的遍历操作和底层实现,从而解耦。缺点是增加新的集合类需要对应增加新的迭代器类,迭代器类与集合类成对增加。迭代器通常被称为“轻量级”对象,因为创建它的代价小。

Iterator 怎么使用?有什么特点?

java中的Iterator功能比较简单,并且只能单向移动:

使用方法iterator()要求容器返回一个Iterator。第一次调用Iterator的next()方法时,它返回序列的第一个元素。注意:iterator()方法是java.lang.Iterable接口,被Collection继承。

使用next()获得序列中的下一个元素。

使用hasNext()检查序列中是否还有元素。

使用remove()将迭代器新返回的元素删除。

Iterator 和 ListIterator 有什么区别?

Iterator可用来遍历Set和List集合,但是ListIterator只能用来遍历List。
Iterator对集合只能是前向遍历,ListIterator既可以前向也可以后向。
ListIterator实现了Iterator接口,并包含其他的功能,比如:增加元素,替换元素,获取前一个和后一个元素的索引。

final的作用?

为什么局部内部类和匿名内部类只能访问局部的final修饰的变量?

因为在编译之后会产生两个class文件,有可能外部类执行完毕变量被回收了,但是里面的内部类还在引用这个变量,这就导致了内部类访问了一个不存在的变量,为了解决这个问题,那就将外部类的局部变量复制一份到内部类作为成员变量,即使外部类销毁,内部类也能正常使用。

final修饰类:表示该类不能被继承

final修饰变量:表示这个变量的值一旦赋值,不能在做修改,在使用之前一定要赋值

final修饰的是方法:不可以被子类重写,但是可以重载

final修饰的是静态变量:一定要为其赋值,要么就是在静态代码块中给他赋值,在使用之前一定要赋值

final修饰的是引用类型变量:该对象的引用不可以被改变,但是该对象中的属性是可以改变的

Java中的异常体系

Java中的所有异常都来自顶级父类Throwable。Throwable下有两个子类Exception和Error。
Error是程序无法处理的错误,一旦出现这个错误,则程序将被迫停止运行。
Exception不会导致程序停止,又分为两个部分RunTimeException运行时异常和CheckedException检查异常。RunTimeException常常发生在程序运行过程中,会导致程序当前线程执行失败。CheckedException常常发生在程序编译过程中,会导致程序编译不通过。

HashCode和equals

hashCode: hashCode()的作用是获取哈希码,也称为散列码;它实际上是返回一个int整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。hashCode()定义在JDK的Object.java中,Java中的任何类都包含有hashCode()函数。散列表存储的是键值对(key-value),它的特点是:能根据"键"快速的检索出对应的"值"。这其中就利用到了散列码!(可以快速找到所需要的对象)

为什么要有hashCode?
以"“HashSet如何检查元素是否重复”"为例子来说明为什么要有hashcode:
对象加入HashSet时,HashSet会先计算对象的hashcode值来判断对象加入的位置,看该位置是否有值,如果没有、HashSet会假设对象没有重复出现。但是如果发现有值,这时会调用equals ()方法来检查两个对象是否真的相同。如果两者相同,HashSet就不会让其加入操作成功。如果不同的话,就会重新散列到其他位置。这样就大大减少了equals的次数,相应就大大提高了执行速度。

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

ConcurrentHashMap原理

jdk7:
数据结构: ReentrantLock+Segment+HashEntry,一个Segment中包含一个HashEntry数组,每个HashEntry又是一个链表结构
元素查询;二次hash,第一次Hash定位到Segment,第二次Hash定位到元素所在的链表的头部
锁: Segment分段锁Segment继承了ReentrantLock,锁定操作的Segment,其他的Segment不受影响,并发度为segment个数,可以通过构造函数指定,数组扩容不会影响其他的segment
get方法无需加锁,volatile保证

jdk8:
数据结构: synchronized+CAS+Node+红黑树,Node的val和next都用volatile修饰,保证可见性查找,替换,赋值操作都使用CAS
锁:锁链表的head节点,不影响其他元素的读写,锁粒度更细,效率更高,扩容时,阻塞所有的读写操作、并发扩容

读操作无锁:
Node的val和next使用volatile修饰,读写线程对该变量互相可见数组用volatile修饰,保证扩容时被读线程感知

设计模式有哪些原则?

①单一职责原则:单一职责原则又称单一功能原则,它规定一个类只有一个职责。如果有多个职责(功能)设计在一个类中,这个类就违反了单一职责原则。

②开闭原则:开闭原则规定软件中的对象(类、模块、函数等)对扩展开放,对修改封闭,这意味着一个实体允许在不

以上是关于熬夜总结的2022java面试题的主要内容,如果未能解决你的问题,请参考以下文章

大厂总结的前200页Java面试题都在这里了

Java面试题总结

2022年Python技术类面试题总结(面试题+答案解析)

2022年春招牛客网最热门的Java岗面试八股文汇总

Java进阶学习资料!熬夜整理小米Java面试题

最新JAVA面试合集:熬夜整理蚂蚁金服Java高级笔试题