java基础面试知识点

java基础

1. Java面试核心概念

Java三大特点

:平台无关性、面向对象、内存管理。

  • 平台无关性:通过JVM(Java虚拟机)实现。源代码编译成字节码(.class文件),可在任何安装了相应JVM的操作系统上运行。
  • 面向对象 :核心是"对象"。对象是类的一个运行实例,且对象实际是堆内存中的一块具体空间
  • 内存管理:Java具备自动垃圾回收机制(GC),开发者无需手动释放内存,这与C/C++不同。

2. JDK、JRE、JVM关系

三者是包含与被包含的关系,从大到小依次为:JDK > JRE > JVM

  • JDK (Java Development Kit):完整的Java开发工具包,包含JRE和编译器(javac)、调试器(jdb)等。
  • JRE (Java Runtime Environment):Java运行环境,包含JVM和Java类库(rt.jar)。
  • JVM (Java Virtual Machine):核心组件,负责执行字节码、内存管理、垃圾回收等。不同平台有不同版本的JVM。

3. 值传递与引用传递

  • 核心原则:Java中只有值传递,不存在真正的引用传递。

  • 基本数据类型:传递的是值的副本,适用于基本数据类型。方法内部对参数的修改不影响原始变量。

  • 引用数据类型:传递的是对象引用的副本(地址值),而非对象本身。如果修改对象内部的状态,会影响原始对象;如果修改引用指向,则不影响原始对象。

4. Java数据类型

  • 八种基本数据类型:byte, short, int, long, float, double, char, boolean。

  • 三种引用数据类型:接口, 数组, 类。

    类型转换:

    • 自动类型转换:由小范围向大范围转换(如int -> long),安全。
    • 强制类型转换:由大范围向小范围转换(如long -> int),可能导致数据溢出或精度丢失。
    • 字符串与基本类型转换 :在字符串和数值 / 布尔等类型之间互相转换,字符串 → 基本类型 :使用对应包装类的 parseXxx() 方法,基本类型 → 字符串 :方式 1:String.valueOf(基本类型值),方式 2:基本类型值 + ""
    • 数值与字符 / 包装类之间的转换intchar:直接强转(基于 ASCII/Unicode 编码),charint:直接赋值或强转。数值类型之间的包装类转换 :通过包装类提供的方法实现,例如:IntegerLongInteger.intValue() 后再装箱,DoubleFloatDouble.doubleValue() 后再装箱。

5. BigDecimal与Double

  • Double的局限性 :在计算机底层以二进制科学计数法存储,无法精确表示十进制小数(如0.1),会导致精度丢失和溢出问题,尤其在金融计算中风险极高。
  • BigDecimal的优势 :BigDecimal是Java提供的高精度数值类型,使用字符串作为底层存储,能够精确表示任意精度的十进制小数,完全避免精度丢失问题。

6. 自动装箱与拆箱

  • 概念:将基本数据类型与对应的包装类(如int与Integer)之间进行自动转换。
  • 自动装箱:基本类型 -> 包装类。使用Integer.valueof()方法。
  • 自动拆箱:包装类 -> 基本类型。使用intvalue()方法。
  • 128陷阱:Integer.valueOf()方法会对-128到127范围内的整数进行缓存复用,new Integer()创建的对象不会复用缓存中的对象。

7.面向对象三大特征

  • 封装:把属性私有化,对外提供公共方法访问。。
  • 继承:子类可以继承父类的属性和方法,实现代码的复用。本质是代码的复用。java只支持单继承(防止资源浪费)
  • 多态:父类引用指向子类对象,调用重写方法时,执行子类的逻辑。主要体现为方法重载、方法重写、接口实现及向上转型,向下转型。

8.多态

多态是面向对象编程的核心特性,主要体现在以下四个方面:

方法重载(编译时多态)

  • 定义 :同一类中存在多个同名方法,参数列表(类型、数量、顺序)不同。
  • 绑定时机 :编译器在编译阶段根据传入参数确定调用版本。
  • 核心作用:实现 "同名不同参" 的方法复用,提升代码可读性。
  • 示例add(int a, int b)add(double a, double b) 会根据参数类型被分别调用。

方法重写(运行时多态,核心实现方式)

  • 定义:子类对父类同名方法提供具体实现,方法签名(方法名、参数列表)必须与父类一致。
  • 绑定时机 :JVM 在运行阶段 根据对象的实际类型确定调用哪个子类的方法版本。
  • 核心作用:实现 "父类引用指向子类对象,调用子类实现",是多态最典型的表现形式。
  • 示例 :父类 Animal 定义 sound(),子类 Dog 重写为 bark()Cat 重写为 meow(),运行时自动匹配。

接口与实现(多态的扩展体现)

  • 定义 :多个类实现同一个接口,用接口类型引用指向不同实现类对象。
  • 调用特点:调用接口方法时,会自动执行对应实现类的具体逻辑,调用方式保持一致。
  • 核心作用:解耦 "规范" 与 "实现",便于扩展和替换不同业务实现。
  • 示例DogCat 都实现 Animal 接口,用 Animal animal = new Dog() 调用 makeSound() 时,执行 Dog 的实现。

向上转型与向下转型(多态的类型转换基础)

  • 向上转型

    :父类引用指向子类对象(自动完成),屏蔽子类差异,统一调用父类 / 接口方法。

    • 例:Animal a = new Dog();
  • 向下转型

    :将父类引用转回子类类型(需手动强转),用于调用子类特有方法。

    • 风险:需用 instanceof 判断类型,否则可能抛出 ClassCastException
  • 核心作用:支撑 "父类 / 接口调用、子类实现" 的多态场景,实现类型灵活切换。

重载与重写有什么区别?

  • 重载(Overloading)指的是在同一个类中,可以有多个同名方法,它们具有不同的参数列表(参数类型、参数个数或参数顺序不同),编译器根据调用时的参数类型来决定调用哪个方法。
  • 重写(Overriding)指的是子类可以重新定义父类中的方法,方法名、参数列表和返回类型必须与父类中的方法一致,通过 @Override 注解来明确表示这是对父类方法的重写。

重载是指在同一个类中定义多个同名方法,而重写是指子类重新定义父类中的方法。

9.抽象类与接口

  • 抽象类:用于描述类的共同性,可以包含成员变量、构造方法、具体方法和抽象方法。不能被直接实例化,只能被继承。抽象类当中可以定义抽象方法,抽象方法子类必须重写。
  • 接口:用于对行为进行规范,定义了常量和抽象方法。一个类可以实现多个接口。接口当中的变量都是常量。
  • 区别:抽象类描述的是"是什么",接口描述的是"做什么"。抽象类可以有方法实现,接口则不能(Java 8后接口可有default方法)。

10.final关键字

  • 修饰类:该类不能被继承。
  • 修饰方法:该方法不能被重写。
  • 修饰变量:对于基本数据类型,值不能被改变;对于引用类型,引用地址不能改变,但可以改变引用指向对象的内容。

11.抽象类能加final修饰吗?

不能,Java 中的抽象类是用来被继承的,而 final 修饰符用于禁止类被继承或方法被重写,因此,抽象类和 final 修饰符是互斥的,不能同时使用。

12.静态变量与静态方法

  • 静态变量:属于类,所有实例共享一个副本,通过类名或对象均可访问,但推荐通过类名访问。
  • 静态方法:属于类,不依赖于实例,可通过类名直接调用。
  • 加载时机:静态变量和静态方法在类加载时初始化,只分配一次内存。

13.static的作用

static 核心作用是将类成员与类本身关联,而非与类实例(对象)关联,让成员属于类、所有实例共享。

修饰变量(静态变量)

  • 归属:属于类本身,所有对象共享同一份数据,内存中仅存一份副本。
  • 访问方式 :通过 类名.变量名 直接访问,无需创建对象。

修饰方法(静态方法)

  • 归属 :属于类,不依赖任何实例,因此不能直接访问非静态成员(非静态成员依赖对象存在),但可访问静态成员。
  • 访问方式 :通过 类名.方法名 直接调用,无需创建对象。

修饰代码块(静态代码块)

  • 执行时机 :在类加载时执行,且仅执行一次,执行顺序优先于构造方法和非静态代码块。

修饰内部类(静态内部类)

  • 特性 :不依赖外部类实例,可独立存在;不能直接访问外部类非静态成员,需通过外部类实例访问。

14.深拷贝与浅拷贝

  • 浅拷贝:复制对象本身及其内部的值字段,但对象内部的引用字段仍指向原对象,不进行复制。

  • 浅拷贝的逻辑是: 只拷贝对象表层

    • 对「值类型字段」:直接复制值(新对象和原对象的这个字段互不影响);
    • 对「引用类型字段」:只复制 "内存地址"(新对象和原对象的这个字段指向同一个数据,改一个会影响另一个)。
  • 深拷贝:复制对象本身及其内部的所有引用对象,确保新旧对象相互独立。

  • 深拷贝的三种核心实现方法

    • 手动递归拷贝
    • 通过序列化 / 反序列化实现
    • 实现Cloneable接口并重写clone()方法

15.序列化与反序列化

  • 概念:将对象转换为字节流(序列化)以便在网络或文件间传输,再将字节流还原为对象(反序列化)。
  • 实现:需让类实现Serializable接口,并使用ObjectOutputStream进行序列化,ObjectInputStream进行反序列化。
  • 局限性:存在安全漏洞、不支持跨语言、序列化后的数据体积较大等问题。

16. 泛型

  • 作用 :允许类、接口和方法在定义时使用参数化类型,提高代码的通用性和安全性,在编译期进行类型检查,避免了运行时的强制类型转换(ClassCastException)。
  • 类型擦除:Java 的泛型是"伪泛型",在编译成字节码期间,所有的泛型信息都会被擦除掉,变成 Object 或者其限定的边界类型。

16.1 对象的创建与垃圾回收(GC)机制

  • 创建对象的方式 :主要包括 new 关键字、反射、克隆(Cloneable 接口)和反序列化。
  • 对象回收的原则 :无论通过哪种方式创建在堆内存中的对象,当它不再被任何"有效引用"指向时,就会成为 GC 的回收目标。
  • 如何判断对象已死?(核心考点)
    • 引用计数法(已淘汰) :给对象维护一个计数器。致命缺点:无法解决两个对象互相引用(循环引用)的问题,导致这俩对象永远无法被回收(内存泄漏)。
    • 可达性分析算法(正在使用) :以 GC Roots 为起点顺着引用链往下找。如果一个对象到所有 GC Roots 都没有任何路径相连(即不可达),说明它死了,可以被回收。
  • 哪些对象可以作为 GC Roots?(八股文必背)
    1. 虚拟机栈(方法中的局部变量)中引用的对象。
    2. 方法区中类静态变量(static)引用的对象。
    3. 方法区中常量(final)引用的对象。
    4. 本地方法栈(Native 方法)中引用的对象。
  • finalize() 机制的坑
    如果对象重写了 finalize(),GC 回收前理论上会调用它。注意避坑 :绝对不要依赖它去清理资源(如关闭文件、数据库连接)!由于它优先级极低、执行时机完全不确定,甚至有可能导致对象"复活",还会严重拖慢 GC 的性能。在 JDK 9 以后已被彻底标记为不再推荐使用(@Deprecated)。

17. 反射机制

  • 反射的本质:在运行时动态获取类信息(如字段、方法、构造器)并操作对象的能力。

  • 获取类对象的方式:主要有三种,分别对应类加载的三个阶段:通过Class.forName(全类名)、通过类名.class、通过对象.getClass()。

  • 反射的特点:运行时数据访问、动态数据创建、动态方法获取。

方法类型 访问权限范围 是否包含继承成员
getDeclaredXXX() 所有访问权限(private/protected/default/public) 不包含
getXXX() 仅公有(public) 包含
  • 暴力反射:通过设置Field对象的setAccessible(true)来访问和修改private修饰的字段,称为暴力反射。

  • 反射的应用:在JDBC连接池(Class.forName加载驱动)、Spring框架(通过配置文件动态创建Bean对象)中有广泛应用。

18.代理

1. 代理模式

  • 定义 :不直接访问目标对象,而是通过一个 "代理对象" 作为中间层,既可以控制对目标对象的访问(比如权限校验),又能在访问前后增加额外功能(比如日志、计时)。。

2. 静态代理

  • 实现方式: 代理类与目标类实现相同的接口,代理类中需要为每一个目标类编写一个独立的代理类。
  • 优点: 逻辑清晰,易于理解和实现。
  • 缺点: 违反了开闭原则。当目标类增多或接口功能增多时,需要频繁地编写和修改代理类,导致代码冗余且维护困难。

3. 动态代理

  • 实现方式 : 代理类不直接实现接口,而是实现一个公共的接口(如java.lang.reflect.InvocationHandler),由JDK在运行时动态生成代理对象。JDK 动态代理 (Java 原生),CGLIB 动态代理(第三方库)

  • 核心思想: 代理对象的创建依赖于外部传入的目标对象,使得代理类可以灵活地代理任意一个实现了特定接口的对象。

  • 实现步骤:

    • 代理类通过构造函数接收目标对象。
    • 通过反射技术获取目标对象所实现的接口,从而得知其核心方法。
    • 重写接口中的invoke()方法,在调用目标对象方法前后插入业务增强逻辑(如权限验证)。
    • 最终通过invoke方法调用目标对象的核心方法。

19.Java注解原理

  • 注解的本质是继承了@interface接口的特殊接口,其具体实现类由JVM在运行时动态生成。
  • 通过反射机制可以获取注解,返回的是一个动态代理对象。
  • 注解分为三种类型:源码级别(编译后不保留)、类文件级别(运行时不可见)和运行时级别(可通过反射解析)。
  • 元注解
    • @Target
    • @Retention
    • @Documented
    • @Inherited
  • 自定义注解若要通过反射获取,其生命周期必须声明为@Retention(RetentionPolicy.RUNTIME)

21.IO模型

  • BIO (Blocking IO): 主线程负责监听和处理请求,采用同步阻塞模式。效率较低,但实现简单。
  • NIO (Non-blocking IO): 使用Selector(选择器)管理多个Channel(通道),将任务分发给多个子线程处理,是同步非阻塞模式,效率更高。
  • Netty: 基于NIO的高性能网络框架,常用于处理WebSocket等实时通信场景。

22. Object类有哪些方法?

  • clone() :保护方法,实现对象的浅拷贝。只有实现了Cloneable接口才可以调用该方法,否则抛出CloneNotSupportedException异常。
  • getClass():final方法,获得运行时类型。
  • toString():返回对象的字符串表示,一般子类都会被重写。
  • finalize():用于释放资源。因为无法确定该方法什么时候被调用,目前已不推荐使用。
  • equals():用于比较对象的值是否相等。默认比较内存地址,一般需配合业务逻辑重写。
  • hashCode() :用于哈希查找,重写了equals方法一般都要重写hashCode方法。此方法在一些具有哈希功能的集合(如HashMapHashSet)中用到。
  • wait():使当前线程等待该对象的锁,当前线程必须是该对象的拥有者。
  • notify() / notifyAll():唤醒在该对象上等待的某个(或所有)线程。

23. == 与 equals 有什么区别?

**

  • == 运算符

    • 对于基本数据类型== 比较的是它们的
    • 对于引用数据类型== 比较的是它们在内存中的地址(即是否为同一个对象)。
  • equals() 方法

    • equalsObject 类提供的方法,默认实现与 == 相同,即也是比较对象的内存地址
    • 许多类(如 StringInteger 等)都重写equals() 方法,将其改为比较对象**内部的内容(值)**是否相等,而非仅比较内存地址。

24. hashcode和equals方法有什么关系?

  • 答案: 先执行hashcode (一样的话)再执行equals
    在使用哈希表结构的集合(比如 HashMapHashSet)时,它们的工作原理是先通过 hashCode() 计算对象存放在哈希表中的位置,如果位置一样(哈希冲突),再通过 equals() 判断对象内容是否真的相等。
    • 如果你只重写了 equals() 而不重写 hashCode()
      那么两个逻辑上"相等"的对象,它们的 hashCode()(默认是根据内存地址计算的)却不一样。
    • 导致的后果
      在把对象存入 HashMapHashSet 时,会被存放到两个完全不同的格子里,导致集合里存放了重复的逻辑对象,破坏了集合去重和键唯一性的原则。

25. Java里String的常用方法有哪些?

  • 获取与查找

    • length():返回字符串长度。
    • charAt(int index):返回指定索引处的字符。
    • indexOf(String str):返回子串第一次出现的索引位置。如未找到返回 -1。
    • substring(int beginIndex, int endIndex):截取字符串(左闭右开区间)。
  • 判断与比较

    • equals(Object anObject):比较字符串内容是否相等。
    • equalsIgnoreCase(String anotherString):忽略大小写比较字符串内容。
    • startsWith(String prefix) / endsWith(String suffix):判断是否以指定前缀/后缀开头或结尾。
    • isEmpty():判断字符串是否为空(长度为0)。
  • 转换与截取

    • replace(CharSequence target, CharSequence replacement):替换字符串中的指定字符或序列。
    • trim():去除字符串首尾的空白字符。
    • split(String regex):根据正则表达式拆分字符串,返回字符串数组。
    • toLowerCase() / toUpperCase():转换为小写/大写形式。
    • toCharArray():将字符串转换为字符数组。

26. 为什么说字符串不可变

27. String、StringBuffer、StringBuilder的区别和联系

  • String

    • 特性 :不可变的字符序列(底层是由 final 修饰的 char[]byte[] 数组)。
    • 效率 :每次修改操作都会生成一个新的 String 对象,效率最低,浪费内存。
    • 线程安全 :由于不可变,天然是线程安全的。
  • StringBuffer

    • 特性:可变的字符序列,相对String修改时不会创建新对象。
    • 效率 :相比 String 效率较高,但在单线程环境下不如 StringBuilder
    • 线程安全 :所有修改数据的方法(如 append)都加了 synchronized 同步锁,是线程安全的。
  • StringBuilder

    • 特性 :可变的字符序列,与 StringBuffer的 API 完全一致。
    • 效率 :没有使用同步锁,执行效率最高
    • 线程安全非线程安全
  • 总结与联系

    • 三者都实现了 CharSequence 接口,用于处理字符串。
    • 使用场景 :如果字符串内容很少变化,优先使用 String;如果在单线程 中频繁拼接修改字符串,使用 StringBuilder;如果在多线程 中频繁拼接修改字符串,使用 StringBuffer

28. 异常体系(Error 与 Exception 的区别)

  • 顶级父类 :Java 所有异常和错误的超类是 Throwable。它有两个重要子类:ErrorException
  • Error(错误) :指 JVM 系统级别的重大问题,程序无法通过代码处理恢复。比如内存溢出(OutOfMemoryError)、栈溢出(StackOverflowError)。通俗解释:病入膏肓,只能罢工拔电源。
  • Exception(异常) :指程序运行中出现的可预见、可处理的问题,程序可以通过 catch 捕获并恢复。通俗解释:小感冒,吃点药(catch)还能接着干活。

29. 受查异常(Checked)与非受查异常(Unchecked)的区别

  • 非受查异常(RuntimeException 及其子类) :编译器不要求 强制处理。通常是由代码逻辑错误引起的。常见的有:NullPointerException(空指针)、IndexOutOfBoundsException(数组越界)、ClassCastException(类型转换异常)。通俗解释:代码没写好导致的低级错误,运行时才会炸。
  • 受查异常(非 RuntimeException 的其他 Exception) :编译器强制要求 必须处理(要么 try-catch 捕获,要么 throws 抛出),否则编译报错无法运行。常见的有:IOException(IO异常)、SQLException(数据库异常)、ClassNotFoundException通俗解释:老天爷(外部环境)带来的风险(比如文件不存在),写代码时必须提前买好保险。

30. throw 和 throws 的区别是什么?

  • 位置不同throw 用在方法内部throws 用在方法声明处(方法签名的末尾)。
  • 作用不同throw主动 抛出一个具体的异常对象(真正制造异常的动作);throws 是高呼"我这个方法可能会出这个异常",把它甩锅给调用者处理。
  • 数量不同throw 一次只能抛出一个 异常对象;throws 后面可以跟多个异常类名,用逗号隔开。

31. try-catch-finally 执行顺序陷阱(带有 return 时)

  • 核心规则finally 块中的代码一定会执行 (除非在前面执行了 System.exit(0) 强行退出 JVM)。
  • 带有 return 的执行顺序
    1. 执行 trycatch 中的逻辑。
    2. 遇到 return 时,会先把返回值暂存起来。
    3. 执行 finally 块中的代码。
    4. 最后把刚才暂存的返回值返回给调用者。
  • 神仙打架(覆盖陷阱) :如果 finally 块里面也有 return 语句,那么它会覆盖trycatch 中的 return 值。实际开发中极度反感在 finally 里写 return!

32. String s = new String("abc") 到底创建了几个对象?

这是考察对**字符串常量池(String Pool)**的理解:

  • 情况 A:创建了 1 个对象。 如果字符串常量池中已经存在 "abc" 这个字面量,那么只会在堆内存(Heap)中创建一个 String 对象,并且这个堆对象指向常量池中的 "abc"
  • 情况 B:创建了 2 个对象(常考点)。 如果字符串常量池中还没有 "abc",那么:
    1. 首先在字符串常量池中创建一个 "abc" 对象。
    2. 然后在堆内存中再创建一个 String 对象(其实是对常量池对象的拷贝/包装),并把堆中这个对象的地址返回给变量 s
  • 通俗解释new 关键字就像是个强迫症,只要有它,堆里必定会建个新房子(对象)。但里面的家具(字符串内容)要不要去厂家(常量池)现做,就看之前有没有人做过同样的款式了。
相关推荐
晔子yy2 小时前
【JAVA探索之路】简单聊聊Kafka
java·开发语言
lclcooky2 小时前
【postgresql】分区表管理
java·数据库·postgresql
东离与糖宝2 小时前
Jakarta EE新规范解读:Java+AI在微服务与云原生中的落地实战(附代码示例)
java·人工智能
带娃的IT创业者8 小时前
Python 异步编程完全指南:从入门到精通
服务器·开发语言·python·最佳实践·asyncio·异步编程
zzb158010 小时前
RAG from Scratch-优化-query
java·数据库·人工智能·后端·spring·mybatis
一只鹿鹿鹿10 小时前
信息安全等级保护安全建设防护解决方案(总体资料)
运维·开发语言·数据库·面试·职场和发展
wuqingshun31415910 小时前
如何停止一个正在退出的线程
java·开发语言·jvm
朱包林10 小时前
Python基础
linux·开发语言·ide·python·visualstudio·github·visual studio
Eward-an11 小时前
【算法竞赛/大厂面试】盛最多水容器的最大面积解析
python·算法·leetcode·面试·职场和发展