Java面试(基础篇)——解构Java常见的基础面试题 结合Java源码分析

在这里插入图片描述

fail-safe 和fail-fast机制分别有什么作用?

Fail-fast:快速失败

Fail-fast : 表示快速失败,在集合遍历过程中,一旦发现容器中的数据被修改了,会立刻抛出ConcurrentModificationException 异常,从而导致遍历失败

在这里插入图片描述

package com.tianju.test;import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;public class DemoTest {public static void main(String[] args) {ArrayList<Integer> list = new ArrayList<>();list.add(1);list.add(2);Iterator<Integer> iterator = list.iterator();while (iterator.hasNext()){Integer next = iterator.next();list.add(55);System.out.println(next);}}
}

Fail-safe:失败安全

fail-safe:表示失败安全,也就是在这种机制下, 出现集合元素的修改,不会抛出
ConcurrentModificationException。

原因是采用安全失败机制的集合容器,在遍历时不是直接在集合内容上访问的,而是先
复制原有集合内容,在拷贝的集合上进行遍历。由于迭代时是对原集合的拷贝进行遍历,所以在遍历过程中对原集合所作的修改并不能被迭代器检测到。

在这里插入图片描述

package com.tianju.test;import java.util.ArrayList;
import java.util.Iterator;
import java.util.concurrent.CopyOnWriteArrayList;public class DemoTest2 {public static void main(String[] args) {CopyOnWriteArrayList<Integer> list = new CopyOnWriteArrayList<>();list.add(1);list.add(2);Iterator<Integer> iterator = list.iterator();while (iterator.hasNext()){Integer next = iterator.next();list.add(55);System.out.println(next);}}
}

java.util.concurrent 包下的容器都是安全失败的,可以在多线程下并发使用,并发修改。

常见的的使用fail-safe 方式遍历的容器有ConcerrentHashMap 和 CopyOnWriteArrayList 等。

HashMap

hash冲突的问题

散列表Hash table & 散列函数 & 哈希冲突

在这里插入图片描述

Hash 算法,就是把任意长度的输入,通过散列算法输出结果是散列值。

在hashMap中,每个关键字被映射到从0到TableSize-1这个范围中的某个数,并且被放到适当的单元中。这个映射就叫作散列函数(hash function),理想情况下它应该计算起来简单,并且应该保证任何两个不同的关键字映射到不同的单元。不过,这是不可能的,因为单元的数目是有限的,而关键字实际上是用不完的。因此,我们寻找一个散列函数,该函数要在单元之间均匀地分配关键字。

在这里插入图片描述

        public final int hashCode() {return Objects.hashCode(key) ^ Objects.hashCode(value);}

在这里插入图片描述

如何解决hash冲突:

  • (1)开放定址法:
    也称为线性探测法,就是从发生冲突的那个位置开始,按照一定的次序从hash 表中找到一个空闲的位置,然后把发生冲突的元素存入到这个空闲位置中。ThreadLocal 就用到了线性探测法来解决hash 冲突的。

  • (2)链式寻址法:
    这是一种非常常见的方法,简单理解就是把存在hash 冲突的key ,以单向链表的方式来存储,比如HashMap 就是采用链式寻址法来实现的。

  • (3)再hash 法:
    就是当通过某个hash 函数计算的key 存在冲突时,再用另外一个hash 函数对这个key 做hash,一直运算直到不再产生冲突。这种方式会增加计算时间,性能影响较大。

  • (4)建立公共溢出区:
    就是把hash 表分为基本表和溢出表两个部分,在冲突的元素,一律放入到溢出表中。

HashMap怎么解决哈希冲突的?

链式寻址法+红黑树解决hash 冲突

HashMap 在JDK1.8 版本中,通过链式寻址法+红黑树的方式来解决hash 冲突问题,其中红黑树是为了优化Hash 表链表过长导致时间复杂度增加的问题。当链表长度大于8 并且hash 表的容量大于64 的时候,再向链表中添加元素就会触发转化。

在这里插入图片描述

当链表长度大于8 并且hash 表的容量大于64 的时候,再向链表中添加元素就会触发转化。

在这里插入图片描述

HashMap 中的hash 方法为什么要右移16 位异或

在这里插入图片描述

之所以要对hashCode 无符号右移16 位并且异或,核心目的是为了让hash 值的散列度更高,尽可能减少hash 表的hash 冲突,从而提升数据查找的性能。

首先使用key 的hashCode 无符号右移16 位,意味着把hashCode 的高位移动到了低位。

然后再用hashCode 与右移之后的值进行异或运算,就相当于把高位和低位的特征进行和组合。

从而降低了hash 冲突的概率。

受检异常和非受检异常

Java基础(8)——java的异常机制初步 & 异常的捕获和处理 & 自定义异常

受检异常和非受检异常,都是继承自Throwable 这个类中,分别是Error 和Exception,

  • Error 是程序报错,系统收到无法处理的错误消息,它和程序本身无关。
  • Excetpion 是指程序运行时抛出需要处理的异常信息如果不主动捕获,则会被jvm 处理。

在这里插入图片描述

  1. 受检异常的定义是程序在编译阶段必须要主动捕获的异常,遇到该异常有两种处理方法
    (1)通过try/catch 捕获该异常;
    (2)通过throw 把异常抛出去;
  2. 非受检异常的定义是程序不需要主动捕获该异常,一般发生在程序运行期间,比如
    NullPointException

在这里插入图片描述

受检异常的定义是程序在编译阶段必须要主动捕获的异常,遇到该异常有两种处理方法

在这里插入图片描述

为什么阿里巴巴的Java 开发手册不建议使用Java 自带的线程池

了解Java中线程池

Java进阶(5)——创建多线程的方法extends Thread和implements Runnable的对比 & 线程池及常用的线程池

4.【强制】线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这
样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。

在这里插入图片描述

Executors 里面默认提供的几个线程池是有一些弊端的,如果是不懂多线程、或者是新手直接盲目使用,就可能会造成比较严重的生产事故。

为什么不能用

  • 1.FixedThreadPool 和SingleThreadPool 中,阻塞队列长度是Integer.Max_Value,一旦请求量增加,就会堆积大量请求阻塞在队列中,可能会造成内存溢出的问题;

在这里插入图片描述

  • 2.CachedThreadPool 和ScheduledThreadPool 中最大线程数量是Integer.Max_value,一旦请求量增加,导致创建大量的线程,使得处理性能下降。

在这里插入图片描述

JDK 动态代理为什么只能代理有接口的类

在Java 里面,动态代理是通过Proxy.newProxyInstance()方法来实现的,它需要传入被动态代理的接口类。

在这里插入图片描述
加入如下代码进行运行:

System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

或者加入下面这句

System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");

在这里插入图片描述
JDK 动态代理会在程序运行期间动态生成一个代理类$Proxy0,这个动态生成的代理类会继承java.lang.reflect.Proxy 类,同时还会实现被代理类的接口。

在Java 中,是不支持多重继承的,而每个动态代理类都会继承Proxy 类(这也是JDK动态代理的实现规范) ,所以就导致JDK 里面的动态代理只能代理接口,而不能代理实现类。

在这里插入图片描述

spring中的代理

Spring进阶(AOP的理解)——静态/动态代理 & 面向切面编程AOP(Aspect Oriented Programming) & 日志记录 & 增强方法

在这里插入图片描述

如果一定要针对普通类来做动态代理,可以选择cglib 这个组件,它会动态生成一个被代理类的子类,子类重写了父类中所有非final 修饰的方法,在子类中拦截父类的所有方法调用从而实现动态代理。

Java对象相关的面试题

对象的创建过程

在这里插入图片描述

(1)类加载检查
JVM 首先会去检查目标对象是否已经被加载并初始化了。

如果没有,JVM 需要立刻去加载目标类,然后调用目标类的构造器完成初始化。目标类的加载是通过类加载器来实现的,主要就是把一个类加载到内存里面。

然后初始化的过程,主要是对目标类里面的静态变量、成员变量、静态代码块进行初始化。

(2)分配内存空间

当目标类被初始化以后,就可以从常量池里面找到对应的类元信息,并且目标对象的大小在类加载之后就已经确定了,所以这个时候就需要为新创建的对象,根据目标对象的大小在堆内存里面分配内存空间。

内存分配的方式一般有两种,一种指针碰撞,另一种是空闲列表,JVM 会根据Java 堆内存是否规整来决定内存分配方式。

(3)初始化 ”零值”

JVM 会把目标对象里面的普通成员变量初始化为零值,比如int 类型初始化为0 ,对象类型初始化为null, (类变量在类加载的准备阶段就已经初始化过了) 。

这一步操作主要是保证对象里面的实例字段,不用初始化就可以直接使用,也就是程序能够获得这些字段对应数据类型的零值。

(3)设置对象头”

然后,JVM 还需要对目标对象的对象头做一些设置,比如对象所属的类元信息、对象的GC 分代年龄、hashcode、锁标记等等。

(4)执行init方法

完成这些步骤以后,对于JVM 来说,新对象的创建工作已经完成。但是对于Java 语言来说,对象创建才算是开始。

接下来要做的,就是执行目标对象内部生成的init 方法,初始化成员变量的值、执行构造块、最后执行目标对象的构造方法,完成对象的创建。

其中,init 方法是Java 文件编译之后在字节码文件中生成的,它是一个实例构造器,这个构造器会把语句块、变量初始化、调用父类构造器等操作组织在一起。

所以调用init方法能够完成一系列的初始化动作。

什么是深拷贝和浅拷贝?

Java进阶(4)——结合类加载JVM的过程理解创建对象的几种方式:new,反射Class,克隆clone(拷贝),序列化反序列化

浅拷贝

在这里插入图片描述

深拷贝

在这里插入图片描述

深拷贝和浅拷贝是用来描述对象或者对象数组这种引用数据类型的复制场景的。

浅拷贝, 就是只复制某个对象的指针,而不复制对象本身。这种复制方式意味着两个引用指针指向被复制对象的同一块内存地址。

在Java 里面,无论是深拷贝还是浅拷贝,都需要通过实现Cloneable 接口,并实现clone()方法。然后我们可以在clone()方法里面实现浅拷贝或者深拷贝的逻辑。

实现深拷贝的方法有很多,比如

  1. 通过序列化的方式实现,也就是把一个对象先序列化一遍,然后再反序列化回来,就会得到一个完整的新对象。

  2. 在clone()方法里面重写克隆逻辑,也就是对克隆对象内部的引用变量再进行一次克隆。

Book.java实体类

implements Cloneable{ // 可以克隆的

package com.tianju.auth.reflect;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;@Data
@NoArgsConstructor
@AllArgsConstructor
public class Book implements Cloneable{ // 可以克隆的private String title;private Author author;public double price;static {System.out.println("book的静态代码块");}// protected:代表本包或者继承// 继承的时候,可以将子类的访问控制符扩大,但不能缩小;// 子类不能比父类抛出更多的异常@Overridepublic Object clone() throws CloneNotSupportedException {return super.clone();}public Book deepClone(){Book book = new Book();Author au = new Author();au.setName(author.getName());book.setAuthor(au);book.setTitle(this.title);book.setPrice(this.price);return book;}
}

进行测试

在这里插入图片描述

package com.tianju.auth.reflect;public class TestDemo{public static void main(String[] args) throws CloneNotSupportedException {Author author = new Author();author.setName("吴承恩");Book book = new Book("三国演义", author,12.56);Book book1 = book;System.out.println(book1==book);// == 两个引用是否指向同一个对象// clone创建了一个新的对象,只是值一样Book bookClone = (Book) book.clone();// 深拷贝,创建了新的对象,上面的浅拷贝,只是拷贝了引用Book deepClone = book.deepClone();System.out.println(bookClone==book);System.out.println("克隆前:"+book);System.out.println("克隆后:"+bookClone);author.setName("小柯基");System.out.println("修改后的原对象:"+book);System.out.println("修改后的clone对象:"+bookClone);// 深拷贝System.out.println("***********");System.out.println("深拷贝的方法:"+deepClone);}
}

String相关的面试题

new String(“abc”)到底创建了几个对象

  1. 如果abc 这个字符串常量不存在,则创建两个对象,分别是abc 这个字符串常量,以及new String 这个实例对象。
  2. 如果abc 这字符串常量存在,则只会创建一个对象

Java基础(1)——数据类型&包装类,引用类型String&StringBuilder,正则表达式,定点数,日期类

(1)String str = new String(“hello”);

执行上述代码,底层进行了如下工作,在栈内存里存放变量str,在堆内存新创建一个String的对象,在常量池空间中创建常量hello,如果存在则不创建;创建了一个或两个对象。
在这里插入图片描述

(2)String str = “hello”;

栈内存中的str指向常量池中的hello
在这里插入图片描述

String、StringBuffer、StringBuilder 区别

(1)可变性
String 内部的value 值是final 修饰的,所以它是不可变类。所以每次修改String 的值,都会产生一个新的对象。

StringBuffer 和StringBuilder 是可变类,字符串的变更不会产生新的对象。

在这里插入图片描述

StringBuffer 和StringBuilder 是可变类

在这里插入图片描述

(2)线程安全性
String 是不可变类,所以它是线程安全的。

StringBuffer 是线程安全的,因为它每个操作方法都加了synchronized 同步关键字。
StringBuilder 不是线程安全的,所以在多线程环境下对字符串进行操作,应该使用StringBuffer ,否则使用StringBuilder

在这里插入图片描述

(3)性能方面
String 的性能是最的低的,因为不可变意味着在做字符串拼接和修改的时候,需要重新
创建新的对象以及分配内存。

其次是StringBuffer 要比String 性能高,因为它的可变性使得字符串可以直接被修改,最后是StringBuilder ,它比StringBuffer 的性能高,因为StringBuffer 加了同步锁。

在这里插入图片描述

(4)存储方面
String 存储在字符串常量池里面;
StringBuffer 和StringBuilder 存储在堆内存空间。

最后再补充一下, StringBuilder 和StringBuffer 都是派生自AbstractStringBuilder这个抽象类。

Integer 使用不当导致生产的事故

为什么两个Integer 的对象不能用==号来判断?

在这里插入图片描述

Integer 是一个封装类型。它是对应一个int 类型的包装。

在Java 里面之所以要提供Integer 这种基本类型的封装类,是因为Java 是一个面向对象的语言,而基本类型不具备对象的特征,所以在基本类型上做了一层对象的包装并且提供了相关的属性和访问方法来完善基本类型的操作。

在Integer 这个封装类里面,除了基本的int 类型的操作之外,还引入了享元模式的设计,对-128 到127 之间的数据做了一层缓存,也就是说,如果Integer 类型的目标值在-128 到127 之间,就直接从缓存里面获取Integer 这个对象实例并返回,否则创建一个新的Integer 对象。

在这里插入图片描述

这么设计的好处是减少频繁创建Integer 对象带来的内存消耗从而提升性能。

因此在这样一个前提下,如果定义两个Integer 对象,并且这两个Integer 的取值范围正好在-128 到127 之间。

如果直接用==号来判断,返回的结果必然是true ,因为这两个Integer 指向的内存地址是同一个。否则,返回的结果是false。

ArrayList 的自动扩容机制

Java进阶(3)——手动实现ArrayList & 源码的初步理解分析 & 数组插入数据和删除数据的问题

在这里插入图片描述

ArrayList 是一个数组结构的存储容器,默认情况下,数组的长度是10.
当然我们也可以在构建ArrayList 对象的时候自己指定初始长度。

随着在程序里面不断的往ArrayList 中添加数据,当添加的数据达到10 个的时候,ArrayList 就没有多余容量可以存储后续的数据。

这个时候ArrayList 会自动触发扩容。

扩容的具体流程很简单,

  1. 首先,创建一个新的数组,这个新数组的长度是原来数组长度的1.5 倍。
  2. 然后使用Arrays.copyOf 方法把老数组里面的数据拷贝到新的数组里面。扩容完成后再把当前要添加的元素加入到新的数组里面,从而完成动态扩容的过程。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/115807.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何学会从产品经理角度去思考问题?

如何学会从产品经理角度去思考问题&#xff1f; 从产品经理的角度思考问题意味着你需要关注产品从构思到上市全过程中的各个方面&#xff0c;包括用户需求、市场趋势、设计、开发、测试、上市后的用户反馈等。以下是一些策略和方法&#xff0c;帮助你培养从产品经理角度思考问…

Python爬虫:ad广告引擎的模拟登录

⭐️⭐️⭐️⭐️⭐️欢迎来到我的博客⭐️⭐️⭐️⭐️⭐️ &#x1f434;作者&#xff1a;秋无之地 &#x1f434;简介&#xff1a;CSDN爬虫、后端、大数据领域创作者。目前从事python爬虫、后端和大数据等相关工作&#xff0c;主要擅长领域有&#xff1a;爬虫、后端、大数据…

Mybatis 相关模块以及设计模式分析

一、缓存模块 MyBatis作为一个强大的持久层框架&#xff0c;缓存是其必不可少的功能之一&#xff0c;Mybatis中的缓存分为一级缓存和二级缓存。但本质上是一样的&#xff0c;都是使用Cache接口实现的。缓存位于 org.apache.ibatis.cache包下。 通过结构我们能够发现Cache其实使…

螺杆支撑座是如何维持精度和稳定性的?

螺杆支撑座是机械设备中重要的支撑元件&#xff0c;主要用于支撑和固定螺杆&#xff0c;以确保其精度和稳定性&#xff0c;以下是螺杆支撑座在实际使用中的优势&#xff1a; 1、良好的耐腐蚀性&#xff1a;螺杆支撑座通常采用防腐蚀材料制造&#xff0c;能够抵抗各种腐蚀性介质…

linux安装visual studio code

下载 https://code.visualstudio.com/ 下载.deb文件 安装 假如文件被下载到了 /opt目录下 进入Opt目录&#xff0c;右键从当前目录打开终端。 输入下面的安装命令。 sudo apt-get install ./code_1.83.1-1696982868_amd64.deb 安装成功。 配置 打开 visual studio cod…

【蓝桥每日一题]-动态规划 (保姆级教程 篇11)#方格取数2.0 #传纸条

目录 题目&#xff1a;方格取数 思路&#xff1a; 题目&#xff1a;传纸条 思路&#xff1a; 题目&#xff1a;方格取数 &#xff08;跑两次&#xff09; 思路&#xff1a; 如果记录一种方案后再去跑另一个方案&#xff0c;影响因素太多了&#xff0c;所以两个方案要同时开…

手搭手Ajax经典基础案例省市联动

环境介绍 技术栈 springbootmybatis-plusmysql 软件 版本 mysql 8 IDEA IntelliJ IDEA 2022.2.1 JDK 1.8 Spring Boot 2.7.13 mybatis-plus 3.5.3.2 pom.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http:/…

分类预测 | MATLAB实现SSA-CNN-BiLSTM-Attention数据分类预测(SE注意力机制)

分类预测 | MATLAB实现SSA-CNN-BiLSTM-Attention数据分类预测&#xff08;SE注意力机制&#xff09; 目录 分类预测 | MATLAB实现SSA-CNN-BiLSTM-Attention数据分类预测&#xff08;SE注意力机制&#xff09;分类效果基本描述模型描述程序设计参考资料 分类效果 基本描述 1.MAT…

认识哈希表和哈希表的实现

哈希函数的定义&#xff1a; out f(in) 1&#xff09;in -> ∞&#xff0c; out -> S 输入域是无穷的&#xff0c;输出域是有限的&#xff0c;也就是S域&#xff1b; 2&#xff09;相同的输入一定会得到相同的输出&#xff1b; 3&#xff09;不同的输入可能会有相同…

Swift 判断 A B 两个时间是不是同一天,A 是不是 B 的昨天

1. 今天要做这个效果&#xff08;在时间旁边显示今天&#xff0c;昨天&#xff09; 2. Preview 3. Code: // 添加 今天 昨天 func show_today_yesterday(d: Date Date()) -> String {let calendar Calendar.currentlet today: Date Date()if calendar.isDate(today, inS…

C++DAY50

源文件代码 #include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this);if(!db.contains()){db QSqlDatabase::addDatabase("QSQLITE");db.setDatabaseName(&q…

QTday05(TCP的服务端客户端通信)

实现聊天室功能 服务端代码&#xff1a; pro文件需要导入 network 头文件&#xff1a; #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QTcpServer>//服务端 #include <QTcpSocket>//客户端 #include <QList> #include <QMes…

超声波清洗机频率如何选择?高频和低频有什么区别

超声波清洗原理就是在清洗液中产生“空化效应”&#xff0c;即清洗液产生拉伸和压缩现象&#xff0c;清洗液拉伸时会产生大量微小气泡&#xff0c;清洗液压缩时气泡会被压碎破裂。这些气泡产生和破裂的局部压强可达到上千个大气压的冲击力&#xff0c;这种极强大的压力足以使得…

Leetcode 1089. 复写零

复写零 题目链接1089. 复写零 给你一个长度固定的整数数组 arr &#xff0c;请你将该数组中出现的每个零都复写一遍&#xff0c;并将其余的元素向右平移。 注意&#xff1a;请不要在超过该数组长度的位置写入元素。请对输入的数组 就地 进行上述修改&#xff0c;不要从函数返回…

网络协议--UDP:用户数据报协议

11.1 引言 UDP是一个简单的面向数据报的运输层协议&#xff1a;进程的每个输出操作都正好产生一个UDP数据报&#xff0c;并组装成一份待发送的IP数据报。这与面向流字符的协议不同&#xff0c;如TCP&#xff0c;应用程序产生的全体数据与真正发送的单个IP数据报可能没有什么联…

一分钟带你了解什么是0day攻击什么是Nday攻击

1. 什么是零日漏洞 零日攻击是指利用零日漏洞对系统或软件应用发动的网络攻击。 零日漏洞也称零时差漏洞&#xff0c;通常是指还没有补丁的安全漏洞。由于零日漏洞的严重级别通常较高&#xff0c;所以零日攻击往往也具有很大的破坏性。目前&#xff0c;任何安全产品或解决方案…

吃瓜教程2|线性模型

线性回归 “广义的线性模型”&#xff08;generalized linear model&#xff09;&#xff0c;其中&#xff0c;g&#xff08;*&#xff09;称为联系函数&#xff08;link function&#xff09;。 线性几率回归&#xff08;逻辑回归&#xff09; 线性判别分析 想让同类样本点的…

ATPCS:ARM-Thumb程序调用的基本规则

为了使单独编译的c文件和汇编文件之间能够互相调用&#xff0c;需要制定一系列的规则&#xff0c;AAPCS就是ARM程序和Thumb程序中子程序调用的基本规则。 1、ATPCS概述 ATPCS规定了子程序调用过程中寄存器的使用规程、数据站的使用规则、参数的传递规则。为了适应一些特殊的需…

【C++】继承 ⑧ ( 继承 + 组合 模式的类对象 构造函数 和 析构函数 调用规则 )

文章目录 一、继承 组合 模式的类对象 构造函数和析构函数调用规则1、场景说明2、调用规则 二、完整代码示例分析1、代码分析2、代码示例 一、继承 组合 模式的类对象 构造函数和析构函数调用规则 1、场景说明 如果一个类 既 继承了 基类 ,又 在类中 维护了一个 其它类型 的…

腾讯地图基本使用(撒点位,点位点击,弹框等...功能) 搭配Vue3

腾讯地图的基础注册账号 展示地图等基础功能在专栏的上一篇内容 大家有兴趣可以去看一看 今天说的是腾讯地图的在稍微一点的基础操作 话不多说 直接上代码 var marker ref(null) var map var center ref(null) // 地图初始化 const initMap () > {//定义地图中心点坐标…