Java 基础系统学习笔记
- [Java 基础](#Java 基础)
- 流程控制
-
- [if-else 语句](#if-else 语句)
- [switch 语句](#switch 语句)
- 循环
- [break 和 continue](#break 和 continue)
- 数组
-
- 数组定义与遍历
- System.arraycopy
- [Arrays 工具类](#Arrays 工具类)
- 方法与可变参数
- 面向对象:类与对象
- [字符串 API](#字符串 API)
-
- StringBuilder
- StringJoiner
- [String / StringBuilder / StringJoiner 对比](#String / StringBuilder / StringJoiner 对比)
- [常用工具 API](#常用工具 API)
-
- [Math 类](#Math 类)
- [Runtime 类](#Runtime 类)
- [System 类](#System 类)
- [Object 类](#Object 类)
- 继承
- 多态
- 抽象类
- 接口
- 内部类
- 代码块
- [final 关键字](#final 关键字)
- 异常处理
- 集合框架
-
- [Collection 体系](#Collection 体系)
- [Map 体系](#Map 体系)
- [List 遍历的 5 种方式](#List 遍历的 5 种方式)
- [TreeMap 示例:字符频率统计](#TreeMap 示例:字符频率统计)
- [HashSet 底层原理](#HashSet 底层原理)
- 不可变集合
- [ArrayList 底层实现](#ArrayList 底层实现)
- 泛型
- 数据结构
- [Lambda 表达式与方法引用](#Lambda 表达式与方法引用)
- [Stream API](#Stream API)
- [日期时间 API](#日期时间 API)
-
- [JDK 8+ 时间 API](#JDK 8+ 时间 API)
- [ZonedDateTime 方法](#ZonedDateTime 方法)
- [Calendar 注意事项](#Calendar 注意事项)
- [IO 流](#IO 流)
-
- 字节流
- 字符流
- 缓冲流
- 转换流(字符集转换)
- 序列化
- Commons-IO
- [File 类](#File 类)
- 多线程
- 网络编程
-
- 网络三要素
- [IP 地址](#IP 地址)
- [UDP 通信](#UDP 通信)
- [TCP 通信](#TCP 通信)
- 反射
- 正则表达式
- 实战练习:随机点名器
- [Java 内存分配](#Java 内存分配)
本笔记结合课件图片与实际代码,覆盖 Java 基础到高级的核心知识点,所有代码示例均采用英文命名规范。
Java 基础
Hello World
每个 Java 程序的起点都是 public static void main(String[] args)。
java
package com.anti.test;
import java.util.Scanner;
public class HelloWorld {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int a = sc.nextInt();
int b = sc.nextInt();
System.out.println(a + b);
}
}
命名规范
Java 遵循两种命名规范:
- 小驼峰 (方法和变量):
name、appleCount、firstName - 大驼峰 / PascalCase (类名):
Student、HelloWorld、SnakeGame
标识符规则
- 由字母、数字、下划线
_、美元符$组成 - 不能以数字开头
- 不能是关键字
- 区分大小写
基本数据类型
| 类型 | 大小 | 范围 |
|---|---|---|
byte |
1 字节 | -128 到 127 |
short |
2 字节 | -32768 到 32767 |
int |
4 字节 | -2^31 到 2^31-1 |
long |
8 字节 | -2^63 到 2^63-1 |
float |
4 字节 | 约 6-7 位小数 |
double |
8 字节 | 约 15 位小数 |
char |
2 字节 | 0 到 65535 |
boolean |
1 位 | true / false |
类型转换
- 隐式转换 :小类型 → 大类型(如
int→long→float→double) - 显式转换 :大类型 → 小类型(如
(int) 3.14) byte、short、char参与运算时会先提升为int
字面量类型
整数、小数、字符串(双引号)、字符(单引号)、布尔值(true/false)、空值(null)。
流程控制
if-else 语句
java
if (condition) {
// 条件为真时执行
} else if (anotherCondition) {
// 另一个条件为真时执行
} else {
// 所有条件都为假时执行
}
注意事项:
- 大括号写在行尾,不另起一行
- 如果语句体只有一行,可以省略大括号
- 条件括号后不要加分号
- 布尔变量直接写变量名:
if (flag)而非if (flag == true)
switch 语句
java
switch (expression) {
case value1:
// code
break;
case value2:
// code
break;
default:
// code
}
switch 支持 byte、short、int、char、String 和 enum 类型。JDK 12/14 新增了箭头标签、多 case 值和 yield 关键字。
循环
java
// for 循环
for (int i = 0; i < 100; i++) {
// 循环体
}
// while 循环
while (condition) {
// 循环体
}
// do-while 循环(至少执行一次)
do {
// 循环体
} while (condition);
break 和 continue
- break:终止当前循环或 switch
- continue:跳过当前迭代,继续下一次循环
java
// break: i == 3 时停止
for (int i = 1; i <= 5; i++) {
if (i == 3) { break; }
System.out.println(i); // 输出 1, 2
}
// continue: 跳过 i == 3
for (int i = 1; i <= 5; i++) {
if (i == 3) { continue; }
System.out.println(i); // 输出 1, 2, 4, 5
}
数组
数组定义与遍历
java
package com.anti.test.arr1;
public class ArrayDemo {
public static void main(String[] args) {
int[] arr = {1, 2, 3, 4, 5};
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}
System.arraycopy
Java 提供 System.arraycopy() 用于高效数组复制:
java
System.arraycopy(src, srcPos, dest, destPos, length);
Arrays 工具类
| 方法 | 说明 |
|---|---|
toString(arr) |
将数组转为字符串 |
binarySearch(arr, key) |
二分查找 |
copyOf(arr, newLength) |
复制数组 |
copyOfRange(arr, from, to) |
范围复制 |
fill(arr, value) |
填充数组 |
sort(arr) |
排序 |
方法与可变参数
可变参数(VarArgs)
可变参数允许方法接收零个或多个同类型参数,语法为 Type... args。
java
package com.anti.map;
public class VarArgs {
public static void main(String[] args) {
System.out.println(sum(1, 2, 3, 4, 5)); // Output: 15
}
private static int sum(int... args) {
int sum = 0;
for (int i : args) {
sum += i;
}
return sum;
}
}
要点:
- 可变参数必须是方法的最后一个参数
- 底层本质是数组
- 一个方法最多只能有一个可变参数
面向对象:类与对象
类的定义与封装
java
package com.anti.test.classtest;
public class Student {
private int age;
private String name;
private char gender;
public Student() {
System.out.println("no-arg constructor");
}
public Student(int age, String name, char gender) {
this.age = age;
this.name = name;
this.gender = gender;
}
public void show() {
System.out.println("age:" + age + " name:" + name + " gender:" + gender);
}
public void setAge(int age) { this.age = age; }
public int getAge() { return age; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
}
使用类
java
package com.anti.test.classtest;
public class StudentDemo {
public static void main(String[] args) {
Student s = new Student(18, "Zhang San", 'M');
s.show();
s.setAge(19);
s.show();
System.out.println(s.getAge());
}
}
this 关键字
- 区分成员变量和局部变量(Java 的"就近原则")
- 调用其他构造方法:
this(args) - 访问成员变量:
this.variableName
权限修饰符
实际开发中,成员变量用 private,方法用 public,从公共代码中提取的辅助方法也用 private。
字符串 API
StringBuilder
| 方法 | 说明 |
|---|---|
append(any type) |
追加数据,返回对象本身 |
reverse() |
反转内容 |
length() |
返回长度 |
toString() |
转为 String |
StringJoiner
| 方法 | 说明 |
|---|---|
add(String) |
添加内容,返回对象本身 |
length() |
返回长度 |
toString() |
返回拼接后的字符串 |
java
StringJoiner sj = new StringJoiner(", ", "[", "]");
sj.add("a").add("b").add("c");
System.out.println(sj.toString()); // [a, b, c]
String / StringBuilder / StringJoiner 对比
- String:不可变字符串
- StringBuilder:高效拼接可变字符串
- StringJoiner(JDK 8+):带分隔符的拼接
常用工具 API
Math 类
所有方法均为静态方法。常用:abs、ceil、floor、max、min、pow、sqrt、cbrt、round、random。
Runtime 类
java
package com.anti.test2.runtime;
public class Test {
public static void main(String[] args) {
System.out.println(Runtime.getRuntime().freeMemory() / 1024 / 1024);
System.out.println(Runtime.getRuntime().totalMemory() / 1024 / 1024);
System.out.println(Runtime.getRuntime().maxMemory() / 1024 / 1024);
}
}
System 类
常用方法:currentTimeMillis()、arraycopy()、exit(status)、getProperty(key)。
Object 类
Object 是所有类的顶层父类:
toString():返回对象的字符串表示equals():比较对象是否相等clone():浅拷贝
继承
基本继承
java
package com.anti.test.inheritance;
public class Animal {
public void eat() { System.out.println("eat"); }
public void water() { System.out.println("drink water"); }
}
class Cat extends Animal {
public void catchMouse() { System.out.println("catching mouse"); }
}
class Dog extends Animal {
public void guardHouse() { System.out.println("guarding house"); }
}
class Husky extends Dog {
public void demolishHouse() { System.out.println("demolishing house"); }
}
继承中的构造方法
- 子类不能继承父类的构造方法,但可以通过
super()调用 - 子类构造方法首行默认有
super()调用 - 父类无参构造先执行,然后子类构造方法执行
- 调用父类有参构造需显式写
super(args)
继承中的变量访问
遵循就近原则:局部变量 → 本类成员变量 → 父类成员变量。使用 this.name 和 super.name 区分。
JavaBean 继承模式
java
package com.anti.test.inheritance;
public class EmployeeDemo {
public static void main(String[] args) {
Manager m = new Manager();
m.work();
m.eat();
Cook c = new Cook();
c.work();
c.eat();
}
}
class Employee {
int id;
String name;
double salary;
private char category;
public void setCategory(char category) { this.category = category; }
public void work() {
if (category == 'A') {
System.out.println("manager is working");
} else {
System.out.println("cook is working");
}
}
public void eat() { System.out.println("eating"); }
}
class Manager extends Employee {
public Manager() { this.setCategory('A'); }
double bonus;
}
class Cook extends Employee {}
继承规则
- Java 只支持单继承,但支持多层继承
- 所有类的顶层父类是 Object
多态
多态成员访问规则
- 变量:编译看左边,运行也看左边
- 方法:编译看左边,运行看右边(动态分派)
多态示例
java
package com.anti.test.polymorphism;
public class Animal {
int age;
String color;
Animal() { System.out.println("no-arg constructor"); }
Animal(int age, String color) {
this.age = age;
this.color = color;
}
public void eat(String something) {
System.out.println("eating " + something);
}
}
public class Dog extends Animal {
Dog() { super(); }
Dog(int age, String color) { super(age, color); }
@Override
public void eat(String something) {
System.out.println("puppy is eating " + something);
}
public void guardHouse() { System.out.println("puppy guarding house"); }
}
public class Cat extends Animal {
Cat() { super(); }
@Override
public void eat(String something) {
System.out.println("kitten is eating " + something);
}
public void catchMouse() { System.out.println("kitten catching mice"); }
}
instanceof 模式匹配
java
package com.anti.test.polymorphism;
import java.io.Serializable;
public class Person implements Serializable {
String name;
int age;
public Person(String name, int age) {
this.age = age;
this.name = name;
}
public void keepPet(Animal a, String something) {
if (a instanceof Dog d) {
d.eat(something);
} else if (a instanceof Cat c) {
c.eat(something);
} else {
System.out.println("no such animal");
}
}
@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + '}';
}
}
Person实现了Serializable接口,便于后续对象序列化(见第 22.5 节)。
抽象类
定义
抽象类不能实例化,可以包含抽象方法(无方法体)和具体方法。
java
package com.anti.test.abstractclass;
public abstract class Animal {
String name;
int age;
public void drink() {
System.out.println("drinking water");
}
public abstract void eat();
}
具体子类
java
public class Dog extends Animal {
@Override
public void eat() { System.out.println("eating bones"); }
}
public class Frog extends Animal {
@Override
public void eat() { System.out.println("eating bugs"); }
}
public class Sheep extends Animal {
@Override
public void eat() { System.out.println("eating grass"); }
}
抽象类注意事项
- 不能实例化
- 可以没有抽象方法
- 可以有构造方法
- 子类必须重写所有抽象方法,否则子类也要声明为抽象类
接口
接口的演进
- JDK 7:只有抽象方法
- JDK 8:新增默认方法和静态方法(有方法体)
- JDK 9:新增私有方法
接口定义与实现
java
package com.anti.test.interface;
public abstract class Animal {
private String name;
private int age;
public Animal() {}
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
public void show() {
System.out.println("name:" + name + " age:" + age);
}
public abstract void eat();
}
public interface Swim {
public void swim();
}
public class Dog extends Animal implements Swim {
public Dog() {}
public Dog(String name, int age) { super(name, age); }
@Override
public void eat() { System.out.println("eating bones"); }
@Override
public void swim() { System.out.println("dog paddle"); }
}
public class Frog extends Animal implements Swim {
public Frog() {}
public Frog(String name, int age) { super(name, age); }
@Override
public void eat() { System.out.println("eating bugs"); }
@Override
public void swim() { System.out.println("breaststroke"); }
}
public class Rabbit extends Animal {
public Rabbit() {}
public Rabbit(String name, int age) { super(name, age); }
@Override
public void eat() { System.out.println("rabbit eating carrots"); }
}
接口成员特点
- 变量都是常量(
public static final) - 没有构造方法
- 方法默认是
public abstract
适配器模式
当接口方法很多但只需要一个时,可以创建一个适配器类做空实现,然后继承适配器类只重写需要的方法。
内部类
分类
- 成员内部类:在外部类的成员位置定义
- 静态内部类 :用
static修饰的成员内部类 - 局部内部类:在方法内定义
- 匿名内部类:没有名字的局部内部类
静态内部类
java
// 直接创建
Outer.Inner oi = new Outer.Inner();
// 非静态方法:先创建对象,再调用
// 静态方法:Outer.Inner.methodName()
匿名内部类
常用于作为方法参数或实现函数式接口:
java
new InterfaceName() {
@Override
public void method() {
// implementation
}
};
内部类总结
- 成员内部类被 private 修饰时:在外部类编写方法对外提供内部类对象
- 成员内部类非 private 修饰时:
Outer.Inner oi = new Outer().new Inner(); - 外部类和内部类变量重名时:
Outer.this.variableName访问外部类变量
代码块
| 类型 | 位置 | 作用 |
|---|---|---|
| 局部代码块 | 方法内 | 限定变量作用域(已过时) |
| 构造代码块 | 类中 | 提取构造方法共性代码 |
| 静态代码块 | 类中加 static |
数据初始化(重要) |
静态代码块在类加载时执行一次,优先于任何构造方法。
final 关键字
- final 类 :不能被继承(如
String) - final 方法:不能被重写
- final 变量:只能赋值一次(常量)
- final 引用:引用不可变,但对象字段可变
异常处理
异常类型
- 编译时异常 :除
RuntimeException及其子类外的所有异常,编译时必须处理 - 运行时异常 :
RuntimeException及其子类,编译时不检查,通常由参数错误导致
自定义异常
java
package com.anti.exception;
public class CustomException extends RuntimeException {
public CustomException() { super(); }
public CustomException(String message) { super(message); }
}
try-catch 使用
java
package com.anti.exception;
public class Test {
public static void main(String[] args) {
try {
test();
} catch (CustomException e) {
e.printStackTrace();
}
}
public static void test() {
try {
int[] a = {};
int i = a[1];
} catch (Exception e) {
throw new CustomException("index out of bounds");
}
}
}
异常处理总结
- JVM 默认处理:打印异常名称、原因、位置并退出
- try-catch:捕获异常并处理
- throw/throws:抛出异常
集合框架
Collection 体系
Collection
|-- List(有序,可重复)
| |-- ArrayList
| |-- LinkedList
| |-- Vector
|-- Set(不可重复)
|-- HashSet
| |-- LinkedHashSet
|-- TreeSet
Map 体系
Map(键值对)
|-- HashMap
| |-- LinkedHashMap
|-- TreeMap
List 遍历的 5 种方式
- 迭代器(Iterator)
- 列表迭代器(ListIterator)
- 增强 for 循环
- Lambda 表达式
- 普通 for 循环(List 有索引)
TreeMap 示例:字符频率统计
java
package com.anti.map;
import java.util.TreeMap;
public class Test {
public static void main(String[] args) {
String s = "helloworldmississippithequickbrownfoxjumpsoverthelazydog";
TreeMap<Character, Integer> map = new TreeMap<>();
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (map.containsKey(c)) {
int count = map.get(c);
count++;
map.put(c, count);
} else {
map.put(c, 1);
}
}
StringBuilder sb = new StringBuilder();
map.forEach((k, v) -> {
sb.append(k).append("(").append(v).append(")");
});
System.out.println(sb);
}
}
HashSet 底层原理
HashSet 底层是 HashMap:
- 计算元素的哈希值
- 如果桶为空,直接添加
- 如果不为空,用
equals()比较:- 相同:重复,不添加
- 不同:添加到链表(链表长度 >= 8 时转红黑树)
参数:初始数组长度 16,负载因子 0.75。
不可变集合
使用 List.of()、Set.of()、Map.of() 创建:
- List:直接使用
- Set:元素不能重复
- Map :键值对不能重复,最多 10 对。超过 10 对用
Map.ofEntries()
ArrayList 底层实现
创建长度为 0 的数组,首次添加元素时扩容为 10,满了后按 1.5 倍增长。
泛型
泛型类
java
class MyGeneric<T> {
private T value;
public T getValue() { return value; }
public void setValue(T value) { this.value = value; }
}
泛型方法
java
public <T> void printArray(T[] array) {
for (T item : array) { System.out.println(item); }
}
通配符
<?>:无界通配符,接受任何类型<? extends T>:上界,T 及其子类<? super T>:下界,T 及其父类
要点:
- 泛型只能使用引用类型
- 运行时类型擦除
- 泛型不参与继承
数据结构
常见数据结构
| 数据结构 | 特点 |
|---|---|
| 栈 | 先进后出(LIFO) |
| 队列 | 先进先出(FIFO) |
| 数组 | 查询快、增删慢(内存连续) |
| 链表 | 查询慢、增删快(内存不连续) |
二叉树
每个节点最多两个子节点:
9
7 2
3 8 1
4 6 5 10
二叉搜索树(BST)
左小右大,相同值不存:
7
4 10
2 5 11
1 3 6 12
遍历方式:
- 前序遍历:根 → 左 → 右
- 中序遍历:左 → 根 → 右(BST 中序遍历结果有序)
- 后序遍历:左 → 右 → 根
平衡二叉树
任何节点的左右子树高度差不超过 1。旋转情况:
- 左左:右旋
- 左右:先左旋再右旋
- 右右:左旋
- 右左:先右旋再左旋
红黑树
自平衡二叉搜索树,规则:
- 每个节点是红色或黑色
- 根节点是黑色
- 每个叶子节点(NIL)是黑色
- 红色节点的子节点必须是黑色
- 从任一节点到其叶子的所有路径包含相同数量的黑色节点
Lambda 表达式与方法引用
Lambda 表达式
简化函数式接口(只有一个抽象方法)的写法:
java
// 匿名内部类
Arrays.sort(arr, new Comparator<String>() {
public int compare(String o1, String o2) {
return o1.length() - o2.length();
}
});
// Lambda 表达式
Arrays.sort(arr, (o1, o2) -> o1.length() - o2.length());
实际示例
java
package com.anti.date;
import java.util.Arrays;
public class Test {
public static void main(String[] args) {
String[] arr = {"a", "aaa", "aa", "aaaa"};
Arrays.sort(arr, (o1, o2) -> o1.length() - o2.length());
System.out.println(Arrays.toString(arr)); // [a, aa, aaa, aaaa]
}
}
方法引用
java
// Lambda: s -> System.out.println(s)
// Method ref: System.out::println
// Lambda: (s1, s2) -> s1.equals(s2)
// Method ref: String::equals
方法引用五种类型:静态方法引用、成员方法引用、构造方法引用、类名成员方法引用、数组构造方法引用。
Stream API
概述
Stream API + Lambda 简化集合/数组操作。
步骤:
- 获取 Stream 对象
- 使用中间方法处理数据
- 使用终结方法产出结果
获取 Stream
- 单列集合 :
collection.stream() - 双列集合:先转 entrySet
- 数组 :
Arrays.stream(array) - 零散数据 :
Stream.of(items...)
常用方法
| 分类 | 方法 |
|---|---|
| 中间方法 | filter、limit、skip、distinct、concat、map |
| 终结方法 | forEach、count、toArray、collect |
java
list.stream()
.filter(s -> s.startsWith("a"))
.map(String::toUpperCase)
.forEach(System.out::println);
注意:Stream 的修改不会影响原始集合。
日期时间 API
JDK 8+ 时间 API
JDK8 Time
|-- Date class
| |-- ZoneId: 时区
| |-- Instant: 时间戳
| |-- ZonedDateTime: 带时区的时间
|-- Calendar class
| |-- Calendar
| |-- LocalDate: 年月日
| |-- LocalTime: 时分秒
| |-- LocalDateTime: 以上全部
|-- Formatting
| |-- SimpleDateFormat
| |-- DateTimeFormatter
|-- Utility
|-- Duration: 间隔(秒、纳秒)
|-- Period: 间隔(年、月、日)
|-- ChronoUnit: 间隔(所有单位)
ZonedDateTime 方法
| 方法 | 说明 |
|---|---|
static now() |
当前时间 |
static ofXxxx(...) |
指定时间 |
withXxx(time) |
修改时间 |
minusXxx(time) |
减少时间 |
plusXxx(time) |
增加时间 |
Calendar 注意事项
- 月份范围:0-11(1 月是 0)
- 星期日是每周第一天
IO 流
字节流
java
package com.anti.file.iostream;
import java.io.FileOutputStream;
import java.io.IOException;
public class Test {
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream("/Users/umi/Desktop/a.txt");
fos.write(97); // writes 'a'
fos.close();
}
}
FileInputStream 读取:
read():每次读一个字节,末尾返回 -1read(byte[] buffer):读入缓冲区,末尾返回 -1
java
// 文件复制
FileInputStream fis = new FileInputStream("a.txt");
FileOutputStream fos = new FileOutputStream("b.txt");
byte[] bytes = new byte[1024 * 1024 * 5]; // 5MB buffer
int len;
while ((len = fis.read(bytes)) != -1) {
fos.write(bytes, 0, len);
}
fos.close();
fis.close();
字符流
java
package com.anti.file.iostream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class FileWriterFileReader {
public static void main(String[] args) throws IOException {
String userDir = System.getProperty("user.dir");
String url = userDir + "/src/com/anti/file/iostream/a.txt";
FileWriter fw = new FileWriter(url, true);
fw.write("I am Java");
fw.close();
FileReader fr = new FileReader(url);
char[] chars = new char[1024];
fr.read(chars);
System.out.println(chars);
}
}
字符流底层使用 8192 字节缓冲区,先从缓冲区读取,缓冲区空了再从文件读取。
缓冲流
java
package com.anti.file.iostream;
import java.io.*;
public class BufferDemo {
public static void main(String[] args) throws IOException {
String userDir = System.getProperty("user.dir");
String url = userDir + "/src/com/anti/file/iostream/a.txt";
FileInputStream fis = new FileInputStream(url);
BufferedInputStream bis = new BufferedInputStream(fis);
int b = bis.read();
System.out.println((char) b);
bis.close();
}
}
四种缓冲流:BufferedInputStream、BufferedOutputStream、BufferedReader、BufferedWriter,内置 8192 字节缓冲区。BufferedReader.readLine() 和 BufferedWriter.newLine() 是关键方法。
转换流(字符集转换)
java
package com.anti.file.iostream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.charset.Charset;
public class ConversionStream {
public static void main(String[] args) throws IOException {
String userDir = System.getProperty("user.dir");
String rUrl = userDir + "/src/com/anti/file/iostream/c.txt";
String wUrl = userDir + "/src/com/anti/file/iostream/d.txt";
FileReader fr = new FileReader(rUrl, Charset.forName("GBK"));
FileWriter fw = new FileWriter(wUrl, Charset.forName("UTF-8"));
int b;
while ((b = fr.read()) != -1) { fw.write(b); }
fr.close();
fw.close();
}
}
序列化
java
package com.anti.file.iostream;
import com.anti.test.polymorphism.Person;
import java.io.*;
public class Serialization {
public static void main(String[] args) throws IOException, ClassNotFoundException {
String userDir = System.getProperty("user.dir");
String path = userDir + "/src/com/anti/file/iostream/serialized.txt";
// Serialize
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(path));
oos.writeObject(new Person("Zhang San", 18));
oos.close();
// Deserialize
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(path));
Person p = (Person) ois.readObject();
System.out.println(p.toString());
ois.close();
}
}
序列化注意事项:
- 必须实现
Serializable接口 - 使用
serialVersionUID进行版本控制 transient关键字可排除字段不被序列化
Commons-IO
java
package com.anti.file.iostream;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
public class CommonsIoTest {
public static void main(String[] args) throws IOException {
String userDir = System.getProperty("user.dir");
String path = userDir + "/src/com/anti/file/iostream/aaa/test.txt";
FileUtils.touch(new File(path));
ArrayList<String> list = new ArrayList<>();
list.add("1");
list.add("2");
FileUtils.writeLines(new File(path), list);
}
}
File 类
java
package com.anti.file;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
public class Test {
public static void main(String[] args) throws IOException {
File file = new File("/Users/umi/Desktop");
File[] files = file.listFiles();
System.out.println(Arrays.toString(files));
}
}
字节流 vs 字符流使用场景: 字节流用于复制任意文件,字符流用于读写纯文本文件。
多线程
线程状态
NEW --> RUNNABLE --> TERMINATED
|
+--> BLOCKED(获取不到锁)
+--> WAITING(wait 方法)
+--> TIMED_WAITING(sleep 方法)
创建线程的三种方式
方式一:继承 Thread
java
package com.anti.thread;
public class CustomThread extends Thread {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " started");
}
}
方式二:实现 Runnable
java
package com.anti.thread;
import java.util.Objects;
public class C2Thread implements Runnable {
@Override
public void run() {
String name = Thread.currentThread().getName();
for (int i = 0; i < 10; i++) {
System.out.println(name + " Runnable running " + i);
}
}
}
方式三:实现 Callable(可以有返回值)
java
package com.anti.thread;
import java.util.concurrent.Callable;
public class CallableThread implements Callable<Integer> {
@Override
public Integer call() throws Exception { return 0; }
}
三种方式综合示例
java
package com.anti.thread;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class ThreadDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 1. Extend Thread
Thread t1 = new CustomThread();
t1.setName("Thread-1");
t1.start();
// 2. Implement Runnable
Thread t4 = new Thread(new C2Thread());
t4.start();
// 3. Implement Callable
CallableThread myCallable = new CallableThread();
FutureTask<Integer> futureTask = new FutureTask<>(myCallable);
Thread t6 = new Thread(futureTask);
t6.start();
int result = futureTask.get();
System.out.println("Result: " + result);
}
}
同步:同步代码块
java
package com.anti.thread;
public class TicketSyncBlock implements Runnable {
static int tickets = 0;
static Object obj = new Object();
@Override
public void run() {
while (true) {
synchronized (obj) {
if (tickets < 100) {
tickets++;
System.out.println(Thread.currentThread().getName() + " selling ticket #" + tickets);
} else { break; }
try { Thread.sleep(10); }
catch (InterruptedException e) { throw new RuntimeException(e); }
}
}
}
}
同步:同步方法
java
package com.anti.thread;
public class TicketSyncMethod implements Runnable {
static int tickets = 0;
@Override
public void run() {
while (true) { if (sellTicket()) break; }
}
private synchronized boolean sellTicket() {
if (tickets < 100) {
tickets++;
System.out.println(Thread.currentThread().getName() + " selling ticket #" + tickets);
} else { return true; }
try { Thread.sleep(10); }
catch (InterruptedException e) { throw new RuntimeException(e); }
return false;
}
}
同步:Lock 接口
java
package com.anti.thread;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockDemo extends Thread {
static int tickets = 0;
static Lock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
lock.lock();
try {
if (tickets < 100) {
tickets++;
System.out.println(Thread.currentThread().getName() + " selling ticket #" + tickets);
} else { break; }
} finally { lock.unlock(); }
try { Thread.sleep(10); }
catch (InterruptedException e) { throw new RuntimeException(e); }
}
}
}
线程属性
- 优先级:范围 1-10,默认 5。不是绝对的,是概率问题
- 守护线程 :
setDaemon(true),所有非守护线程结束后自动结束 - 插入线程 :
t1.join(),当前线程等待 t1 执行完毕
线程池
使用 Executors:
java
package com.anti.thread.threadpool;
import com.anti.thread.ThreadPoolTask;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolDemo {
public static void main(String[] args) {
ExecutorService pool = Executors.newFixedThreadPool(5);
ThreadPoolTask task = new ThreadPoolTask();
pool.submit(task);
pool.submit(task);
pool.shutdown();
}
}
自定义 ThreadPoolExecutor:
java
package com.anti.thread.threadpool;
import com.anti.thread.ThreadPoolTask;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class CustomThreadPool {
public static void main(String[] args) {
ThreadPoolExecutor pool = new ThreadPoolExecutor(
4, // core pool size
8, // max pool size
60, // keep-alive time
TimeUnit.SECONDS, // time unit
new ArrayBlockingQueue<>(5), // work queue
new ThreadPoolExecutor.AbortPolicy() // rejection policy
);
ThreadPoolTask task = new ThreadPoolTask();
pool.submit(task);
}
}
线程池核心原理: 创建空线程池 → 提交任务时创建线程 → 任务完成后线程归还 → 无空闲线程时任务排队 → 队列满后创建临时线程 → 达到最大线程数后执行拒绝策略。
线程池大小建议:
- CPU 密集型:最大并行数 + 1
- IO 密集型:最大并行数 × 预期 CPU 利用率 × 总时间 / CPU 时间
生产者-消费者(等待/唤醒)
- 消费者:检查桌子 → 没食物?等待 → 有食物?吃 → 唤醒生产者
- 生产者:检查桌子 → 有食物?等待 → 没食物?做 → 放桌上 → 唤醒消费者
网络编程
网络三要素
- IP 地址:设备在网络中的唯一标识
- 端口号:应用程序在设备中的唯一标识
- 协议:数据传输的规则(UDP、TCP、HTTP、HTTPS、FTP)
IP 地址
- IPv4 有 2^32 个地址(已耗尽),IPv6 有 2^128 个地址
- 特殊 IP:
127.0.0.1(本机) - 常用命令:
ipconfig(查看 IP)、ping(检测连通性)
UDP 通信
发送端:
java
package com.anti.network.udp;
import java.io.IOException;
import java.net.*;
public class UdpDemo {
public static void main(String[] args) throws IOException {
DatagramSocket ds = new DatagramSocket();
String message = "hello world";
byte[] bytes = message.getBytes();
InetAddress inetAddress = InetAddress.getByName("127.0.0.1");
DatagramPacket dp = new DatagramPacket(bytes, bytes.length, inetAddress, 8082);
ds.send(dp);
ds.close();
}
}
接收端:
java
package com.anti.network.udp;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class Receiver {
public static void main(String[] args) throws IOException {
DatagramSocket ds = new DatagramSocket(8082);
while (true) {
byte[] bytes = new byte[1024];
DatagramPacket dp = new DatagramPacket(bytes, bytes.length);
ds.receive(dp);
System.out.println(new String(dp.getData()));
}
}
}
TCP 通信
客户端:
java
package com.anti.network.tcp;
import java.io.IOException;
import java.net.Socket;
public class Sender {
public static void main(String[] args) throws IOException {
Socket socket = new Socket("127.0.0.1", 10086);
socket.getOutputStream().write("hello world".getBytes());
socket.close();
}
}
服务端:
java
package com.anti.network.tcp;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
public class Receiver {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(10086);
Socket socket = serverSocket.accept();
InputStreamReader reader = new InputStreamReader(socket.getInputStream());
int b;
while ((b = reader.read()) != -1) {
System.out.print((char) b);
}
reader.close();
socket.close();
serverSocket.close();
System.out.println("reception complete");
}
}
反射
获取 Class 对象的三种方式
java
package com.anti.reflection;
public class Test {
public static void main(String[] args) throws ClassNotFoundException {
// Method 1: Class.forName
Class<?> c1 = Class.forName("com.anti.reflection.Student");
// Method 2: .class
Class<?> c2 = Student.class;
// Method 3: getClass()
Student s = new Student();
Class<?> c3 = s.getClass();
System.out.println(c1.hashCode());
System.out.println(c2.hashCode());
System.out.println(c3.hashCode());
}
}
Student 类
java
package com.anti.reflection;
public class Student {
private String name;
private int age;
public int score;
public Student() {
System.out.println("no-arg constructor");
}
public Student(String name, int age) {
this.name = name;
this.age = age;
System.out.println("parameterized constructor");
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "score" + score + "]";
}
}
获取构造方法
java
Constructor<?>[] cons1 = clazz.getConstructors(); // public only
Constructor<?>[] cons2 = clazz.getDeclaredConstructors(); // all including private
Constructor<?> con1 = clazz.getDeclaredConstructor(); // no-arg
Constructor<?> con2 = clazz.getDeclaredConstructor(String.class); // with String param
获取方法
java
Method[] methods = clazz.getMethods(); // public including inherited
Method[] methods = clazz.getDeclaredMethods(); // all including private, not inherited
Method m = clazz.getDeclaredMethod("eat", String.class);
int modifiers = m.getModifiers();
获取字段
java
Field field = clazz.getDeclaredField("name");
field.setAccessible(true);
field.set(student, "New Name");
动态代理
java
Proxy.newProxyInstance(
classLoader,
interfaces,
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(target, args);
}
}
);
正则表达式
| 符号 | 含义 | 示例 |
|---|---|---|
? |
0 次或 1 次 | \d? |
* |
0 次或多次 | \d* |
+ |
1 次或多次 | \d+ |
{n} |
恰好 n 次 | a{7} |
{n,m} |
n 到 m 次 | \d{7,19} |
(?i) |
忽略大小写 | (?i)abc |
java
boolean matches = "hello".matches("h.*");
String[] parts = "a,b,,c".split(",");
String result = "a1b2c3".replaceAll("\\d", "_");
实战练习:随机点名器
java
package com.anti.exercise;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
public class RandomRollCall {
public static void main(String[] args) {
ArrayList<String> arr = new ArrayList<>();
Collections.addAll(arr, "fan", "jian", "cc", "bb", "aa");
Random random = new Random();
int index = random.nextInt(arr.size());
System.out.println(arr.get(index));
List<String> list = List.of("fan", "jian", "cc", "bb", "aa");
}
}
Java 内存分配
| 区域 | 存储内容 |
|---|---|
| 栈 | 方法运行时使用的内存 |
| 堆 | new 关键字创建的对象 |
| 方法区 | 字节码文件加载后的存储 |
| 本地方法栈 | JVM 调用操作系统功能时使用 |
| 寄存器 | CPU 寄存器 |
基本类型存储真实数据,引用类型存储地址值。