从零到进阶,系统性学好 Java

一份面向初学者的 Java 编程学习指南——从基础语法多线程并发,知识点循序渐进、代码即看即练,每个板块都支持评论交流,让学习路上不再孤单。

🚀 学习路线 📖 开始学习
6 大板块
系统化知识体系
5 段代码示例
边看边练
5 个评论区
随时提问交流
🧭

学习路线

建议顺序
1

基础语法

变量、运算、流程控制,打好地基

2

面向对象

封装继承多态,理解 Java 核心思想

3

集合框架

List/Set/Map,日常开发最常用

4

异常处理

写出健壮、不轻易崩溃的代码

5

多线程

进阶提升,应对高并发场景

💡学习建议:按 1→5 顺序逐步推进,每学完一个板块就亲手敲一遍示例代码,遇到不懂的直接在对应评论区提问,我会尽量解答。
1

Java 基础语法

入门必学

变量与数据类型

Java 是强类型语言,变量必须先声明类型再使用。基本类型共 8 种:整数(byte、short、int、long)、浮点(float、double)、字符 char、布尔 boolean。引用类型:String、数组、类、接口。不同类型占用内存不同,如 int 占 4 字节,long 占 8 字节。

运算符与表达式

算术(+ - * / %)、赋值(= += -=)、比较(== != > <)、逻辑(&& || !)、三元(条件 ? a : b)。重点:== 比较基本类型值,但比较字符串内容必须用 equals();整数相除结果仍是整数,如 5/2 = 2

流程控制

分支:if/elseswitch。循环:forwhiledo-while。掌握 break 跳出循环、continue 跳过本次、return 结束方法。循环次数已知用 for,未知用 while。for-each 遍历集合最简洁。

HelloWorld.java
public class HelloWorld {
    public static void main(String[] args) {
        // 1. 变量与数据类型
        int age = 18;              // 整型:占用 4 字节
        long big = 10000000000L;   // 长整型:末尾要加 L
        double score = 92.5;      // 浮点:默认是 double
        boolean isOk = true;     // 布尔:只有 true / false
        char grade = 'A';         // 字符:用单引号

        // 2. 运算符
        int a = 10, b = 3;
        int sum = a + b;          // 13
        int mod = a % b;          // 1(取余)
        double avg = a / (double) b; // 3.33(强转)

        // 3. 流程控制
        for (int i = 0; i < 3; i++) {
            if (i == 1) continue;  // 跳过 i=1
            System.out.println("Hello World! 第 " + i + " 次");
        }
    }
}
💡小贴士:源文件名必须与 public 类同名(如 HelloWorld.java),且大小写敏感。字符串比较用 equals() 而不是 ==,这是初学者最容易踩的坑。

💬 评论区 · 基础语法

遇到问题?写段代码分享?在这里和一起学习的小伙伴交流吧。
评论加载中…
2

面向对象编程

核心思想

封装 Encapsulation

private 隐藏内部字段,通过 getter/setter 暴露受控的访问入口。好处:保护数据不被随意修改、内部逻辑变化不影响外部调用方。

继承 Inheritance

子类用 extends 复用父类的字段和方法,用 @Override 重写方法实现差异化行为。单继承多实现:一个类只能 extends 一个父类,但可实现多个接口

多态 Polymorphism

父类引用指向子类对象,运行期根据实际对象动态绑定方法。核心是方法重写向上转型,实现"一个接口,多种实现",大幅提高代码扩展性。

PolymorphismDemo.java
class Animal {
    protected String name;
    public Animal(String name) { this.name = name; }
    public void speak() { System.out.println(name + " 发出声音"); }
}

class Dog extends Animal {
    public Dog(String name) { super(name); }
    @Override
    public void speak() { System.out.println(name + " 汪汪叫"); }
}

class Cat extends Animal {
    public Cat(String name) { super(name); }
    @Override
    public void speak() { System.out.println(name + " 喵喵叫"); }
}

public class PolymorphismDemo {
    public static void main(String[] args) {
        // 多态:父类引用指向不同子类对象,调用同一方法表现不同
        Animal a1 = new Dog("旺财");
        Animal a2 = new Cat("咪咪");
        a1.speak();  // 输出:旺财 汪汪叫
        a2.speak();  // 输出:咪咪 喵喵叫
    }
}
💡小贴士:面向对象三大特征——封装、继承、多态。设计类时坚持单一职责原则(一个类只做一件事)和开闭原则(对扩展开放、对修改关闭),会让代码更好维护。

💬 评论区 · 面向对象

对继承、多态有疑问?在这里提问交流。
评论加载中…
3

集合框架

开发必备

List 列表

有序、可重复。底层实现不同决定了性能差异:ArrayList(数组,查询快 O(1)、尾部增删快)vs LinkedList(链表,任意位置增删快、查询慢 O(n))。开发中 ArrayList 用得最多。

Set 集合

无序、不可重复,用于去重。内部靠 hashCode() + equals() 判断重复。HashSet(哈希,查找 O(1))、LinkedHashSet(保序)、TreeSet(自动排序,红黑树)。

Map 映射

键值对存储,根据 key 的哈希快速定位 value。HashMap 是最常用的集合,键不可重复、允许一个 null 键。TreeMap 按键自动排序。ConcurrentHashMap 多线程安全。

CollectionDemo.java
import java.util.*;

public class CollectionDemo {
    public static void main(String[] args) {
        // 1. List:有序可重复,常用 ArrayList
        List<String> names = new ArrayList<>();
        names.add("小明"); names.add("小红"); names.add("小明");
        System.out.println("List 大小:" + names.size());   // 3(可重复)

        // 2. Set:不可重复,自动去重
        Set<String> unique = new HashSet<>(names);
        System.out.println("Set 大小:" + unique.size());  // 2(去重后)

        // 3. Map:键值对存储
        Map<String, Integer> scores = new HashMap<>();
        scores.put("小明", 98);
        scores.put("小红", 95);

        // 遍历 Map:entrySet() 同时取 key 和 value
        for (Map.Entry<String, Integer> e : scores.entrySet()) {
            System.out.println(e.getKey() + ":" + e.getValue());
        }
    }
}
💡小贴士:遍历 Map 用 entrySet() 同时拿到 key 和 value,比分别用 keySet()+get() 更高效。需要按插入顺序保存时,List 用 ArrayList、Set 用 LinkedHashSet、Map 用 LinkedHashMap。

💬 评论区 · 集合框架

ArrayList 和 LinkedList 怎么选?HashMap 原理不懂?来聊聊。
评论加载中…
4

异常处理

健壮代码

异常体系

所有异常与错误继承自 Throwable。下层分两类:Error(系统级,如内存溢出,无法处理)和 Exception(程序级,可处理)。Exception 又分受检异常(编译期强制处理,如 IOException)和运行时异常(运行期,如 NullPointerException)。

try-catch-finally

try 包裹可能出错的代码,catch 捕获并处理,finally 无论是否异常都会执行(常用于释放资源)。Java 7+ 的 try-with-resources 可自动关闭实现了 AutoCloseable 的资源(如文件流)。

ExceptionDemo.java
public class ExceptionDemo {
    public static void main(String[] args) {
        // 1. try-catch-finally
        try {
            int result = 10 / 0;  // 抛 ArithmeticException
        } catch (ArithmeticException e) {
            System.out.println("除数不能为 0:" + e.getMessage());
        } finally {
            System.out.println("这里无论是否异常都会执行");
        }

        // 2. 自定义异常:继承 Exception(受检)或 RuntimeException(非受检)
        class AgeException extends RuntimeException {
            AgeException(String msg) { super(msg); }
        }
        int age = -1;
        if (age < 0) {
            throw new AgeException("年龄不能为负数");
        }
    }
}
💡小贴士:catch 子句必须按"子类 → 父类"顺序书写(先具体异常,再 Exception),否则子类异常永远捕获不到会编译报错。能用 try-with-resources 就不要手写 close()。

💬 评论区 · 异常处理

受检异常和运行时异常分不清?分享你的踩坑经历吧。
评论加载中…
5

多线程编程

进阶提升

创建线程

三种方式:继承 Thread 类、实现 Runnable 接口(推荐,解耦且可再继承)、实现 Callable 接口(能返回结果)。生产中更常用线程池 ExecutorService 管理线程,避免频繁创建销毁。

线程安全

多线程同时修改共享变量会引发竞态问题。用 synchronizedLock 加锁保证原子性,用 volatile 保证可见性(不保证原子性)。并发工具类:ConcurrentHashMapAtomicIntegerCountDownLatch

ThreadDemo.java
import java.util.concurrent.*;

public class ThreadDemo {
    public static void main(String[] args) {
        // 方式一:实现 Runnable + Lambda(推荐)
        Runnable task = () -> {
            for (int i = 1; i <= 3; i++) {
                System.out.println(Thread.currentThread().getName() + " 执行 " + i);
            }
        };
        new Thread(task, "线程A").start();
        new Thread(task, "线程B").start();

        // 方式二:线程池(生产推荐,避免反复创建线程)
        ExecutorService pool = Executors.newFixedThreadPool(3);
        pool.submit(task);
        pool.submit(task);
        pool.shutdown();
    }
}
💡小贴士:创建线程后必须调用 start() 才会真正启动,直接调 run() 只在当前线程执行。生产环境优先用线程池,不要 new 裸线程;共享变量优先选 AtomicInteger 等并发类,而非手动加锁。

💬 评论区 · 多线程

synchronized 和 volatile 的区别?线程池怎么用?一起讨论。
评论加载中…
🎯

Java 面试题库

高频考点

精选 Java 高频面试题,每题含核心答案代码示例面试官追问。先心里作答,再点开对照。

核心答案:== 比较基本类型、引用类型内存地址equals() 默认继承 Object,行为等同 ==,但 String、Integer 等类重写后改为比较内容。自定义类要比较内容,必须重写 equals(并同步重写 hashCode)。
String a = new String("abc");
String b = new String("abc");
System.out.println(a == b);          // false,两个不同对象
System.out.println(a.equals(b));   // true,内容相同
💡 面试官追问 为什么重写 equals 必须重写 hashCode? 答: hashCode 用于散列定位(如 HashMap)。若不重写,两个 equals 相等的对象 hashCode 不同,会在 Set/Map 中当作两个键,导致去重失效、取值失败。
核心答案:Java 8 及以前 String 用 char[] 存储,Java 9+ 改用 byte[] + coder(LATIN1/UTF16)以节省空间。字符串字面量存在字符串常量池(方法区/堆),new String 则创建堆对象。设计不可变是为安全(可作 HashMap 键、类加载)、常量池复用线程安全
String s1 = "abc";      // 常量池
String s2 = "abc";      // 复用常量池同一对象
String s3 = new String("abc"); // 堆上新建对象
System.out.println(s1 == s2);   // true
System.out.println(s1 == s3);   // false
💡 面试官追问 String 拼接用 + 好还是 StringBuilder? 答: 编译器对常量 + 会在编译期合并;但循环内用 + 会反复 new StringBuilder,性能差。循环拼接用显式 StringBuilder。
核心答案:三者都是字符序列。String 不可变,修改生成新对象;StringBuilder 可变、线程不安全、性能最高StringBuffer 可变、方法加 synchronized、线程安全、性能略低
// 单线程拼接,用 StringBuilder
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
    sb.append(i);
}
String result = sb.toString();
💡 面试官追问 StringBuffer 什么时候才需要? 答: 多个线程同时向同一个缓冲追加内容时。绝大多数业务是单线程,直接上 StringBuilder。
核心答案:int 基本类型存数值,默认 0;Integer 包装类,是对象,默认 null,含工具方法。装箱调 valueOf(),拆箱调 intValue()。最大的坑:Integer 有缓存(-128~127 复用对象),超出范围 == 比较会失败。
Integer x = 100;   // 缓存范围内
Integer y = 100;
Integer p = 200;   // 超出缓存
Integer q = 200;
System.out.println(x == y); // true(缓存复用)
System.out.println(p == q); // false(不同对象!)
System.out.println(p.equals(q)); // true,应这样比
⚠️ 易错点 对象与基本类型混合运算会自动拆箱,若 Integer 为 null 会抛 NullPointerException
核心答案:重载:同类内方法名相同、参数列表不同,编译期多态,与返回值无关;重写:子类方法签名与父类一致,用 @Override,运行期多态。一句话——重载看参数,重写看继承
// 重载:同名不同参
void print(int a) {}
void print(String s) {}

// 重写:子类覆盖父类方法
@Override
void print(int a) { // 签名必须一致 }
💡 面试官追问 重写时子类方法的访问权限能比父类更小吗? 答: 不能。重写规则要求访问权限只能相同或更大(protected→public 可以,public→private 不行),否则编译报错。
核心答案:抽象类:用 abstract,可含普通方法成员变量单继承,表达"是什么 is-a";接口:方法默认抽象(Java 8+ 有 default/static),可多实现,表达"能做什么 like-a"。Java 8 后接口默认方法淡化了两者差异。
// 抽象类:共享状态 + 模板方法
abstract class Animal {
    protected String name;   // 可以有字段
    abstract void speak();
}

// 接口:定义能力契约
interface Flyable {
    void fly();
}
💡 面试官追问 为什么接口的字段默认是 public static final? 答: 接口设计为"纯能力契约",不能有实例状态,字段只能是公开的、共享的、不可变的常量。
核心答案:三者毫无关系。final 修饰符:类不可继承、方法不可重写、变量不可变;finally:异常处理中无论是否异常都执行的代码块;finalize:Object 的回收前回调,已过时,Java 9+ 标记废弃,不要依赖。
final class Config {}    // 类不可被继承

try {
    // 业务代码
} finally {
    // 无论是否异常都执行(释放资源)
}
💡 面试官追问 final 修饰的引用类型,对象内容还能改吗? 答: 能。final 只保证引用不能变(不能再指向别的对象),但对象内部字段仍可修改。
核心答案:ArrayList 底层动态数组,随机访问 O(1),尾部增删快、中间插入 O(n);LinkedList 底层双向链表,头尾增删 O(1)、随机访问 O(n)。LinkedList 每个节点多存前后指针,内存更大。绝大多场景用 ArrayList。
// 需要按下标快速取,用 ArrayList
List<String> list = new ArrayList<>();
String x = list.get(5);  // O(1)

// 频繁在头部插入,用 LinkedList
LinkedList<String> queue = new LinkedList<>();
queue.addFirst("头部");
💡 面试官追问 ArrayList 扩容机制? 答: 默认容量 10,满时扩容为原来的1.5 倍(old + old>>1),新建数组并 Arrays.copyOf 拷贝。频繁扩容有开销,可预估容量用构造器初始化。
核心答案:Java 8+ 底层为数组 + 链表 + 红黑树。put:① 对 key 的 hashCode 做扰动再 & (n-1) 定位数组下标;② 无元素直接放,有元素按链表尾插;③ 链表长度 >8 且数组 ≥64 转红黑树(查询 O(n)→O(log n));④ 默认容量 16、负载因子 0.75,超过阈值扩容为 2 倍并 rehash。
// 常见使用
Map<String, Integer> map = new HashMap<>();
map.put("A", 1);   // 定位下标 → 存放
Integer v = map.get("A"); // 定位下标 → 取/遍历
💡 面试官追问 为什么容量是 2 的幂次? 答: 为了用位运算 hash & (n-1) 替代取模,效率更高,且能充分利用低位、减少哈希碰撞。
核心答案:Hashtable 整体加 synchronized,线程安全但并发效率低,已过时;HashMap 线程不安全但单线程性能最好;ConcurrentHashMap(Java 8+ 用 CAS + synchronized 锁桶)线程安全且并发性能高,是并发首选。
// 并发场景用 ConcurrentHashMap,不要用 Hashtable
Map<String, Integer> c = new ConcurrentHashMap<>();
💡 面试官追问 ConcurrentHashMap 为什么比 Hashtable 快? 答: Hashtable 锁整个 Map,一次只能一个线程写;ConcurrentHashMap 只锁单个桶/节点,不同桶可并发写,读基本无锁。
核心答案:受检异常(继承 Exception 非 RuntimeException):编译期强制 try-catch 或 throws,如 IOExceptionSQLException运行时异常(继承 RuntimeException):编译期不强制,如 NullPointerExceptionArithmeticException。设计上:可预期的外部错误用受检,编程缺陷用运行时。
// 受检异常:必须处理
try {
    new FileInputStream("a.txt");  // IOException,强制 catch
} catch (IOException e) { }

// 运行时异常:可以不处理
int x = 1 / 0;  // ArithmeticException
💡 面试官追问 throw 和 throws 的区别? 答: throw 在方法内部主动抛出异常对象;throws 在方法声明处声明可能抛出的异常,把处理交给调用方。口诀:throw 是,throws 是
核心答案:不一定。以下情况 finally 不执行:① System.exit() 直接退出 JVM;② 发生不可恢复的 Error;③ 线程被 kill。另外,finally 中有 return覆盖 try 的返回值,务必避免。
try {
    return 1;
} finally {
    return 2;  // 返回值被覆盖为 2!
}
⚠️ 易错点 资源释放推荐用 try-with-resources(Java 7+),自动 close,无需手写 finally。
核心答案:volatile 只保证可见性有序性(禁止指令重排),不保证原子性,适合单个状态标志;synchronized 同时保证原子性 + 可见性 + 有序性,是互斥锁,适合复合操作/临界区。
// volatile 适用:标志位
volatile boolean running = true;
while (running) { // 另一个线程改 running 可见 }

// synchronized 适用:复合操作
synchronized (this) {
    count++;  // i++ 是三步操作,需要锁
}
💡 面试官追问 volatile 能替代 synchronized 吗? 答: 不能。volatile 无法保证复合操作(i++)原子性,只能解决可见性。要原子性必须用锁或 AtomicInteger。
核心答案:6 种状态:NEW(创建未启动)→ RUNNABLE(就绪/运行)→ 可进入 BLOCKED(等锁)、WAITING(wait/join)、TIMED_WAITING(sleep/wait(ms))→ 结束为 TERMINATED。核心:start 启动、遇到锁/等待进入阻塞等待、唤醒后回 RUNNABLE。
Thread t = new Thread(() -> {
    try { Thread.sleep(100); } catch (InterruptedException e) {}
});
System.out.println(t.getState()); // NEW
t.start();                       // → RUNNABLE
💡 面试官追问 怎么查看线程当前状态? 答: t.getState() 返回 Thread.State 枚举;生产上用 jstack 查看线程栈和锁等待。
核心答案:sleep 是 Thread 静态方法,不释放锁,到点自动醒,任何地方可调;wait 是 Object 方法,释放锁,必须在 synchronized 内,需 notify/notifyAll 唤醒。
synchronized (lock) {
    lock.wait();   // 释放 lock 锁,等待 notify
}
Thread.sleep(1000);  // 不释放任何锁,睡 1 秒
💡 面试官追问 wait 为什么定义在 Object 而非 Thread? 答: wait 需要操作对象监视器锁,而任意对象都能当锁,所以放在所有对象共同的父类 Object 上。
核心答案:运行时数据区分线程共享线程私有共享——堆(对象、数组,GC 主要区域)、方法区(类信息、常量池,Java 8 后为元空间 Metaspace);私有——虚拟机栈(局部变量、方法调用)、本地方法栈、程序计数器。
// 内存划分(面试背诵版)
// 堆 Heap:存放所有 new 出来的对象
// 栈 Stack:方法调用、局部变量、引用
Object obj = new Object();
//  ↑引用存栈里      ↑对象本身存堆里
💡 面试官追问 栈溢出和堆溢出分别指什么? 答: 递归过深、方法无限调用 → 栈溢出 StackOverflowError;对象太多、堆放不下 → 堆溢出 OutOfMemoryError
👤

关于我

站长主页

刘仕鑫

Java 学习者 · 本站站长

正在从零系统学习 Java,一边学习一边把知识整理成这份网站。希望能帮到和我一样在编程路上摸索的小伙伴,也欢迎大家在评论区多多交流、共同进步。

🛠️我在学的技能

以下是我正在学习和掌握的知识,也是本站覆盖的内容:

Java 基础面向对象集合框架 异常处理多线程HTML / CSSJavaScript前后端协作

🎯我的学习目标

  • 掌握 Java 核心语法与面向对象思想,能独立完成小项目
  • 🚀攻克多线程与并发,理解线程池与锁的原理
  • 📚持续更新本站内容,与大家一起边学边交流

💬 给我留言

想认识我?有学习问题想交流?在这里留言,我会看到的!
留言加载中…