首页 > 编程语言 >Java面试八股文2024最新版

Java面试八股文2024最新版

时间:2024-06-20 19:31:49浏览次数:14  
标签:调用 Java 变量 对象 2024 线程 数组 最新版 方法

一、java基础

1、java有哪几种数据类型?
  • 基本数据类型:byte(1),char(2),short(2),int(4),long(8),double(8),float(4),boolean(1)
  • 引用数据类型:各种类和接口,枚举,数组
2、 面向对象和面向过程的区别?

面向对象和面向过程都是一种开发思想。

  • 面向过程就是根据解决问题所需要的步骤,具体化的一步一步的去实现。
  • 面向对象就是把数据及对数据的操作方法放在一起,作为一个整体,也就是对象,若干个这样的整体组成一个系统去解决实际问题。
  • 面向过程只用函数实现,性能比较快因为不需要进行实例化,但是不容易扩展、维护,复用
  • 面向对象通过类去实现功能模块,代码安全性高,容易扩展和复用,比较灵活且便于维护

篇幅限制下面就只能给大家展示小册部分内容了。这份面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafka 面试专题

需要全套面试笔记的【点击此处即可】即可免费获取

3、JDK/JRE/JVM三者的关系

JVM指的是Java的虚拟机,Java程序运行需要在JVM虚拟机上,不同平台都有自己的JVM虚拟机,所以说java语言实现了跨平台

JRE指的是Java的运行时环境,包含了java核心内库和JVM虚拟机

JDK就是 Java 开发工具包,里面包含了工java运行时需要的工具类和运行时环境,比如javac.exe和其他的一系列编译调试打包工具

4、什么是字节码文件

我们使用java编译命令就能将java源文件编译对应成字节码文件(.class),字节码文件是一种八位数据的二进制流文件,可以被JVM快速加载到内存中运行

5、面向对象有哪些特性?

面向对象有三大特性:封装,继承和多态

  • 封装就是将类信息隐藏在类内部,不允许外部程序直接访问,而是通过调用类的方法去访问,良好的封装能减少耦合,增加安全性。
  • 继承就是从已有的类派生出新的类,新的类继承父类的属性和方法,并扩展新的能力,极大提高了代码的复用行和易维护性。
  • 多态是指同一个行为具有多种形式的表现,具体体现在继承,重写,重载,父类引用指向子类对象等,多态体现出面向对象的灵活性。
  • 另外面向对象还有抽象的特性,指把客观事物用代码抽象出来。
6、StringBuilder和StringBuffer的区别

它们都继承于abstractStringBuilder,相对于String来说都是可变,StringBuffer添加了syncronized所以是线程安全的,而StringBuilder是线程不安全的

线程安全指,多个线程访问一个对象时,每个线程调用该对象的行为都能获得正确的结果

7、为什么静态方法不能调用非静态方法和变量?

类在加载的时候,会将所有被static修饰的内容(包括方法和属性)初始化,但此时非静态方法和变量还没有经过初始化,也就是没有内存空间,调用的时候就会出错。

8、异常,error和exception的区别,运行时异常和非运行时异常(checked)的区别?

JAVA异常的层次结构如图:

 Throwable类是java所有异常和错误的顶类,有两个重要的子类

Error:处理JVM的内部严重问题,如资源不足,无法恢复

Exception: 可以恢复的异常,分为RuntimeException(unchecked Exception)和其他Exception(checked Exception)

        RuntimeException:运行时异常,处理或者不处理都可以(try/catch throw操作)

        其他Exception:必须捕获或者声明抛出的异常

9、java内存模型JMM

        JMM是一个抽象的规范,它定义了共享内存中多线程程序读写规范,即变量如何存储在内存中又如何从内存中读取(这里的变量指实例字段,静态变量,构成数组的元素)

JMM将内存分为主内存和工作内存

  1. 主内存是线程共享的公共区域,里面存储了共享变量
  2. 工作内存是线程私有的工作区域,里面存储了共享变量的副本
  3. 线程通常是对工作内存中的共享变量进行修改,然后同步到主内存,然后其他线程从主内存中同步更新自己的变量,也即,线程之间的交互是通过主内存的。

 JMM对Java编程提供了原子性,可见性,有序性的保障

原子性:原子性就是说一个操作是不可分割的,不可中断的,要么执行完成,要么不执行,在JMM中通过lock操作实现,它保证了一个变量在同一时刻只能被一个线程访问,不会被其他线程影响而中断操作,对应关键字Syncronized

可见性:可见性指线程对一个变量的操作对其他线程来说是立即可见的,在JMM中通过在修改变量后变量同步到主内存,其他线程读取时刷新变量值来实现,对应关键字volatile,Syncronized

volatile关键字!

作用:它可以用来修饰成员变量和静态变量(放在主存线程共享的变量),它可以避免线程从自己的工作内存中查找读取变量的值,而是每次都从主内存中去读取,线程操作volatile修饰的变量都是直接操作主存的。

有序性:指本线程的所有操作都是有序的

在多线程中会有指令重排序的问题, 通过volatile关键字解决

volatile的底层原理是内存屏障,Memory Barrier(Memory Fence)

写屏障:对 volatile 变量的写指令后会加入写屏障,

  • 写屏障保证在该屏障之前的,对共享变量的改动,都同步到主存当中

  • 写屏障会确保指令重排序时,不会将写屏障之前的代码排在写屏障之后

读屏障:对 volatile 变量的读指令前会加入读屏障

  • 读屏障保证在该屏障之后,对共享变量的读取,加载的是主存中最新数据
  • 读屏障会确保指令重排序时,不会将读屏障之后的代码排在读屏障之前

 篇幅限制下面就只能给大家展示小册部分内容了。这份面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafka 面试专题

需要全套面试笔记的【点击此处即可】即可免费获取

10、常见的集合

Java中的集合主要由Collection(单列集合)和Map(双列集合)这两个接口派生而来

Collection下有子接口List,Queue,Set(无序)

List接口的实现类有ArrayList,vector(线程安全),stack,LinkedList(双向链表)

Set接口的实现类有hashset(底层是hashMap),sortedSet(接口)、treeSet(底层是treeMap,遍历有序),LinkedHashSet

线程安全的集合:Vector、HashTable、ConcurrentHashMap、Stack(继承Vector)、ArrayBlockingQueue、ConcurrentLinkedQueue

11、重写与重载的区别

1.定义不同---重载是定义相同的方法名,参数不同;重写是子类重写父类的方法。

2.范围不同---重载是在一个类中,重写是子类与父类之间的。

3.多态不同---重载是编译时的多态性,重写是运行时的多态性。

4.返回不同---重载对返回类型没有要求,而重写要求返回类型只能与父类一致或者是父类方法返回类型的子类。

5.参数不同---重载的参数个数、参数类型、参数顺序可以不同,而重写要求参数列表一致

6.修饰不同---重载对访问修饰没有特殊要求,重写访问修饰符的访问权限一定要大于被重写方法的访问修饰符。

12、HashMap底层,插入,扩容

  前置知识:

  二叉树:每个节点至多只有两个子节点的树

  搜索二叉树:满足当前节点的左子树上的节点不能大于当前节点,右子树上的节点不能小于当前节点的二叉树

  红黑树:一种自平衡的搜索二叉树,能保证遍历,插入,删除的时间复杂度永远是O(logn)

  红黑树规则:

    红黑:节点只有红黑两种颜色,根节点和叶节点一定是黑色,红节点的子节点一定是黑色

    树:叶节点一定是null,任一节点到叶节点的所有路径包含相同数量的黑节点

  散列表:又称Hash表,是一种kv存储结构,存储时先计算k经过哈希函数运算后的值,然后根据这个值将v存放在对应的位置

HashMap底层实现是数组+链表/红黑树

添加数据时,根据key的hash值来确定value所在的数组下标

如果数组下标对应的链表/红黑树中有相同的key,就会替换value,否则加入到链表/红黑树中

jdk1.8之前采用的拉链法 数组+链表

jdk1.8之后采用的数组+链表+红黑树法,链表长度大于8时,先尝试扩容数组,如果数组长度此时大于64则会从链表转化为红黑树。

插入操作(简化):

  1、判断数组是否为空,如果为空调用resize方法,初始化一个容量16的数组

  2、对key进行hash运算,得到数组中的索引,table下标i=(table.length- 1) & ((h = key.hashCode()) ^ (h >>> 16)),如果索引指向的位置为空,则直接插入一个新的node

  3、如果不为空,则判断当前key是否存在,如果存在直接进行替换value操作

  4、如果不存在,判断当前Node节点是否是红黑树结构,如果是树类型,则按照树的方式去追加节点,否则在链表尾部插入数据

  5、最后判断链表长度是否大于8,如果大于8,会先尝试扩容,判断数组长度是否小于64,是就扩容,否则链表转换为红黑树。

  6、插入操作结束后,判断如果hashmap的元素个数超过了负载因子*容量的阈值,则需要进行扩容操作

  7、扩容操作会将原来数组的元素分配到新的数组中,重新每个元素在数组中的下标

  8、扩容操作完成后,再将元素插入到新的数组中

扩容操作:

  1、在添加元素或初始化时扩容,执行resize方法,第一次初始化长度为16,以后每次在元素达到容量*负载因子(0.75)时,进行扩容

  2、每次扩容操作后,容量都是之前的2倍

  3、扩容之后,会创建一个新的数组,然后把老的数组中的元素移动到新的数组中

  4、如果是链表,则计算每个链表元素的下标,然后移动到新的数组

  5、如果是红黑树,则走红黑树的添加。

13、对象的创建方式

1、new

2、将类继承Cloneable接口,然后实现clone方法,调用clone方法即可复制对象

3、反射(类派发),Student.class.newInstance()

4、反射(动态加载),Class.forName("entity.Student").newInstance()

5、反射(构造方法),Student.class.getConstructor().newInstance()

6、Spring容器启动时将Bean注入

14、四种访问修饰符

1)public :公共权限,可以被任意类访问,不同包不同类依然可以访问,

可修饰:类、成员变量、方法,内部类

2)protected:受保护的权限,可以被同包类访问,如果不是同包类,必须是该类的子类才可以访问

可修饰:成员变量、方法、内部类

3)default:默认的(无),同包权限,只能被同包的类访问

可修饰:类、成员变量、方法,内部类

4)private:私有权限,只能在本类中访问,同包其他类也不能访问

二、多线程

1、进程与线程的区别

概念:

  • 进程是程序的一次执行结果,是系统运行的基本单位,每个进程都有自己的内存空间和系统资源,进程直接相互独立,但线程之间的运行是可以相互影响的。

  • 线程是比进程更小的单位,一个进程中包含的多个线程,这些多个线程共享进程的内存空间和系统资源,所以线程之间的切换开销比较小。//线程运行在用户态,但是线程切换由操作系统内核完成,需要切换到核心态

  • 协程是一种轻量级的线程,协程在用户态就可以控制,协程的上下文切换更加节省资源。

上下文的切换指的是CPU从当前正在运行的进程或线程中保存状态,然后切换到另一个线程或进程

区别:

  1. 进程是正在运行的程序的实例,线程是操作系统运算调度的最小单位,包含在进程当中,一个进程可以有多个线程

  2. 进程有自己的内存空间和系统资源,线程之间共享一个进程的内存空间和系统资源

  3. 进程之间相互独立,而线程之间是可以相互影响的

  4. 线程更加轻量,上下文切换开销比进程低

2、并行与并发的区别

并发是同一时间处理多件事的能力,比如多个线程轮流使用一个CPU

并行是同一时间做多件事的能力,比如4核CPU同时执行4个线程

关键区别在于是否同时执行

3、创建线程的方式有哪几种?Runnable与Callable有什么区别?run方法与start方法有什么区别
  1. 继承Tread类 ——直接调用start执行线程

  2. 实现Runnable接口——先创建MyRunnable对象,再创建Thread对象(将MyRunnable作为构造器参数),再执行start方法

  3. 实现Callable接口——先创建MyCallable对象,再创建futureTask对象,再创建Tread对象,再执行start方法

  4. 线程池创建(项目中一般使用方式)——对象要实现Runnable,然后创建线程池对象,调用线程池对象的submit方法

 篇幅限制下面就只能给大家展示小册部分内容了。这份面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafka 面试专题

需要全套面试笔记的【点击此处即可】即可免费获取

Runnable与Callable的区别:

  • Runnable接口的run方法没有返回值,而Callable接口的call方法有返回值,可以配合FutureTask获取返回结果

  • Runnable接口的run方法的异常只能在内部消化,而Callable接口的call方法可以将异常抛出

run方法与start方法区别:

  • start方法是开启一个线程,然后线程去调用run方法,start方法只能被调用一次

  • run方法封装了线程要执行的方法,可以调用多次

4、线程包含了几种状态,状态之间是如何转换的

线程包含了六种状态:

  1. 新建(New):创建线程对象时

  2. 可执行(Runnable):调用start方法后,有执行资格

  3. 阻塞(Blocked):进入可执行状态后,无法获得锁

  4. 等待(Waiting):执行时调用Wait方法进入(wait方法会释放锁)

  5. 计时等待(Timed_Waiting):执行时调用Time.sleep进入

  6. 死亡(Terminated):运行结束,线程死亡,变成垃圾

5、假设有t1,t2,t3三个线程,如何保证他们按顺序执行?

第一种方法:调用join方法:它使当前线程进入time_waiting状态,直到调用join方法的线程结束,比如t1.join(),就是在t1结束后才会继续运行。

具体:在t2线程的run方法中调用t1.join,在t3线程的run方法中调用t2.join

第二种方法:使用计数器countdownLatch:它需要先设定一个起始值,然后在线程中调用countdown方法让值-1,当值为0时,会解除wait状态

具体:

countDownLatch1.await(); //第一个计数器值为0时结束等待 System.out.println("拿出一瓶牛奶!"); //执行线程方法 countDownLatch2.countDown(); //让控制另一个线程的计数器值为0

6、wait和sleep方法有哪些不同?

wait方法是Object类方法,每个对象都有,而sleep方法是Tread类静态方法

wait方法需要在同步块(synchronized)中执行,并且需要先获取锁,而sleep方法没有这个限制

wait方法执行后会释放锁,而sleep方法在拿到锁的情况下执行不会释放锁

7、为什么wait方法和notify方法要在synchronized关键字中使用?

从程序层面来说:不这样做会报IllegalMonitorStateException错误

从线程安全层面来说:如果不加同步锁,wait() 方法还没有执行完,notify() / notifyAll() 方法已经执行完,这样 notify() / notifyAll() 就进行了一次空唤醒操作,而 wait() 执行完后由于再没有notify() / notifyAll()的唤醒,会导致wait() 所在线程一直阻塞。

从底层代码层面来说:

首先,每个Java对象底层都会关联一个 Monitor 对象,在 Monitor 中维护了 两个队列 WaitSet 和 EntryList ,owner 属性。

synchronized( lock ) 被调用后会将当前线程 赋值给lock对象所关联的 monitor 对象的 owner 属性,其他线程 再想获取 lock 锁对象的话如果发现 lock对象所关联的 monitor 对象的 owner 属性不为空,就会进入 EntryList 进行阻塞,而调用了wait() 后就会将 owner 指向的线程对象放入 WaitSet 中进行等待,并将 owner 置为 null (释放掉锁),直到其他线程获取到 lock 这个对象锁以后通过 notify() / notifyAll() 方法 唤醒 WaitSet 中 的线程,这时 WaitSet中等待的线程才会进入 EntryList 参与lock 锁的竞争。( notify() / notifyAll() 并不会释放锁,只有等待 synchronized 执行完才会释放锁 )

8、Synchronized关键字底层原理

synchronized是采用互斥的方式,让同一时刻只能有一个线程能够获取对象锁

java中的Synchronized有偏向锁,轻量级锁,重量级锁三种形式,分别对应了只被一个线程所有,被多个线程交替持有,被多个线程竞争三种情况

在对象锁被多个线程竞争的情况下(重量级锁):

  • 底层由Monitor实现,Monitor是JVM级别的对象,Monitor分为三个部分,owner,entryset,waitset

  • 当一个线程要获取对象锁时,会先将对象与Monitor关联,然后将owner与当前线程绑定

  • 如果owner已经被其他线程绑定了,当前线程就会加入到entryset中进入阻塞状态

  • 另外,当对象调用wait方法时,当前线程会进入到waitset队列中进入阻塞状态

在对象锁被多个进程无竞争交替持有的情况下(轻量级锁):

这个时候不需要monitor

当线程尝试获取锁时,会创建一个栈帧,栈帧里包含一个锁记录的结构,主要内容有锁记录地址和对象指针

执行到加锁时,让锁记录中的对象指针指向对象,并尝试使用cas替换锁记录地址和对象头中的mark word,如果标记位为无锁则进行交换,如果非无锁,则有两种情况

1、其他线程已经获取了这个对象的锁,当前线程获取锁失败,线程会尝试自旋获取锁,如果自旋次数超过一定阈值,或者存在多个线程在等待同一个锁,轻量级锁会升级为重量级锁

 篇幅限制下面就只能给大家展示小册部分内容了。这份面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafka 面试专题

需要全套面试笔记的【点击此处即可】即可免费获取

2、当前线程已经获取了这个对象的锁(锁重入),也就是对象头的mark word指向当前线程的栈帧,此时会在栈帧中再添加一条锁记录,并设置锁记录的锁地址为null,最后在退出synchronized代码块时,如果有锁记录地址为null的锁记录,会清除锁记录,表示重入次数-1

当对象锁被一个线程持有的情况下(偏向锁):

  在上文中可知,轻量级锁在没有竞争时(还是这个线程来获取锁),将会发生锁重入,要执行CAS操作,并在栈桢创建新的锁记录。这样耗费了资源。

   优化方式:只有第一次使用CAS将线程ID设置到被锁对象的MarkWord中,以后再来的线程只用验证这个线程ID是自己则没有竞争,无需CAS。

加锁过程:在当前线程的栈帧中创建一个锁记录对象,然后将锁记录的Lock record地址与对象的mark word进行CAS操作,

标签:调用,Java,变量,对象,2024,线程,数组,最新版,方法
From: https://blog.csdn.net/CTZL123456/article/details/139840761

相关文章

  • 微信小程序源码-基于Java后端的教学质量评价系统的计算机毕业设计(附源码+论文)
    大家好!我是程序员一帆,感谢您阅读本文,欢迎一键三连哦。......
  • Java跳动爱心代码
    1.计算爱心曲线上的点的公式计算爱心曲线上的点的公式通常基于参数方程。以下是两种常见的参数方程表示方法,用于绘制爱心曲线:1.1基于(x,y)坐标的参数方程x=a*(2*cos(θ)-sin(θ))^3y=a*(2*sin(θ)-cos(θ))^3其中,a是一个常数,用于控制爱心的大小;θ是参......
  • IDEA 2024 配置 Flink Scala开发环境
    IDEA2024配置FlinkScala开发环境一、环境IntelliJIDEA2024.1(UltimateEdition)项目JDK版本:ZuluJDK11Scala2.12.19Scala编译ServerJDK版本:JDK21Flink1.19.1二、步骤、创建Java项目安装Scala插件,安装后重启位置:Settings-->Plugins-->Marketplace......
  • chatGPT帮我优化代码-2024.06.20
    改成面向对象源代码defret_roi_value_dict(txt_path):output=[]line_number=0withopen(txt_path,'r')asfile:forlineinfile:line_number+=1#使用正则表达式提取case_name和pixel_valuematch=......
  • 腾讯云部署的java服务,访问阿里云的mysql数据库,带宽异常偏高,可能是什么原因
    个人名片......
  • java通过jts获取点到线的垂足点
    在Java中,可以使用JTS(JavaTopologySuite)库来获取点到线段的垂足点。以下是一个简单的示例代码,展示了如何使用JTS获取点到线段的垂足点:首先,确保你的项目中包含了JTS库。importorg.locationtech.jts.algorithm.distance.DistanceToPoint;importorg.locationtech.jts.algor......
  • 2024中国ScrumDay大会,全面开启早鸟票启售!智驭未来,敏捷先行!Scrum.org CEO及敏捷行业专
    关于ScrumDay智驭未来,敏捷先行——2024中国Scrum大会启航在全球数字化转型的浪潮中,敏捷已成为企业脱颖而出的关键。Scrum中文网携手全球敏捷行业巨擘——Scrum.org联袂呈现年度敏捷盛会ScrumDay,将于今秋盛大启幕!大会聚焦“AI时代下的敏捷(AgileintheAIAge)”,汇......
  • 【Java】如何根据应用场景选择合适的消息中间件?
    一、问题解析21.1消息中间件的应用场景消息中间件的应用场景主要有两个:异步解耦与削峰填谷。我们首先通过电商平台用户注册送积分、送优惠券这个场景来理解异步解耦合。如果不使用消息中间件,电商平台送积分的实现也许是下图这个样子:我们简单看一下这个流程。用户在网站......
  • Effective Java 学习总结
    前言EffectiveJava作为Java四大名著之一,聚焦于Java语言习惯和高效的用法。EJ告诉读者如何更好地构建代码,以便代码能够更好地工作;也便于其他人能够理解这些代码,便于修改和改善;程序也会因此变得更加令人愉快,更加优雅。全书共90条,接下来笔者将逐条进行总结。第1条:用......
  • 【Java】如何提升RocketMQ顺序消费性能?
    一、问题解析我们先来了解一下RocketMQ顺序消费的实现原理。RocketMQ支持局部顺序消息消费,可以保证同一个消费队列上的消息顺序消费。例如,消息发送者向主题为ORDER_TOPIC的4个队列共发送12条消息,RocketMQ可以保证1、4、8这三条按顺序消费,但无法保证消息4和消息......