Java基础高频面试题【更新中】

Java基础篇

1 什么是面向对象?

面向对象注重事情的参与者(对象)、以及各自需要做什么。

举例:

洗衣机洗衣服

面向对象会拆分出人和洗衣机两个对象

人: 打开洗衣机、放衣服、放洗衣液

洗衣机:清洗、烘干

面向过程比较直接高效,但面向对象易于维护、易于扩展易于复用。

面向对象三大特性

封装、继承、多态

封装

明确标识允许外部使用的所有成员函数和数据项

内部对外调用透明,外部无需关心或者修改内部的实现

继承

继承父类的方法,并自己做出扩展或者改变

子类共有的方法、属性可以直接使用父类,而不需要自己定义、只需要扩展自己个性化的方法或属性

多态

对象所属的类不同,外部对同一个方法的调用,实际的执行逻辑不同

继承,方法重写,父类引用指向子类对象

父类类型  变量名 = new 子类对象;
变量名.方法名();

无法调用子类特有的功能、方法

Java的基本数据类型

整数类型

  • byte 【1B】
  • short 【2B】
  • int 【4B】
  • long 【8B】

浮点数:

  • float 【4B】
  • double 【8B】

字符类型:

  • char 【2B】

布尔型

  • bolean 【true/flase】

2 JRE、JDK、JVM的关系

JDK包含 JRE和JVM

JDK :开发工具

JRE:运行环境

Java文件——> .class文件 —— > JVM ——> 映射到Windows系统

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

内部类和外部类是一个级别,内部类不会因为定义在方法中就会随着方法的执行完毕就被销毁。

这里产生一个问题:外部类方法结束时,局部变量就销毁了,但是内部类可能还存在,这里就有一个矛盾的地方:内部类对象访问了一个不存在的变量,为了解决这个问题,就将局部变量COPY一份,作为内部类的成员变量,这样当局部变量销毁之后,内部类还可以访问他,实际上访问的是局部变量的COPY,这样就变相延长了局部变量的生命周期。

那在copy时就必须要保证两个变量一直,所以使用final。

4 String和StringBuffer、StringBuilder(*)

  1. 可变性:
  • String不可变,使用final修饰
  • StringBuffer和StringBuilder可变没有使用final修饰,继承来自AbstractStringBuilder,在原对象上进行操作。
  1. 线程安全:

    • String是线程安全的,因为他不可变,也可理解为常量
    • StringBuffer是线程安全的,Buffer对线程增加了同步锁或者对调用方法增加了同步锁(synchronized),所以线程是安全的
  2. 性能:
    StringBuilder > String Buffer > String

  • String每次改变的时候都会new 一个新的对象所以性能差
  • StringBuffer因为有同步机制,所以在单线程下执行效率低
  • StingBuilder没有同步机制,所以单线程环境下性能更好
  1. 使用场景

    • 经常需要改变字符串使用StringBufferStringBuilder
    • 优先使用StringBuilder,多线程使用共享变量时使用StringBuffer

5 重载和重写的区别(*)

重载:发生在同一个类中,方法名和参数不同,个数不同,顺序不同,方法返回值和访问修饰符可以不同

重写:发生在子父类中,方法名、参数列表必须相同,返回值范围必须小于等于父类,抛出异常必须小于等于父类,访问修饰符范围必须大于等于父类;如果父类方法的修饰符是private私有的,那么子类不能重写。

6 接口和抽象类的区别以及使用场景 *

  • 抽象类可以存在普通成员函数,而接口只能存在Public abstract方法。
  • 抽象类中的成员变量可以是各种类型,而接口的成员变量只能是public static final类型的
  • 抽象类只能继承一个,而接口可以实现多个

使用场景:

7 List和Set的区别

  • List:==有序,按照对象进入的顺序保存对象,可重复,允许多个Null元素对象,==可以使用iterator取出所有元素。再逐一遍历,还可使用get方法获取指定下标的元素
  • Set:==无序,不可重复,最多允许有一个Null元素对象,==取元素只能用iterator接口取得所有元素,再逐一遍历各个元素。

8 hashCode与equals

hashCodehashCode的作用是获取哈希码,也称散列码。他实际上返回的是一个int整数。这个哈希码作用是确定该对象在哈希表中的索引位置。

散列表存储的是键值对(Key-Value)。

特点是能根据key快速检索对应的Value

为什么会有hashCode

以“HashSet“如何检查重复为例说明为什么要有hashCode

对象加入HashSet时,会先调用HashCode去计算值来判断加入的位置,看看该位置是否有值,如果没有,HashSet会假设没有重复,如果有重复则会调用equals方法区判断两个对象是否一致,如果一致则不会让其加入操作成功。如果不同,就会重新散列(重新计算)到其他位置。,这样就减少了equals的次数,提高了执行速度。

区别or联系

  1. 如果两个对象相等,那么hashCode一定相同
  2. 两个对象相等,分别调用equals方法都返回true
  3. 两个对象有相同的hashCode,他们也不一定相同
  4. 所以,覆盖equals方法,就必须覆盖hashCode方法
  5. hashCode的默认行为是对堆上的对象产生独特值。如果没有重写hashCode方法,那么这个类的两个对象无论如何不会相等(即使这两个对象指向相同的数据)

9 ArrayList和LinkedList的区别

ArrayList

基于动态数组,连续内存存储,适合下标访问(随机访问),

扩容机制:数组长度固定、超出长度存数据时需要新建数组,然后将老数组的数据拷贝到新数组里面。如果不是尾部插入的数据还会涉及到元素的移动(往后复制一份,插入新元素),使用尾插法并指定初始容量可以极大提升性能,甚至超过LinkList(需要创建大量的node对象)

LinkList

基于链表,可以储存在分散的内存中,适合做数据的插入和删除操作,不适合查询需要逐一遍历

遍历LinkedList必须使用iterator不能使用for循环,因为每次for循环体内通过get取得某个元素时都需要对list重新遍历,性能消耗大。

10 冒泡排序

冒泡排序:通过对排序序列从前向后(从下标较小的元素开始)依次比较相邻元素的值,若发现逆序则交换,使得值比较大的元素逐渐从前向后移动,就像水底下的气泡一样逐渐向上冒。

int[] arr = {5,9,7,28,9,18,38} 

for( i = 0 , i < arr.length - 1 , i++ ){
    for( j = 0 , j < arr.length - 1 - i , j++ ){
        if( arr[j] > arr[j+1] ){
            temp = arr[j];
            arr[ j ] = arr [ j + 1 ];
            arr [j + 1] = temp;
        }
    }
}

11 HashMap和HashTable的区别?底层的实现(*)

区别:

  • HashTable的每个方法都增加了synchronized修饰,所以是线程安全的
  • HashMap没有,所以不安全
  • HashMap允许key和value为空,但是HashTable不允许

HashMap底层实现:

数组 + 链表实现

JDK8开始当==链表达到8,数组长度超过64时,链表会转为红黑树,==元素以内部类Node节点存在

  • 计算key的Hash值,然后二次Hash后对数组取模,对应放到数组下标的位置。
  • 如果没有产生hash冲突(下标位置没有元素),则直接创建Node节点存入数组中
  • 如果产生hash冲突,则调用equals方法判断是否相等,相等直接取代原有元素。不相等则判断链表高度插入链表,当链表达到8,数组长度大于64则转为红黑树,长度低于6转回链表
  • key为null,存在下标0的位置

数组扩容:

  • 和ArrayList方式一样。

12 ConcurrentHashMap原理,JDK7和JDK8版本的区别

JDK7:

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

JDK8:

数据结构:synchronized+CAS+Node+红黑树,Node的val和next都用volatile修饰,保证可见性

查找、替换、赋值操作都用CAS

锁 :锁链表的head节点,不影响其他元素读写,锁粒度更细,效率更高,扩容时,阻塞所有的读写操作、并发扩容

读操作无锁:

Node的val和next使用volatile修饰,读写线程对该变量互相可见

数组用volatile修饰,保证扩容时被读线程感知

13 如何实现一个IOC容器

  1. 配置文件配置包扫描路径
  2. 递归包扫描获取.class文件
  3. 反射、确定需要交给IOC管理的类
  4. 对需要注入的类进行依赖注入

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zg6ImnX6-1692959870051)(C:\Users\23234\Desktop\笔记\面经\Java基础面试题.assets\image-20230821102353209.png)]

14 什么是字节码?采用字节码的好处是什么?

Java中的编译器和解释器:

Java中引入了虚拟机的概念,即在机器和编译程序之间加入了一层抽象的虚拟的机器。这台虚拟的机器在任何平台上都提供给编译程序一个共同的接口。

编译程序只需要面向虚拟机。生成虚拟机能够理解的代码,然后由解释器来将虚拟机代码转换为特定系统的机器码执行。在Java中,这种供虚拟机理解的代码叫做字节码==(扩展名为.class的文件)==,他不面向任何特定的处理器,只面向虚拟机。

每一种平台的解释器是不同的,但是实现的虚拟机是相同的。Java源程序经过编译器编译成字节码,字节码由虚拟机解释运行。虚拟机将每一条要执行的字节码送给解释器。解释器将其翻译为特定机器的机器码,然后在特定的机器上运行。这也解释了Java的编译和解释并存的特点,

Java源代码——> 编译器 ——>JVM可执行的Java指令——> JVM ——> JVM中解释器 ——> 机器可执行二进制机器码 ——> 程序运行

采用字节码的好处:

Java语言通过字节码的方式,在一定程度上解决了传统解释型语言执行效率低的问题。同时有保留了解释语言可一直的特点。所以Java程序运行时比较高效。而且,由于字节码并不专对一种特定的机器。因此,Java程序无序重新编译便可在多种不同的计算机上运行。

15 Java类加载器有哪些

JKD自由三个类加载器:Bootstrap ClassLoader、ExtClassLoader、AppClassLoader

BootstrapClassLoader是ExtClassLoader的父类加载器,默认加载%JAVA_HOME%/lib下的jar包和class文件

ExtClassLoader是AppClassLoader的父类加载器,负责加载%JAVA_HOME%/ lib / ext文件夹下的jar包和class类

AppClassLoader是自定义类加载器的父类,负责加载classpash下的类文件(系统类加载器,线程上下文加载器)

继承ClassLoader实现自定义类加载器

16 双亲委派机制(模型)

双亲委派机制是java类加载机制的一种实现方式,用于避免类的重复加载。

双亲指的是:类加载先向上寻找,再向下寻找的流程就叫双亲委派机制。

在这里插入图片描述

原理

  1. 类加载器收到类加载的请求
  2. 将这个请求向上委托给父类加载器去完成,一直向上委托,直到启动类加载器
  3. 启动类加载器检查是否能够加载这个类,能加载就结束,使用当前加载器,否则就抛出异常,通知子加载器进行加载
  4. 重复步骤 3

都找不到就会报错:Class Not Found

作用

  1. 避免类的重复加载

    类加载器加载某个类时,先委托父类加载器加载,如果父类找不到,才会自己尝试去加载,这种机制可以避免重复加载一个类,节省内存空间,同时也保证类的唯一性,避免出现类不一致的情况。

  2. 确保类的安全性

    可以避免应用程序自己写的类替换掉核心类库中的类

  3. 提高类加载的效率

    因为每个加载器在加载前都会先委托父类加载器去加载,所以如果父类已经加载了就不需要在加载一次。这种机制可以提高加载效率,减少资源的消耗。

17 Java中的异常体系

Java中所有的异常都来自顶级父类Throwable

Throwstable有两个子类:Exception 和 Error

Error是程序无法处理的错误,一旦出现错误,则程序被迫停止

Exception不会导致程序停止,分为两部分:RunTimeException和CheckedException检查异常

RunTimeException常发生在程序运行过程中,会导致程序当前线程执行失败。CheckedException常发生在程序编译过程中,会导致程序编译不通过

18 什么是线程安全?

代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码,如果每次运行结果和单线程的结果是一样的,而且其他的变量的值也和预期是一样的,就是线程安全的。

19 并发编程的三要素?

  1. 原子性
    • 原子性指的是一个或多个操作,要么全部执行并且在执行过程中不被打断,要么就全部不执行。
  2. 可见性
    • 可见性指多个线程操作一个共享变量时,其中一个线程先对变量进行修改后,其他线程可以立刻看到修改结果。
  3. 有序性
    • 程序的执行顺序按照代码的先后顺序执行。

20 深拷贝和浅拷贝

深拷贝:复制变量值,对于引用数据,则递归至基本类型后,再复制。深拷贝后的对象与原来的对象是完全隔离的,互不影响,对一个对象的修改并不会影响另一个对象。

  • 既拷贝克隆基本类型变量,又克隆引用类型变量

浅拷贝:会将对象的每个属性进行依次复制,但是当对象的属性值是引用类型时,实质复制的是其引用,当引用指向的值改变时也会跟着变化。

  • 只拷贝基本类型变量,不拷贝引用类型变量

21 HashMap和TreeMap的区别 *

  • HashMap是无序的,TreeMap是有序的(会按照键的大小排序)
  • HashMap是数组+链表+红黑树,TreeMap是红黑树
  • HashMap允许键值都为null,但是TreeMap不允许键为null,允许值为null
  • HashMap插入删除等操作时间复杂度为O(1),TreeMap为O(log n),

MYSQL篇

1 mysql的事务特性

  • 原子性
    • 一个事务内操作要么失败要么成功。
  • 隔离性
    • 事务与事务之间互相不影响
  • 一致性
    • 事务前后的数据总量不变
  • 持久性
    • 事务的改变一旦提交发生的改变不可逆

2 mysql如何做慢SQL优化

  • 尽量减少select的数据列
  • orderby查找时使用索引排序
  • groupby查询时,同样使用索引,尽量避免使用到临时表
  • 使用复杂查询时,适用关联查询来替代子查询,并且最好使用内连接
  • 使用count函数的话,count(*)的效率是最高的
  • 写update语句时,where条件使用索引
  • 表中的数据是否太大,太大时使考虑分库分表

3 为什么使用内连接不使用外连接?

外链接的话连接顺序是固定死的,比如left join 必须先对左表进行全表扫描然后一条条到右表去匹配,内连接的话mysql会自己根据查询优化器去判断用哪个表做驱动

4 mysql的整个查询过程

  1. 客户端向SQL服务器发送一条查询请求
  2. 服务器首先检查查询缓存。如果命中查缓存,返回存储在缓存中的结果,否则进入下一阶段
  3. 服务器进行SQL解析,预处理、再由优化器生成对应执行的计划
  4. mysql根据执行计划,调用存储引擎的API执行查询
  5. 将结果返回给客户端,同时缓存查询结果(8.0之间才有查询缓存,之后没有)

5 如何设计数据库?

  1. 抽取实体,如商品信息,用户信息

  2. 分析其中属性,如商品名称,商品价格,用户姓名

  3. 分析表与表之间的关联关系

    参考三大范式进行设计,设计主键时,主键要尽量小并且定义为自增和不可修改

6 三大范式

  • 第一范式:每个列都不可再拆分
  • 第二范式:在第一范式的基础上,非主键列完全依赖主键,而不能是依赖主键的一部分
  • 第三范式:在第二范式的基础上,非主键列只能依赖主键,不能依赖其他非主键

7 where和Having的区别?

where是执行约束,having是过滤条件

where早于having执行

where不可使用聚合函数,having可以使用

8 char 和 vachar 的区别

char是不可变的,最大长度为255,vachar是可变的字符串最大长度2^16

SPring篇

1 Spring是什么?

轻量级的开源的J2EE框架,他是一个容器框架,用来装JavaBean(Java对象),中间层框架可以起一个连接作用

Spring是一个轻量级的控制反转(IOC)和面向切面(AOP)的容器框架

2 谈谈你对AOP的理解?

AOP即面向切面编程,简单来说就是把程序重复的代码抽取出来,在需要执行的时候,使用动态代理技术,在不修改原有代码的基础上,对已有的方法进行扩展、增强。

3 谈谈你对IOC的理解

容器概念、控制反转、依赖注入(DI)

IOC是一种设计思想,就是将原本在程序中手动创建对象的控制权,交由Spring框架来管理

IOC思想基于IOC容器来完成的。

之前需要我们自己手动new对象的,但是我们现在不需要反复去new对象了,而是把new对象的主动权交给IOC容器,我们什么时候用什么时候取就可以了。

4 Spirng Bean的生命周期?*

  • 实例化Bean
  • 属性注入
  • 初始化方法调用
  • 使用和销毁

SpringMVC篇

1 Sping MVC 的执行流程?*

  1. 客户端发送请求:客户端向服务器发送请求,请求可以是通过URL、表单提交、AJAX等方式发起。
  2. DisPatcherServlet接受请求:视图解析器将控制器返回的视图名解析为具体的视图对象。
  3. 处理器映射器(HandlerMapping)定位处理器:DispatcherServlet通过处理器映射器(HandlerMapping)将请求的URL映射到对应的处理器(Controller)。
  4. 处理器适配器(HandlerAdapter)执行处理器:处理器适配器根据处理器的类型调用相应的方法来处理请求,并返回一个ModelAndView对象,其中包含视图名和模型数据。
  5. 控制器(Controller)处理请求:控制器是实际处理请求的组件,根据业务逻辑处理请求并生成模型数据
  6. 视图解析器(ViewResolver)解析视图:视图解析器将控制器返回的视图名解析为具体的视图对象。
  7. 视图渲染器(ViewRenderer)渲染视图:视图渲染器将模型数据填充到视图中,并生成最终的HTML响应
  8. 返回响应给客户端:DispatchServlet将悬案后的视图响应给客户端。

Redis篇

1 Redis中Value常用的五种数据类型 *

  • 字符串 string
  • 哈希 hash
  • 列表 list
  • 集合 set
  • 有序集合 sorted set / zset

网络基础篇

1 TCP和UDP的区别?*

  1. TCP是可靠的,UPD是不可靠的
    1. TCP提供可靠的数据传输,确保数据的完整性和顺序性。他使用序号、确认和重传机制来保证数据的正确性。
    2. UDP不提供可靠保证,数据包可能会丢失,重复或者乱序
  2. 连接性
    1. TCP是面向连接的协议,通过建立虚拟的连接来进行数据传输
    2. UDP是无连接协议,数据包之间相互独立,每个数据包都是独立发送
  3. 数据量
    1. TCP没有数据大小限制,可以处理任意大小的数据
    2. UDP对长度有限制,单个数据包长度通常在64KB以内
  4. 延迟
    1. TCP比UDP延迟更高,因为他有可靠性保证和流量控制机制
  5. 使用场景:
    1. TCP可靠,常用于文件传输、电子邮件等
    2. UDP则适用实时性较强高的应用,如音视频流传输
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值