Java 核心知识点与实战应用解析
简介:
我梳理的这些内容涵盖了 Java 众多核心知识点。包括 final 关键字的作用(修饰类、方法、变量的特性);重载与重写的区别;反射机制的定义、优缺点及项目中的应用(如结合自定义注解处理数据、框架底层实现)。还涉及 String、StringBuffer、StringBuilder 的差异;常见集合类及线程安全类,ArrayList 与 LinkedList 的区别;HashMap 的实现原理、put 流程、扩容机制,以及 ConcurrentHashMap 的底层实现。线程相关知识中,创建线程的四种方式,Runnable 与 Callable 的区别,加锁方式(synchronize
- Final 的作用:被 final 修饰的类没法被继承,被 final 修饰的方法不能被重写,被 final 修饰的变量其引用不可改变,但引用指向的内容能改。比如一个 final 数组,数组里的元素是可以修改的。
- 重载(Overload)和重写(Override):重载发生在同一个类中,方法名相同但参数列表(类型、个数、顺序)不同,和返回值、访问修饰符无关,没法通过返回类型区分。重写是在父子类中,方法名和参数列表必须相同,返回值要小于等于父类,抛出的异常小于等于父类,访问修饰符大于等于父类,父类 private 方法不能被重写。
- 反射机制:就是在运行时,能知道任意类的所有属性和方法,还能调用任意对象的任意方法。优点是能动态加载类,提高代码灵活度;缺点是性能差,比直接代码慢很多。项目中常用反射 + 自定义注解,比如前后端交互时,后端 Long 类型返回前端会精度丢失,我们自定义 @IdEncrpt 注解,通过反射获取类的属性,若有该注解就把属性值转成字符串;整合 EMQ 时,自定义 @Topic 注解作用在类上,反射获取类的字节码和注解里的主题,用策略模式分发不同主题的消息。而且 Mybatis、Spring 等框架和 common-utils、hutool 工具库都大量用了反射。
- String、StringBuffer、StringBuilder 的区别:String 是不可变的,因为用字符数组保存字符串;StringBuffer 和 StringBuilder 是可变的。线程安全方面,String 是常量所以安全,StringBuffer 方法加了同步锁所以安全,StringBuilder 没加所以不安全。性能上,String 每次改变都生成新对象,效率低;StringBuffer 操作自身,比 String 好;StringBuilder 比 StringBuffer 效率更高。
- 常见集合类:Map 和 Collection 是所有集合的父接口。Collection 的子接口有 Set 和 List,Set 实现类有 HashSet、TreeSet 等,List 有 ArrayList、LinkedList 等。Map 实现类有 HashMap、TreeMap、Hashtable、ConcurrentHashMap 等。线程安全的类有 Vector(比 ArrayList 多 synchronized,效率低)、HashTable(比 HashMap 多 synchronized)、ConcurrentHashMap(高效线程安全)。
- ArrayList 和 LinkedList 的区别:ArrayList 是动态数组实现,LinkedList 是双向链表实现。随机访问 ArrayList 效率高,LinkedList 要移动指针遍历。非首尾增删 LinkedList 效率高,因为 ArrayList 会影响其他元素下标。LinkedList 更占内存,因为节点还存前后引用。两者都线程不安全。
- HashMap 的实现原理:是 “链表散列”(数组 + 链表),JDK1.8 后链表长度超 8 会转红黑树,红黑树节点数≤6 退回链表。put 时用 key 的 hashCode 算数组下标,hash 冲突时,key 相同就覆盖,不同就放链表。put 流程:先判数组是否为空,空就扩容;算下标,数组位置为空直接放,不为空就判 key 是否相同,相同覆盖;是红黑树就插入树中,否则遍历链表,长度超 8 转树,有相同 key 就覆盖;最后判 size 是否超阈值,超就扩容。扩容机制:JDK1.8 中,当键值对数量超阈值(0.75 * 容量)时扩容 2 倍,元素位置要么在原位置,要么在原位置 + 旧容量,通过 (e.hash & oldCap) 是否为 0 判断,不用重新算 hash。
- ConcurrentHashMap 底层实现:是线程安全的高效 Map。JDK1.7 底层用分段的数组 + 链表,分段锁保证安全;JDK1.8 用数组 + 链表 / 红黑树,靠 CAS 和 synchronized(只锁链表或红黑树首节点)实现,效率更高。
- 创建线程的四种方式:继承 Thread 类、实现 Runnable 接口、实现 Callable 接口、使用匿名内部类。Runnable 的 run 方法无返回值,只能抛运行时异常;Callable 的 call 方法有返回值,能抛异常,配合 Future、FutureTask 获取结果,且 get 方法会阻塞主进程。
- 加锁方式:synchronized 是 Java 内置关键字,可给类、方法、代码块加锁,不用手动获取和释放锁,异常会自动释放,不会死锁,但没法知道是否获取到锁。Lock 是 Java 类,只能给代码块加锁,需手动加锁和释放锁,否则可能死锁,还能知道是否成功获取锁。
- 线程池队列已满的情况:若用无界队列(如 LinkedBlockingQueue),会继续加任务到队列;若用有界队列(如 ArrayBlockingQueue),队列满了会先增加线程到 maximumPoolSize,还满就用拒绝策略(默认 AbortPolicy)。项目中常用线程池处理优先级低的业务,比如搜索历史记录异步保存、用户行为数据异步入库;还用于大量任务,如 1000w 数据统计,10 个线程各算 100w 再合并结果,像每天晚上的运营统计数据、售货机补货数据计算。
- 线程池的种类:newCachedThreadPool(可缓存,灵活回收空闲线程)、newFixedThreadPool(定长,控制最大并发数)、newScheduledThreadPool(定长,支持定时及周期性任务)、newSingleThreadExecutor(单线程,按顺序执行任务)。核心参数有 corePoolSize(核心线程数)、maximumPoolSize(最大线程数)、keepAliveTime(线程保持时间)、unit(时间单位)、workQueue(阻塞队列)、threadFactory(线程工厂)、handler(拒绝策略)。核心线程数配置:IO 密集型任务约为 CPU 核心数 * 2-3 倍;计算密集型任务约为 CPU 核心数 + 1。执行原理:先判断核心线程是否空闲,不空闲就放队列;队列满了就增加线程到 maximumPoolSize;还满就用拒绝策略。
- 保证 T1、T2、T3 按顺序执行:可以用 join 方法,让后执行的线程等待前一个线程完成。
- JVM 的组成:包含类加载器、执行引擎两个子系统,以及运行时数据区、本地接口两个组件。类加载器根据全限定名把 class 文件加载到方法区;执行引擎执行 classes 中的指令;本地接口与 native 库交互;运行时数据区就是 JVM 内存。Java 代码执行流程:编译器转字节码→类加载器加载到方法区→执行引擎转系统指令→CPU 执行,期间调用本地接口。
- JVM 运行时数据区:程序计数器是当前线程执行的字节码行号指示器,线程私有,支持分支、循环等功能。Java 虚拟机栈中,每个方法执行时会创建栈帧,存局部变量表等,线程私有。本地方法栈为虚拟机调用 Native 方法服务,线程私有。Java 堆是最大的内存区域,所有线程共享,几乎所有对象实例都在这分配。方法区存类信息、常量、静态变量等,线程共享。
- 类加载器及双亲委派模型:类加载器有启动类加载器(加载核心类库,无法被 Java 程序直接引用)、扩展类加载器(加载扩展库)、系统类加载器(按 CLASSPATH 加载,可通过 ClassLoader.getSystemClassLoader () 获取)、用户自定义类加载器(继承 ClassLoader)。双亲委派模型是指类加载器收到请求后,先委派父类加载器加载,父类无法加载时自己再加载,避免类重复加载。
- JDK8 新特性:有函数式接口、方法引用、stream 流、optional 等。