整理自《Java入门到精通》,覆盖日常开发90%以上的语法点。适合快速翻阅,不适合零基础精读。
将下面代码保存到 HelloWorld.java 文件:
// 文件名必须与 public class 同名
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, Java!");
}
}编译与运行:
javac HelloWorld.java # 编译 → HelloWorld.class
java HelloWorld # 运行(不用写.class)几个常识:
一个 .java文件最多一个 public class,但可以有多个非 public 类
main 方法签名固定,少一个 static 都不行
Java 11+ 可以 java HelloWorld.java 直接运行单个源文件(省掉 javac)
// 单行注释
/*
* 多行注释
*/
/**
* 文档注释(会被 javadoc 提取生成 API 文档)
* @param name 名字
* @return 问候语
*/
public String sayHello(String name) { ... }| 规则 | 说明 |
| 组成 | 字母、数字、下划线 _、美元符 $ |
| 首字符 | 不能是数字 |
| 大小写 | 严格区分,name 和 Name 是两个东西 |
| 关键字 | class、public、static 等 50 个保留字,不能用做标识符 |
命名约定(不强制但大家都这么写):
类/接口:PascalCase → HelloWorld、Runnable
方法/变量:camelCase → getUserName、userCount
常量:UPPER_SNAKE → MAX_SIZE
包名:全小写 → com.example.utils
int age = 18; // 基本类型
String name = "张三"; // 引用类型
var list = new ArrayList<>(); // Java 10+ 局部变量类型推断
// 常量:final 修饰,只能赋值一次
final double PI = 3.14159;
final String APP_NAME = "MyApp";Java 10 的 var 注意事项:
只能用于局部变量(有初始化表达式),不能做字段/参数/返回值类型
代码 var x = null; 编译报错(推断不出类型)
代码 var list = new ArrayList<>() 将被推断为 ArrayList<Object> 类型,注意类型安全
// 自动转换(小 → 大):byte → short → int → long → float → double
int a = 100;
long b = a; // OK,自动提升,不丢失数据
// 强制转换(大 → 小):可能丢数据
double d = 3.14;
int i = (int) d; // 3,小数部分没了
// 包装类转换
String s = "123";
int n = Integer.parseInt(s); // String → int
String s2 = String.valueOf(456); // int → String| 类型 | 大小 | 范围 | 默认值 | 包装类 |
byte | 1字节 | -128 ~ 127 | 0 | Byte |
short | 2字节 | -215 ~ 215-1 | 0 | Short |
int | 4字节 | -231 ~ 231-1 | 0 | Integer |
long | 8字节 | -263 ~ 263-1 | 0L | Long |
float | 4字节 | IEEE 754 | 0.0f | Float |
double | 8字节 | IEEE 754 | 0.0d | Double |
char | 2字节 | 0 ~ 65535 | '\u0000' | Character |
boolean | JVM决定 | true/false | false | Boolean |
简单示例:
long money = 100_000_000L; // L 后缀,JDK7+ 支持下划线分割,更容易阅读
float pi = 3.14f; // f/F 后缀
double e = 2.71828; // 不加后缀默认 double
char ch = 'A';
boolean flag = true;String 是引用类型(不可变对象),但用起来像基本类型。
String s1 = "hello"; // 字面量,存常量池
String s2 = new String("hello"); // 强制在堆上新开辟
// 常用方法
"hello".length(); // 5 字符串长度
"hello".charAt(1); // 'e' 下标为1的字符
"hello".substring(1, 3); // "el"(左闭右开)
"hello".contains("ll"); // true 是否包含“ll”子字符串
"hello".indexOf("l"); // 2 首次“l”出现的下标
"hello".toUpperCase(); // "HELLO" 转换成大写
" abc ".trim(); // "abc" 去除首尾空白
"a,b,c".split(","); // ["a","b","c"] 根据“,”分隔
String.join("-", "a", "b", "c"); // "a-b-c"
// 比较(重要!)
"abc".equals("abc"); // true —— 值比较
"abc" == "abc"; // true(常量池),但不要依赖 ==
"abc" == new String("abc"); // false —— 地址不同
// 拼接:编译器自动优化为 StringBuilder
String s = "Hello " + "World";
String s = "Age: " + 18; // 自动转换
// 格式化
String.format("Hello %s, age %d", "Tom", 20); // "Hello Tom, age 20"
"Hello %s, age %d".formatted("Tom", 20); // Java 15+StringBuilder(非线程安全,常用)/ StringBuffer(线程安全,性能稍逊):
StringBuilder sb = new StringBuilder();
sb.append("Hello").append(" ").append("World");
sb.insert(5, ",");
sb.toString(); // "Hello, World"// 声明
int[] arr1 = new int[5]; // 默认全0
int[] arr2 = {1, 2, 3, 4, 5}; // 直接初始化
int[] arr3 = new int[]{1, 2, 3};
// 二维数组
int[][] matrix = {{1,2}, {3,4}, {5,6}};
// 常用操作
arr1.length; // 5(属性不是方法)数组长度
Arrays.toString(arr2); // "[1, 2, 3, 4, 5]"
Arrays.sort(arr2); // 原地排序
Arrays.binarySearch(arr2, 3); // 二分查找(需先排序)
int[] copy = Arrays.copyOf(arr2, 10); // 扩容/截取
Arrays.fill(arr1, 0); // 全部填0Integer i = 100; // 自动装箱:Integer.valueOf(100)
int j = i; // 自动拆箱:i.intValue()
// 缓存坑(-128 ~ 127 内走缓存池,== 返回 true)
Integer a = 127, b = 127;
System.out.println(a == b); // true
Integer c = 128, d = 128;
System.out.println(c == d); // false —— 超出缓存范围int r = a + b; // 加
int r = a - b; // 减
int r = a * b; // 乘
int r = a / b; // 除(整数除得整数,7/2=3)
int r = a % b; // 取余
a == b; a != b; a > b; a < b; a >= b; a <= b;
// 短路逻辑:&& 遇 false 停止,|| 遇 true 停止
boolean r = a > 0 && b > 10; // && 短路与
boolean r = a > 0 || b > 10; // || 短路或
boolean r = !flag; // 取反int r = a & b; // 位与
int r = a | b; // 位或
int r = a ^ b; // 异或
int r = ~a; // 取反
int r = a << 2; // 左移(乘以2^2)
int r = a >> 2; // 右移(除以2^2,保留符号位)
int r = a >>> 2; // 无符号右移a += b; // a = a + b
a -= b; // a = a - b
a *= b; // a = a * b
a /= b; // a = a / b
a %= b; // a = a % b
a++; // 后置自增
++a; // 前置自增
// 三元运算符
// 如果 a > b 为 true,则 max 为 a
// 如果 a > b 为 false,则 max 为 b
int max = a > b ? a : b;// 判断变量 obj 是不是字符串类型;如果是,自动转型,把值给到变量 s,在 if 里面直接当做 String 使用
Object obj = "hello";
if (obj instanceof String s) { // Java 16+ 模式匹配
System.out.println(s.toUpperCase());
}// if-else
if (score >= 90) {
grade = 'A';
} else if (score >= 80) {
grade = 'B';
} else {
grade = 'C';
}
// switch(支持 int/String/枚举/char/byte/short)
// Java 14+ 箭头语法
switch (day) {
case MONDAY, FRIDAY -> System.out.println("工作日");
case SATURDAY, SUNDAY -> System.out.println("周末");
default -> System.out.println("未知");
}
// switch 表达式(Java 14+,可以有返回值)
String result = switch (status) {
case 1 -> "成功";
case 2, 3 -> "处理中";
default -> "未知";
};// for(确定次数)
for (int i = 0; i < 10; i++) {
System.out.println(i);
}
// 增强 for(遍历数组/集合)
for (String s : list) {
System.out.println(s);
}
// while(条件满足就循环)
while (condition) {
// ...
}
// do-while(至少执行一次)
do {
// ...
} while (condition);
// break:跳出当前循环
// continue:跳过本次循环,继续下一次
// 带标签的 break/continue(跳出多层)
outer:
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (i == 1 && j == 1) break outer; // 跳出外层循环
}
}import java.util.Scanner;
Scanner sc = new Scanner(System.in);
String name = sc.nextLine(); // 读一行
int age = sc.nextInt(); // 读整数
double score = sc.nextDouble(); // 读浮点数
sc.close(); // 是否资源// [修饰符] 返回类型 方法名([参数列表]) {
// 方法体
// }
public static int add(int a, int b) {
return a + b;
}
// 调用方法
int result = add(3, 5);Java 只有值传递:
基本类型:传的是值的副本,不影响原值
引用类型:传的是地址的副本,可以通过它改对象内容,但不能让形参指向新对象
// 不影响原值
void change(int x) {
x = 10;
}
// 影响了原对象
void change(StringBuilder sb) {
sb.append("!");
}
// 不影响原引用
void change(StringBuilder sb) {
sb = new StringBuilder();
}同一个类中,方法名相同、参数列表不同(类型/个数/顺序),与返回值无关。
int max(int a, int b) { ... }
int max(int a, int b, int c) { ... }
double max(double a, double b) { ... }public int sum(int... nums) { // 本质是数组
int total = 0;
for (int n : nums) total += n;
return total;
}
sum(1, 2, 3); // 6
sum(); // 0
// 如果一个方法既有普通参数又有可变参数,可变参数必须放最后
public void print(String prefix, int... nums) { ... }public long factorial(int n) {
if (n <= 1) return 1;
return n * factorial(n - 1);
}public class Person {
// 字段(成员变量)
private String name;
private int age;
// 构造方法
public Person() {} // 无参
public Person(String name, int age) { // 有参
this.name = name;
this.age = age;
}
// getter / setter
public String getName() { return name; }
public void setName(String name) { this.name = name; }
// 普通方法
public void sayHello() {
System.out.println("我是" + name + ",今年" + age);
}
// toString
@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + "}";
}
}
// 使用
Person p = new Person("张三", 20);
p.sayHello();用 private 隐藏内部状态,通过 public 的 getter/setter 控制访问。
| 修饰符 | 本类 | 同包 | 子类 | 其他包 |
private | ✓ | |||
default (不写) | ✓ | ✓ | ||
protected | ✓ | ✓ | ✓ | |
public | ✓ | ✓ | ✓ | ✓ |
// Java 只能单继承(一个子类只能有一个父类)
public class Student extends Person {
private String school;
public Student(String name, int age, String school) {
super(name, age); // 调用父类构造,必须在第一行
this.school = school;
}
// 方法重写(Override)—— 方法签名必须完全一致
@Override
public void sayHello() {
super.sayHello(); // 可调父类版本
System.out.println("我在" + school + "上学");
}
}super关键字:
super() —— 调用父类构造
super.method() —— 调用父类方法
super.field —— 访问父类字段
final修饰:
final class —— 不能被继承(如 String)
final 方法 —— 不能被子类重写
final 变量 —— 常量
// 编译看左边,运行看右边
Person p = new Student("小明", 18, "清华");
p.sayHello(); // 调用的是 Student 重写后的版本
// instanceof 判断实际类型
if (p instanceof Student s) {
System.out.println(s.getSchool());
}public abstract class Animal {
protected String name;
public Animal(String name) { this.name = name; }
// 抽象方法:没有方法体,子类必须实现
public abstract void makeSound();
// 普通方法(可以被子类直接用)
public void eat() {
System.out.println(name + "在吃东西");
}
}
public class Cat extends Animal {
public Cat(String name) { super(name); }
@Override
public void makeSound() {
System.out.println("喵喵喵");
}
}// Java 8+ 接口可以有 default 方法和 static 方法
public interface Flyable {
// 常量(默认 public static final)
int MAX_SPEED = 300;
// 抽象方法(默认 public abstract)
void fly();
// Java 8:默认方法(有实现,子类可选重写)
default void land() {
System.out.println("降落中...");
}
// Java 8:静态方法(属于接口本身)
static boolean canFly(int speed) {
return speed > 0 && speed <= MAX_SPEED;
}
// Java 9:私有方法(给 default 方法复用内部逻辑)
private void check() { ... }
}
// 一个类可以实现多个接口
public class Bird extends Animal implements Flyable, Runnable {
@Override
public void fly() { ... }
@Override
public void run() { ... }
}抽象类 vs 接口:
| 抽象类 | 接口 | |
| 继承 | 单继承 extends | 多实现 implements |
| 构造方法 | 有 | 没有 |
| 字段 | 可以有普通字段 | 只能有常量 |
| 方法 | 抽象 + 具体都行 | 抽象 + default + static |
| 用途 | 模板(is-a 关系) | 能力契约(can-do 关系) |
public class Outer {
private String name = "外部";
// 成员内部类
class Inner {
void print() { System.out.println(name); }
}
// 静态内部类(不依赖外部类实例)
static class StaticInner {
void print() { System.out.println("静态内部类"); }
}
// 局部内部类(方法里定义)
void method() {
class LocalInner {
void print() { System.out.println("局部内部类"); }
}
}
// 匿名内部类(Lambda 出现前用得很多)
Runnable r = new Runnable() {
@Override
public void run() { System.out.println("匿名内部类"); }
};
}不可变数据载体,自动生成构造器、getter、equals、hashCode、toString:
public record Point(int x, int y) {}
Point p = new Point(3, 5);
int x = p.x(); // 自动生成的访问器
System.out.println(p); // Point[x=3, y=5]Throwable
├── Error(严重问题,一般不管)
│ ├── OutOfMemoryError 内存溢出异常
│ └── StackOverflowError 堆栈溢出异常
└── Exception
├── RuntimeException(非检查异常,编译不强制处理)
│ ├── NullPointerException 空指针异常
│ ├── IndexOutOfBoundsException 下标越界异常
│ ├── ArithmeticException 非法数学运算异常
│ └── IllegalArgumentException 非法参数异常
└── 其他(检查异常,编译期强制处理)
├── IOException IO异常
├── SQLException SQL异常
└── ClassNotFoundException 类没有找到异常try {
int r = 10 / 0; // 抛 ArithmeticException
} catch (ArithmeticException e) {
System.out.println("算术异常:" + e.getMessage());
} catch (Exception e) {
e.printStackTrace(); // 子类在前,父类在后
} finally {
System.out.println("始终执行"); // 除非 System.exit(0)
}资源用完后自动调用 close(),省掉 finally 的样板代码:
// Java 7写法:在 try() 里声明资源
try (BufferedReader br = new BufferedReader(new FileReader("file.txt"))) {
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
// Java 9 改进:可以用外部声明的 final/effectively final 变量
BufferedReader r = new BufferedReader(new FileReader("file.txt"));
try (r) { ... }// throws:声明方法可能抛异常
public void readFile(String path) throws IOException {
// throw:实际抛出异常
if (path == null) throw new IllegalArgumentException("路径不能为空");
// ...
}泛型本质是类型参数化,让代码在编译期就保证类型安全。
// 泛型类
public class Box<T> {
private T content;
public T get() { return content; }
public void set(T content) { this.content = content; }
}
Box<String> stringBox = new Box<>();
stringBox.set("Hello");
// stringBox.set(123); // 编译错误!
// 泛型方法
public static <T> T first(List<T> list) {
return list.get(0);
}
// 泛型接口
public interface Comparator<T> {
int compare(T a, T b);
}// ? —— 无界通配符
void printList(List<?> list) {
for (Object o : list) System.out.println(o);
}
// ? extends T —— 上界(生产/读,get 出来的都是 T 或 T 的子类)
void readNumbers(List<? extends Number> list) {
Number n = list.get(0); // OK
// list.add(1); // 编译错误,不知道具体子类型
}
// ? super T —— 下界(消费/写,能往里放 T 或 T 的子类)
void addNumbers(List<? super Integer> list) {
list.add(1); // OK
// Integer i = list.get(0); // 编译错误,只能当 Object 取
}PECS 原则:Producer Extends, Consumer Super
泛型只在编译期生效,运行时会被擦除。List<String>和 List<Integer>在运行时都是 List。
List<String> l1 = new ArrayList<>();
List<Integer> l2 = new ArrayList<>();
System.out.println(l1.getClass() == l2.getClass()); // trueCollection
├── List(有序、可重复)
│ ├── ArrayList —— 数组实现,查快增删慢(尾部例外)
│ └── LinkedList —— 双向链表,增删快
├── Set(不重复)
│ ├── HashSet —— 哈希表,无序
│ ├── LinkedHashSet —— 哈希表+链表,保留插入顺序
│ └── TreeSet —— 红黑树,自然排序
└── Queue / Deque
├── ArrayDeque —— 双端队列,比 Stack 好用
└── PriorityQueue —— 优先级队列(堆)
Map(键值对,独立接口)
├── HashMap —— 哈希表,常用
├── LinkedHashMap —— 保留插入顺序
├── TreeMap —— 红黑树,key 排序
└── ConcurrentHashMap —— 线程安全的 HashMapList<String> list = new ArrayList<>();
list.add("apple");
list.add(1, "banana"); // 指定位置插入
list.get(0); // "apple"
list.set(0, "orange"); // 替换
list.remove(0); // 按下标删除
list.remove("banana"); // 按元素删除
list.contains("apple"); // true
list.indexOf("apple"); // 下标,不存在返回-1
list.size(); // 元素个数
// 遍历
for (int i = 0; i < list.size(); i++) { ... }
for (String s : list) { ... }
list.forEach(System.out::println); // Java 8 Lambda
list.forEach((s) -> System.out.println(s));
// 截取
List<String> sub = list.subList(0, 3); // 视图,改 sub 会影响原 listSet<Integer> set = new HashSet<>();
set.add(1);
set.add(2);
set.add(1); // 重复,不会加进去
set.contains(1); // true
set.remove(2);
set.size();
// 注意:HashSet 靠 hashCode + equals 判重,自定义类要重写这两个方法Map<String, Integer> map = new HashMap<>();
map.put("apple", 10);
map.get("apple"); // 10
map.getOrDefault("banana", 0); // 0(不存在返回默认值)
map.containsKey("apple"); // true
map.containsValue(10); // true
// 遍历
for (Map.Entry<String, Integer> entry : map.entrySet()) {
System.out.println(entry.getKey() + " -> " + entry.getValue());
}
map.forEach((k, v) -> System.out.println(k + ": " + v));
// 只遍历 key 或 value
for (String key : map.keySet()) { ... }
for (Integer val : map.values()) { ... }
// 安全删除
map.keySet().removeIf(k -> k.startsWith("a"));
// 合并
map.merge("apple", 1, Integer::sum); // 存在就加1,不存在就放1// Collections
Collections.sort(list);
Collections.reverse(list);
Collections.shuffle(list);
Collections.max(list);
Collections.min(list);
Collections.frequency(list, "apple");
Collections.synchronizedList(list); // 包装成线程安全
// Arrays → List
List<String> list = Arrays.asList("a", "b", "c"); // 不可变大小!
list.set(0, "x"); // OK
// list.add("d"); // 抛异常 UnsupportedOperationException
List<String> normalList = new ArrayList<>(Arrays.asList("a", "b", "c")); // 这样才行
// List.of / Set.of / Map.of(Java 9+,不可变集合)
List<String> list = List.of("a", "b", "c");
Set<Integer> set = Set.of(1, 2, 3);
Map<String, Integer> map = Map.of("a", 1, "b", 2);// 读文本
try (BufferedReader reader = new BufferedReader(new FileReader("input.txt"))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
}
// 写文本
try (BufferedWriter writer = new BufferedWriter(new FileWriter("output.txt"))) {
writer.write("Hello");
writer.newLine(); // 写换行(跨平台)
writer.write("World");
}
// 二进制读写
try (FileInputStream in = new FileInputStream("in.dat");
FileOutputStream out = new FileOutputStream("out.dat")) {
byte[] buffer = new byte[1024];
int len;
while ((len = in.read(buffer)) != -1) {
out.write(buffer, 0, len);
}
}import java.nio.file.*;
import java.io.IOException;
// 读文本(一次全读)
String content = Files.readString(Path.of("file.txt"));
// 按行读
List<String> lines = Files.readAllLines(Path.of("file.txt"));
// 写文本
Files.writeString(Path.of("out.txt"), "Hello World!");
// 复制/移动/删除
Files.copy(Path.of("src"), Path.of("dest"), StandardCopyOption.REPLACE_EXISTING);
Files.move(Path.of("src"), Path.of("newName"));
Files.delete(Path.of("target")); // 不存在抛异常
Files.deleteIfExists(Path.of("target"));
// 遍历目录
try (var stream = Files.list(Path.of("."))) {
stream.filter(Files::isRegularFile)
.forEach(System.out::println);
}
try (var walk = Files.walk(Path.of("."))) { // 递归遍历
walk.filter(p -> p.toString().endsWith(".java"))
.forEach(System.out::println);
}// 实现 Serializable 接口即可
public class User implements Serializable {
private static final long serialVersionUID = 1L; // 版本号
private String name;
private transient String password; // transient 不序列化
}
// 序列化
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("user.dat"))) {
oos.writeObject(user);
}
// 反序列化
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("user.dat"))) {
User user = (User) ois.readObject();
}所有类的祖宗:
obj.toString(); // getClass().getName() + "@" + hashCode()
obj.equals(otherObj); // 默认 == 比较地址,通常需要重写
obj.hashCode(); // 重写 equals 必须重写 hashCode
obj.getClass(); // 获取运行时类信息(反射入口)
obj.clone(); // 浅拷贝(需实现 Cloneable)// 判空
str == null || str.isEmpty(); // 长度=0
str == null || str.isBlank(); // Java 11+,只有空白也算空
// 匹配
"abc".matches("\\w+"); // true
"abc".startsWith("a"); // true
"abc".endsWith("c"); // true
// 替换
"abcabc".replace("a", "x"); // "xbcxbc"
"abcabc".replaceAll("\\w", "x"); // "xxxxxx" 正则
// 反转(String 本身没有,得绕一下)
new StringBuilder("abc").reverse().toString(); // "cba"
// 重复
"ha".repeat(3); // "hahaha" Java 11+Math.abs(-5); // 5
Math.max(3, 7); // 7
Math.min(3, 7); // 3
Math.pow(2, 10); // 1024.0
Math.sqrt(16); // 4.0
Math.random(); // [0.0, 1.0) 随机小数
Math.round(3.5); // 4(四舍五入,.5 往偶数靠)
Math.ceil(3.1); // 4.0(向上取整)
Math.floor(3.9); // 3.0(向下取整)
Math.PI; // 3.14159...// 获取当前时间
LocalDate date = LocalDate.now(); // 2024-01-15
LocalTime time = LocalTime.now(); // 14:30:00
LocalDateTime dt = LocalDateTime.now(); // 日期+时间
// 创建指定时间
LocalDate.of(2024, 1, 15);
LocalTime.of(14, 30, 0);
// 格式化
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String s = dt.format(fmt);
LocalDateTime parsed = LocalDateTime.parse("2024-01-15 14:30:00", fmt);
// 运算(返回新对象,不改变原对象)
LocalDate tomorrow = date.plusDays(1);
LocalDate lastMonth = date.minusMonths(1);
boolean isAfter = date1.isAfter(date2);
// 时间差
Duration d = Duration.between(time1, time2); // 时、分、秒
Period p = Period.between(date1, date2); // 年、月、日public enum Season {
SPRING, SUMMER, AUTUMN, WINTER
}
// 自带方法
Season s = Season.SPRING;
s.name(); // "SPRING"
s.ordinal(); // 0(声明顺序)
Season.values(); // [SPRING, SUMMER, AUTUMN, WINTER]
Season.valueOf("SUMMER"); // Season.SUMMER
// 带字段、构造、抽象方法(策略模式)
public enum Operation {
PLUS("+") { public double apply(double x, double y) { return x + y; } },
MINUS("-") { public double apply(double x, double y) { return x - y; } };
private final String symbol;
Operation(String symbol) { this.symbol = symbol; }
public abstract double apply(double x, double y);
}
// enum 实现单例(Effective Java 推荐)
public enum Config {
INSTANCE;
public void doSomething() { ... }
}枚举专用集合(性能比 HashSet/HashMap 高):
EnumSet<Season> set = EnumSet.of(Season.SPRING, Season.SUMMER);
EnumMap<Season, String> map = new EnumMap<>(Season.class);
map.put(Season.SPRING, "春天");| 注解 | 作用 |
@Override | 标记方法重写,编译期校验 |
@Deprecated | 标记已废弃(since、forRemoval) |
@SuppressWarnings | 压制编译器警告 |
@FunctionalInterface | 标记函数式接口,编译期校验 |
@SafeVarargs | 声明泛型可变参数安全 |
@Target | 限制注解能用在哪(TYPE/METHOD/FIELD/PARAMETER 等) |
@Retention | SOURCE(编译后消失)/ CLASS(class 中保留)/ RUNTIME(运行时可通过反射读取) |
@Documented | 是否写入 Javadoc |
@Inherited | 子类是否继承该注解 |
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String value() default ""; // 只有一个元素且叫 value 时,可省略 key
int count() default 1;
String[] tags() default {};
}
// 使用
@MyAnnotation(value = "hello", count = 3)
public void doSomething() {}
// 反射读取
Method m = obj.getClass().getMethod("doSomething");
if (m.isAnnotationPresent(MyAnnotation.class)) {
MyAnnotation anno = m.getAnnotation(MyAnnotation.class);
System.out.println(anno.value());
}反射就是在运行时拿到类的信息并操作它,是框架的基石。
// 获取 Class 的三种方式
Class<?> clazz1 = Person.class;
Class<?> clazz2 = obj.getClass();
Class<?> clazz3 = Class.forName("com.example.Person");
// 创建实例
Constructor<?> ctor = clazz.getDeclaredConstructor(String.class, int.class);
Object obj = ctor.newInstance("张三", 20);
// 访问字段
Field field = clazz.getDeclaredField("name");
field.setAccessible(true); // 私有字段要突破访问限制
String name = (String) field.get(obj);
field.set(obj, "李四");
// 调用方法
Method method = clazz.getDeclaredMethod("sayHello");
method.setAccessible(true);
method.invoke(obj);
// 获取注解
MyAnnotation anno = method.getAnnotation(MyAnnotation.class);JDK 动态代理:运行时自动生成代理类,实现指定接口。所有方法调用会被转发到 InvocationHandler 的 invoke() 方法中处理。
import java.lang.reflect.*;
// 1. 定义接口
interface UserService {
void addUser(String name);
}
// 2. 真实实现
class UserServiceImpl implements UserService {
public void addUser(String name) {
System.out.println("添加用户: " + name);
}
}
// 3. 处理器(类似拦截器)
class LogHandler implements InvocationHandler {
private final Object target;
public LogHandler(Object target) { this.target = target; }
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("[日志] 开始执行 " + method.getName());
Object result = method.invoke(target, args);
System.out.println("[日志] 执行完毕");
return result;
}
}
// 4. 使用
UserService target = new UserServiceImpl();
UserService proxy = (UserService) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
new Class<?>[]{UserService.class},
new LogHandler(target)
);
proxy.addUser("Alice");限制:只能代理接口,因为生成的代理类已经继承 Proxy了,Java 不支持多继承。
// Lambda 的演变
// 方式1:匿名内部类
Runnable r1 = new Runnable() {
@Override public void run() { System.out.println("hi"); }
};
// 方式2:Lambda 表达式
Runnable r2 = () -> System.out.println("hi");
// 有参有返回值
Comparator<String> c = (a, b) -> a.compareTo(b);
// 多行用花括号
BiFunction<Integer, Integer, Integer> add = (a, b) -> {
int result = a + b;
return result;
};// 静态方法引用
Function<String, Integer> f1 = Integer::parseInt;
// 实例方法引用
String s = "hello";
Supplier<Integer> f2 = s::length;
// 构造器引用
Supplier<ArrayList<String>> f3 = ArrayList::new;List<String> names = List.of("Alice", "Bob", "Charlie", "David");
// 中间操作(惰性)+ 终端操作(触发执行)
List<String> result = names.stream()
.filter(n -> n.length() > 3) // 过滤
.map(String::toUpperCase) // 转换
.sorted() // 排序
.distinct() // 去重
.limit(2) // 截取前2个
.collect(Collectors.toList()); // 收集
// 统计
long count = names.stream().filter(n -> n.startsWith("A")).count();避免 NullPointerException 的优雅方案:
Optional<String> opt = Optional.ofNullable(someString);
// 安全取值
String value = opt.orElse("默认值");
String value2 = opt.orElseGet(() -> computeDefault());
// 安全操作
opt.ifPresent(v -> System.out.println(v));
String upper = opt.map(String::toUpperCase).orElse("");
// 抛出异常
String value = opt.orElseThrow(() -> new IllegalStateException("不能为空"));
// 过滤
Optional<String> result = opt.filter(v -> v.length() > 3);// 箭头语法(不需要 break)
String type = switch (day) {
case 1, 2, 3, 4, 5 -> "工作日";
case 6, 7 -> "周末";
default -> "未知";
};
// yield 返回复杂值
int result = switch (status) {
case "A" -> 1;
case "B" -> {
int n = compute();
yield n * 2; // yield 代替 return
}
default -> 0;
};== vs equals:==比引用地址,equals比值(String 重写了 equals,所以比较内容)
Integer 缓存:-128 ~ 127 内的值用 == 比可能是 true(缓存),范围外一定是 false
空指针:调用方法前想想是不是 null,多用 Optional
Arrays.asList() 返回的 List 不可增删,只是数组的一个视图
泛型擦除:List<String>和 List<Integer>在运行时是同一个类
finally 不一定会执行:System.exit(0) 和 JVM 崩溃会跳过 finally
多态不再用父类私有方法:private 方法不能被重写,父类私用自己的版本
switch 的类型限制:支持 int/String/枚举/char/byte/short,不支持 double/float/long/boolean
到这里,Java基础语法就介绍完了,该笔记定位是速查,碰到具体问题还是得翻书或看源码。Java 要多写代码,多踩坑,自然就熟了。后续有新知识将更新……