2024年Java最新Core Java 52 问(含答案),2024必看-Java高级面试题总结

Kafka实战笔记

关于这份笔记,为了不影响大家的阅读体验,我只能在文章中展示部分的章节内容和核心截图

image.png

  • Kafka入门
  • 为什么选择Kafka
  • Karka的安装、管理和配置

image.png

  • Kafka的集群
  • 第一个Kafka程序
  • image.png

afka的生产者

image.png

  • Kafka的消费者
  • 深入理解Kafka
  • 可靠的数据传递

image.png

image.png

  • Spring和Kalka的整合
  • Sprinboot和Kafka的整合
  • Kafka实战之削峰填谷
  • 数据管道和流式处理(了解即可)

image.png

  • Kafka实战之削峰填谷

image.png

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

对象和基本类型

17. 为什么说 String 不可变 ?
  • Stringfinal 类,不可以被扩展
  • private final char value[],不可变
  • 没有对外提供任何修改 value[] 的方法

参见我的文章 String 为什么不可变 ?

18. 什么是 String.intern() ? 何时使用? 为什么使用 ?

如果常量池中存在当前字符串, 就会直接返回当前字符串. 如果常量池中没有此字符串, 会将此字符串放入常量池中后, 再返回。

将运行时需要大量使用的字符串放入常量池。

深入解析 String.intern()

19. 列举 8 种基本类型
基本类型大小最大值最小值包装类虚拟机中符号
boolean---BooleanZ
char16 bits655360CharacterC
byte8 bits127-128ByteB
short16 bits215-1- 215ShortS
int32 bits231-1231IntegerI
long64 bits263-1-263LongJ
float32 bits3.4028235e+38f-3.4028235e+38fFloatF
double64 bits1.7976931348623157e+308-1.7976931348623157e+308DoubleD
20. int 和 Integer 区别

int 是基本数据类型,一般直接存储在栈中,更加高效

Integer 是包装类型,new 出来的对象存储在堆中,比较耗费资源

21. 什么是自动装箱拆箱 ?

把基本数据类型转换成包装类的过程叫做装箱。

把包装类转换成基本数据类型的过程叫做拆箱。

在Java 1.5之前,要手动进行装箱,

Integer i = new Integer(10);

java 1.5 中,提供了自动拆箱与自动装箱功能。需要拆箱和装箱的时候,会自动进行转换。

Integer i =10; //自动装箱
int b= i; //自动拆箱

自动装箱都是通过Integer.valueOf()方法来实现的,Integer的自动拆箱都是通过integer.intValue来实现的。

关于 Java 基本类型可以看我的一篇总结文章:走进 JDK 之谈谈基本类型

22. Java 中的类型转换

赋值和方法调用转换规则:从低位类型到高位类型自动转换;从高位类型到低位类型需要强制类型转换:

  1. 布尔型和其它基本数据类型之间不能相互转换;
  2. byte 型可以转换为 shortintlongfloatdouble
  3. short 可转换为 intlongfloatdouble
  4. char 可转换为 intlongfloatdouble
  5. int 可转换为 longfloatdouble
  6. long 可转换为 floatdouble
  7. float 可转换为 double

基本类型 与 对应包装类 可自动转换,这是自动装箱和折箱的原理。

两个引用类型间转换:

  1. 子类能直接转换为父类 或 接口类型

  2. 父类转换为子类要强制类型转换,且在运行时若实际不是对应的对象,会抛出 ClassCastException 运行时异常;

23. Java 值传递还是引用传递 ?

值传递。

值传递(pass by value)是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。

引用传递(pass by reference)是指在调用函数时将实际参数的地址直接传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。

Java 调用方法传递的是实参引用的副本。

为什么说Java中只有值传递。

24. 对象实例化和初始化之间的区别 ?

Initialization(实例化) 是创建新对象并且分配内存的过程。新创建的变量必须显示赋值,否则它将使用存储在该内存区域上的上一个变量包含的值。为了避免这个问题,Java 会给不同的数据类型赋予默认值:

  • boolean defaults to false;
  • byte defaults to 0;
  • short defaults to 0;
  • int defaults to 0;
  • long defaults to 0L;
  • char defaults to \u0000;
  • float defaults to 0.0f;
  • double defaults to 0.0d;
  • object defaults to null.

Instantiation(初始化)是给已经声明的变量显示赋值的过程。

int j; // Initialized variable (int defaults to 0 right after)
j = 10; // Instantiated variable

25. 局部变量、实例变量以及类变量之间的区别?

局部变量仅仅存在于创建它的方法中,他们被保存在栈内存,在方法外无法获得它们的引用。Java 的方法执行不是依赖寄存器的,而是栈帧,每个方法的执行和结束都伴随着栈帧的入栈和出栈,也伴随着局部变量的创建和释放。

实例变量也就是成员变量,声明在类中,依赖类实例而存在,不同类实例中变量值也可能不同。

类变量也就是静态变量,在所有类实例中只有一个值,在一个地方改变它的值将会改变所有类实例中的值。

Java 内存模型和垃圾收集器

26. 什么是垃圾收集器 ? 它是如何工作的 ?

Java 和 C++ 之前有一堵由内存动态分配和垃圾收集技术所围成的高墙,墙外面的人想进去,墙里面的人想出去。

垃圾收集器主要用来回收堆上的无用对象,Java 开发者只管创建和使用对象,JVM 来为你自动分配和回收内存。

JVM 通过可达性分析算法来判定对象是否存活。这个算法的基本思路就是通过一系列的称为 GC Roots 的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到 GC Roots 没有任何引用链相连(用图论的话来说,就是从 GC Roots 到这个对象不可达)时,则证明此对象是不可用的。

即使在可达性分析算法中不可达的对象,也并非是 非死不可 的,这时候他们暂时处于缓刑阶段,要真正宣告一个对象死亡,至少要经历两次标记过程。

更多详细内容可以阅读 《深入理解 Java 虚拟机》 第三章 垃圾收集器与内存分配策略

27. 什么是 java 内存模型? 它遵循了什么原则?它的堆栈是如何组织的 ?

Java 虚拟机规范中试图定义一种 Java 内存模型(Java Memory Model,JMM)来屏蔽掉各种硬件和操作系统的内存访问差异,以实现让 Java 程序在各种平台下都能达到一致的内存访问效果。JMM 是语言级的内存模型,它确保在不同的编译器和不同的处理器平台上,通过禁止特定类型的编译器重排序和处理器重排序,为程序员提供一致的内存可见性保证。

JMM 内存模型的抽象表示如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

结合上图,在 Java 中,所有实例域、静态域和数组元素都存储在堆内存中,堆内存在线程之间共享。局部变量、方法定义参数和异常处理器参数在栈中,不会在线程之间共享,它们不会有内存可见性问题,也不会受内存模型影响。

Java 线程之间的通信由 JMM 控制,JMM 决定一个线程对共享变量的写入何时对另一个线程可见。JMM 通过控制主内存与每个线程的本地内存之间的交互,来为 Java 程序员提供内存可见性保证。

更多详细内容可以阅读 《Java 并发编程的艺术》

28. 什么是 内存泄漏,java 如何处理它 ?

内存泄露就是不会再被使用的对象无法被 GC 回收,即这些对象在可达性分析中是可达的,但在程序中的确不会再被使用。比如长生命周期的对象引用了短生命周期的对象,导致短生命周期对象不能被回收。

Java 应该不会处理内存泄漏,我们能做的更多是防患于未然,以及使用合理手段监测,比如 Android 里常用的 LeakCanary,详细原理可以看我之前的一篇文章 LeakCanary 源码解析

29. 什么是 强引用,软引用,弱引用,虚引用 ?

在 JDK 1.2 之后,Java 对引用的概念进行了扩充,将引用分为 强引用、软引用、弱引用、虚引用 4 种,这 4 种引用强度依次逐渐减弱。

  • 强引用就是指程序代码之中普遍存在的,类似 Object obj = new Object() 这类的引用,只要强引用还存在,GC 永远不会回收掉被引用的对象。

  • 软引用是用来描述一些还有用但并非必须的对象。对于软引用关联着的对象,在系统将要发生内存溢出异常之前,将会把这些对象列进回收范围之中进行第二次回收。如果这次回收还没有足够的内存,才会抛出内存溢出异常。在 JDK 1.2 之后,提供了 SoftReference 类来实现软引用。

  • 弱引用也是用来描述非必须对象的,但它的强度比软引用要弱一些,被弱引用关联的对象只能生存到下一次 GC 发生之前。当 GC 工作时,无法当前内存是否足够,都会回收掉只被弱引用关联的对象。在 JDK 1.2 之后,提供了 WeakReference 类来实现弱引用。

  • 虚引用也称为幽灵引用或者幻影引用,它是最弱的一种引用关系。一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。为一个对象设置虚引用关联的唯一目的就是能在这个对象被 GC 回收时收到一个系统通知。在 JDK 1.2 之后,提供了 PhantomReference 类来实现虚引用。

并发

30. 关键字 synchronized 的作用 ?

关键字 synchronized 可以修饰方法或者以同步块的形式来进行使用,它主要确保多个线程在同一时刻,只能有一个线程处于方法或同步块中,它保证了线程对变量访问的可见性和排他性。

  • 对于普通同步方法,锁是当前实例对象
  • 对于静态同步方法,锁是当前类的 Class 对象
  • 对于同步方法块,锁是 Synchonized 括号里配置的对象
31. ThreadPoolExecutor 作用 ?

在 Java 中,使用线程来执行异步任务。Java 线程的创建与销毁需要一定的开销,如果我们为每一个任务都创建一个新线程来执行,这些线程的创建与销毁将消耗大量的计算资源。同时,为每一个任务创建一个新线程来执行,这种策略可能会使处于高负荷的应用最终崩溃。

关于线程池的详细介绍,推荐一篇文章 Java并发编程:线程池的使用

32. 关键字 volatile 的作用 ?

volatile 是轻量级的 synchronized,它在多处理器开发中保证了共享变量的 可见性。volatile 用来修饰字段(成员变量),就是告知程序任何对该变量的访问均需从共享内存中获取,而对它的改变必须同步刷新回共享内存,它能保证所有线程对变量访问的可见性。

但是,过多的使用 volatile 是不必要的,因为它会降低程序执行的效率。

异常

33. try{} catch{} finally{} 是如何工作的 ?

try 代码块用来标记需要进行异常监控的代码

catch 代码块跟在 try 代码块之后,用来捕获 try 代码块中触发的某种指定类型的异常。除了声明所捕获的异常类型之外,catch 代码块还定义了针对该异常类型的异常处理器。在 Java 中,try 代码块后面可以跟着多个 catch 代码块,来捕获不同的异常。Java 虚拟机会从上至下匹配异常处理器。因此,前面的 catch 代码块所捕获的异常类型不能覆盖后面的,否则编译器会报错。

finally 代码块跟在 try 代码块和 catch 代码块之后,用来声明一段必定运行的代码。它的设计初衷是为了避免跳过某些关键的清理代码,例如关闭已打开的系统资源。

在编译生成的字节码中,每个方法都附带一个异常表。异常表中的每一个条目代表一个异常处理器,并且由 from 指针,to 指针, target 指针以及所捕获的异常类型构成。这些指针的值是字节码索引(bytecode index,bci),用以定位字节码。

其中,from 指针和 to 指针标示了该异常处理器所监控的范围,例如 try 代码块所覆盖的范围。target 指针则指向异常处理器的起始位置,比如 catch 代码块的起始位置。

finally 代码块的编译比较复杂。当前版本 Java 编译器的做法,是复制 finally 代码块的内容,分别放在try-catch 代码块所有正常执行路径以及异常执行路径的出口中。

以上内容来自极客时间专栏 深入拆解 Java 虚拟机

34. Checked Exception 和 Un-Checked Exception 区别 ?

在 Java 中,所有异常都是 Throwable 类或者其子类的实例。Throwable 有两大直接子类。一个是 Error,涵盖程序不应捕获的异常。当 Error 发生时,它的执行状态已经无法恢复,需要终止线程甚至虚拟机。第二个子类是 Exception,涵盖程序可能需要捕获并且处理的异常。

Exception 有一个特殊的子类 RuntimeException,运行时异常,用来表示 “程序虽然无法继续执行,但还能抢救一下” 的情况。

RuntimeException 和 Error 属于 Java 里的非检查异常(unchecked exception)。其他异常则属于检查异常(checked exception)。在 Java 语法中,所有的检查异常都需要程序显式地捕获,或者在方法声明中用 throws 关键字标注。通常情况下,程序中自定义的异常应为检查异常,以便最大化利用 Java 编译器的编译时检查。

以上内容来自极客时间专栏 深入拆解 Java 虚拟机

其他

35. 什么是序列化?如何实现 ?

序列化是将对象转换成字节流以便持久化存储的过程。它可以保存对象的状态和数据,方便在特定时刻重新构建该对象。在 Android 中,一般使用 Serializable , Externalizable (implements Serializable) 或者 Parcelable 接口。

Serializable 最容易实现,直接实现接口即可。Externalizable 可以在序列化的过程中插入一些自己的逻辑代码,考虑到它是 Java 早期版本的遗留物,现在基本已经没人再使用它。在 Android 中推荐使用 Parcelable ,它就是为 Android 而实现,性能是 Serializable 的十倍,因为 Serializable 使用了反射。反射不仅慢,还会创建大量临时对象,导致频繁 GC。

例子:

/**

  • Implementing the Serializeable interface is all that is required
    */
    public class User implements Serializable {

private String name;
private String email;

public User() {
}

public String getName() {
return name;
}

public void setName(final String name) {
this.name = name;
}

public String getEmail() {
return email;
}

public void setEmail(final String email) {
this.email = email;
}
}

Parcelable 需要多一些工作:

public class User implements Parcelable {

private String name;
private String email;

/**

  • Interface that must be implemented and provided as a public CREATOR field
  • that generates instances of your Parcelable class from a Parcel.
    */
    public static final Creator CREATOR = new Creator() {

/**

  • Creates a new USer object from the Parcel. This is the reason why
  • the constructor that takes a Parcel is needed.
    */
    @Override
    public User createFromParcel(Parcel in) {
    return new User(in);
    }

/**

  • Create a new array of the Parcelable class.
  • @return an array of the Parcelable class,
  • with every entry initialized to null.
    */
    @Override
    public User[] newArray(int size) {
    return new User[size];
    }
    };

public User() {
}

/**

  • Parcel overloaded constructor required for
  • Parcelable implementation used in the CREATOR
    */
    private User(Parcel in) {
    name = in.readString();
    email = in.readString();
    }

public String getName() {
return name;
}

public void setName(final String name) {
this.name = name;
}

public String getEmail() {
return email;
}

public void setEmail(final String email) {
this.email = email;
}

@Override
public int describeContents() {
return 0;
}

/**

  • This is where the parcel is performed.
    */
    @Override
    public void writeToParcel(final Parcel parcel, final int i) {
    parcel.writeString(name);
    parcel.writeString(email);
    }
    }
36. 关键字 transient 的作用 ?

transient 很简单,它的作用就是让被其修饰的成员变量在序列化的过程中不被序列化。

37. 什么是匿名内部类 ?

匿名内部类是唯一一种没有构造器的类。正因为其没有构造器,所以匿名内部类的使用范围非常有限,大部分匿名内部类用于接口回调。匿名内部类在编译的时候由系统自动起名为 Outter$1.class。一般来说,匿名内部类用于继承其他类或是实现接口,并不需要增加额外的方法,只是对继承方法的实现或是重写。

Android 中应用最常见的就是各种点击事件。

38. 对象的 == 和 .equals 区别 ?

对于对象而言,== 永远比较的都是其内存地址。而 equals() 则要看该对象是否重写了 equals() 方法,如果没有则会调用父类的 equals() 方法,如果父类也没有实现的话,就不断向上追溯,直至 Object 类。看一下 Object.java 中的 equals() 方法:

public boolean equals(Object obj) {
return (this == obj);
}

Object 中,equals 等同于 ==,都是比较内存地址。再看一下不是比较内存地址的,String.equals()

public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}

这里比较的就不再是内存地址,而是其实际的值。

39. hashCode() 和 equals() 用处 ?

equals() 用于判断两个对象是否相等,未被重写的话就是判断内存地址,和 == 语义一致。重写了的话,就按照重写的逻辑进行判断。

hashCode() 用于计算对象的哈希码,默认实现是将对象的内存地址作为哈希码返回,可以保证不同对象的返回值不同。理论上,hashCode 也可以用来比较对象是否相等。hashCode() 主要用在哈希表中,比如 HashMapHashSet 等。

当我们向哈希表(如HashSet、HashMap等)中添加对象object时,首先调用hashCode()方法计算object的哈希码,通过哈希码可以直接定位object在哈希表中的位置(一般是哈希码对哈希表大小取余)。如果该位置没有对象,可以直接将object插入该位置;如果该位置有对象(可能有多个,通过链表实现),则调用equals()方法比较这些对象与object是否相等,如果相等,则不需要保存object;如果不相等,则将该对象加入到链表中。

equals() 相等,hashCode 必然相等。反之则不然,hashCode 相等,equals() 不能保证一定相等。

40. 构造函数中为什么不能调用抽象方法 ?

构造函数中不能调用抽象方法,说的更严谨一点,构造函数中不能调用可被覆盖的方法

先看这样一个例子:

public abstract class Super {

Super(){
overrideMe();
}

abstract void overrideMe();
}

public class Sub extends Super {

private final Instant instant;

public Sub() {
instant = Instant.now();
}

@Override
void overrideMe() {
System.out.println(instant);
}

public static void main(String[] args) {
Sub sub=new Sub();

最后

分享一些系统的面试题,大家可以拿去刷一刷,准备面试涨薪。

这些面试题相对应的技术点:

  • JVM
  • MySQL
  • Mybatis
  • MongoDB
  • Redis
  • Spring
  • Spring boot
  • Spring cloud
  • Kafka
  • RabbitMQ
  • Nginx

大类就是:

  • Java基础
  • 数据结构与算法
  • 并发编程
  • 数据库
  • 设计模式
  • 微服务
  • 消息中间件

程序员,每个月给你发多少工资,你才会想老板想的事?

程序员,每个月给你发多少工资,你才会想老板想的事?

程序员,每个月给你发多少工资,你才会想老板想的事?

程序员,每个月给你发多少工资,你才会想老板想的事?

程序员,每个月给你发多少工资,你才会想老板想的事?

程序员,每个月给你发多少工资,你才会想老板想的事?

程序员,每个月给你发多少工资,你才会想老板想的事?

程序员,每个月给你发多少工资,你才会想老板想的事?

程序员,每个月给你发多少工资,你才会想老板想的事?

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

[外链图片转存中…(img-WuLWmOdr-1714906361238)]

[外链图片转存中…(img-dwvRFqnA-1714906361238)]

[外链图片转存中…(img-1nuA6Evr-1714906361239)]

[外链图片转存中…(img-1yCnXmMx-1714906361239)]

[外链图片转存中…(img-xoXpjZwd-1714906361239)]

[外链图片转存中…(img-EnWfwuu8-1714906361239)]

[外链图片转存中…(img-bLLk09yK-1714906361240)]

[外链图片转存中…(img-pZzF2RyL-1714906361240)]

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

  • 18
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值