《面试季》经典面试题(三)

97 阅读19分钟

Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情


  • 💂 个人网站: IT学习日记
  • 🤟 版权: 本文由【IT学习日记】原创、需要转载请联系博主
  • 💬 如果文章对你有帮助、欢迎关注、点赞、收藏(一键三连)和订阅专栏哦

前言

  • 大家好,这里是IT学习日记,相信大家对今年IT的行情应该也有所了解了,从大厂到小厂,各种裁员消息。公司裁员我们无法决定,我们能做的就是不断提升自己,提前准备。

  • 本系列文章主要分享了之前博主真实面试中遇到的一些问题,希望能够帮助准备就业或者跳槽的朋友。

后端基础知识篇

一: Java中的队列和它们的区别

  1、定义

    队列是一个先进先出的数据结构,Queue是JDK1.5引入的。

  2、用途

    将大量的请求转换成服务器能够处理的队列请求,可以保证有序性,如秒杀系统

  3、分类

    第一类: 没有实现阻塞接口,但是实现了Queue和AbstractQueue接口

    (1)、LinkedList:: 实现了Deque接口,双向有序队列

    (2)、PriorityQueue: 有限队列,维护一个有序列表,可以自然排序也可以传递Comparator构造函数实现自定义排序

    (3)、ConcurrentLinkedQueue: 基于链表、线程安全的队列,并发访问不需要同步,它是从尾部添加元素从头部删除元素,对公共的集合访问效率做得很不错,添加删除O(1),查询O(n)。

    第二类: 实现了阻塞接口,concurrent包中引入了BlockQueue接口和五个阻塞队列,他们不是操作就立即向队列中添加或者删除元素,而是线程执行阻塞操作,直至队列有空间可以添加或者有元素可以删除。

    (1)、ArrayBlockingQueue: 数组型有界队列

    (2)LinkedBlockingQueue: 链接节点,支持可选的有界队列

    (3)PriorityBlockingQueue: 一个优先级堆,支持的无界队列

    (4)DelayQueue: 一个优先级堆,支持基于时间的无界队列

    (5)SynchronousQueue: 可以用来在线程间安全的交换单一元素

    三: 关于阻塞队列的操作

    1、add、remove、element: 操作一个已满或者为空的队列时,如果操作的集合为空或者已满时,remove或者add则会抛出异常。

    2、Offer、poll、peek: 在无法完成操作时,只会返回true或者null,不会抛出异常

    3、Take操作: 队列为空的时候阻塞。put操作:队列满时阻塞

二: 简单描述下JVM内存模型以及它们的作用

    注: 该图是jdk1.7时对应的JVM内存模型

JVM内存模型

  1、程序计数器

      用来标识下一条需要执行指令的位置,它是线程私有的。

  2、虚拟机栈

    线程私有,声明周期和线程相同,它描述了Java方法执行的内存模型。每个方法在执行的同时会创建一个栈帧(Stack Frame),用于存储局部变量表,动态链接等信息。从调用到调用完成对应的是一个栈帧从虚拟机栈入栈到出栈的过程。

    局部变量表存放了编译器可知的基本类型、独享引用。Long、Double占用了2个局部变量控件slot,故为非原子性,它在编译时便完成了内存的分配,运行时不会进行修改。

    当申请的栈深度大于虚拟机允许的宽度时会抛出:StackoverFlowError,如虚拟机自动拓展、但申请不到足够的内存时,则抛出OutofMemeryError

  3、本地方法栈

    线程私有、为本地方法(native)服务,如:hotspot是将它和虚拟机栈合并在一起。可能抛出的错误有:StackoverFlowError和OutofMemeryError。

  4、堆

    线程共享、JVM中占用内存最大的一块区域,VM启动时创建,主要目的是: 存放对象实例。当实例没有申请到足够的空间时,堆大小也无法拓展时,会抛出OutofMemeryError错误,它也是GC主要进行收集的地方。(jdk1.8后,常量池也是存放在堆中,因为永久代废除了)

  5、方法区

    线程共享用于存储被虚拟机加载的类信息、变量、静态变量既即时编译的代码,JVM规范是将它和堆进行分开。永久代和元空间其实都是方法区的一种实现。

三: 常见的GC算法和它们的差异

  知识小贴士:

    Hotspot将堆划分为新生代(Young Generation)和年老代(Tetured Generation),新生代又分为Eden去和Survivor区,它们的比例是:8:2。所有新建的对象存放在新生代,其中survivor区又划分为from区和to区,比例是1:1。

  1、标记-清除(Mark Sweap)

    主要的步骤: 标记回收对象,回收被标记的对象

    标记方式有两种: 1、引用计数算法(Reference Counting), 2、可达性分析法(Rechability Analysis)

  缺点:

    回收后会出现大量非连续内存,且需要扫描两次内存,效率低

  2、复制算法(coping)

    思路: 将内存划分为等大的两块区域,,一次只用一块,用完将存活的对象复制到另一块,然后清空自己。

    特点:

    解决了标记-清楚算法效率和内存碎片问题,但是需要浪费一块内存空间,利用率不高,主要是用于回收新生代(因为新生代的对象基本是”朝生暮死”,存活的时间很短)

  3、标记-整理(Mark - Compact)算法

    思路:

    从根节点开始对所有可达对象进行一次标记、之后,不是简单的清除未标记的对象,而是将所有存活的对象压缩到内存的一端,之后,清理边界外的所有空间。

    特点:

    避免了内存碎片的产生、同时又不需要两块相同的内存空间,相对来说性价比较高。多适用于老年代的区域回收,因为老年代的对象存活率大。

  4、分代收集算法

    分代收集算法是目前虚拟机使用的回收算法。它解决了标记清除算法不使用于老年代的问题。在不同年代中使用不同的收集方式,新生代存活率低,可以使用复制算法。老年代对象存活率高,没有额外的控件对它进行分配担保,可以使用标记清除或者标记整理算法。

四: 反射的优缺点

  优点:

    体现了灵活性、降低了类之间的耦合性、体现了多态的作用。

  缺点:

    性能存在一定问题。反射本身算是一种解释操作,告诉JVM我们想要做什么并让它满足我们的要求,它会比直接执行操作更慢一些。

五: 多继承的弊端和解决方案

  弊端: 如果有多个父类,有相同的功能时,子类调用,会产生不确定性,所以JAVA中类的只有单继承。

  解决: 通过”实现”解决,因为接口中的功能都是未实现的,需要子类明确。接口的出现避免了单继承的局限性,所以,一般是父类中定义的事物的基本功能,接口定义的是事物的拓展功能。

六: 抽象类和接口的区别

  一: 相同点

    都不能直接实例对象,都可以包含抽象方法

  二: 不同点

    1、接口中除了静态方法和默认方法外只能存在抽象方法,但是抽象类中既可以存在抽象方法也可以存在非抽像方法。

    2、接口可以多实现,但是类只能单继承

    3、接口中只能定义常量,抽象类中可以定义常量和变量

    4、接口中没有构造函数,抽象类中有构造函数

七: 继承、封装、多态的作用

  继承: 子类自动拥有父类所有可以继承的属性和方法

  封装: 隐藏对象的属性和方法的细节实现,提供一些公共访问的方式

  多态: 配合继承与方法的重写提高代码的重用性和拓展性,如无方法重写,则多态同样无意义。

八: ClassNotFoundException知道?遇到的场景是什么?如何解决?

  场景:

    1、调用Classs.forName(“类的全限路径”)加载类时

    2、ClassLoader.findSystemClass方法调用时

    3、ClassLoader.loadClass方法时

  解决:

    检查类名是否正确或者是否真的存在需要加载的类

九: NoClassDefError错误的常见场景

  场景:

    1、类依赖的class或者jar包不存在

    2、类文件存在,但是存在于不同的域中

    3、大小写问题,javac编译时是无视大小写的,可能编译出的class文件和想要的不一样

十: hashcode和equals方法的特点

  1、重写equals方法则必须同时重写hashCode方法

  2、如果两个对象的equals方法相等,则两个对象的hashCode也一定相等

  3、如果两个对象的hashCode相等,那么两个对象的equals方法不一定相等,只能说明两个对象在散列存储结构中,存放在相同的一个位置

十一: hashcode的作用

  用于快速定位对象在散列表的位置。在JVM中new一个对象时,会将这个对象丢到Hash表中,下次再进行对象的比较或者取该丢向时,根据该对象的hashCode从hash表中获取,目的,提高获取对象的效率。

  若HashCode相同则再去调用equals方法,所以hashCode是用于查找的,而equals方法是用于判断两个对象是否相等。

十二: 为什么需要重写hashcode方法

  HashMap或者HashSet中如果不重写会导致存对象进去了,但是取对象的时候却取不到正确的。重写hashCode时为了保证相同的对象有相同的hashCode。

十二: java.util包和java.awt包的区别

  Java,util包中存放的是常用的工具包,如集合。

  Java.awt包存放的是: 包含用于创建用户界面和绘制图形图像的所有类。

十三: 项目中常用的包有哪些

  Java.lang: 提供利用 Java 编程语言进行程序设计的基础类。

  java.util: 包含集合框架、遗留的 collection 类、事件模型、日期和时间设施、国际化和各种实用工具类

  java.io.*: Java的核心库java.io提供了全面的IO接口。包括:文件读写、标准设备输出等。

  java.net.*: 包含与网络相关的一些类库。

  java.sql.*: 包含了与数据库相关的一些类库。

  此外还有如果java.util.concurrent等与线程安全相关的包。

十四: fail-fast和fail-safe的区别

  并发修改: 当一个线程或者多个线程在遍历集合时,另外的线程对该集合进行了内容变动(添加、删除、修改)。

  Fail-fast-快速失败: java.util下的集合都是这种模式

    在遍历集合的时候,如果进行了并发修改,会抛出concurrentModificationException异常,因为它所有的操作都是在原来的集合中进行的,在遍历的时候判断是否存在下一个元素时会进行:expectModcount和modCount的判断,如果不相等,则直接抛出异常。

  Fail-Safe-安全失败: java.util.concurrent下的集合都是这种模式

    属于这种机制的集合,任何对集合的操作(添加、修改、删除)都会在当前集合的复制出来的集合上操作,不会直接在当前的集合上进行,所以不会抛出异常,但是会存在以下的问题:

    1、需要复制集合,产生无效对象,开销较大

    2、无法保证读取的数据是目前原始数据结构中的最新数据

十五: Bean工厂和Appcation Context的区别

  Bean工厂:

    是Spring的原始接口,Bean工厂实现的容器特点是:每次获取对象的时候才会去创建对象,而不是在容器启动的时候去加载所有的对象。这是因为早期机器的容量和资源都是比较稀缺的,如果在启动时一次加载完所有的对象,资源可能全被占用,程序无法正常运行。

  Bean工厂的优缺点:

  优点:

    1、项目启动速度快,且资源占用少

  缺点:

    因为对象是在获取的时候才进行创建,所以无法在启动时检查出对象配置是否存在问题,需要在获取的时候才知道.

  ApplicationContext:

    它是继承了Bean工厂,同时拓展了许多功能,每次容器启动的时候会创建所有的对象。

  优点:

    可以及时发现对象配置的问题,因为在容器启动的时候会创建所有的对象。

  缺点:

    项目启动速度较慢,且资源占用较多。

十六: 垂直拆分和水平拆分

  垂直拆分:

    把一个数据库中不同的业务单元的数据分配到不同的数据库中,如:用户信息存存储在库1,订单信息存储在库2。

  水平拆分:

    根据一定的规则,将同一个业务单元的数据分配存储在不同的数据库中,防止单表的数据量太大,导致查询速率降低。

十七: 保证线程安全的方式

  1、添加锁,如synchronized,Reentractlock

  2、使用ThreadLocal线程副本,每个线程独享一份数据,互不干扰

  3、使用阻塞队列,线性执行任务

  4、使用JDK提供的原子类如java.concurrent.atomic包下的类

十八: 重写equals方法需要注意的事项

  注: java规范中要求重写equals方法需要具有以下的特性:

  1、自反性: 针对非空的x,使用x.equals(x)应该返回true

  2、对称性: 针对x,y,如果x.equals(y)为true,那么y.equals(x)也应该为true

  3、传递性: 如有x,y,z,存在x.equals(y)和y.equals(z)都为True,那么x.equals(z)也应该为true

  4、一致性: 如果比较对象未发生改变,则反复调用equals方法应该返回同样的结果

  5、对于任意的非空x,x.equals(null)应该返回false

十九: JAVA中哪些属性不能被序列化

  1、被static修饰的属性,它是属于类级别的,序列化针对的是对象。序列化保存的是对象的状态,静态变量是以类的状态,因此序列化并不保存静态变量。这里的不能序列化的意思,是序列化信息中不包含这个静态成员域

  2、被transient修饰的属性

二十: JRE和JDK的区别

  JRE:

    核心的内容是JVM及相关的核心类库及支持文件。

  JDK:

    除了包含JRE外,还包含了JAVA工具(编译器、调试器等)和java的基础类库,开发者可以进行: 开发 -> 编译 -> 执行java应用程序。

小结

   不积跬步,无以至千里;不积小流,无以成江海。今天播种努力的种子,总会有一天发芽!

   欢迎大家关注,如果觉得文章对你有帮助,不要忘记一键三连哦,你的支持是我创作更加优质文章的动力,希望大家都能够早日拿到心仪的Offer,有任何面试问题可以私信我,欢迎大家投稿面试题目哦!


  该篇文章已经被收录在个人开源专栏:《IT知识小屋》中。专栏以小白视角切入,讲解通俗易懂,内容包含IT各方向知识(JAVA基础、进阶、面试真题、算法、面试采坑经验、996公司等),是IT知识学习+面试首选IT小屋。