完整教程:Java编程基础:从入门到精通
阶段一:Java 基础与编程思维
一、Java 基础语法(变量、条件、循环、数组)
变量
- 定义:用于存储程序运行过程中需要临时保存的数据,需指定 “数据类型 + 变量名”,遵循 “先声明、后使用” 原则。
- 核心分类:
- 基本数据类型:8 种,分别对应不同数据场景,无需手动回收内存,具体如下:
类型分类 具体类型 占用空间 应用场景 整数型 byte 1 字节 存储小范围整数(如年龄、状态码) short 2 字节 中等范围整数(如短 ID) int 4 字节 常用整数(如数量、索引),默认整数类型 long 8 字节 大范围整数(如时间戳、大 ID),需加后缀 L(如100L)浮点型 float 4 字节 单精度小数(如粗略计算),需加后缀 F(如3.14F)double 8 字节 双精度小数(如精确计算:金额、坐标),默认小数类型 字符型 char 2 字节 单个字符(如 'A'、'中'),用单引号包裹布尔型 boolean 1 位 逻辑判断( true/false),常用于条件语句 - 引用数据类型:如数组、类对象(后续学习的
String、集合等),存储的是数据在堆内存中的 “地址”,需手动管理或依赖 GC 回收。
- 基本数据类型:8 种,分别对应不同数据场景,无需手动回收内存,具体如下:
- 使用规则:变量名需符合 “字母、数字、下划线、$” 组成,且不能以数字开头,建议遵循 “小驼峰命名法”(如
userAge、totalScore)。
条件
- 定义:根据 “条件是否成立” 执行不同代码分支,解决程序中的 “选择逻辑”(如判断用户权限、数据合法性)。
- 核心结构:
if-else:基础条件判断,支持嵌套,适合 1-3 个分支场景。// 示例:判断成绩等级 int score = 85; if (score >= 90) { System.out.println("优秀"); } else if (score >= 80) { System.out.println("良好"); } else if (score >= 60) { System.out.println("及格"); } else { System.out.println("不及格"); }switch-case:适合 “固定值匹配” 场景(如菜单选择、状态码处理),JDK 7 + 支持String类型,需注意break(避免穿透)和default(默认分支)。// 示例:判断星期几 String week = "周二"; switch (week) { case "周一": case "周二": case "周三": case "周四": case "周五": System.out.println("工作日"); break; case "周六": case "周日": System.out.println("休息日"); break; default: System.out.println("输入错误"); }
循环
- 定义:重复执行一段代码,解决 “批量处理” 场景(如遍历数组、统计数据),避免代码冗余。
- 核心结构:
for:适合 “已知循环次数” 场景(如遍历固定长度的数组、执行指定次数的操作),结构为 “初始化变量→循环条件→更新变量”。// 示例:计算1-10的和 int sum = 0; for (int i = 1; i <= 10; i++) { sum += i; // 每次循环累加i } System.out.println("1-10的和:" + sum); // 输出55while:适合 “未知循环次数,已知循环条件” 场景(如读取用户输入直到输入正确),需先初始化变量,避免死循环。// 示例:循环接收用户输入,直到输入数字 Scanner scanner = new Scanner(System.in); int num = 0; while (!scanner.hasNextInt()) { System.out.println("请输入数字:"); scanner.next(); // 丢弃非数字输入 } num = scanner.nextInt(); System.out.println("你输入的数字:" + num);do-while:与while类似,但 “先执行一次循环体,再判断条件”,确保循环体至少执行一次(如菜单交互:先显示菜单,再接收选择)。
数组
- 定义:存储 “同类型、固定长度” 的批量数据,通过 “索引”(从 0 开始)访问元素,是后续集合框架的基础。
- 核心操作:
- 初始化:分 “静态初始化”(已知元素)和 “动态初始化”(已知长度)两种。
// 静态初始化:直接指定元素,长度由元素个数决定 int[] scores = {85, 92, 78}; // 动态初始化:指定长度,元素默认初始化(int默认0,String默认null) String[] names = new String[3]; names[0] = "张三"; names[1] = "李四"; - 遍历:通过
for循环(普通 / 增强)访问所有元素,增强for循环更简洁(无需关注索引)。// 普通for循环:适合需要索引的场景(如修改元素) for (int i = 0; i < scores.length; i++) { System.out.println("索引" + i + ":" + scores[i]); } // 增强for循环:适合仅读取元素的场景 for (String name : names) { System.out.println("姓名:" + name); } - 注意事项:数组长度一旦确定无法修改,若索引超出范围(如访问
scores[3],而数组长度为 3),会抛出ArrayIndexOutOfBoundsException(数组越界异常)
- 初始化:分 “静态初始化”(已知元素)和 “动态初始化”(已知长度)两种。
二、面向对象编程(类、继承、多态、接口、抽象类)
类
- 定义:对 “现实事物” 的抽象描述,包含 “属性”(事物的特征,如用户的姓名、年龄)和 “方法”(事物的行为,如用户的登录、下单),是创建对象的 “模板”。
- 核心组成:
- 成员变量:定义在类中、方法外,描述对象的属性,默认有初始值(如
int默认 0,String默认null)。 - 成员方法:定义在类中,描述对象的行为,可带参数和返回值,调用时需通过对象实例。
- 构造方法:用于初始化对象,与类名同名、无返回值,默认提供无参构造;若自定义构造方法,默认无参构造会失效,需手动添加。
- 成员变量:定义在类中、方法外,描述对象的属性,默认有初始值(如
- 示例:
// 定义“用户”类(模板) class User { // 成员变量(属性) private String username; // 私有属性,通过getter/setter访问 private int age; // 构造方法(初始化对象) public User(String username, int age) { this.username = username; // this表示当前对象 this.age = age; } // 成员方法(行为) public void showInfo() { System.out.println("用户名:" + username + ",年龄:" + age); } // Getter/Setter(访问私有属性) public String getUsername() { return username; } public void setAge(int age) { if (age > 0 && age < 150) { // 合法性校验 this.age = age; } } } // 创建对象(实例化)并使用 public class ClassDemo { public static void main(String[] args) { User user = new User("张三", 25); // 调用构造方法创建对象 user.showInfo(); // 调用方法:输出“用户名:张三,年龄:25” user.setAge(30); // 修改属性 System.out.println("修改后年龄:" + user.getUsername() + "," + user.getAge()); } }
继承
- 定义:让 “子类” 复用 “父类” 的属性和方法,同时可添加子类专属的属性和方法,实现 “代码复用”,通过
extends关键字实现,Java 支持单继承(一个子类只能有一个直接父类)。 - 核心特性:
- 方法重写(Override):子类对父类的方法进行 “重新实现”,要求方法名、参数列表、返回值类型完全一致,且父类方法不能是
private(私有方法无法继承)。 super关键字:用于访问父类的属性、方法和构造方法,如super.username访问父类属性,super()调用父类构造方法(需放在子类构造方法第一行)。
- 方法重写(Override):子类对父类的方法进行 “重新实现”,要求方法名、参数列表、返回值类型完全一致,且父类方法不能是
- 示例:
// 父类:动物(通用属性和方法) class Animal { protected String name; // protected:子类可访问 public Animal(String name) { this.name = name; } // 父类方法 public void eat() { System.out.println(name + "正在进食"); } } // 子类:狗(继承Animal) class Dog extends Animal { // 子类专属属性 private String breed; // 子类构造方法:必须调用父类构造方法 public Dog(String name, String breed) { super(name); // 调用父类构造方法 this.breed = breed; } // 方法重写:重写父类的eat方法 @Override public void eat() { System.out.println(name + "(" + breed + ")正在吃狗粮"); } // 子类专属方法 public void bark() { System.out.println(name + "汪汪叫"); } } // 测试继承 public class ExtendDemo { public static void main(String[] args) { Dog dog = new Dog("旺财", "金毛"); dog.eat(); // 调用重写后的方法:输出“旺财(金毛)正在吃狗粮” dog.bark(); // 调用子类专属方法:输出“旺财汪汪叫” } }
多态
- 定义:“同一行为,不同实现”,即父类引用指向子类对象时,调用方法会执行子类的重写实现,实现代码的 “灵活性” 和 “扩展性”。
- 实现条件:
- 存在继承关系(子类继承父类);
- 子类重写父类方法;
- 父类引用指向子类对象(如
Animal animal = new Dog();)。
- 示例:
// 父类:动物(已定义) // 子类:猫(新增子类) class Cat extends Animal { public Cat(String name) { super(name); } // 重写父类eat方法 @Override public void eat() { System.out.println(name + "正在吃猫粮"); } // 子类专属方法 public void meow() { System.out.println(name + "喵喵叫"); } } // 测试多态 public class PolymorphismDemo { public static void main(String[] args) { // 父类引用指向子类对象(多态核心) Animal animal1 = new Dog("旺财", "金毛"); Animal animal2 = new Cat("咪宝"); // 调用eat方法:执行子类重写的实现(多态体现) animal1.eat(); // 输出“旺财(金毛)正在吃狗粮” animal2.eat(); // 输出“咪宝正在吃猫粮” // 注意:父类引用无法直接调用子类专属方法(需强制转型) if (animal1 instanceof Dog) { // 判断对象类型 Dog dog = (Dog) animal1; // 强制转型为Dog dog.bark(); // 调用子类专属方法:输出“旺财汪汪叫” } } }
接口
- 定义:一种 “特殊的抽象类型”,仅包含抽象方法(JDK 8 + 支持默认方法
default和静态方法static),用于定义 “方法规范”,强制实现类遵循统一标准,通过implements关键字实现,Java 支持多实现(一个类可实现多个接口)。 - 核心特性:
- 接口中的方法默认是
public abstract(可省略),属性默认是public static final(常量); - 实现类必须重写接口中的所有抽象方法,否则实现类需定义为抽象类;
- 多实现:解决 Java 单继承的限制,一个类可同时实现多个接口,如
class Student implements Study, Sports。
- 接口中的方法默认是
- 示例:
// 定义接口1:学习(规范学习行为) interface Study { void study(); // 抽象方法,默认public abstract String COURSE = "Java"; // 常量,默认public static final } // 定义接口2:运动(规范运动行为) interface Sports { void doSports(); // 抽象方法 } // 实现类:学生(实现两个接口) class Student implements Study, Sports { private String name; public Student(String name) { this.name = name; } // 重写Study接口的study方法 @Override public void study() { System.out.println(name + "正在学习" + Study.COURSE); } // 重写Sports接口的doSports方法 @Override public void doSports() { System.out.println(name + "正在打篮球"); } } // 测试接口 public class InterfaceDemo { public static void main(String[] args) { Student student = new Student("李四"); student.study(); // 输出“李四正在学习Java” student.doSports(); // 输出“李四正在打篮球” } }
抽象类
- 定义:包含 “抽象方法” 的类,通过
abstract关键字定义,无法直接实例化(需通过子类继承并实现抽象方法),用于 “既提供代码复用,又强制子类实现核心逻辑” 的场景。 - 与接口的区别:
对比维度 抽象类 接口 方法类型 可包含抽象方法、普通方法、默认方法 JDK 8 前仅抽象方法;JDK 8 后支持默认 / 静态方法 属性类型 可包含任意属性(成员变量、常量) 仅支持常量(public static final) 继承 / 实现 子类通过 extends单继承实现类通过 implements多实现实例化 无法直接实例化 无法直接实例化 核心作用 代码复用(提供普通方法)+ 规范方法(抽象方法) 纯方法规范(强制实现类遵循标准) - 示例:
// 抽象类:图形(提供通用方法,强制子类实现核心方法) abstract class Shape { // 普通方法(代码复用) public void show() { System.out.println("这是一个图形"); } // 抽象方法(强制子类实现:计算面积) public abstract double calculateArea(); } // 子类1:圆形(继承抽象类,实现抽象方法) class Circle extends Shape { private double radius; // 半径 public Circle(double radius) { this.radius = radius; } // 实现抽象方法:计算圆形面积(πr²) @Override public double calculateArea() { return Math.PI * radius * radius; } } // 子类2:矩形(继承抽象类,实现抽象方法) class Rectangle extends Shape { private double width; // 宽 private double height; // 高 public Rectangle(double width, double height) { this.width = width; this.height = height; } // 实现抽象方法:计算矩形面积(宽×高) @Override public double calculateArea() { return width * height; } } // 测试抽象类 public class AbstractClassDemo { public static void main(String[] args) { Shape circle = new Circle(5); circle.show(); // 输出“这是一个图形” System.out.println("圆形面积:" + circle.calculateArea()); // 输出≈78.54 Shape rectangle = new Rectangle(4, 6); rectangle.show(); // 输出“这是一个图形” System.out.println("矩形面积:" + rectangle.calculateArea()); // 输出24.0 } }
三、核心 API(集合、字符串、日期、工具类)
集合
- 定义:用于存储 “动态长度、可灵活操作” 的批量数据,解决数组长度固定的问题,主要分为
Collection(单列集合)和Map(双列集合,键值对)两大体系。 - 核心体系与应用场景:
集合类型 具体实现类 核心特点 应用场景 Collection → List ArrayList 基于数组实现,查询快、增删慢(尾部增删快),线程不安全 需频繁查询、少量增删的场景(如商品列表、成绩排名) LinkedList 基于链表实现,查询慢、增删快,线程不安全 需频繁增删、少量查询的场景(如购物车添加 / 删除商品) Collection → Set HashSet 基于哈希表实现,无序、不可重复,线程不安全 去重场景(如用户标签、不重复的 ID 列表) TreeSet 基于红黑树实现,有序(自然排序 / 自定义排序)、不可重复,线程不安全 排序 + 去重场景(如按价格排序的商品 ID) Map HashMap 基于哈希表实现,无序、键唯一、值可重复,线程不安全 键值对映射场景(如用户 ID→用户信息、配置项 key→value) TreeMap 基于红黑树实现,键有序(自然排序 / 自定义排序),线程不安全 有序键值对场景(如按日期排序的日志记录) - 示例(ArrayList 与 HashMap):
import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class CollectionDemo { public static void main(String[] args) { // 1. ArrayList:存储商品列表 List products = new ArrayList<>(); products.add("手机"); // 添加元素 products.add("电脑"); products.add("平板"); System.out.println("商品列表:" + products); // 输出[手机, 电脑, 平板] // 遍历ArrayList System.out.print("遍历商品:"); for (String product : products) { System.out.print(product + " "); // 输出“手机 电脑 平板 ” } System.out.println(); // 2. HashMap:存储用户ID→用户姓名 Map userMap = new HashMap<>(); userMap.put(1, "张三"); // 添加键值对 userMap.put(2, "李四"); userMap.put(3, "王五"); System.out.println("用户映射:" + userMap); // 输出{1=张三, 2=李四, 3=王五} // 根据键获取值 String userName = userMap.get(2); System.out.println("ID=2的用户:" + userName); // 输出“李四” // 遍历HashMap(键值对) System.out.print("遍历用户:"); for (Map.Entry entry : userMap.entrySet()) { System.out.print("(" + entry.getKey() + ":" + entry.getValue() + ") "); } // 输出“(1:张三) (2:李四) (3:王五) ” } }
字符串
- 核心类:
String、StringBuilder、StringBuffer,均用于处理字符串,但特性不同,需根据场景选择。 - 核心区别与应用场景:
类名 可变性 线程安全 效率 应用场景 String 不可变(每次修改生成新对象) 安全(无修改操作) 低(频繁修改会产生大量临时对象) 字符串常量、少量修改的场景(如用户名、固定文本) StringBuilder 可变(修改自身,不生成新对象) 不安全(无同步锁) 高 单线程下频繁修改字符串(如日志拼接、单线程文本处理) StringBuffer 可变 安全(有同步锁) 中(锁开销) 多线程下频繁修改字符串(如多线程日志记录) - 常用方法(String):
length():获取字符串长度;charAt(int index):获取指定索引的字符;substring(int beginIndex, int endIndex):截取子串(左闭右开);equals(Object obj):比较字符串内容(区分大小写);contains(CharSequence s):判断是否包含指定子串;replace(CharSequence old, CharSequence new):替换子串。
- 示例:
public class StringDemo { public static void main(String[] args) { // 1. String:不可变字符串 String str = "Hello Java"; System.out.println("字符串长度:" + str.length()); // 输出10 System.out.println("第6个字符:" + str.charAt(5)); // 输出'J'(索引从0开始) System.out.println("截取子串(0-5):" + str.substring(0, 5)); // 输出"Hello" System.out.println("是否包含'Java':" + str.contains("Java")); // 输出true // 2. StringBuilder:单线程频繁修改 StringBuilder sb = new StringBuilder(); sb.append("Hello"); // 拼接字符串 sb.append(" "); sb.append("World"); sb.insert(5, " Java"); // 在索引5处插入 System.out.println("StringBuilder结果:" + sb); // 输出"Hello Java World" // 3. StringBuffer:多线程频繁修改(示例略,用法与StringBuilder类似) } }
日期
- 核心类:JDK 8 前用
Date、Calendar,JDK 8 后推荐用java.time包下的LocalDateTime、LocalDate、LocalTime(线程安全、API 更友好),主要用于处理时间的获取、格式化、计算。 - 核心类与功能:
LocalDate:处理日期(年、月、日),如2024-05-20;LocalTime:处理时间(时、分、秒),如15:30:45;LocalDateTime:处理日期 + 时间,如2024-05-20T15:30:45;DateTimeFormatter:格式化日期时间(自定义格式,如yyyy-MM-dd HH:mm:ss)。
- 常用操作:
- 获取当前时间:
LocalDateTime.now(); - 格式化:
localDateTime.format(formatter); - 时间计算:
plusDays(1)(加 1 天)、minusHours(2)(减 2 小时); - 解析字符串为日期:
LocalDateTime.parse(str, formatter)。
- 获取当前时间:
- 示例:
import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; public class DateDemo { public static void main(String[] args) { // 1. 获取当前日期时间 LocalDateTime now = LocalDateTime.now(); System.out.println("当前时间(默认格式):" + now); // 输出如“2024-05-20T15:30:45.123456789” // 2. 格式化日期时间(自定义格式) DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); String formattedTime = now.format(formatter); System.out.println("格式化后时间:" + formattedTime); // 输出如“2024-05-20 15:30:45” // 3. 时间计算(加1小时、减2天) LocalDateTime nextHour = now.plusHours(1); LocalDateTime twoDaysAgo = now.minusDays(2); System.out.println("1小时后:" + nextHour.format(formatter)); System.out.println("2天前:" + twoDaysAgo.format(formatter)); // 4. 解析字符串为LocalDateTime String timeStr = "2024-05-18 10:00:00"; LocalDateTime parsedTime = LocalDateTime.parse(timeStr, formatter); System.out.println("解析后的时间:" + parsedTime); // 输出“2024-05-18T10:00” } }
工具类
- 定义:Java 提供的
java.util包下的工具类,封装了常用的工具方法(如数组操作、集合操作、随机数生成),无需自己实现,直接调用即可。 - 核心工具类与方法:
工具类 核心方法 功能描述 Arrays sort(Object[] a)对数组排序(自然排序 / 自定义排序) toString(Object[] a)将数组转换为字符串(便于打印) copyOf(Object[] original, int newLength)复制数组(指定新长度) binarySearch(Object[] a, Object key)二分查找数组中的元素(需先排序) Collections sort(List<T> list)对 List 排序(自然排序 / 自定义排序) max(Collection<? extends T> coll)获取集合中的最大值 min(Collection<? extends T> coll)获取集合中的最小值 shuffle(List<?> list)随机打乱 List 中的元素 synchronizedList(List<T> list)将非线程安全的 List 转为线程安全 - 示例:
import java.util.Arrays; import java.util.Collections; import java.util.List; public class ToolClassDemo { public static void main(String[] args) { // 1. Arrays工具类:操作数组 int[] arr = {3, 1, 4, 1, 5, 9}; Arrays.sort(arr); // 排序数组 System.out.println("排序后数组:" + Arrays.toString(arr)); // 输出[1, 1, 3, 4, 5, 9] int[] copyArr = Arrays.copyOf(arr, 4); // 复制前4个元素 System.out.println("复制后数组:" + Arrays.toString(copyArr)); // 输出[1, 1, 3, 4] int index = Arrays.binarySearch(arr, 4); // 二分查找4的索引 System.out.println("元素4的索引:" + index); // 输出3 // 2. Collections工具类:操作集合 List list = Arrays.asList(3, 1, 4, 1, 5, 9); Collections.sort(list); // 排序List System.out.println("排序后List:" + list); // 输出[1, 1, 3, 4, 5, 9] Integer max = Collections.max(list); // 获取最大值 Integer min = Collections.min(list); // 获取最小值 System.out.println("最大值:" + max + ",最小值:" + min); // 输出“最大值:9,最小值:1” Collections.shuffle(list); // 随机打乱List System.out.println("打乱后List:" + list); // 输出随机顺序(如[3, 5, 1, 9, 4, 1]) } }
四、异常处理与 I/O 基础
异常处理保障程序稳定性(避免崩溃),I/O 基础实现数据的 “读写操作”(如文件、网络数据),两者是程序与外部交互、应对错误的核心能力。
异常处理
- 定义:程序运行过程中出现的 “不正常情况”(如空指针、数组越界、文件不存在),若不处理会导致程序崩溃,通过 “捕获” 或 “抛出” 异常,让程序优雅处理错误。
- 异常体系:
- 顶层类:
Throwable,分为Error(错误,如内存溢出,程序无法处理)和Exception(异常,程序可处理); Exception分为 “编译时异常”(需强制处理,如IOException)和 “运行时异常”(无需强制处理,如NullPointerException、ArrayIndexOutOfBoundsException)。
- 顶层类:
- 核心处理结构:
try-catch-finally:try包裹可能出错的代码,catch捕获并处理异常,finally无论是否异常都会执行(用于释放资源);throw:手动抛出异常(如参数校验不通过时);throws:声明方法可能抛出的异常(由调用者处理);- 自定义异常:继承
Exception(编译时异常)或RuntimeException(运行时异常),实现业务专属异常(如 “余额不足异常”)。
- 示例:
import java.util.Scanner; // 自定义异常:余额不足异常 class InsufficientBalanceException extends Exception { public InsufficientBalanceException(String message) { super(message); // 调用父类构造方法,传递异常信息 } } // 银行账户类:模拟取款 class BankAccount { private double balance; public BankAccount(double balance) { this.balance = balance; } // 取款方法:可能抛出自定义异常,用throws声明 public void withdraw(double amount) throws InsufficientBalanceException { if (amount balance) { // 手动抛出自定义异常 throw new InsufficientBalanceException("余额不足!当前余额:" + balance + ",取款金额:" + amount); } balance -= amount; System.out.println("取款成功!剩余余额:" + balance); } } // 测试异常处理 public class ExceptionDemo { public static void main(String[] args) { BankAccount account = new BankAccount(1000); Scanner scanner = new Scanner(System.in); System.out.print("请输入取款金额:"); double amount = scanner.nextDouble(); try { account.withdraw(amount); // 可能抛出异常的代码 } catch (InsufficientBalanceException e) { // 捕获自定义异常 System.out.println("异常信息:" + e.getMessage()); } catch (IllegalArgumentException e) { // 捕获运行时异常 System.out.println("异常信息:" + e.getMessage()); } finally { // 释放资源(关闭Scanner) scanner.close(); System.out.println("取款操作结束"); } } }
I/O 基础
- 定义:I/O(Input/Output,输入 / 输出)用于程序与外部设备(如文件、键盘、网络)进行数据交互,核心分为 “字节流”(处理所有文件类型)和 “字符流”(处理文本文件,避免中文乱码)。
- 核心流体系:
流类型 输入流(读数据) 输出流(写数据) 核心用途 字节流 InputStream(抽象类)→FileInputStream(文件输入流)OutputStream(抽象类)→FileOutputStream(文件输出流)读取 / 写入任意文件(如图片、视频、文本) 字符流 Reader(抽象类)→FileReader(文件字符输入流)→BufferedReader(缓冲字符输入流)Writer(抽象类)→FileWriter(文件字符输出流)→BufferedWriter(缓冲字符输出流)读取 / 写入文本文件(如.txt、.java),缓冲流提升效率 - 核心操作(文件读写):
- 写入文件:创建输出流→调用
write()方法写入→关闭流(或用try-with-resources自动关闭); - 读取文件:创建输入流→调用
read()方法读取→关闭流; try-with-resources:JDK 7 + 特性,流对象在try括号中声明,无需手动关闭,程序结束后自动关闭。
- 写入文件:创建输出流→调用
- 示例(字符流读写文本文件):
import java.io.*; public class IODemo { public static void main(String[] args) { // 1. 写入文本文件(try-with-resources自动关闭流) String content = "Hello, Java I/O!这是一段测试文本。"; try (Writer writer = new FileWriter("test.txt"); BufferedWriter bufferedWriter = new BufferedWriter(writer)) { // 缓冲流提升效率 bufferedWriter.write(content); bufferedWriter.newLine(); // 换行 bufferedWriter.write("这是第二行文本。"); System.out.println("文件写入成功!"); } catch (IOException e) { e.printStackTrace(); // 处理I/O异常 } // 2. 读取文本文件 try (Reader reader = new FileReader("test.txt"); BufferedReader bufferedReader = new BufferedReader(reader)) { String line; // 存储每行内容 System.out.println("\n文件内容:"); while ((line = bufferedReader.readLine()) != null) { // 按行读取(null表示文件结束) System.out.println(line); } } catch (IOException e) { e.printStackTrace(); } } }
五、Java 内存模型入门
Java 内存模型(JMM)定义了 Java 虚拟机(JVM)在内存中如何存储数据、如何进行线程间通信,理解 JMM 是后续学习 GC(垃圾回收)、并发编程的基础。
核心内存分区
JVM 内存分为 5 个区域,各区域功能、线程归属(私有 / 共享)不同,具体如下:
| 内存区域 | 核心功能 | 线程归属 | 关键特点 |
|---|---|---|---|
| 程序计数器 | 存储当前线程执行的 “字节码指令地址”(如分支、循环、线程恢复的位置) | 线程私有 | 内存空间最小,无 OOM(内存溢出)异常 |
| 本地方法栈 | 管理 “本地方法”(用native修饰、C/C++ 实现的方法,如System.currentTimeMillis())的调用 | 线程私有 | 可能抛出StackOverflowError(栈溢出)和OutOfMemoryError |
| Java 虚拟机栈 | 存储线程执行方法时的 “局部变量表”(方法内定义的变量)、“操作数栈”(方法执行时的临时数据)、“动态链接”(方法引用)、“方法出口”(方法返回地址) | 线程私有 | 1. 局部变量表存储基本数据类型和引用数据类型的地址;2. 栈帧(每个方法对应一个栈帧)随方法调用创建、方法结束销毁;3. 可能抛出StackOverflowError(方法嵌套过深)和OutOfMemoryError |
| 堆 | 存储 “几乎所有对象实例”(如new User()创建的对象)和数组 | 线程共享 | 1. JVM 内存中最大的区域;2. GC(垃圾回收)的主要区域;3. 可能抛出OutOfMemoryError(对象过多,GC 无法回收) |
| 方法区(JDK 8 + 叫 “元空间”) | 存储 “已加载的类信息”(类结构、方法信息)、“常量”(如final修饰的常量)、“静态变量”(static修饰的变量)、“即时编译后的代码” | 线程共享 | 1. JDK 7 及之前叫 “永久代”,JDK 8 后用 “元空间”(基于本地内存,而非 JVM 内存);2. 可能抛出OutOfMemoryError(类加载过多) |
关键概念:线程私有与共享
- 线程私有:每个线程在创建时会单独分配一份内存,线程间不共享,随线程销毁而释放,包括 “程序计数器”“本地方法栈”“Java 虚拟机栈”;
- 好处:避免线程间数据干扰,无需同步控制。
- 线程共享:所有线程共用一份内存,线程间可共享数据,包括 “堆”“方法区”;
- 好处:实现数据共享(如多个线程操作同一个对象);
- 问题:可能出现线程安全问题(如多个线程修改同一个对象的属性),需通过锁机制(如
synchronized)控制。
与 GC 的关联
- GC(垃圾回收):负责回收 “堆” 和 “方法区” 中 “不再被使用的对象”,释放内存,避免内存溢出;
- 为什么需要 GC:
- 栈内存(虚拟机栈、本地方法栈)的栈帧随方法结束自动销毁,无需 GC;
- 堆内存的对象由
new创建,方法结束后对象可能仍被其他线程或引用指向(如静态变量引用的对象),无法自动销毁,需 GC 判断对象是否 “可达”(是否有引用指向),不可达则回收。
- 示例理解:
public class MemoryDemo { public static void main(String[] args) { // 1. user1是局部变量,存储在“Java虚拟机栈”中,指向堆中的User对象 User user1 = new User("张三", 25); // 2. user2是局部变量,也指向堆中的同一个User对象(堆对象被两个引用指向) User user2 = user1; // 3. user1置为null,不再指向堆对象,但user2仍指向,对象可达,GC不回收 user1 = null; // 4. user2置为null,堆对象无任何引用指向,对象不可达,GC会在合适时机回收 user2 = null; } }

浙公网安备 33010602011771号