Java八股文

目录

第一章 JAVA基础篇

Java基础概念

        1、如何理解OOP(面向对象)

                 2、面向对象的五大原则(SOLID原则)

        3、面向对象的三大特征

        4、JDK、JRE、JVM的区别?

数据类型与操作

        1、Java的基本数据类型有哪些?

        2、int与Integer的区别 

        3、==和equals的区别?

        4、自动拆箱装箱原理 

        5、String、StringBuffer和StringBuilder的区别?

        6、深拷贝和浅拷贝

面向对象特性

        1、抽象类和接口的区别?

        2、什么是重载和重写?二者的区别?

二者区别对比表

        3、final、finally、finalize的区别?

        4、Java创建对象的方式有哪些?

异常处理

        1、异常体系结构

        2、try-catch-finally

        3、Error和Exception的区别?

        4、运行时异常和检查型异常的区别

        5、常见的5个RuntimeException和Checked Exception

        6、代码中,全局异常处理类怎么实现?

集合框架

        1、Java集合体系结构?

        2、ArrayList和LinkedList的区别?

        3、HashMap底层实现原理?

        4、HashMap和HashTable的区别?

        5、HashSet如何保证元素不重复?

        6、线程安全的集合都有哪些?

Collections工具类包

IO流

        1、流的类型都有哪些?

        2、字节流和字符流的区别?

        3、字节流和字符流的使用场景

        4、Java中有哪些IO模型?

        5、什么是Java序列化?如何实现?

        6、BIO、NIO、AIO的区别

多线程基础

        1、创建线程的方式有哪些?

        2、sleep()和wait()的区别?

        3、synchronized和Lock的区别?

        4、线程池的参数有哪些?


第一章 JAVA基础篇


Java基础概念

        1、如何理解OOP(面向对象)

        面向对象(OOP)是一种编程范式(程序设计方法论),它以"对象"作为程序的基本单元。

        所谓的对象即现实世界中具体或抽象事物的表示,而对象中存在着属性和行为。

        2、面向对象的五大原则(SOLID原则)

        单一职责原则(SRP): 一个类只负责一项职责(就像厨师只管做饭,服务员只管上菜)

        开放封闭原则(OCP): 对扩展开放,对修改封闭(就像房子装修,可以加家具但别拆承重墙)

        里氏替换原则(LSP): 子类必须能够替换其父类(说"这是鸟"时,企鹅不能不会飞让你尴尬)

        接口隔离原则(ISP): 使用多个专用接口而非单一通用接口(别搞万能遥控器,结果90%按键用不上)

        依赖倒置原则(DIP): 依赖抽象而非具体实现

        3、面向对象的三大特征

        面向对象的三大特征:封装、继承、多态

封装:把东西打包好,只露该露的

  • 将数据和行为包装在一个单元(类)中

  • 隐藏内部实现细节,只暴露必要接口

  • 通过访问修饰符(public/private/protected)控制访问级别

继承:儿子可以继承老爸的特征

  • 子类继承父类的属性和方法

多态:同一个方法在不同对象表现不同

  • 同一操作作用于不同对象可以有不同的解释

  • 主要通过方法重写(override)和接口实现

  • 包括编译时多态(重载)和运行时多态(重写)

        4、JDK、JRE、JVM的区别?

        JDK(开发工具包) ⊃ JRE(运行环境) ⊃ JVM(虚拟机)

JDK:

  • Java 开发工具包 = JRE + 开发工具(如编译器、调试器、文档生成器等)。

  • 开发者必须安装 JDK 才能编写、编译和调试 Java 程序。

JRE:

  • 运行 Java 程序的最小环境 = JVM + 核心类库(如java.langjava.io等)。

  • 普通用户只需安装 JRE 即可运行 Java 程序(如 Jar 文件)。

JVM:

  • 执行字节码:将编译后的.class文件(字节码)解释/编译为机器码并执行。

  • 跨平台核心:"Write Once, Run Anywhere" 的关键,不同操作系统需要对应的 JVM 实现。

JDK (开发工具包)

├── JRE (运行环境)
│   │
│   ├── JVM (虚拟机)
│   ├── 核心类库 (如 java.lang, java.util)

├── 开发工具 (如 javac, javadoc)

数据类型与操作

        1、Java的基本数据类型有哪些?

        java的数据类型共有八种,四大类。分别为: 整数类型、浮点类型、字符类型、布尔类型

整数类型

类型大小(字节)取值范围默认值示例
byte1-128 ~ 1270byte b = 100;
short2-32,768 ~ 32,7670short s = 500;
int4-2³¹ ~ 2³¹-1(约±21亿)0int i = 100000;
long8-2⁶³ ~ 2⁶³-10Llong l = 100L;

浮点类型

类型大小(字节)取值范围默认值示例
float4±3.4E38(约6-7位有效数字)0.0ffloat f = 3.14f;
double8±1.7E308(约15位有效数字)0.0ddouble d = 3.14;

字符类型

类型大小(字节)取值范围默认值示例
char2Unicode字符(0 ~ 65535)'\u0000'char c = 'A';

布尔类型

类型大小(官方未明确定义)取值范围默认值示例
boolean通常按JVM实现(1位或1字节)true/falsefalseboolean flag = true;

        2、int与Integer的区别 

  • 数据类型不同:int 是基础数据类型,而 Integer 是包装数据类型(是 java.lang.Integer 的实例对象)
  • 默认值不同:int 的默认值是 0,而 Integer 的默认值是 null
  • 内存中存储的方式不同:

        int:直接存储在栈(stack)中,存储实际数值。

        Integer:对象存储在堆(heap)中,变量存储的是对象的引用(指针)。

        注意:JVM 对 -128~127 的 Integer 做了缓存(通过 IntegerCache),直接赋值时可能复用同一对象。

  • 实例化方式不同:Integer 必须实例化才可以使用,而 int 不需要;
  • 变量比较方式不同:

        int:直接比较数值是否相等(如 a == b)。

        Integer

  ==:比较对象引用地址(缓存范围内可能相等,范围外一定不等)。

  equals():比较包装的数值是否相等(推荐方式)

Integer x = 127, y = 127;
System.out.println(x == y); // true(缓存范围内)  
Integer m = 128, n = 128;
System.out.println(m == n); // false(超出缓存范围)  
System.out.println(m.equals(n)); // true(值相等)  

        3、==和equals的区别?

比较项==equals()
比较对象基本类型:比较是否相等
引用类型:比较内存地址(是否同一对象)
引用类型:默认比较内存地址(与 == 相同)
但可被重写(如 StringInteger 等类重写后比较内容
适用类型基本类型(intchar 等)和引用类型(Object仅适用于引用类型(对象)
是否可重写否(Java 运算符,无法修改)是(可自定义比较逻辑)
示例int a = 5, b = 5;
a == b; // true
String s1 = new String("abc");
String s2 = new String("abc");
s1 == s2; // false
String s1 = new String("abc");
String s2 = new String("abc");
s1.equals(s2); // true

        4、自动拆箱装箱原理 

基本数据类型包装类
byteByte
shortShort
intInteger
longLong
floatFloat
doubleDouble
booleanBoolean
charCharacter

装箱:把基本类型转换为包装类的过程就是装箱

拆箱:把包装类转换为基本数据类型就是拆箱

自动装箱都是通过包装类的valueOf方法实现的

自动装箱都是通过包装类对象xxxValue方法实现的(如intValue)

        5、String、StringBuffer和StringBuilder的区别?

在java中,String、StringBuffer和StringBuilder都是用来操作字符串,然而他们三个中,各有使用合适的场景:单线程用 Builder,多线程用 Buffer,不变字符用 String。下面就展开讲讲

String是Java中最常用的字符串类,创建String对象常见的方式有以下几种:

// 方式1:直接使用双引号创建
String str1 = "Hello";

// 方式2:使用new关键字创建
String str2 = new String("Hello");

        String底层是被final修饰,所以是不可变性,当进行字符串拼接时,就会产生新的对象。

特点

  • 不可变性:String对象一旦创建就不能被修改,任何修改操作都会生成新的String对象

  • 字符串常量池:直接使用双引号创建的字符串会存储在字符串常量池中,可以复用

  • 线程安全:由于不可变性,String是天然线程安全的

        StringBuffer和StringBuilder 都是可变的,这两底层实现的是一样的,然后StringBuffer的所有实现方法都加了synchronized 锁

特点

特性StringBufferStringBuilder
线程安全✅ 所有方法加 synchronized 锁❌ 无锁,非线程安全
性能较低(锁竞争开销)更高 更快(无锁)
适用场景多线程环境(如全局变量)单线程环境(如方法内部变量)

        6、深拷贝和浅拷贝

浅拷贝(Shallow Copy)

        浅拷贝是指创建一个新对象,但只复制对象的基本数据类型的值,而对于引用类型,只复制引用地址而不复制引用的对象本身。

特点

  • 新旧对象共享同一块内存中的引用类型数据

  • 修改任一方的引用类型成员变量会影响另一方

  • 实现简单,性能开销小

  • 适用于不包含引用类型或引用类型不可变的对象

深拷贝(Deep Copy)

        深拷贝是指创建一个新对象,并递归复制对象的所有字段,包括引用类型的字段,使得原始对象和拷贝对象完全独立。

特点

  • 新旧对象不共享任何引用类型数据

  • 修改任一方的数据不会影响另一方(双方是独立的)

  • 实现复杂,性能开销大

  • 适用于包含可变引用类型的对象

面向对象特性

        1、抽象类和接口的区别?

特性抽象类接口
定义关键字abstract classinterface
方法实现可以包含具体方法和抽象方法(抽象类可以没有抽象方法,但是有抽象方法的类一定是抽象类)Java 8前只能有抽象方法,之后可以有默认方法和静态方法
成员变量可以有普通成员变量只能是public static fina修饰后的l常量
构造方法有构造方法没有构造方法
多继承一个类只能继承一个抽象类(不能多继承)一个类可以实现多个接口

        2、什么是重载和重写?二者的区别?

重载(Overloading)

        定义:同一个类里,方法名相同但参数不同(数量、类型或顺序不同),让同一个方法名可以干不同的事。

重写(Overriding)

        定义:子类重新实现父类的方法。方法名、参数、返回类型必须一模一样,但具体逻辑可以改。

二者区别对比表

对比点重载(Overloading)重写(Overriding)
发生位置同一个类中子类重写父类方法(属于不同类)
方法签名必须参数不同(类型/数量/顺序)必须完全相同(方法名、参数、返回类型)
返回类型可以不同必须相同(或子类返回类型)
访问权限可以不同(比如publicprivate共存)不能比父类更严格(子类 ≥ 父类权限)
目的让同一方法名适应不同参数让子类自定义父类行为

        3、final、finally、finalize的区别?

final 是一个修饰符,用于修饰变量、方法、类等。

  • final修饰变量后,该变量的值就无法改变 → 变成常量
  • final修饰方法后,该方法就无法被重写(覆盖) → 不能被子类重写
  • final修饰类后,这个类就成固定的,无法拥有子类 → 不能被继承(比如String就是final类)

finally用于try-catch,无论是否发生异常,finally里的代码都会执行。

  • 典型用途:关闭文件、释放资源(如数据库连接)。

  • 即使在 try 或 catch 中使用了 return 语句,finally 块仍然会执行。如果 finally 中也有 return 语句,那么 finally 中的 return 会覆盖 try 或 catch 中的返回值。一般来说,是不会在finally中写有return语句,可能会导致原本的返回值或者异常被覆盖。

finalize() 是 Object 类中的一个受保护方法(protected),在对象被垃圾回收(GC)前由 JVM 自动调用。

  • 目的:让开发者有机会在对象销毁前执行资源释放(如关闭文件、断开网络连接等)。

推荐:这里给大家一个推荐 try-with-resources

适用于实现了 AutoCloseable 接口的资源(如文件流、数据库连接)。

try (FileInputStream fis = new FileInputStream("file.txt")) {
    // 自动关闭资源,无需手动调用 close()
} catch (IOException e) {
    e.printStackTrace();
}

        4、Java创建对象的方式有哪些?

        1)使用 new 关键字 - 最常见的方式。

        2)工厂方法 - 封装了对象创建的逻辑。

        3)克隆 - 使用已有对象生成新实例(浅拷贝)

        4)反射 - 动态地创建对象。

        5)序列化与反序列化 - 将对象转换为字节流并恢复。

方式优点缺点典型场景
new简单高效紧耦合具体类常规对象创建
工厂方法解耦,扩展性强需额外代码框架、多态设计
克隆快速复制对象浅拷贝问题原型模式、对象拷贝
反射动态灵活性能差,安全性低框架(如Spring)
反序列化跨网络/持久化恢复对象不调用构造方法,需序列化缓存、RPC通信

异常处理

        1、异常体系结构

        2、try-catch-finally

   try-catch-finally 是处理异常的核心结构,它用于捕获并处理程序运行时抛出的异常,确保即使发生异常也能安全地执行某些操作(如资源释放)。

        3、Error和Exception的区别?

        Error 和 Exception 都是 Throwable 的子类,但它们在 Java 异常体系中代表不同类型的问题

Error表示严重系统错误,应用程序通常无法恢复。Exception表示程序可以处理的异常情况

        4、运行时异常和检查型异常的区别

        在异常体系中,Exception可以分为两大类:运行时异常(非受检异常)和检查型异常(受检异常)。

特性运行时异常(RuntimeException)检查型异常(Checked Exception)
继承关系继承自RuntimeException继承自Exception但不继承RuntimeException
是否强制处理不需要在代码中显式捕获或声明必须在代码中捕获或声明抛出
编译检查编译器不强制检查编译器强制检查

        5、常见的5个RuntimeException和Checked Exception

RuntimeException

  • NullPointerException
    空指针异常,表示程序试图访问为 null 的对象的字段或方法。

  • ArrayIndexOutOfBoundsException
    数组下标越界异常,表示访问了数组中不存在的索引位置。

  • ArithmeticException
    算术运算异常,最常见的是除以 0 的操作。

  • IllegalArgumentException
    非法参数异常,当方法接收到不合法或不合适的参数时抛出。

  • ClassCastException
    类型转换异常,表示将对象强制转换为不兼容的类。

Checked Exception

  • IOException

输入输出异常,文件或流操作中发生错误时抛出

  • SQLException

数据库访问出错时抛出,如 SQL 语法错误、连接失败等。

  • ClassNotFoundException

在运行时找不到指定类的定义。

  • FileNotFoundException

试图打开一个不存在的文件。

  • ParseException

日期、数字等解析格式错误时抛出。

6、代码中,全局异常处理类怎么实现?

Spring Boot 中的全局异常处理通常使用 @ControllerAdvice + @ExceptionHandler

import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.http.ResponseEntity;

@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(BusinessException.class)
    public ResponseEntity<?> handleBusinessException(BusinessException e) {
        return ResponseEntity
                .status(e.getCode())
                .body("业务异常:" + e.getMessage());
    }

    @ExceptionHandler(Exception.class)
    public ResponseEntity<?> handleOtherExceptions(Exception e) {
        return ResponseEntity
                .status(500)
                .body("系统异常:" + e.getMessage());
    }
}

集合框架

        1、Java集合体系结构?

        2、ArrayList和LinkedList的区别?

ArrayList:基于动态数组实现的列表,内存空间连续,类似于会自动扩容的数组,通过索引访问元素,支持随机访问(O(1))。

LinkedList:基于双向链表实现的列表,每个元素通过节点(Node)连接,前驱和后继指针,内存空间不连续,因此不能随机访问,只能从头或尾顺序遍历。

对比点ArrayListLinkedList
查询效率高,O(1),基于索引低,O(n),需要遍历
插入/删除效率慢,O(n),可能需要移动大量元素快,O(1),只需修改指针(已定位节点)
内存使用较少,只存数据较多,每个节点有额外指针开销
随机访问支持不支持
线程安全都不是线程安全的,需要外部同步处理同上 

注:ArrayList的扩容机制

ArrayList 底层是一个可变长度的数组。初始容量默认是 10。当向 ArrayList 添加元素时,如果数组容量不够,就会触发扩容操作。扩容时会创建一个新的更大数组,并将原数组的所有元素复制过去。

默认扩容算法是:

新容量 = 原容量 × 1.5(即原容量 + 原容量 / 2)

        3、HashMap底层实现原理?

HashMap(jdk8后)采用数组+链表+红黑树结构。这是一个双列集合,它允许存在一个null键。

在做插入时,采用哈希算法定位,发生冲突时,采用尾插法形成链表,当链表长度超过8,且数组容量大于等于64就会变成红黑树,然而当树节点数小于等于6,就会退化成链表。

HashMap是一个线程非安全的集合,它的扩容机制:

HashMap的默认长度是 16,它有一个负载因子 0.75。当数组长度达到当前长度的0.75即 当达到length*0.75长度时,就会自动扩容。默认的扩容算法是:

新容量=原容量*2

        4、HashMap和HashTable的区别?

特性HashMapHashtable
线程安全非线程安全线程安全(方法全用synchronized修饰)
性能更高较低(同步开销)
null处理允许1个null键和多个null值不允许null键或null值
初始容量16(2的幂)11(质数)
扩容机制2n(位运算优化)2n+1(较慢)

        5、HashSet如何保证元素不重复?

HashSet 底层是基于 HashMap 实现的,本质上是一个没有 value 的 Map

每个元素都作为 HashMap 的 key 存储,value 是一个固定对象(如 PRESENT)。

当你往 HashSet 添加元素时,它通过 HashMap.put(key, value) 来判断是否重复。判断重复的过程主要依赖:

计算 hash 值

  • 先调用元素的 hashCode() 方法,确定该元素应放在哪个桶(桶 = 哈希表的某个位置)。

equals 比较

  • 如果有哈希冲突(多个元素 hash 值相同),就会遍历该桶内的链表(或红黑树),逐个用 equals() 比较。

只有当 equals() 返回 true,才认为是重复元素,不会加入

        6、线程安全的集合都有哪些?

  • Hashtable

  • Vector

  • Stack

  • ConcurrentHashMap

Collections工具类包

  • Collections.synchronizedMap()

  • Collections.synchronizedList()

  • Collections.synchronizedSet()

  • Collections.synchronizedCollection()

  • Collections.synchronizedSortedMap()

  • Collections.synchronizedSortedSet()

IO流

        1、流的类型都有哪些?

按数据流向分类:输入流和输出流

分类描述字节流基类字符流基类示例
输入流读取数据(读取源数据)InputStreamReaderFileInputStream, BufferedReader
输出流写出数据(写到目的地)OutputStreamWriterFileOutputStream, BufferedWriter

 按数据处理方式分类:字节流和字符流

分类描述抽象基类常见子类示例
字节流以**字节(8位)**为单位,适用于所有类型数据(如图片、视频、音频、二进制文件等)InputStream / OutputStreamFileInputStream, BufferedOutputStream, DataInputStream
字符流以**字符(16位 Unicode)**为单位,适用于文本数据Reader / WriterFileReader, BufferedWriter, InputStreamReader

        2、字节流和字符流的区别?

字节流顾名思义处理时是以字节为单位(1字节 = 8bit);而字符流是以字符为单位。

这二者皆有适用的数据类型,字节流适合处理二进制文件,如:图片、音频、视频等;而字符流适合处理文本数据,如txt、xml文件等

因为字节流是直接操作字节,所以不需要考虑编码;而字符流处理文件,需要考虑到文本的编码格式(UTF-8、GBK)

        3、字节流和字符流的使用场景

字节流适合直接处理字节,所以使用场景:

  • 图片、音频、视频(如 .jpg.mp3.mp4

  • 压缩文件(如 .zip.jar

  • 网络传输(如 Socket 通信)

  • 加密/解密(如 AES 加密数据)

  • 序列化数据(如 ObjectOutputStream 存储对象)

字符流是直接处理文本相关,所以使用场景:

  • 文本文件(如 .txt.csv.xml.json

  • 配置文件(如 .properties.yaml

  • 日志文件(如 .log

        4、Java中有哪些IO模型?

        BIO、NIO、AIO

        5、什么是Java序列化?如何实现?

Java序列化(Serialization)是将Java对象转换为字节序列的过程,以便可以将对象保存到文件中、通过网络传输或在内存中缓存。反序列化(Deserialization)则是将字节序列恢复为Java对象的过程。

要使一个类可序列化,只需实现java.io.Serializable接口:

建议显式声明一个版本ID,以避免类变更导致的兼容性问题

如果不想类中某个属性进行序列化,则可使用关键字transient

        6、BIO、NIO、AIO的区别

多线程基础

        1、创建线程的方式有哪些?

java 中创建线程的主要方式有以下4种:

(1)继承 Thread

class MyThread extends Thread {
    public void run() {
        // 线程执行体
    }
}
new MyThread().start()

(2)实现 Runnable 接口

class MyRunnable implements Runnable {
    public void run() {
        // 线程执行体
    }
}
new Thread(new MyRunnable()).start();
 

(3)实现 Callable 接口 + FutureTask

Callable<Integer> callable = () -> {
    return 123;
};
FutureTask<Integer> task = new FutureTask<>(callable);
new Thread(task).start();
 

(4)使用线程池(推荐)

ExecutorService executor = Executors.newFixedThreadPool(2);
executor.submit(() -> {
    // 任务体
});
 

        2、sleep()和wait()的区别?

区别点sleep()wait()
所属类ThreadObject
是否释放锁不释放锁释放锁
用法场景暂停当前线程一段时间多线程协作(等待唤醒机制)
是否需要同步代码块不需要必须在同步代码块或方法中使用
是否可以被中断可以抛出 InterruptedException可以抛出 InterruptedException

        3、synchronized和Lock的区别?

区别点synchronizedLock(如 ReentrantLock
所属包Java 关键字java.util.concurrent.locks
锁粒度只能加在方法或代码块上可以更灵活控制,支持公平锁/可重入锁等
锁的释放自动释放(同步代码块/方法结束)必须手动释放(lock() / unlock()
是否可中断不可中断可中断(lockInterruptibly()
是否支持超时不支持支持(tryLock(long timeout)
是否可重入是(ReentrantLock 是可重入锁)
是否支持条件变量是(Condition await()/signal()

        4、线程池的参数有哪些?

参数名说明
corePoolSize核心线程数,始终保留,不会回收
maximumPoolSize最大线程数(含核心线程)
keepAliveTime非核心线程的最大空闲时间
unit时间单位(如 TimeUnit.SECONDS)
workQueue任务队列(如 LinkedBlockingQueue
threadFactory创建线程的工厂(一般用默认即可)
handler拒绝策略(如 AbortPolicyCallerRunsPolicy

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值