Java常见面试题及答案汇总(2025最新版)

一、Java基础语法与核心特性

1. Java的核心特性有哪些?

答案

  • 跨平台性(Write Once, Run Anywhere):通过JVM(Java虚拟机)实现,字节码文件可在任意支持JVM的操作系统运行;
  • 面向对象(OOP):封装、继承、多态三大核心特性;
  • 安全性:支持沙箱机制、字节码校验、权限控制(如文件IO权限);
  • 健壮性:自动垃圾回收(GC)避免内存泄漏,强类型检查、异常处理机制减少运行时错误;
  • 分布式:支持RMI(远程方法调用)、HTTP协议,便于开发分布式应用;
  • 多线程:内置多线程API,支持并发编程。

2. 基本数据类型与包装类的区别?

答案

维度基本数据类型(如int、float)包装类(如Integer、Float)
本质原始值,无对象属性引用类型,继承Object类
默认值有(如int默认0,boolean默认false)无,默认null
适用场景简单运算、局部变量,效率高集合框架(如List)、泛型、需要null值的场景
缓存机制部分包装类(Integer[-128~127]、Byte、Short等)有常量池缓存

关键考点

  • 自动装箱/拆箱:Java 5+特性,编译器自动完成基本类型与包装类的转换(如int i = new Integer(10)→拆箱,Integer j = 10→装箱);
  • 缓存陷阱:Integer a = 127; Integer b = 127;a == b为true(复用缓存);Integer c = 128; Integer d = 128;c == d为false(新建对象),需用equals()比较值。

3. String、StringBuffer、StringBuilder的区别?

答案
核心差异在于可变性线程安全

  • String:不可变(底层是final修饰的char数组/JDK9+ byte数组),每次修改都会创建新对象,效率低;
  • StringBuffer:可变,线程安全(方法加synchronized锁),适用于多线程环境的字符串拼接;
  • StringBuilder:可变,线程不安全,效率高于StringBuffer,适用于单线程环境的字符串拼接。

底层原理
String的不可变性源于private final char value[](JDK8),final修饰数组引用不可变,且数组无暴露修改接口;StringBuffer和StringBuilder继承AbstractStringBuilder,底层是可变char数组,扩容机制为:默认初始容量16,当长度超过容量时,新容量=原容量×2+2,不足则直接扩容到所需长度。

4. final关键字的三种用法?

答案

  • 修饰类:类不可被继承(如String、Math),子类无法扩展其功能;
  • 修饰方法:方法不可被重写,可防止子类修改父类核心逻辑;
  • 修饰变量:变量不可被重新赋值(基本类型:值不可变;引用类型:引用地址不可变,但对象内容可修改)。

易错点
final int[] arr = {1,2,3}; arr[0] = 4; 合法(数组内容可变);arr = new int[5]; 非法(引用地址不可变)。

5. 接口(Interface)与抽象类(Abstract Class)的区别?

答案

维度接口(Interface)抽象类(Abstract Class)
继承方式多实现(一个类可实现多个接口)单继承(一个类只能继承一个抽象类)
成员变量只能是public static final常量可包含普通变量、静态变量、常量
成员方法JDK8前:只能是抽象方法;JDK8+:支持default/static方法;JDK9+:支持private方法可包含抽象方法、普通方法、静态方法
构造方法有(不能实例化,供子类调用)
设计目的定义行为规范,解耦(如List接口)定义类的模板,复用代码(如HttpServlet)

应用场景

  • 接口:不同类需统一行为但实现不同(如Runnable接口);
  • 抽象类:同类组件共享核心逻辑(如AbstractList封装List的公共方法)。

6. Java异常体系的核心结构?

答案

  • 顶层父类:Throwable,包含两个核心子类:
    1. Error:严重错误(如OutOfMemoryError、StackOverflowError),程序无法恢复,无需捕获;
    2. Exception:可处理的异常,分为:
      • 受检异常(Checked Exception):编译时必须捕获(如IOException、SQLException);
      • 非受检异常(Unchecked Exception):运行时异常(如NullPointerException、ArrayIndexOutOfBoundsException),继承自RuntimeException,无需强制捕获。

异常处理关键字

  • try:包裹可能抛出异常的代码;
  • catch:捕获并处理异常(可多个catch,按异常子类→父类顺序);
  • finally:无论是否抛出异常,都会执行(常用于关闭资源,如流、数据库连接);
  • throw:手动抛出异常(如throw new IllegalArgumentException("参数非法"));
  • throws:声明方法可能抛出的异常,告知调用者。

最佳实践

  • 避免捕获Throwable(包含Error,无法处理);
  • 不要忽略异常(空catch块);
  • 优先使用try-with-resources自动关闭资源(JDK7+,支持实现AutoCloseable接口的类)。

二、Java集合框架

1. 集合框架的核心接口与继承关系?

答案
Java集合框架核心分为两大体系(均位于java.util包):

  • 单列集合(Collection):存储单个元素,核心子接口:
    • List:有序、可重复(如ArrayList、LinkedList、Vector);
    • Set:无序、不可重复(如HashSet、TreeSet、LinkedHashSet);
  • 双列集合(Map):存储键值对(key-value),核心实现类:HashMap、TreeMap、LinkedHashMap、ConcurrentHashMap。

关键特性

  • List:支持索引访问,可通过get(int index)获取元素;
  • Set:基于equals()hashCode()保证元素唯一性;
  • Map:key不可重复(重复会覆盖value),value可重复;JDK8+中Map提供forEach()computeIfAbsent()等便捷方法。

2. ArrayList与LinkedList的区别?

答案

维度ArrayList(数组实现)LinkedList(双向链表实现)
底层结构动态数组(Object[])双向链表(每个节点存储prev、next、value)
访问效率随机访问快(O(1)),通过索引直接定位随机访问慢(O(n)),需遍历链表
增删效率尾部增删快(O(1)),中间增删慢(需移动数组元素,O(n))中间增删快(O(1),只需修改节点指针),尾部增删需遍历到末尾(O(n),可通过last指针优化)
内存占用连续内存,占用少(无额外指针开销)非连续内存,每个节点有额外指针开销
线程安全不安全不安全

应用场景

  • ArrayList:频繁查询、少量增删(如数据展示列表);
  • LinkedList:频繁中间增删、队列/栈实现(如消息队列)。

3. HashMap的底层实现原理(JDK1.7 vs JDK1.8)?

答案
HashMap是基于“哈希表”的Map实现,核心是“数组+链表/红黑树”的结构,目的是平衡查询和增删效率。

JDK1.7实现:
  • 底层:数组(Entry[])+ 单向链表;
  • 存储流程:
    1. 计算key的hashCode() → 经过哈希扰动(hashCode() ^ (hashCode() >>> 16))得到哈希值;
    2. 哈希值 & 数组长度-1 → 定位数组索引(保证索引在数组范围内);
    3. 若索引位置无元素,直接存储;若有元素(哈希冲突),采用“头插法”插入链表。
JDK1.8优化:
  • 底层:数组(Node[])+ 单向链表 + 红黑树(链表长度≥8且数组长度≥64时,链表转为红黑树;链表长度≤6时,红黑树转回链表);
  • 存储流程:哈希扰动逻辑不变,哈希冲突时采用“尾插法”插入链表(避免JDK1.7头插法导致的链表循环问题);
  • 其他优化:
    • 扩容机制:默认初始容量16,负载因子0.75,当元素个数≥容量×负载因子时,触发扩容(新容量=原容量×2);
    • 支持null key和null value(null key的hash值为0,存储在数组索引0位置)。

哈希冲突解决

  • 哈希扰动:减少哈希值的高位忽略问题,提升哈希分布均匀性;
  • 链地址法:冲突元素以链表/红黑树形式存储在同一索引位置。

线程安全问题
HashMap线程不安全,多线程环境下可能出现:

  • JDK1.7:扩容时头插法导致链表循环;
  • JDK1.8:put操作可能覆盖数据。
    解决方案:使用ConcurrentHashMapCollections.synchronizedMap(new HashMap<>())

4. ConcurrentHashMap的线程安全实现(JDK1.7 vs JDK1.8)?

答案
ConcurrentHashMap是HashMap的线程安全版本,核心差异在于锁机制:

JDK1.7实现:
  • 底层:Segment数组 + HashEntry数组 + 链表;
  • 锁机制:分段锁(Segment继承ReentrantLock),每个Segment对应一把锁,仅锁定当前Segment,支持多线程并发访问不同Segment,提高并发度(默认Segment数量16,支持16个线程并发)。
JDK1.8优化:
  • 底层:Node数组 + 链表 + 红黑树(与HashMap结构一致);
  • 锁机制:放弃分段锁,采用“CAS + synchronized”实现线程安全:
    • 无冲突时:CAS操作原子性插入元素;
    • 有冲突时:synchronized锁定当前链表/红黑树的头节点,仅锁定冲突节点所在的链表/树,并发度更高;
  • 其他优化:支持computeIfAbsent()forEach()等原子操作,性能优于JDK1.7。

5. HashSet的实现原理?

答案
HashSet底层依赖HashMap实现,核心逻辑:

  • HashSet的构造方法会创建一个HashMap实例;
  • 存储元素时:add(E e) → 调用HashMap的put(e, PRESENT),其中PRESENT是一个静态空Object(仅占位,不存储实际值);
  • 元素唯一性:依赖HashMap的key不可重复特性(通过equals()hashCode()判断);
  • 特性:无序、不可重复、线程不安全,查询/增删效率O(1)(无哈希冲突时)。

关键考点
自定义对象作为HashSet元素时,必须重写equals()hashCode(),否则无法保证唯一性(默认使用Object类的方法,比较对象地址)。重写原则:

  • 两个对象equals()返回true → hashCode()必须相等;
  • 两个对象hashCode()相等 → equals()不一定返回true(哈希冲突)。

三、Java多线程与并发

1. Java创建线程的三种方式?

答案

方式3:实现Callable接口,重写call()方法(支持返回值和抛出异常),通过FutureTask包装后传入Thread启动;

classMyCallableimplementsCallable<String>{@OverridepublicStringcall()throwsException{return"Callable result";}}// 启动FutureTask<String> future =newFutureTask<>(newMyCallable());newThread(future).start();String result = future.get();// 获取返回值(阻塞直到线程完成)

方式2:实现Runnable接口,重写run()方法,将实例传入Thread类启动;

classMyRunnableimplementsRunnable{@Overridepublicvoidrun(){System.out.println("Runnable running");}}// 启动newThread(newMyRunnable()).start();

方式1:继承Thread类,重写run()方法(线程执行逻辑),调用start()方法启动线程(底层调用start0() native方法创建操作系统线程);

classMyThreadextendsThread{@Overridepublicvoidrun(){System.out.println("Thread running");}}// 启动newMyThread().start();

对比

  • 继承Thread:无法继承其他类(Java单继承),代码简单;
  • 实现Runnable/Callable:可继承其他类,支持多线程共享资源,Callable支持返回值和异常处理。

2. 线程的生命周期与状态转换?

答案
Java线程有6种状态(定义在Thread.State枚举中),状态转换如下:

  • NEW:线程创建后未启动(未调用start());
  • RUNNABLE:线程启动后,处于可运行状态(包含操作系统的“运行中”和“就绪”);
  • BLOCKED:线程等待同步锁(如synchronized未获取锁时);
  • WAITING:线程无限期等待(如调用Object.wait()Thread.join()LockSupport.park(),需其他线程唤醒);
  • TIMED_WAITING:线程限时等待(如调用Thread.sleep(ms)Object.wait(ms)Thread.join(ms),超时自动唤醒);
  • TERMINATED:线程执行完成或异常终止。

核心转换路径
NEWRUNNABLE(start()) → TERMINATED(执行完成);
RUNNABLEBLOCKED(竞争锁失败) → RUNNABLE(获取锁);
RUNNABLEWAITING/TIMED_WAITING(调用等待方法) → RUNNABLE(被唤醒/超时)。

3. synchronized与Lock的区别?

答案

维度synchronized(内置锁)Lock(显式锁,如ReentrantLock)
锁实现JVM层面实现(C++代码)JDK层面实现(Java代码)
锁获取与释放自动获取(进入同步块)、自动释放(退出同步块/异常)手动获取(lock())、手动释放(unlock(),需在finally中执行)
锁类型可重入锁、非公平锁(默认),JDK6+支持偏向锁/轻量级锁/重量级锁升级可重入锁,支持公平锁/非公平锁(构造函数指定)
功能扩展无(仅支持基础同步)支持中断锁(lockInterruptibly())、超时锁(tryLock(ms))、条件变量(Condition)、读写锁(ReentrantReadWriteLock)
性能JDK6+优化后,性能接近Lock高并发场景下性能更优,灵活度高

应用场景

  • synchronized:简单同步场景(如单例模式、简单方法同步),代码简洁,无需手动管理锁;
  • Lock:复杂并发场景(如超时获取锁、中断锁、读写分离),如缓存系统、分布式锁实现。

4. volatile关键字的作用?

答案
volatile是Java提供的轻量级同步机制,核心作用有两个:

  1. 保证可见性:一个线程修改volatile变量后,其他线程能立即看到最新值(禁止CPU缓存,变量读写直接操作主内存);
  2. 禁止指令重排序:编译器和CPU会对指令重排序优化,volatile通过内存屏障(Memory Barrier)阻止重排序(如DCL单例模式中,volatile修饰实例变量防止指令重排导致的空指针)。

局限性

  • 不保证原子性:如volatile int i = 0; i++; 非原子操作(包含读取、加1、写入三步),多线程下可能出现计数错误,需配合synchronized或AtomicInteger使用;
  • 不能替代锁:仅适用于“单写多读”或“状态标记”场景(如volatile boolean flag = false; 控制线程启停)。

经典应用:双重校验锁(DCL)单例模式:

publicclassSingleton{// volatile禁止指令重排,防止instance未初始化完成就被其他线程获取privatestaticvolatileSingleton instance;privateSingleton(){}publicstaticSingletongetInstance(){if(instance ==null){// 第一次校验(无锁,提高效率)synchronized(Singleton.class){// 加锁if(instance ==null){// 第二次校验(防止多线程并发创建) instance =newSingleton();// 禁止重排:分配内存→初始化→赋值}}}return instance;}}

5. 线程池的核心参数与工作原理?

答案
Java线程池核心类是ThreadPoolExecutor,基于“池化思想”减少线程创建/销毁开销,提高并发效率。

核心参数(构造方法):
publicThreadPoolExecutor(int corePoolSize,// 核心线程数(常驻线程,即使空闲也不销毁)int maximumPoolSize,// 最大线程数(核心线程+临时线程的总上限)long keepAliveTime,// 临时线程空闲时间(超过则销毁)TimeUnit unit,// keepAliveTime的时间单位BlockingQueue<Runnable> workQueue,// 任务阻塞队列(核心线程满时,任务入队)ThreadFactory threadFactory,// 线程创建工厂(自定义线程名称、优先级等)RejectedExecutionHandler handler // 拒绝策略(队列和最大线程数都满时,处理新任务))
工作原理:
  1. 提交任务时,若核心线程数未满,创建核心线程执行任务;
  2. 核心线程满时,任务加入阻塞队列;
  3. 队列满时,若未达到最大线程数,创建临时线程执行任务;
  4. 临时线程空闲时间超过keepAliveTime,销毁临时线程;
  5. 队列和最大线程数都满时,执行拒绝策略。
常见拒绝策略:
  • AbortPolicy(默认):直接抛出RejectedExecutionException
  • CallerRunsPolicy:由提交任务的线程(调用者)执行任务;
  • DiscardPolicy:直接丢弃新任务,无异常;
  • DiscardOldestPolicy:丢弃队列中最旧的任务,加入新任务。
常见线程池(Executors工具类):
  • Executors.newFixedThreadPool(n):固定核心线程数和最大线程数(n),队列无界(LinkedBlockingQueue);
  • Executors.newCachedThreadPool():核心线程数0,最大线程数Integer.MAX_VALUE,临时线程空闲60秒销毁,队列同步移交(SynchronousQueue);
  • Executors.newSingleThreadExecutor():核心线程数1,最大线程数1,队列无界,保证任务串行执行;
  • Executors.newScheduledThreadPool(n):核心线程数n,支持定时/延迟执行任务(ScheduledFutureTask)。

注意:阿里巴巴Java开发手册禁止使用Executors创建线程池,原因是:

  • newFixedThreadPool/newSingleThreadExecutor:队列无界,可能导致OOM;
  • newCachedThreadPool:最大线程数无界,可能创建大量线程导致CPU/内存耗尽。
    推荐直接使用ThreadPoolExecutor构造方法,指定合理参数(如核心线程数=CPU核心数±1,队列使用有界队列)。

6. ThreadLocal的原理与内存泄漏问题?

答案
ThreadLocal是线程本地存储工具,允许每个线程拥有独立的变量副本,避免多线程共享变量的并发问题。

原理:
  • 底层结构:每个Thread对象持有一个ThreadLocalMap(ThreadLocal的内部类),ThreadLocalMap的key是ThreadLocal实例(弱引用),value是线程本地变量副本;
  • 核心方法:
    • set(T value):获取当前线程的ThreadLocalMap,将(当前ThreadLocal实例,value)存入;
    • get():获取当前线程的ThreadLocalMap,根据当前ThreadLocal实例获取value,无则调用initialValue()初始化;
    • remove():删除当前线程的ThreadLocalMap中对应的键值对。
内存泄漏问题:
  • 原因:ThreadLocalMap的key是弱引用(WeakReference<ThreadLocal<?>>),当ThreadLocal实例被回收(如外部引用置null),key会变成null,而value是强引用,若线程未结束(如线程池核心线程),value无法被GC回收,导致内存泄漏;
  • 解决方案:
    1. 用完ThreadLocal后调用remove()方法删除value;
    2. 避免使用静态ThreadLocal(生命周期长,易导致内存泄漏);
    3. 线程池场景下,确保任务执行完成后清理ThreadLocal变量。

应用场景

  • 存储线程上下文信息(如用户登录信息、数据库连接、事务对象);
  • 避免方法参数传递(如Spring的RequestContextHolder底层使用ThreadLocal存储HttpServletRequest)。

四、JVM核心原理

1. JVM内存模型(运行时数据区)?

答案
JVM运行时数据区分为5个部分(基于JDK8):

    1. 程序计数器(Program Counter Register):
    • 作用:存储当前线程执行的字节码指令地址(行号),线程切换时恢复执行位置;
    • 特点:线程私有(每个线程一个),无OOM风险(唯一不会抛出OutOfMemoryError的区域)。
    1. 虚拟机栈(VM Stack):
    • 作用:存储线程执行方法时的栈帧(包含局部变量表、操作数栈、方法出口等);
    • 特点:线程私有,栈帧入栈(方法调用)和出栈(方法返回)对应方法执行生命周期;
    • 异常:栈深度超过JVM限制→StackOverflowError(如递归调用无终止);栈扩展时内存不足→OutOfMemoryError
    1. 本地方法栈(Native Method Stack):
    • 作用:与虚拟机栈类似,仅支持Native方法(如Thread.start0())的执行;
    • 特点:线程私有,可能抛出StackOverflowErrorOutOfMemoryError
    1. 堆(Heap):
    • 作用:存储对象实例和数组,是JVM内存最大的区域,也是GC的主要区域;
    • 特点:线程共享,可通过-Xms(初始堆大小)、-Xmx(最大堆大小)配置;
    • 分区(逻辑划分):
      • 年轻代(Young Generation):分为Eden区、Survivor0(S0)区、Survivor1(S1)区,比例默认8:1:1;
      • 老年代(Old Generation):存储存活时间长的对象(年轻代对象多次GC后存活则进入老年代);
      • 元空间(Metaspace,JDK8+):替代永久代,存储类元信息(类名、方法信息、字段信息),使用本地内存,默认无大小限制(可通过-XX:MetaspaceSize-XX:MaxMetaspaceSize配置)。
    • 异常:堆内存不足→OutOfMemoryError: Java heap space;元空间不足→OutOfMemoryError: Metaspace
    1. 方法区(Method Area):
    • 作用:存储类元信息、常量池(String常量池JDK7+移至堆)、静态变量、即时编译后的代码;
    • 特点:线程共享,JDK8前为永久代(PermGen),JDK8后被元空间替代。

2. 垃圾回收(GC)的核心原理?

答案
GC是JVM自动回收堆中无用对象(无引用的对象)的过程,核心目标是释放内存,避免内存泄漏。

1. 垃圾判定算法:
  • 引用计数法:给对象添加引用计数器,引用+1,引用失效-1,计数器为0则判定为垃圾;缺点:无法解决循环引用(如A引用B,B引用A,计数器均为1,无法回收);
  • 可达性分析算法(JVM采用):以“GC Roots”为起点,遍历对象引用链,不可达的对象判定为垃圾;
    • GC Roots包括:虚拟机栈局部变量表中的引用、本地方法栈中的引用、方法区静态变量引用、常量池引用、活跃线程的引用。
2. 常见GC算法:
  • 标记-清除算法(Mark-Sweep):
    • 步骤:标记垃圾对象→清除垃圾对象;
    • 优点:简单高效;
    • 缺点:产生内存碎片,后续大对象分配可能失败。
  • 复制算法(Copying):
    • 步骤:将内存分为两块(如Eden和S0/S1),标记存活对象→复制到另一块内存,清除原内存;
    • 优点:无内存碎片,分配效率高;
    • 缺点:内存利用率低(仅50%),适合年轻代(存活对象少)。
  • 标记-整理算法(Mark-Compact):
    • 步骤:标记存活对象→将存活对象向内存一端移动→清除另一端垃圾;
    • 优点:无内存碎片,内存利用率高;
    • 缺点:移动对象成本高,适合老年代(存活对象多)。
  • 分代收集算法(JVM采用):
    • 原理:根据对象存活时间划分代(年轻代、老年代),不同代采用不同GC算法;
    • 年轻代:存活对象少,采用复制算法;
    • 老年代:存活对象多,采用标记-清除或标记-整理算法。
3. 常见GC收集器:
  • Serial收集器:单线程GC,年轻代采用复制算法,老年代采用标记-整理算法,适合单CPU环境(如客户端应用);
  • Parallel Scavenge收集器:多线程GC,年轻代复制算法,追求高吞吐量(吞吐量=运行用户代码时间/(运行用户代码时间+GC时间)),适合服务器应用;
  • ParNew收集器:Parallel Scavenge的多线程版本,支持与CMS收集器配合;
  • CMS收集器(Concurrent Mark Sweep):老年代GC,基于标记-清除算法,并发收集(与用户线程同时执行),追求低延迟,适合响应时间敏感的应用(如Web应用);缺点:产生内存碎片、并发开销大;
  • G1收集器(Garbage-First):JDK9+默认GC,基于标记-整理算法,将堆划分为多个大小相等的Region,优先回收垃圾多的Region,兼顾吞吐量和低延迟,支持大堆内存(如数十GB);
  • ZGC/Shenandoah收集器:新一代低延迟GC,暂停时间控制在毫秒级以下,支持TB级堆内存。

3. 类加载机制与双亲委派模型?

答案
类加载是将.class字节码文件加载到JVM内存,生成Class对象的过程,核心分为5个阶段:

1. 类加载流程:
  • 加载(Loading):通过类加载器读取.class文件,生成二进制字节流,在堆中创建Class对象;
  • 验证(Verification):校验字节码合法性(如文件格式、语法、语义、符号引用验证),防止恶意字节码;
  • 准备(Preparation):为类静态变量分配内存并设置默认值(如int默认0,boolean默认false),不包含实例变量;
  • 解析(Resolution):将符号引用(如类名、方法名)转换为直接引用(内存地址);
  • 初始化(Initialization):执行类构造器<clinit>()方法(静态变量赋值+静态代码块执行),初始化顺序:父类→子类,静态变量→静态代码块。
2. 类加载器分类:
  • 启动类加载器(Bootstrap ClassLoader):C++实现,加载JDK核心类库(如rt.jar),无父加载器;
  • 扩展类加载器(Extension ClassLoader):Java实现,加载jre/lib/ext目录下的类库;
  • 应用程序类加载器(Application ClassLoader):Java实现,加载应用classpath下的类库(默认类加载器);
  • 自定义类加载器(Custom ClassLoader):继承ClassLoader类,重写findClass()方法,用于加载自定义路径的类(如热部署、加密类)。
3. 双亲委派模型:
  • 核心规则:类加载器加载类时,先委托父加载器加载,父加载器无法加载(找不到类)时,才由自身加载;
  • 流程:应用程序类加载器→扩展类加载器→启动类加载器(顶层),若启动类加载器无法加载,反向逐级尝试加载;
  • 作用:
    1. 避免类重复加载(如java.lang.String仅由启动类加载器加载一次);
    2. 保护核心类库(防止自定义java.lang.String类替换核心类)。
  • 破坏双亲委派模型的场景:
    • 热部署(如OSGi框架):需要不同模块加载同一类的不同版本;
    • JNDI、SPI机制(如JDBC驱动加载):核心类由启动类加载器加载,需加载应用classpath下的驱动类,通过线程上下文类加载器(Thread Context ClassLoader)实现。

五、Spring核心框架

1. Spring IoC的原理与实现?

答案
IoC(Inversion of Control,控制反转)是Spring的核心思想,指将对象的创建、依赖注入(DI)的控制权从应用程序转移到Spring容器,实现解耦。

核心概念:
  • IoC容器:Spring的核心组件(如ApplicationContextBeanFactory),负责管理Bean的生命周期(创建、初始化、销毁)和依赖关系;
  • Bean:IoC容器管理的对象(如Service、Dao层对象);
  • 依赖注入(DI):IoC的具体实现,容器在创建Bean时自动注入其依赖的其他Bean(无需手动new对象)。
依赖注入的三种方式:

字段注入:直接在字段上添加@Autowired注解,代码简洁,但不推荐(无法通过构造方法校验依赖,不利于单元测试);

@ServicepublicclassUserService{@AutowiredprivateUserDao userDao;}

Setter方法注入:通过Setter方法注入依赖,适用于可选依赖;

@ServicepublicclassUserService{privateUserDao userDao;@AutowiredpublicvoidsetUserDao(UserDao userDao){this.userDao = userDao;}}

构造方法注入:通过Bean的构造方法传入依赖,推荐使用(强制依赖,避免空指针);

@ServicepublicclassUserService{privatefinalUserDao userDao;// 构造方法注入(@Autowired可省略,Spring 4.3+支持)publicUserService(UserDao userDao){this.userDao = userDao;}}
IoC容器初始化流程:
  1. 加载配置文件(如XML、注解@Configuration);
  2. 解析配置,扫描Bean定义(如@Component@Service@Repository),注册到BeanDefinitionRegistry;
  3. 实例化Bean(默认单例,懒加载除外);
  4. 依赖注入(DI):通过BeanPostProcessor(后置处理器)自动注入依赖;
  5. 初始化Bean:执行@PostConstruct注解方法、InitializingBean接口的afterPropertiesSet()方法、自定义init-method;
  6. Bean就绪,供应用程序调用;
  7. 容器关闭时,销毁Bean:执行@PreDestroy注解方法、DisposableBean接口的destroy()方法、自定义destroy-method。

2. Spring AOP的原理与应用?

答案
AOP(Aspect-Oriented Programming,面向切面编程)是Spring的核心特性,通过“横切”机制,将日志、事务、权限等通用功能(切面)与业务逻辑解耦,实现代码复用。

核心概念:
  • 切面(Aspect):封装通用功能的类(如日志切面、事务切面),包含通知和切入点;
  • 通知(Advice):切面的具体逻辑(如日志打印、事务提交),分为5种类型:
    • @Before:目标方法执行前执行;
    • @After:目标方法执行后执行(无论是否异常);
    • @AfterReturning:目标方法正常返回后执行;
    • @AfterThrowing:目标方法抛出异常后执行;
    • @Around:环绕目标方法执行(可控制目标方法的执行与否,如事务的开始和提交);
  • 切入点(Pointcut):定义切面作用的目标方法(如“所有Service层的方法”),通过表达式(如execution表达式)指定;
  • 连接点(JoinPoint):目标方法的执行点(如方法调用、异常抛出),是切入点的具体实例;
  • 织入(Weaving):将切面逻辑融入目标方法的过程,Spring AOP默认采用动态代理织入。
实现原理:

Spring AOP基于动态代理实现,分为两种代理方式:

  1. JDK动态代理:
  • 适用场景:目标类实现接口;
  • 原理:通过java.lang.reflect.Proxy类动态生成代理类,代理类实现目标接口,并重写目标方法,在方法中织入切面逻辑;
  • 缺点:仅支持接口代理,无法代理无接口的类。
  1. CGLIB动态代理:
  • 适用场景:目标类未实现接口;
  • 原理:通过CGLIB(Code Generation Library)字节码生成框架,动态生成目标类的子类,重写目标方法,织入切面逻辑;
  • 优点:支持任意类代理(无需接口),性能优于JDK动态代理(创建代理类开销大,但执行效率高)。
应用场景:
  • 日志记录:记录方法调用参数、返回值、执行时间;
  • 事务管理:控制事务的开始、提交、回滚(Spring声明式事务基于AOP实现);
  • 权限校验:方法执行前校验用户权限;
  • 异常处理:统一捕获目标方法的异常并处理。
示例:日志切面
// 切面类@Aspect@ComponentpublicclassLogAspect{// 切入点:所有com.example.service包下的public方法@Pointcut("execution(public * com.example.service..*(..))")publicvoidservicePointcut(){}// 环绕通知@Around("servicePointcut()")publicObjectlogAround(ProceedingJoinPoint joinPoint)throwsThrowable{// 目标方法执行前:打印请求参数String methodName = joinPoint.getSignature().getName();Object[] args = joinPoint.getArgs();System.out.println("方法"+ methodName +"调用,参数:"+Arrays.toString(args));long start =System.currentTimeMillis();Object result = joinPoint.proceed();// 执行目标方法// 目标方法执行后:打印返回值和执行时间long cost =System.currentTimeMillis()- start;System.out.println("方法"+ methodName +"返回值:"+ result +",执行时间:"+ cost +"ms");return result;}}

3. Spring事务管理的原理?

答案
Spring事务管理核心是“声明式事务”(基于AOP)和“编程式事务”(手动编码),其中声明式事务是主流用法。

1. 事务的ACID特性:
  • 原子性(Atomicity):事务是不可分割的最小单位,要么全部执行,要么全部回滚;
  • 一致性(Consistency):事务执行前后,数据完整性保持一致(如转账前后总金额不变);
  • 隔离性(Isolation):多个事务并发执行时,事务之间相互隔离,互不影响;
  • 持久性(Durability):事务提交后,数据修改永久生效(写入磁盘)。
2. 事务隔离级别(Spring支持):
  • DEFAULT:默认隔离级别(依赖数据库,如MySQL默认REPEATABLE READ);
  • READ_UNCOMMITTED:读未提交,最低隔离级别,可能出现脏读、不可重复读、幻读;
  • READ_COMMITTED:读已提交,避免脏读,可能出现不可重复读、幻读(如Oracle默认);
  • REPEATABLE_READ:可重复读,避免脏读、不可重复读,可能出现幻读(如MySQL默认);
  • SERIALIZABLE:串行化,最高隔离级别,避免所有并发问题,性能最低。
3. 事务传播行为(核心,解决事务嵌套问题):

Spring定义了7种传播行为,常用的有:

  • REQUIRED(默认):如果当前存在事务,加入事务;如果没有事务,创建新事务;
  • REQUIRES_NEW:无论当前是否存在事务,都创建新事务(新事务与原事务相互独立,原事务暂停);
  • SUPPORTS:如果当前存在事务,加入事务;如果没有事务,以非事务方式执行;
  • NOT_SUPPORTED:以非事务方式执行,如果当前存在事务,暂停原事务;
  • NEVER:以非事务方式执行,如果当前存在事务,抛出异常。
4. 声明式事务实现(基于AOP):
  • 配置方式:通过@EnableTransactionManagement注解启用事务管理(Spring Boot自动启用);
  • 核心注解:@Transactional(标注在类或方法上,类级别的注解对所有方法生效);
  • 原理:
    1. @Transactional注解被解析为切面,切入点是标注该注解的方法;
    2. 通知逻辑:通过AOP动态代理,在目标方法执行前开启事务,执行后提交事务,异常时回滚事务;
    3. 事务管理器:Spring通过PlatformTransactionManager接口适配不同数据库(如DataSourceTransactionManager适配JDBC,HibernateTransactionManager适配Hibernate)。
5. 事务失效的常见场景:
  • 方法非public修饰(@Transactional仅对public方法生效);
  • 事务方法内部调用(如A方法调用本类的B方法,B方法的@Transactional失效,因为未经过代理类);
  • 异常类型不匹配(默认仅捕获RuntimeExceptionError, checked异常需通过rollbackFor指定);
  • 手动捕获异常未抛出(如try-catch异常但未throw,事务无法感知异常,不会回滚);
  • 传播行为配置错误(如NOT_SUPPORTEDNEVER);
  • 数据源未配置事务管理器(PlatformTransactionManager未被Spring管理)。

六、数据库与MyBatis

1. JDBC的核心操作步骤?

答案
JDBC(Java Database Connectivity)是Java访问数据库的标准API,核心步骤如下:

  1. 加载数据库驱动(JDK8+无需手动加载,DriverManager自动扫描);
  2. 建立数据库连接(通过DriverManager.getConnection(url, username, password));
  3. 创建Statement/PreparedStatement对象(执行SQL语句);
  4. 执行SQL(executeQuery()查询,executeUpdate()增删改);
  5. 处理结果集(查询时通过ResultSet遍历结果);
  6. 关闭资源(ResultSet、Statement、Connection,需在finally中关闭,避免资源泄漏)。

示例代码

publicvoidqueryUser(){Connection conn =null;PreparedStatement pstmt =null;ResultSet rs =null;try{// 1. 加载驱动(MySQL 8.0+驱动类:com.mysql.cj.jdbc.Driver)Class.forName("com.mysql.cj.jdbc.Driver");// 2. 建立连接String url ="jdbc:mysql://localhost:3306/test?useSSL=false&serverTimezone=UTC"; conn =DriverManager.getConnection(url,"root","123456");// 3. 创建PreparedStatement(预编译SQL,防止SQL注入)String sql ="SELECT id, name FROM user WHERE id = ?"; pstmt = conn.prepareStatement(sql); pstmt.setInt(1,1);// 设置参数// 4. 执行查询 rs = pstmt.executeQuery();// 5. 处理结果集while(rs.next()){int id = rs.getInt("id");String name = rs.getString("name");System.out.println("id: "+ id +", name: "+ name);}}catch(ClassNotFoundException|SQLException e){ e.printStackTrace();}finally{// 6. 关闭资源(逆序关闭)try{if(rs !=null) rs.close();if(pstmt !=null) pstmt.close();if(conn !=null) conn.close();}catch(SQLException e){ e.printStackTrace();}}}

关键考点

  • PreparedStatement vs Statement:PreparedStatement支持预编译SQL、参数化查询,防止SQL注入,性能更优;Statement不支持参数化,存在SQL注入风险;
  • SQL注入:如SELECT * FROM user WHERE + name + "'",若name传入' OR '1'='1,则SQL变为SELECT * FROM user WHERE OR '1'='1',查询所有用户;PreparedStatement通过参数绑定避免该问题。

2. MyBatis的核心组件与工作原理?

答案
MyBatis是持久层框架,简化JDBC操作,通过XML或注解配置SQL语句,无需手动编写JDBC代码。

核心组件:
  • SqlSessionFactory:MyBatis核心工厂类,通过SqlSessionFactoryBuilder读取配置文件(mybatis-config.xml)创建,线程安全;
  • SqlSession:会话对象,封装数据库连接和事务管理,线程不安全(每次请求创建新实例);
  • Mapper接口:自定义DAO接口,MyBatis通过动态代理生成实现类,关联XML/注解中的SQL;
  • Mapper.xml:配置SQL语句、参数映射、结果集映射;
  • Configuration:MyBatis全局配置对象,存储核心配置(如数据源、事务管理器、Mapper注册)。
工作原理:
  1. 加载配置文件:SqlSessionFactoryBuilder读取mybatis-config.xml和Mapper.xml,解析配置信息(数据源、SQL语句、映射规则);
  2. 创建SqlSessionFactory:通过解析后的配置信息创建SqlSessionFactory(单例模式);
  3. 创建SqlSession:SqlSessionFactory调用openSession()创建SqlSession,默认不自动提交事务;
  4. 获取Mapper代理对象:SqlSession调用getMapper(Mapper接口.class),通过动态代理生成Mapper接口的实现类;
  5. 执行SQL:调用Mapper接口方法,MyBatis根据方法名匹配Mapper.xml中的SQL语句,执行JDBC操作;
  6. 处理结果集:MyBatis自动将ResultSet映射为Java对象(根据resultType/resultMap配置);
  7. 提交/回滚事务:执行完成后,SqlSession调用commit()提交事务或rollback()回滚事务;
  8. 关闭SqlSession:释放资源。
核心配置(mybatis-config.xml):
<configuration><!-- 环境配置(数据源、事务管理器) --><environmentsdefault="development"><environmentid="development"><transactionManagertype="JDBC"/><!-- 事务管理类型:JDBC/MANAGED --><dataSourcetype="POOLED"><!-- 数据源类型:POOLED(连接池)/UNPOOLED/JNDI --><propertyname="driver"value="com.mysql.cj.jdbc.Driver"/><propertyname="url"value="jdbc:mysql://localhost:3306/test?useSSL=false"/><propertyname="username"value="root"/><propertyname="password"value="123456"/></dataSource></environment></environments><!-- 注册Mapper.xml --><mappers><mapperresource="com/example/mapper/UserMapper.xml"/></mappers></configuration>
Mapper.xml示例:
<mappernamespace="com.example.mapper.UserMapper"><!-- 结果集映射(数据库字段→Java对象属性) --><resultMapid="UserResultMap"type="com.example.entity.User"><idcolumn="id"property="id"/><resultcolumn="name"property="name"/><resultcolumn="age"property="age"/></resultMap><!-- 查询用户 --><selectid="selectUserById"parameterType="int"resultMap="UserResultMap"> SELECT id, name, age FROM user WHERE id = #{id} </select><!-- 新增用户 --><insertid="insertUser"parameterType="com.example.entity.User"> INSERT INTO user (name, age) VALUES (#{name}, #{age}) </insert></mapper>

3. MyBatis的一级缓存与二级缓存?

答案
MyBatis提供缓存机制,减少数据库查询次数,提升性能,分为一级缓存和二级缓存。

一级缓存(SqlSession级别,默认开启):
  • 作用范围:同一个SqlSession内,多次执行相同的SQL查询(参数相同),仅第一次查询数据库,后续从缓存获取结果;
  • 实现原理:SqlSession内部维护一个HashMap,key为缓存键(由SQL语句、参数、RowBounds、环境等组成),value为查询结果;
  • 失效场景:
    • 执行insert/update/delete操作(会清空一级缓存);
    • 调用SqlSession.clearCache()手动清空;
    • SqlSession关闭或提交事务。
二级缓存(Mapper级别,默认关闭):
  • 作用范围:同一个Mapper接口(命名空间)下,多个SqlSession共享缓存;
  • 启用方式:
    1. 在mybatis-config.xml中开启全局缓存(默认开启,可省略):<setting name="cacheEnabled" value="true"/>
    2. 在Mapper.xml中添加<cache/>标签(启用当前Mapper的二级缓存);
  • 实现原理:每个Mapper接口对应一个Cache对象,SqlSession查询后将结果存入二级缓存,其他SqlSession查询相同SQL时,先从二级缓存获取;
  • 注意事项:
    • 缓存的对象必须实现Serializable接口(二级缓存可能序列化存储);
    • 执行insert/update/delete操作会清空当前Mapper的二级缓存;
    • 可通过useCache="false"(查询语句)或flushCache="true"(增删改语句)控制缓存行为。

缓存查询顺序:二级缓存 → 一级缓存 → 数据库。

七、设计模式与性能优化

1. 单例模式的几种实现方式与线程安全?

答案
单例模式确保一个类仅有一个实例,并提供全局访问点,常用实现方式如下:

1. 饿汉式(线程安全,非懒加载):
publicclassSingleton{// 类加载时初始化实例(饿汉式)privatestaticfinalSingleton instance =newSingleton();// 私有构造方法,禁止外部实例化privateSingleton(){}// 提供全局访问方法publicstaticSingletongetInstance(){return instance;}}
  • 优点:简单高效,类加载时初始化,天然线程安全;
  • 缺点:非懒加载,类加载时即创建实例,若实例未被使用,浪费内存。
2. 懒汉式(线程不安全→线程安全优化):
  • 优点:懒加载(使用时才创建实例),线程安全,性能优;
  • 关键:volatile关键字必须加,防止instance = new Singleton()指令重排(分配内存→初始化→赋值),导致其他线程获取到未初始化的实例。

优化版(双重校验锁DCL,线程安全,懒加载):

publicclassSingleton{// volatile禁止指令重排privatestaticvolatileSingleton instance;privateSingleton(){}publicstaticSingletongetInstance(){if(instance ==null){// 第一次校验(无锁,提高效率)synchronized(Singleton.class){// 加锁if(instance ==null){// 第二次校验(防止多线程并发创建) instance =newSingleton();}}}return instance;}}

基础版(线程不安全,多线程下可能创建多个实例):

publicclassSingleton{privatestaticSingleton instance;privateSingleton(){}// 线程不安全:多线程同时进入if条件,创建多个实例publicstaticSingletongetInstance(){if(instance ==null){ instance =newSingleton();}return instance;}}
3. 静态内部类式(线程安全,懒加载,推荐):
publicclassSingleton{privateSingleton(){}// 静态内部类,类加载时不初始化privatestaticclassSingletonHolder{privatestaticfinalSingleton instance =newSingleton();}// 调用getInstance()时,才加载SingletonHolder,初始化实例publicstaticSingletongetInstance(){returnSingletonHolder.instance;}}
  • 优点:懒加载(静态内部类按需加载),线程安全(类加载机制保证),代码简洁,无锁开销;
  • 原理:静态内部类的加载时机是在第一次被引用时,类加载过程是线程安全的,确保实例仅创建一次。
4. 枚举式(线程安全,防反射/序列化,最佳实践):
publicenumSingleton{ INSTANCE;// 枚举类的方法publicvoiddoSomething(){System.out.println("Singleton enum");}}
  • 优点:
    1. 线程安全:枚举类的实例在类加载时创建,天然线程安全;
    2. 防反射:枚举类的构造方法被编译器私有化,无法通过反射创建实例;
    3. 防序列化:枚举类默认实现Serializable,序列化时不会创建新实例;
  • 缺点:非懒加载,类加载时即创建实例。

2. Java性能优化的常见手段?

答案
Java性能优化需从“代码层面、JVM层面、数据库层面、架构层面”多维度入手,核心目标是提升响应速度、降低资源消耗。

1. 代码层面优化:
  • 集合使用优化:
    • ArrayList初始化时指定初始容量(避免频繁扩容);
    • 频繁增删用LinkedList,频繁查询用ArrayList;
    • 避免在循环中使用ArrayList.add(index, element)(O(n)复杂度);
  • 字符串优化:
    • 频繁拼接用StringBuilder(单线程)/StringBuffer(多线程),避免String拼接(创建大量临时对象);
    • 常量字符串用String.intern()复用常量池对象;
  • 循环优化:
    • 减少循环内的对象创建(如for (int i = 0; i < list.size(); i++) → 先获取size:int size = list.size(); for (int i = 0; i < size; i++));
    • 避免循环内的复杂计算(如方法调用、表达式计算);
  • 避免空指针:使用Objects.requireNonNull()、Optional类,减少null判断;
  • 资源管理:使用try-with-resources自动关闭流、数据库连接,避免资源泄漏。
2. JVM层面优化:
  • 堆内存配置:合理设置-Xms(初始堆)和-Xmx(最大堆),建议两者相等(避免频繁扩容),堆大小一般为物理内存的1/2~1/3;
  • 年轻代优化:调整-XX:NewRatio(年轻代与老年代比例,默认2:1)、-XX:SurvivorRatio(Eden与S0/S1比例,默认8:1),根据应用对象存活时间调整;
  • GC收集器选择:
    • 高吞吐量场景:使用Parallel Scavenge收集器;
    • 低延迟场景:使用G1/ZGC收集器;
  • 逃逸分析:启用-XX:+DoEscapeAnalysis(默认启用),JVM自动分析对象是否逃逸,未逃逸的对象可分配在栈上(减少GC压力)。
3. 数据库层面优化:
  • 索引优化:给查询频繁的字段建立索引(如WHERE、JOIN、ORDER BY字段),避免过度索引(影响增删效率);
  • SQL优化:
    • 避免SELECT *,只查询需要的字段;
    • 避免WHERE子句中使用函数(如WHERE DATE(create_time) = '2025-01-01',导致索引失效);
    • 避免JOIN过多表(建议不超过3张表),大表JOIN用分页;
    • 批量操作替代循环单条操作(如MyBatis的batchInsert);
  • 连接池优化:合理设置数据库连接池大小(如HikariCP的maximum-pool-size,建议为CPU核心数×2+1),避免连接泄漏。
4. 架构层面优化:
  • 缓存引入:使用Redis、Ehcache等缓存热点数据(如用户信息、配置数据),减少数据库查询;
  • 异步处理:将耗时操作(如日志打印、邮件发送)异步化(使用线程池、消息队列),提升响应速度;
  • 负载均衡:通过Nginx、LVS等实现多服务器负载均衡,分散请求压力;
  • 分布式部署:将应用拆分为微服务,按业务模块部署,提高并发处理能力。

八、总结

本文覆盖Java面试核心考点,从基础语法、集合框架、多线程、JVM、Spring、数据库到设计模式与性能优化,每个模块均包含高频面试题、详细答案及核心考点解析,兼顾理论深度与实战应用。

面试时,除了记忆答案,更要理解底层原理(如HashMap的哈希冲突解决、Spring AOP的动态代理、JVM的GC机制),并结合项目经验说明实际应用场景(如线程池在项目中的配置、事务失效的排查过程)。建议重点掌握多线程并发、JVM、Spring核心原理等高级考点,这些是区分初级与中高级开发者的关键。

Read more

一篇最全Python 爬虫超详细讲解(零基础入门,适合小白)

一篇最全Python 爬虫超详细讲解(零基础入门,适合小白)

爬虫是指通过编程自动从网页上获取信息的技术.想象你平时打开网页,右键点击 “查看源代码”,那些你看到的HTML代码就是网页的结构,而爬虫就像一位帮你阅读这些网页内容的“机器人”. 本文将详细讲解如何从零开始编写一个Python爬虫,即使是完全没接触过编程的朋友也能理解. 这里插播一条粉丝福利,如果你正在学习Python或者有计划学习Python,想要突破自我,对未来十分迷茫的,可以点击这里获取最新的Python学习资料和学习路线规划(免费分享,记得关注)   一、爬虫的基本流程 1. 发送请求:爬虫向目标网页发送请求,获取网页内容. 2. 解析网页:从返回的网页内容中提取你需要的信息. 3. 保存数据:将提取到的信息保存到文件或数据库中,以便后续分析. 二、常用爬虫库 在Python中,有两个非常流行的库用于爬虫开发: * requests:用于发送网络请求,获取网页内容. * BeautifulSoup:用于解析网页内容,提取需要的数据. 1. 安装库 首先,你需要安装这两个库.在命令行中执行以下命令: pip install requests beau

By Ne0inhk

VS Code 中的 Python 代码格式化插件

在 VS Code 中,有几款非常出色的 Python 代码格式化插件可以帮助你保持代码的整洁与规范。下面这个表格整理了目前主流的几款工具,你可以根据它们的特点进行选择。 工具名称核心特点风格理念推荐适用场景Black开箱即用,几乎无需配置;强制统一的代码风格,可预测性强。“无妥协”的格式化器。它决定格式,讨论空间小,保证所有代码风格一致。团队协作项目;希望零配置快速上手的开发者;追求极简和一致性。autopep8基于 PEP 8 规范,主要修复代码风格问题(如缩进、空格)。相对保守,专注于修复而非重新排版。希望代码严格遵循 PEP 8;对现有代码进行温和的格式化修复。yapf高度可定制,可以模仿多种代码风格;格式化策略更“激进”,会重新排版代码。“自成风格”。目标是通过调整代码来达到最佳可读性,而非严格遵循某一规范。需要高度自定义格式化规则;项目有特殊的代码风格要求。 🔧 如何安装与配置 选好工具后,只需简单几步就能在 VS Code 中启用它们。

By Ne0inhk
【测试基础】Python 核心语法,一篇搞定测试脚本开发基础

【测试基础】Python 核心语法,一篇搞定测试脚本开发基础

🔥个人主页: 中草药  🔥专栏:【Java】登神长阶 史诗般的Java成神之路 本文不做Python以及Pycharm安装的详细教程,请大家自行查阅资料,或到官网去下载         Python作为一门 “优雅且强大” 的编程语言,Python 凭借易上手、用途广的特点,成为很多人入门编程的首选。无论是数据分析、人工智能,还是 Web 开发、自动化脚本,Python 都能胜任。但想要用好 Python,扎实的基础语法是关键 —— 本文将结合系统的语法知识,从 “计算器” 级别的简单运算,到数据持久化的文件操作,带你一站式吃透 Python 核心语法,让你看完就能上手写代码! 变量与数据类型:程序的"原材料仓库"         变量就像快递盒,用来装不同类型的数据;数据类型则是快递盒上的标签,告诉我们里面装的是文件、水果还是电子产品。类型系统其实是在对变量进行 "归类"

By Ne0inhk
【Python基础:语法第三课】Python 函数详解:定义、参数、返回值与作用域

【Python基础:语法第三课】Python 函数详解:定义、参数、返回值与作用域

🎬 个人主页:艾莉丝努力练剑 ❄专栏传送门:《C语言》《数据结构与算法》《C/C++干货分享&学习过程记录》 《Linux操作系统编程详解》《笔试/面试常见算法:从基础到进阶》《Python干货分享》 ⭐️为天地立心,为生民立命,为往圣继绝学,为万世开太平 🎬 艾莉丝的简介: 文章目录 * 1 ~> 什么是函数? * 1.1 函数的概念 * 1.2 代码示例 * 1.2.1 代码示例:求数列的和,不使用函数 * 1.2.2 代码示例:求数列的和,使用函数 * 1.3 最佳实践:针对上面的示例 * 2 ~> 语法格式 * 2.

By Ne0inhk