你已经会编程,所以这门课不是「学编程」,是学 Java 的规矩。
每节都写:Java 怎么做 → 和 C++/Python 差在哪 → 考试怎么考。
配合《java-知识点》(章节索引)和《形象版-java》(比喻)一起用。
| C++ | Python | Java | |
|---|---|---|---|
| 运行方式 | 编译成机器码 | 解释执行 | 编译成字节码,JVM 解释/JIT |
| 内存 | 手动 new/delete | 引用计数 + GC | 自动 GC(你不用管) |
| 类型 | 强类型、可隐式转换多 | 动态类型 | 强类型、静态检查,转换要显式 |
| 一切皆对象? | 否 | 是 | 否(8 种基本类型不是对象,其余都是) |
| 多继承 | 支持类多继承 | 支持 | 不支持,用接口多实现 |
一句话:Java = 去掉指针和手动内存管理的 C++ + 强制静态类型的 Python。
Hello.java --javac--> Hello.class --java--> 运行
(源码) (字节码) (JVM 执行)
javac 是编译器(JDK 才有),java 是启动器(JRE 就够)
public class Hello { // 文件名必须 Hello.java
public static void main(String[] args) {
System.out.println("hi"); // C++: cout << "hi" << endl;
}
}
; 结尾;代码块 {}#include,用 import java.util.*;| Java | 字节 | 范围/说明 | C++ 对应 |
|---|---|---|---|
| byte | 1 | -128~127 | char / int8_t |
| short | 2 | ±3.2万 | short |
| int | 4 | ±21亿 | int |
| long | 8 | 后面要加 L | long long |
| float | 4 | 要加 f | float |
| double | 8 | 默认小数类型 | double |
| char | 2 | Unicode,不是 ASCII | wchar_t |
| boolean | 1 | 只有 true/false | bool |
⚠️ Java 没有无符号类型(没有 unsigned);char 是 2 字节,能存中文。
int a = 10;
long b = 100L; // 不加 L 也能过,但超范围会错
float f = 3.14f; // 不加 f 编译报错
char c = '中'; // 合法
boolean ok = true; // 不能写 1
和 C++ 的关键差异:C++ 里 if (1) 合法,Java 里 if 只接受 boolean。
int i = 100;
long l = i; // 自动(小→大)
int j = (int) l; // 强制(大→小)
byte b = 10;
b = b + 1; // ❌ 编译错误:b+1 变成 int
b = (byte)(b + 1); // ✅
b += 1; // ✅ 复合赋值会自动强转
⚠️ 考点:byte b = 10; b = b + 1; 为什么错 → 整数运算自动提升为 int。
import java.util.Scanner;
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
double d = sc.nextDouble();
String s = sc.next(); // 遇到空白停
String line = sc.nextLine(); // 读一整行(前面有 nextInt 时要注意吃掉换行)
System.out.print("x");
System.out.println("换行");
System.out.printf("%d %.2f%n", n, d); // 类似 C 的 printf
⚠️ 经典坑:nextInt() 后紧跟 nextLine() 会读到空串,要先 sc.nextLine() 吃掉回车。
int[] a = new int[5]; // C++: int a[5];
int[] b = {1, 2, 3}; // 静态初始化
int[][] c = new int[3][4]; // 二维
System.out.println(a.length); // 属性!不是 a.length()(String 才是 length())
length 属性ArrayIndexOutOfBoundsException(不是直接崩)Arrays.toString(a) 打印、Arrays.sort(a) 排序
int[] a = {5, 2, 8};
Arrays.sort(a); // 升序
System.out.println(Arrays.toString(a)); // [2, 5, 8]
大部分和 C++ 一样,重点差异:
int i = 5;
int x = i++; // x=5, i=6
int y = ++i; // i=7, y=7
System.out.println(7 / 2); // 3(整除)
System.out.println(7 % 3); // 1
System.out.println(-7 % 3); // -1(负数取模结果为负)
boolean r = (a > 1) && (b < 2); // 短路
switch(支持 byte/short/char/int/String/枚举):
switch (day) {
case 1: System.out.println("周一"); break; // 忘了 break 会穿透
default: System.out.println("其他");
}
手写排序(考试常考,必须会):
// 冒泡排序
for (int i = 0; i < n - 1; i++)
for (int j = 0; j < n - 1 - i; j++)
if (a[j] > a[j + 1]) {
int t = a[j]; a[j] = a[j + 1]; a[j + 1] = t;
}
// 选择排序
for (int i = 0; i < n - 1; i++) {
int k = i;
for (int j = i + 1; j < n; j++) if (a[j] < a[k]) k = j;
int t = a[i]; a[i] = a[k]; a[k] = t;
}
public class Student {
private String name; // 成员变量(默认值 null)
private int age; // 默认 0
public Student() { } // 无参构造
public Student(String name, int age) { // 有参构造
this.name = name; // this 区分同名
this.age = age;
}
public String getName() { return name; } // getter
public void setName(String name) { this.name = name; } // setter
public String toString() { // 重写,等价 C++ 的 operator<<
return "Student(" + name + "," + age + ")";
}
}
和 C++ 的差异:
.java 文件最多一个 public 类,名字要和文件同名::,调用就是 对象.方法()finalize()(已废弃,别用)this(...) 调本类其他构造,必须第一行⚠️ 考点:构造方法能不能被重写?→ 不能(它不属于继承的方法,名字也不是自己的)。
Student s1 = new Student("张三", 18);
Student s2 = s1; // 不是复制!两个引用指向同一对象
s2.setAge(20);
System.out.println(s1.getAge()); // 20
== 比引用(地址),equals 比内容(默认是地址,可重写)| 成员变量 | 局部变量 | |
|---|---|---|
| 位置 | 类里、方法外 | 方法内/参数 |
| 默认值 | 有(0/null/false) | 无,必须初始化 |
| 内存 | 堆(对象里) | 栈 |
| 生命周期 | 跟对象 | 跟方法调用 |
public class Counter {
static int count = 0; // 所有对象共用
static { count = 0; } // 静态代码块,类加载时执行一次
static int get() { return count; }
int normal() { return count; } // 普通方法能访问静态
// static void f() { normal(); } ❌ 静态不能访问非静态
}
⚠️ 为什么静态方法不能访问非静态成员?→ 静态属于类,加载时可能还没有对象。
private int a; // 只有本类
int b; // 同包
protected int c; // 同包 + 子类
public int d; // 所有
封装 = 成员变量 private + 提供 get/set。
class Animal {
protected String name;
public void speak() { System.out.println("..."); }
}
class Dog extends Animal {
@Override
public void speak() { System.out.println("汪汪"); } // 重写
public void bark() { super.speak(); speak(); } // super 调父类
}
| 重写 Override | 重载 Overload | |
|---|---|---|
| 位置 | 子类 vs 父类 | 同一个类 |
| 方法名 | 相同 | 相同 |
| 参数列表 | 必须相同 | 必须不同 |
| 返回值 | 相同或兼容 | 无关 |
| 权限 | 不能更小 | 无关 |
(形象记忆:重写是"改内容",重载是"加花样"。)
final 类:不能被继承(如 String)final 方法:不能被重写final 变量:常量,只能赋值一次
Animal a = new Dog(); // 上转型
a.speak(); // 汪汪(看实际对象)
// a.bark(); ❌ 编译期按 Animal 检查,没有 bark
Dog d = (Dog) a; // 下转型,要强转
if (a instanceof Dog) { Dog d2 = (Dog) a; } // 先用 instanceof 判断
⚠️ 规则:成员方法看右边(运行时类型),成员变量看左边(编译时类型)。
class A { int x = 1; int get() { return 1; } }
class B extends A { int x = 2; int get() { return 2; } }
A a = new B();
System.out.println(a.x); // 1(变量看左边)
System.out.println(a.get()); // 2(方法看右边)
interface Fly {
int MAX = 100; // 默认 public static final
void fly(); // 默认 public abstract
}
abstract class Bird {
abstract void eat(); // 抽象方法
void sleep() { } // 普通方法
}
class Sparrow extends Bird implements Fly {
void eat() { }
public void fly() { }
}
| 抽象类 | 接口 | |
|---|---|---|
| 关键字 | abstract class | interface |
| 继承 | 单继承 | 多实现 |
| 构造方法 | 有 | 没有 |
| 成员 | 变量/普通方法/抽象方法 | 常量 + 抽象方法(JDK8+ 可有 default/static) |
共同点:都不能 new,都用于多态。
interface Callback { void done(String result); }
class Worker {
void work(Callback cb) { cb.done("完成"); }
}
new Worker().work(new Callback() { // 匿名内部类
public void done(String r) { System.out.println(r); }
});
class Outer {
int x = 1;
class Inner { void f() { System.out.println(x); } } // 成员内部类
static class SInner { } // 静态内部类
void g() { class Local { } } // 局部内部类
}
匿名内部类最常考(上面接口回调的例子)。
Throwable
├── Error(VirtualMachineError 等,程序管不了)
└── Exception
├── RuntimeException(运行时:空指针、越界、类型转换)
└── 其他(编译时异常:IO、SQL,必须处理)
try {
int x = 10 / 0;
} catch (ArithmeticException e) {
System.out.println("除零:" + e.getMessage());
} finally {
System.out.println("一定执行"); // 关资源写这里
}
throw new RuntimeException("...") 主动抛void f() throws IOException 声明(谁调用谁处理)class MyException extends Exception { }⚠️ 执行顺序题:try 里 return 了,finally 还会执行吗?→ 会(在 return 之前执行),除非 System.exit(0)。
String s = "hello";
s.length(); // 5(方法,带括号)
s.charAt(1); // 'e'
s.substring(1, 4); // "ell"(含头不含尾)
s.indexOf("l"); // 2
s.replace("l", "L"); // "heLLo"(返回新串)
s.split(","); // 按逗号切
s.trim(); // 去首尾空格
s.toUpperCase();
Integer.parseInt("123"); // 字符串转 int
String.valueOf(123); // 基本类型转字符串
⚠️ String 不可变:s = s + "x" 每次都新建对象,循环里别这么写。
String a = "abc";
String b = "abc";
String c = new String("abc");
a == b; // true(常量池同一个对象)
a == c; // false(new 出来是新的)
a.equals(c); // true(比内容)
StringBuilder sb = new StringBuilder();
sb.append("a").append(1); // 链式
sb.reverse();
sb.toString();
Integer i = 100; // 自动装箱
int j = i; // 自动拆箱
Integer.parseInt("42");
Integer a = 127, b = 127;
System.out.println(a == b); // true(缓存 -128~127)
Integer c = 128, d = 128;
System.out.println(c == d); // false(超范围是新对象)
Date now = new Date();
SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(fmt.format(now)); // 格式化
Date d = fmt.parse("2025-01-01 00:00:00"); // 解析
Calendar cal = Calendar.getInstance();
cal.set(2025, 0, 1);
cal.add(Calendar.DAY_OF_MONTH, 7);
⚠️ 月份从 0 开始(0=一月)。
字节流:InputStream / OutputStream (什么都能读写)
字符流:Reader / Writer (只能文本,能按行)
// 写文件(字符流 + 缓冲)
try (BufferedWriter bw = new BufferedWriter(new FileWriter("a.txt"))) {
bw.write("hello");
bw.newLine();
} // try-with-resources 自动关闭
// 读文件按行
try (BufferedReader br = new BufferedReader(new FileReader("a.txt"))) {
String line;
while ((line = br.readLine()) != null) System.out.println(line);
}
// 复制文件(字节流)
try (FileInputStream in = new FileInputStream("a.jpg");
FileOutputStream out = new FileOutputStream("b.jpg")) {
byte[] buf = new byte[1024];
int n;
while ((n = in.read(buf)) != -1) out.write(buf, 0, n);
}
⚠️ 对比 Python:with open(...) as f: 就是 Java 的 try-with-resources。
// 方式一:实现 Runnable(推荐)
Thread t = new Thread(() -> System.out.println("跑"));
t.start();
// 方式二:继承 Thread
class MyThread extends Thread {
public void run() { System.out.println("跑"); }
}
new MyThread().start();
start():开新线程执行 runrun():普通方法调用,还在当前线程sleep(1000) 毫秒;join() 等它结束同步:
class Ticket {
private int n = 10;
public synchronized void sell() { // 方法锁
if (n > 0) n--;
}
public void sell2() {
synchronized (this) { if (n > 0) n--; } // 代码块锁
}
}
死锁四条件:互斥、请求并保持、不可剥夺、循环等待。
import java.util.*;
List<String> list = new ArrayList<>();
list.add("a"); list.get(0); list.size(); list.remove(0);
Map<String, Integer> map = new HashMap<>();
map.put("k", 1); map.get("k"); map.containsKey("k");
for (Map.Entry<String, Integer> e : map.entrySet()) {
System.out.println(e.getKey() + "=" + e.getValue());
}
Set<Integer> set = new HashSet<>();
set.add(1); set.contains(1);
Stack<Integer> st = new Stack<>();
st.push(1); st.peek(); st.pop(); st.isEmpty();
Queue<Integer> q = new LinkedList<>();
q.offer(1); q.peek(); q.poll(); q.isEmpty();
和 C++ STL 对照:
| Java | C++ | 说明 |
|---|---|---|
| ArrayList | vector | 动态数组 |
| LinkedList | list | 双向链表 |
| HashMap | unordered_map | 哈希表 |
| TreeMap | map | 红黑树,有序 |
| HashSet | unordered_set | 去重 |
| Stack | stack | 后进先出 |
| Queue / Deque | queue / deque | 先进先出 |
| PriorityQueue | priority_queue | 堆 |
排序:
List<Integer> a = new ArrayList<>(List.of(3, 1, 2));
Collections.sort(a); // 升序
a.sort((x, y) -> y - x); // 降序(Comparator)
⚠️ List 不能直接存 int(要 Integer),自动装箱。
// 1. 学生类
class Student {
private String name; private int score;
public Student(String name, int score) { this.name = name; this.score = score; }
public String getName() { return name; }
public int getScore() { return score; }
public String toString() { return name + ":" + score; }
}
// 2. 冒泡排序
// 3. 求最大值
// 4. 字符串反转
StringBuilder sb = new StringBuilder(s).reverse();
// 5. 统计字符出现次数
Map<Character, Integer> m = new HashMap<>();
for (char c : s.toCharArray()) m.put(c, m.getOrDefault(c, 0) + 1);
// 6. 文件复制(见第 10 章)
// 7. 多线程卖票(见第 12 章)