Java 速成
为零基础同学准备的 Java 速成:类型、运算、流程、类、继承、接口、泛型、Lambda、集合、异常、IO 与 Android 开发相关特性
学习目标
- 掌握 Java 基本数据类型、包装类与运算符,理解自动装箱与拆箱
- 理解类与对象、构造方法、继承、多态、接口、内部类的设计动机
- 会使用泛型与 Lambda 表达式简化集合操作
- 能熟练使用 List/Map/Set 处理常见数据结构
- 理解异常体系、try-with-resources 与 IO 流基础
- 了解 Java 在 Android 开发中的特性与限制(如不能直接用 NIO 与反射限制)
本章定位:Kotlin 自 2017 年起成为 Android 官方首选语言,新项目推荐使用 Kotlin。本章面向需要维护 Java 存量代码、理解 Java 第三方库源码、或从 Java 入门便于后续过渡到 Kotlin 的读者。本章不意味着推荐新项目使用 Java,而是作为 Views 路线的语法基础。
学习目标
本章不是完整的 Java 教程,而是把写 Android 必备的 Java 知识点压成一节“速成课”。读完本章你将能够:
- 看懂 Android Studio 模板生成的
MainActivity中每个 Java 关键字与符号; - 理解类型系统与自动装箱、运算符与流程控制,写出能编译的方法体;
- 用类、继承、接口、内部类组织业务逻辑;
- 用泛型与 Lambda 简化集合操作;
- 看懂异常处理、
try-with-resources与IO流基础; - 知道 Java 在 Android 开发中的特性与限制(如反射、NIO 的可用性)。
变量与数据类型
1. 八种基本类型
| 类型 | 占用 | 范围 | 示例 |
|---|---|---|---|
byte |
1B | -128~127 | byte b = 1; |
short |
2B | -32768~32767 | short s = 100; |
int |
4B | ±21 亿 | int i = 10; |
long |
8B | ±9×10^18 | long l = 100L; |
float |
4B | 单精度浮点 | float f = 1.5f; |
double |
8B | 双精度浮点 | double d = 1.5; |
char |
2B | 0~65535(UTF-16) | char c = 'A'; |
boolean |
1b | true/false | boolean ok = true; |
字面量规则:long 加 L、float 加 f、double 默认、char 用单引号、String 用双引号、十六进制 0x、二进制 0b、下划线分隔 1_000_000。
2. 包装类与自动装箱
每种基本类型都有对应的引用类型(包装类),用于泛型与集合:
Integer a = 10; // 自动装箱:Integer.valueOf(10)
int b = a; // 自动拆箱:a.intValue()
Integer c = Integer.valueOf("123");
int parsed = Integer.parseInt("456");
Integer在[-128, 127]范围内有缓存,Integer.valueOf(127) == Integer.valueOf(127)返回true;128 起会创建新对象,==返回false。比较包装类必须用.equals()。
3. 引用类型
String、数组、自定义类都是引用类型。引用指向对象本身,赋值是“传引用”:
int[] arr = {1, 2, 3};
int[] arr2 = arr; // arr2 与 arr 指向同一数组
arr2[0] = 99;
System.out.println(arr[0]); // 99
String s = "abc";
String t = new String("abc");
System.out.println(s == t); // false,比较引用
System.out.println(s.equals(t)); // true,比较内容
4. var 局部类型推断(Java 10+)
var list = new ArrayList<String>(); // 编译期推断为 ArrayList<String>
var name = "Alice"; // String
Android 项目开启 sourceCompatibility 17 后可自由使用。注意 var 只能用于局部变量,不能用于字段、参数、返回值。
运算符
1. 算术与赋值
int a = 10;
a += 5; // a = 15
int b = a++; // 先取值再自增,b=15
int c = ++a; // 先自增再取值,c=17
整数除法会截断:
int q = 7 / 2; // 3
double r = 7.0 / 2; // 3.5
2. 关系与逻辑
boolean ok = (a > b) && (c < d);
boolean any = (x == 1) || (y == 2);
boolean not = !ok;
&& 与 || 是短路运算:左侧能决定结果时右侧不执行。Android 中常用:
if (user != null && user.isAdmin()) { ... }
3. 三元与位运算
String msg = count > 0 ? "有" + count + "条" : "无";
int mask = 0b00001111;
int result = mask & 0b11110000; // 0
int or = mask | 0b11110000; // 255
int xor = mask ^ 0b11111111; // 240
int shift = 1 << 4; // 16
位运算在 Android 中常用于状态标记:
public static final int FLAG_VISIBLE = 1;
public static final int FLAG_EDITABLE = 2;
public static final int FLAG_DRAGGABLE = 4;
int flags = FLAG_VISIBLE | FLAG_EDITABLE;
boolean visible = (flags & FLAG_VISIBLE) != 0;
流程控制
1. if-else
if (score >= 90) {
grade = "A";
} else if (score >= 80) {
grade = "B";
} else {
grade = "C";
}
2. switch
Java 17 起支持 switch 表达式与模式匹配(preview),但 Android Tooling 17 仅稳定支持经典 switch:
switch (status) {
case 0:
message = "未开始";
break;
case 1:
case 2:
message = "进行中"; // 多个 case 共用一段代码
break;
default:
message = "未知";
}
3. for 与 for-each
int[] arr = {1, 2, 3, 4, 5};
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
for (int x : arr) { // for-each
System.out.println(x);
}
4. while 与 do-while
int n = 10;
while (n > 0) {
n--;
}
int m = 0;
do {
m++;
} while (m < 5);
5. break / continue / 标签
outer:
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 10; j++) {
if (j == 5) break outer; // 直接跳出外层
if (j % 2 == 0) continue; // 跳过本次
}
}
Android 性能:避免在
onDraw/onBindViewHolder中嵌套循环太深,会拖慢渲染。
方法
1. 基本语法
[修饰符] 返回类型 方法名(参数列表) [throws 异常列表] {
方法体
return 返回值;
}
public static int add(int a, int b) {
return a + b;
}
2. 可变参数
public static int sum(int... nums) {
int total = 0;
for (int n : nums) total += n;
return total;
}
sum(1, 2, 3); // 6
sum(); // 0
底层是数组,外部传 int[] 也可。
3. 方法重载
public String format(int v) { return String.valueOf(v); }
public String format(double v) { return String.valueOf(v); }
public String format(String v) { return v; }
编译器根据参数类型选择最匹配的版本。
4. 静态方法与实例方法
public class MathUtil {
public static int square(int x) { return x * x; } // 静态
public int instanceMethod() { return 1; } // 实例
}
int r = MathUtil.square(5);
MathUtil obj = new MathUtil();
obj.instanceMethod();
类与对象
1. 类定义与构造方法
public class User {
private String name; // 实例字段
private int age;
public User(String name, int age) { // 构造方法
this.name = name;
this.age = age;
}
public String getName() { return name; }
public int getAge() { return age; }
public void setAge(int age) {
if (age < 0) throw new IllegalArgumentException("age < 0");
this.age = age;
}
}
User u = new User("Alice", 18);
System.out.println(u.getName());
2. 静态字段与静态初始化块
public class Config {
public static final String APP_ID = "abc";
private static final Map<String, String> CACHE = new HashMap<>();
static {
CACHE.put("env", "prod");
CACHE.put("version", "1.0");
}
}
static {} 在类首次加载时执行一次,常用于初始化静态字段。
3. this / super 调用
public class Animal {
protected String name;
public Animal(String name) { this.name = name; }
public Animal() { this("default"); } // 调用本类其它构造
}
继承与多态
1. extends
public class Dog extends Animal {
private String breed;
public Dog(String name, String breed) {
super(name); // 调用父类构造,必须在第一行
this.breed = breed;
}
public String getBreed() { return breed; }
}
Java 是单继承,一个类只能 extends 一个父类,但可 implements 多个接口。
2. 方法重写与多态
public class Animal {
public String speak() { return "..."; }
}
public class Dog extends Animal {
@Override
public String speak() { return "Woof"; } // 重写
}
public class Cat extends Animal {
@Override
public String speak() { return "Meow"; }
}
Animal a = new Dog();
Animal b = new Cat();
System.out.println(a.speak()); // Woof
System.out.println(b.speak()); // Meow
运行时根据对象真实类型调用对应方法,这就是多态。
3. final 与不可继承
public final class String { ... } // 不能被继承
public class Dog {
public final void bark() { } // 不能被重写
}
4. Object 通用方法
所有类隐式继承 Object,常用方法:
public boolean equals(Object o)
public int hashCode()
public String toString()
protected Object clone() throws CloneNotSupportedException
Android 中重写
equals必须同时重写hashCode,否则在 HashMap / HashSet 中出错。
接口
1. 接口定义
public interface ClickListener {
void onClick(View view);
default void onLongClick(View view) {} // Java 8 默认方法
}
public class Button implements ClickListener {
@Override
public void onClick(View view) { ... }
}
2. 接口与抽象类对比
| 维度 | 接口 | 抽象类 |
|---|---|---|
| 字段 | 只能 public static final | 任意 |
| 方法 | 抽象 / default / static | 抽象 / 具体 |
| 多继承 | 可实现多个 | 单继承 |
| 构造方法 | 无 | 有 |
Android 中大量回调用接口:View.OnClickListener、RecyclerView.Adapter 的接口契约等。
3. 静态接口方法
public interface Util {
static int add(int a, int b) { return a + b; }
}
int r = Util.add(1, 2);
内部类
1. 静态内部类
public class Outer {
static class Inner { } // 不持有 Outer 实例
}
Android 推荐用静态内部类 +
WeakReference持有外部 Activity,避免内存泄漏。Handler 经典坑就源于匿名内部类持有外部 Activity。
2. 成员内部类
public class Outer {
private int x = 10;
class Inner {
public int getX() { return x; } // 可访问外部字段
}
}
3. 局部内部类与匿名内部类
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.d("TAG", "clicked");
}
});
匿名内部类持有外部类引用,且无法再被继承。Lambda 表达式(Java 8)能替代“单方法接口”的匿名内部类,且不持有外部 this。
泛型
1. 泛型类与方法
public class Pair<A, B> {
public final A first;
public final B second;
public Pair(A first, B second) {
this.first = first;
this.second = second;
}
public static <T> Pair<T, T> of(T a, T b) {
return new Pair<>(a, b);
}
}
Pair<String, Integer> p = Pair.of("age", 18);
2. 通配符
// 只读:列表元素只能是 Number 子类
public static double sum(List<? extends Number> list) {
double total = 0;
for (Number n : list) total += n.doubleValue();
return total;
}
// 只写:list 元素是 Number 父类
public static void addNumbers(List<? super Number> list) {
list.add(1);
list.add(2.0);
}
PECS 原则:Producer extends,Consumer super。
3. 泛型擦除
Java 泛型只在编译期检查,运行期擦除:
List<String> a = new ArrayList<>();
List<Integer> b = new ArrayList<>();
System.out.println(a.getClass() == b.getClass()); // true
Lambda 表达式
1. 函数式接口
只有一个抽象方法的接口:
@FunctionalInterface
public interface Runnable { void run(); }
@FunctionalInterface 是可选注解,加上后编译器会检查。
2. Lambda 语法
Runnable r1 = new Runnable() {
@Override public void run() { System.out.println("hi"); }
};
Runnable r2 = () -> System.out.println("hi");
完整写法:
(int a, int b) -> { return a + b; }
省略写法:
(a, b) -> a + b
3. 方法引用
List<String> names = Arrays.asList("alice", "bob");
names.forEach(System.out::println);
names.stream().map(String::toUpperCase).forEach(System.out::println);
// 构造方法引用
Supplier<ArrayList<String>> supplier = ArrayList::new;
4. Android 中的 Lambda
开启 sourceCompatibility 8 即可使用。常见场景:
view.setOnClickListener(v -> doSomething());
textView.postDelayed(() -> update(), 1000);
new Thread(() -> doBackgroundWork()).start();
Android Studio 默认把 lambda desugar(脱糖)成内部类,对 minSdk 24 不需额外配置;如需完整 Java 8 API 脱糖,在
compileOptions加coreLibraryDesugaringEnabled true。
集合(List/Map/Set)
1. List
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add(0, "x");
String first = list.get(0);
list.remove("a");
int size = list.size();
for (String s : list) { ... }
List<String> immutable = List.of("a", "b", "c"); // Java 9+,minSdk 24 需 desugar
2. Map
Map<String, Integer> map = new HashMap<>();
map.put("a", 1);
map.put("b", 2);
int v = map.get("a");
int def = map.getOrDefault("c", 0);
for (Map.Entry<String, Integer> e : map.entrySet()) {
System.out.println(e.getKey() + "=" + e.getValue());
}
map.forEach((k, val) -> { ... });
3. Set
Set<String> set = new HashSet<>();
set.add("a");
set.add("a"); // 重复不生效
boolean has = set.contains("a");
Set<String> linked = new LinkedHashSet<>(); // 保留插入顺序
Set<String> sorted = new TreeSet<>(); // 自然排序
4. 选择哪个实现
| 类型 | 实现 | 场景 |
|---|---|---|
| List | ArrayList |
随机访问多、插入少 |
| List | LinkedList |
头尾频繁插入删除 |
| Map | HashMap |
通用 key-value |
| Map | LinkedHashMap |
需保留插入顺序 |
| Map | TreeMap |
key 排序 |
| Set | HashSet |
去重 |
| Set | TreeSet |
去重 + 排序 |
Android 中不要用
Vector、Hashtable、Stack,它们是线程安全但性能差的老类。并发场景用java.util.concurrent下的ConcurrentHashMap、CopyOnWriteArrayList。
5. Collections 工具类
List<Integer> list = new ArrayList<>(Arrays.asList(3, 1, 2));
Collections.sort(list);
Collections.reverse(list);
List<Integer> unmod = Collections.unmodifiableList(list); // 不可变
异常处理
1. 异常体系
Throwable
├── Error // 不应捕获:OOM、StackOverflow
└── Exception
├── RuntimeException // 非受检:NPE、IllegalArgument
└── 其它 Exception // 受检:IOException,必须 catch 或 throws
2. try-catch-finally
FileInputStream in = null;
try {
in = new FileInputStream("a.txt");
int b = in.read();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if (in != null) {
try { in.close(); } catch (IOException ignored) {}
}
}
3. try-with-resources(Java 7+)
try (FileInputStream in = new FileInputStream("a.txt");
InputStreamReader reader = new InputStreamReader(in)) {
int b = reader.read();
} catch (IOException e) {
e.printStackTrace();
}
// in 与 reader 自动 close,顺序与声明相反
实现了 AutoCloseable 的对象都可放这里,包括 Android 的 Cursor、FileInputStream 等。
4. 自定义异常
public class ApiException extends RuntimeException {
private final int code;
public ApiException(int code, String msg) {
super(msg);
this.code = code;
}
public int getCode() { return code; }
}
throw new ApiException(404, "Not Found");
5. Android 中常见异常
NullPointerException:未初始化的 View、回调 null;IllegalStateException:Fragment 在 onSaveInstanceState 之后 commit;ActivityNotFoundException:startActivity 没匹配到 Intent;SecurityException:缺少权限调用受限 API;NetworkOnMainThreadException:主线程做网络请求。
IO 基础
1. 字节流与字符流
// 字节流
try (FileInputStream in = new FileInputStream("a.txt");
FileOutputStream out = new FileOutputStream("b.txt")) {
byte[] buffer = new byte[1024];
int n;
while ((n = in.read(buffer)) != -1) {
out.write(buffer, 0, n);
}
} catch (IOException e) {
e.printStackTrace();
}
// 字符流
try (BufferedReader reader = new BufferedReader(new FileReader("a.txt"))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
2. NIO 与 Android
java.nio.file.*(Path、Files)在 Android API 26+ 才支持。如果要在低版本使用,会报 NoClassDefFoundError。Android 上推荐用 java.io + File 或 Okio 库(更简洁、性能好)。
3. SharedPreferences 与文件存储
Android 上持久化推荐:
- 小型 KV:
SharedPreferences/DataStore; - 结构化数据:
Room数据库; - 大文件:内部 / 外部存储 +
File。
示例:写入内部存储:
try (FileOutputStream fos = openFileOutput("note.txt", MODE_PRIVATE)) {
fos.write("hello".getBytes(StandardCharsets.UTF_8));
} catch (IOException e) {
e.printStackTrace();
}
与 Android 开发相关的 Java 特性
1. 注解(Annotation)
@Override // 编译期检查是否真的重写
@Deprecated // 标记废弃
@FunctionalInterface // 标记函数式接口
@Nullable // AndroidX 提供的空安全注解
@NonNull
@Keep // 被混淆器保留
@IntDef({MODE_A, MODE_B}) // 自定义枚举替代
2. 反射
Class<?> clz = Class.forName("com.example.helloapp.MainActivity");
Method m = clz.getMethod("onCreate", Bundle.class);
m.setAccessible(true);
Android 中反射开销大且受 hidden API 限制(API 28+ 反射访问非 SDK 接口会被系统拒绝)。生产环境应避免。
3. try-with-resources 自动资源管理
Android API 19+ 起支持 try-with-resources;Cursor、FileInputStream 等都实现了 AutoCloseable。
4. Stream API(Java 8 +)
List<Integer> nums = Arrays.asList(1, 2, 3, 4, 5);
int sum = nums.stream().filter(n -> n % 2 == 1).mapToInt(Integer::intValue).sum();
String joined = nums.stream().map(String::valueOf).collect(Collectors.joining(","));
开启 coreLibraryDesugaringEnabled true 后在 minSdk 24 上也可用。
5. 单元测试与 JUnit
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class CalculatorTest {
@Test
public void add() {
Calculator calc = new Calculator();
assertEquals(3, calc.add(1, 2));
}
}
常见坑与最佳实践
==比较字符串:永远用.equals()或Objects.equals(a, b);TextUtils.equals(a, b)在 Android 中对 null 安全。- 基本类型与包装类混用导致 NPE:
Integer a = null; int b = a;自动拆箱时抛 NPE;DTO 字段优先用Integer而非int,注意判空。 ArrayList容量增长导致的性能问题:构造时指定初始容量new ArrayList<>(expectedSize),避免反复扩容。- HashMap 在多线程下数据丢失:并发用
ConcurrentHashMap,不要给HashMap加synchronized,性能极差。 - lambda 误用 this:lambda 中的
this指外部类实例,匿名内部类的this指自身。Handler 用 lambda 仍可能泄漏 Activity。 finally中改 return 值:finally块里return会覆盖try中的return,且会吞掉异常。绝不要在finally写return。- catch 仅写
e.printStackTrace():生产环境应统一日志框架上报,至少Log.e(TAG, "msg", e)。 - 反射调用被 hidden API 拦截:API 28+ 通过反射访问非 SDK 接口会得到灰名单/黑名单警告,应用商店审核可能拒绝。改用公开 API 或下沉 native。
- Stream API 在大列表中表现差:小数据量用 Stream 反而比 for 循环慢,仅在可读性优先时使用。
java.io.File在 Android 10+ 受限:scoped storage 后,外部存储的访问需走MediaStore或SAF,直接new File()会抛FileNotFoundException。
章节小结
- Java 8 大基本类型 + 对应包装类,自动装箱有缓存区间,比较包装类用
.equals(); - 运算符支持短路逻辑、位运算适合状态标记;流程控制经典
if/switch/for/while+ Java 17 表达式扩展; - 面向对象三件套:封装(private + getter/setter)、继承(extends + super)、多态(@Override + 运行时绑定);
- 接口支持默认方法与静态方法,匿名内部类可被 lambda 替代;静态内部类不持有外部 this,是避免泄漏的常用手段;
- 泛型 + Lambda + 集合(List/Map/Set)是日常 80% 的数据操作;PECS 原则帮你选
? extends还是? super; - 异常分受检/非受检,
try-with-resources自动关闭资源;Android 上 IO 推荐用try-with-resources+AutoCloseable。
下一章预告
至此 Part 1 入门结束。Part 2 我们进入 UI 基础:View 与 ViewGroup 体系、常用布局(LinearLayout / ConstraintLayout)、TextView/Button/EditText/ImageView 等核心控件、Adapter 与 RecyclerView 列表、自定义 View 入门。带上本 Part 学到的项目结构与 Java 语法,正式开始动手做 UI!