Fullstack 面试复习笔记:Java 基础语法 / 核心特性体系化总结

Fullstack 面试复习笔记:Java 基础语法 / 核心特性体系化总结

上一篇笔记:Fullstack 面试复习笔记:操作系统 / 网络 / HTTP / 设计模式梳理

目前上来说,这个系列的笔记本质上来说,是对不理解的知识点进行的一个梳理,而不是是更细节的八股文备战。可以基于这个蓝图做延伸,去找对应的八股文,但是为了方便更快地过一遍面试准备——我基本上做好一周内要ready的打算,就不包含八股文了……

基础结构 & 语法机制

基础类型(primitive type)

这里简单的回顾一下primitive type的基础,java中有的primitive type有:

类型字节数默认值范围 or 特性
byte10-128 ~ 127
short20-32,768 ~ 32,767
int40-2^31 ~ 2^31-1(默认整数类型)
long80L-2^63 ~ 2^63-1(必须加 L 后缀)
float40.0f单精度(精度约为 7 位)
double80.0d双精度(默认浮点数,约 15 位精度)
char2‘\u0000’Unicode 字符编码(不是 ASCII)
boolean1falsetrue / false
  • 自动装箱 & 拆箱(autuboxing/unboxing)
    • Java会对 -128127 这个区间的数字进行缓存,如:

      Integer a = 127;
      Integer b = 127;
      System.out.println(a == b); // true
      
      Integer a = 128;
      Integer b = 128;
      System.out.println(a == b); // false
      

      除了 byte, booleanfloat/ double 外的其他wrapper clas都适用

    • 装箱对象的比较必须用 .equals(),不要用 ==

常用集合

  • HashMap vs Hashtable vs ConcurrentHashMap(线程安全机制)

    • HashMap非线程安全,性能好,适合单线程或通过 Collections.synchronizedMapConcurrentHashMap 实现安全
    • Hashtable线程安全,但通过方法加锁(synchronized),效率低,已过时
    • ConcurrentHashMap : 线程安全, 最新实现,代替 Hashtable
  • ArrayList vs LinkedList

    重点放在操作复杂度(时间复杂度)上

  • HashSet, TreeSet, TreeMap

    特性HashSetTreeSetTreeMap
    类型Set(不重复元素)Set(有序、不重复元素)Map(有序键值对)
    底层结构HashMapTreeMap(基于红黑树)红黑树(Red-Black Tree)
    是否排序❌ 否(无序)✅ 是(元素有序)✅ 是(key 有序)
    允许 null✅ 允许一个 null 元素✅ 默认支持一个 null 元素(但不可比较)✅ key 允许 null(但排序会报错)
    插入性能🚀 快(O(1) 平均)🐢 较慢(O(log n),因排序)🐢 较慢(O(log n),因排序)
    是否线程安全❌ 否❌ 否❌ 否
  • Comparable vs Comparator

    方式用法场景说明
    Comparable实现类内部排序(默认规则实现 compareTo() 方法,修改类本身,例如:User implements Comparable<User>
    Comparator外部传入排序规则(灵活定制传入 TreeSetTreeMap 构造器,可避免修改类
  • BigDecimal

    BigDecimal bd = new BigDecimal("123.45");
    // intVal = 12345
    // scale = 2
    // => intVal × 10^(-scale) = 123.45
    

对象机制

  • equals/hashCode 原则

    若两个对象通过 equals() 判断为相等,则它们的 hashCode() 也必须相等;否则会破坏集合类(如 HashMapHashSet)的查找一致性

  • Java 内存结构 + 对象引用类型(强/软/弱/虚)

    类型是否参与GC前回收典型用途特点说明
    强引用❌ 不可回收常规对象引用默认引用类型,只有失去所有强引用才会GC
    软引用✅ 内存不足时回收缓存(如图片)有可能在OOM前被GC,适合做内存敏感缓存
    弱引用✅ 下一次GC就回收ThreadLocal等生命周期短,不影响GC回收
    虚引用✅ 随时可回收跟踪对象回收无法通过它访问对象,需配合 ReferenceQueue

异常机制

  • checked vs unchecked

    Checked 异常是受检异常,编译器要求你显式处理(try-catch 或 throws);而 Unchecked 异常是运行时异常可以不处理,但最好处理

    异常类型是否强制捕获继承体系使用场景示例
    Checked✅ 是Exception(但非 RuntimeException受控环境、外部 IO、数据库等IOException, SQLException
    Unchecked❌ 否RuntimeException 及其子类程序逻辑错误、空指针等NullPointerException, IndexOutOfBoundsException
  • try-with-resources

    try-with-resources 是 Java 7 引入的新语法,用于在 try 块中自动关闭资源(实现了 AutoCloseable 接口的对象),避免 finally 中手动 close() 的繁琐与遗漏

    try (BufferedReader reader = new BufferedReader(new FileReader("file.txt"))) {
        String line = reader.readLine();
        System.out.println(line);
    } catch (IOException e) {
        e.printStackTrace();
    }
    

注解机制(Annotation)

  • 基本注解:@Override, @SuppressWarnings

  • 自定义注解

  • 元注解(Retention Policy)

    用于修饰注解本身,控制其生命周期、作用范围、能否继承等

    元注解含义(作用)
    @Retention注解保留到何时(编译时 / 运行时 / 类加载时)
    @Target注解可以放在哪些位置(类 / 方法 / 字段等)
    @Documented是否包含在 Javadoc 中
    @Inherited是否允许子类继承注解
    @Repeatable是否允许注解重复使用(Java 8+)

    提供一个案例简单说明一下:

    @Retention(RetentionPolicy.RUNTIME)  // 注解可以在运行时通过反射读取
    @Target(ElementType.METHOD)          // 注解只能贴在方法上
    public @interface MyCustomAnno {     // 自定义注解本体
        String value();
    }
    
    
  • 常见使用场景:Spring 注解驱动

Java 8+ 核心特性

Lambda 表达式

函数式接口实例 语法糖:简化匿名内部类

// comparator
List<String> names = Arrays.asList("Zhang", "Li", "Wang", "Zhao");

names.sort((a, b) -> a.compareTo(b));
names.sort((a, b) -> b.compareTo(a));

// runnable
Runnable task = () -> System.out.println("Hello from thread!");
Thread thread = new Thread(task);
thread.start();

// forEach
List<String> list = Arrays.asList("apple", "banana", "cherry");

list.forEach(item -> System.out.println("水果:" + item));

函数式接口(Functional Interface)

只包含一个抽象方法的接口,用于支持 Lambda 表达式,使方法行为可以像“函数”一样被传递

接口名参数返回用途示例说明
Runnable无参任务() -> System.out.println("Run")
Supplier<T>T提供一个对象() -> "Hello"
Consumer<T>T消费一个对象(只执行动作)(x) -> System.out.println(x)
Function<T,R>TR接收一个 T 返回一个 R(x) -> x.length()
Predicate<T>T布尔判断某个条件是否成立(x) -> x > 0
BiFunction<T,U,R>T,UR接收两个参数返回一个结果(a,b) -> a + b

出了上面的 Runnable 的例子,这里再提供一个 Comparator 的例子:

@FunctionalInterface
public interface StudentComparator {
    int compare(Student s1, Student s2);

    default void log() {
        System.out.println("Comparing students...");
    }

    static void staticHelper() {
        System.out.println("Static helper inside interface");
    }
}

public class Student {
    String name;
    int age;
    double score;

    public Student(String name, int age, double score) {
        this.name = name;
        this.age = age;
        this.score = score;
    }

    // ...
}

List<Student> students = Arrays.asList(
    new Student("Alice", 20, 85.5),
    new Student("Bob", 22, 90.0),
    new Student("Charlie", 19, 78.0)
);

// 使用 Lambda 表达式来定义比较规则(按年龄升序)
students.sort((s1, s2) -> s1.age - s2.age);

// 也可以封装成函数式接口
StudentComparator byScore = (s1, s2) -> Double.compare(s1.score, s2.score);
students.sort(byScore::compare);

// 对比传统开发,即不使用lambda和function interface的实现
// 方式一:匿名内部类(匿名实现 Comparator)
students.sort(new Comparator<Student>() {
    @Override
    public int compare(Student s1, Student s2) {
        return s1.age - s2.age;
    }
});

// 方式二:单独定义一个类
students.sort(new StudentScoreComparator()); // 比分数

// 单独定义一个类:实现 Comparator<Student>
class StudentScoreComparator implements Comparator<Student> {
    @Override
    public int compare(Student s1, Student s2) {
        return Double.compare(s1.score, s2.score);
    }
}

可以看到,通过lambda+functional interface的方式,代码实现变得更加的简洁可读。

推荐做法是添加 @FunctionalInterface 的注解,这样Java可以更好的做验证。如果不加的话,在interface中只有一个abstract method的情况下,Java也可以自动推导当前interface是 @FunctionalInterface

Stream API

依旧使用上面的 Student 作为案例:

List<Student> students = List.of(
    new Student("Alice", 18, 90),
    new Student("Bob", 20, 75),
    new Student("Carol", 19, 90),
    new Student("David", 20, 85)
);

  • 中间操作:map, filter, sorted, distinct

    // 转为名字列表
    List<String> names = students.stream()
        .map(student -> student.name)
        .collect(Collectors.toList());
    
    // 过滤出成绩 ≥ 85 的学生
    List<Student> topStudents = students.stream()
        .filter(s -> s.score >= 85)
        .collect(Collectors.toList());
    
    // 按年龄升序排序
    List<Integer> distinctScores = students.stream()
        .map(s -> s.score)
        .distinct()
        .collect(Collectors.toList());
    
    // 移除重复分数
    List<Integer> distinctScores = students.stream()
        .map(s -> s.score)
        .distinct()
        .collect(Collectors.toList());
    
    
  • 终端操作:collect, reduce, count

    // 收集成列表
    List<String> names = students.stream()
        .map(s -> s.name)
        .collect(Collectors.toList());
    
    // 统计人数
    long count = students.stream().count();
    
    // 累加所有分数
    int totalScore = students.stream()
        .map(s -> s.score)
        .reduce(0, Integer::sum); // or (a, b) -> a + b
    
  • 分组统计:Collectors.groupingBy, partitioningBy

    虽然分组也是 collect 的一部分,但它使用了 Collectors.groupingBy,语义更复杂,因此单独列出

    // 按分数分组
    Map<Integer, List<Student>> groupedByScore = students.stream()
        .collect(Collectors.groupingBy(s -> s.score));
    
    // 按年龄分组,并统计每组人数
    Map<Integer, Long> ageCountMap = students.stream()
        .collect(Collectors.groupingBy(s -> s.age, Collectors.counting()));
    
    // 按分数分组后,每组提取名字列表
    Map<Integer, List<String>> scoreToNames = students.stream()
        .collect(Collectors.groupingBy(
            s -> s.score,
            Collectors.mapping(s -> s.name, Collectors.toList())
        ));
    

总结一下输出结果

场景代码片段输出类型
转字段列表map(...).collect(toList())List<T>
条件过滤filter(...).collect(toList())List<T>
分组统计groupingBy(..., counting())Map<K, Long>
分组后映射groupingBy(..., mapping(..., toList()))Map<K, List<V>>
规约求和reduce(0, Integer::sum)int
去重字段map(...).distinct()Stream<T>

Optional

  • Optional.of, Optional.empty, orElse, orElseGet, map
  • 避免 NPE、链式调用

举个例子:

public class User {
    private Address address;
    public Optional<Address> getAddress() {
        return Optional.ofNullable(address);
    }
}

public class Address {
    private String city;
    public Optional<String> getCity() {
        return Optional.ofNullable(city);
    }
}

// 传统写法
String city = null;
if (user != null && user.getAddress() != null && user.getAddress().getCity() != null) {
    city = user.getAddress().getCity();
}

// 使用Optional
String city = user.getAddress()
    .flatMap(Address::getCity)   // 注意是 flatMap,因为返回值是 Optional
    .orElse("Unknown");

补充一个常见的错误用法 vs 正确用法:

Optional<String> name = Optional.of("Tom");
if (name.isPresent()) {
    System.out.println(name.get()); // 虽然可以,但违背了 Optional 的设计初衷
}

// 更推荐写成
name.ifPresent(System.out::println);

// 或者
String result = name.orElse("Default");

接口默认方法 & 静态方法

  • default method 用于向后兼容

    interface中可以使用 default 添加新方法,继承原本的interface 的client端不需要实现新的 default 方法,需要使用对应方法的新用户也可以实现 overload

  • static method in interfaces

这里用截图举个例子说明比较简单:

ConcurrentHashMap(Java 8 重构点)

  • Java 8 改成了分段锁 + CAS(Compare-And-Swap,一种乐观锁,比起之前的 ReentrantLock 相比重试更快,也不会抢锁),取消了 Segment

    具体修改包括:

    特性Java 8 做了什么
    ❌ 取消 Segment不再有 Segment 数组,直接用 Node[] table
    ✅ 引入 CAS + synchronized取代 ReentrantLock 的粒度控制
    ✅ 链表转红黑树当链表长度超过阈值(默认 8)转为红黑树,提高查询效率(O(n) → O(log n))
    ✅ 支持 computeIfAbsent()forEach() 等并发友好新方法
    ✅ 更轻量的锁机制每个 bucket(table[i])单独加锁,或使用 CAS,无需全表锁定
  • computeIfAbsent 是高频问点

    • 工作流程:
      1. 如果 key 不存在,则执行 Lambda,生成新值(如 new ArrayList<>());
      2. CASsynchronized(针对单个桶) 安全地插入;
      3. 避免了 putIfAbsent() + get() 的双查找问题。
    • 高频面试陷阱:
      • Lambda 表达式可能会被重复调用(并发争抢下),但只有一个结果会被真正放进去;
      • 所以 Lambda 中逻辑要无副作用(side-effect-free)
  • synchronizedMap 对比

    特性ConcurrentHashMapCollections.synchronizedMap
    锁机制分段+CAS+局部 synchronized粗粒度同步(整个 Map 加锁)
    性能高并发读写表现优异写操作竞争大,性能差
    Null 支持❌ 不支持 key/value 为 null✅ 支持 null
    Java 推荐✅ 推荐🚫 已过时(在并发环境)

JUC

核心线程池类(基础构建块)

  • ExecutorService:线程池接口,支持任务提交、关闭、回收线程资源
  • Future:用于获取异步任务的返回结果,支持取消任务
  • Callable<T>:可返回结果并抛出异常的任务(对比 Runnable

并发工具类(线程同步控制)

  • CountDownLatch:等待多个线程完成,适合“倒计时”场景(如等待所有子任务执行完毕)
  • CyclicBarrier:多个线程在屏障点等待,适合阶段性同步(如并行计算后统一合并)
  • Semaphore:控制并发线程数(如数据库连接池限流)
  • ReentrantLock:可重入锁,支持公平/非公平机制,可精细控制加锁与释放
  • ThreadLocal:为每个线程维护独立变量副本,常用于用户上下文、连接隔离

Java 8+ 并发增强(异步编排)

  • CompletableFuture:链式异步任务编排工具,支持组合多个异步结果,替代传统 Future
  • ForkJoinPool:用于递归任务分割并行计算,Java 8 中 parallelStream() 默认使用
  • parallelStream():简化并行流处理,使用 ForkJoinPool.commonPool() 背后实现

线程池实现与调优点

  • ThreadPoolExecutor:线程池核心实现类,支持核心线程数、最大线程数、队列容量、拒绝策略等参数配置
  • ScheduledExecutorService:定时任务调度器,支持延迟/周期执行任务,替代老旧的 Timer

Java 内存可见性与原子性关键词(高频知识点)

  • volatile:保证变量可见性,不保证原子性(常用于双重检查锁 DCL 中)
  • synchronized:内置锁,保证原子性和可见性,支持对象级与类级加锁

反射 / 动态代理(Reflection & Dynamic Proxy)

基础 API(Class 对象)

  • Class<?> clazz = Class.forName("com.example.MyClass")
  • clazz.getDeclaredFields() / getDeclaredMethods():反射读取字段和方法
  • field.setAccessible(true):访问私有字段
  • 常用于通用框架 / 动态注册 / 对象转换等场景

动态代理(Proxy)

  • Proxy.newProxyInstance(classLoader, interfaces, handler):生成实现接口的代理对象

  • InvocationHandler 接口用于定义方法增强逻辑

    public class MyHandler implements InvocationHandler {
        private final Object target;
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            // before
            Object result = method.invoke(target, args); // 调用真实方法
            // after
            return result;
        }
    }
    
  • 🌟 用于 AOP、RPC Stub 构造、权限校验、日志追踪等

  • 只能代理 接口(类代理推荐用 CGLIB)

SPI 机制(Service Provider Interface)

  • Java 原生插件机制,用于服务发现与自动加载
  • 使用方式:
    • META-INF/services/com.example.InterfaceName 文件中声明实现类
    • 加载方式:ServiceLoader.load(InterfaceName.class)
  • 示例:JDBC 驱动加载、Spring Boot Starter 自动注册、Netty Codec 插件

注解处理器 / 自定义注解

这部分和上面的有一点重复,算是稍微多加一点补充吧……

注解元注解(原注解)

  • @Target:注解可用于哪些位置(如 TYPE, METHOD, FIELD)

  • @Retention:注解保留到哪个阶段(SOURCE、CLASS、RUNTIME)

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public @interface LogExecutionTime {}
    

注解处理器(Annotation Processor)

  • 编译期注解处理工具(APT),可用于代码生成
  • 实现 javax.annotation.processing.Processor 或使用 AbstractProcessor
  • 常见场景:Lombok、MapStruct、Dagger、Spring Configuration Processor
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值