Java 入门到精通 (第 1-10 期)
简介:本系列教程从零开始,带你了解 Java 历史、核心特性、环境配置及第一个程序编写。主要基于 Java 8 LTS 版本讲解。
第 1 期:Java 语言发展历史与核心特性介绍
大家好,欢迎来到 Java 入门到精通系列教程的第一期。我是你们的老师,今天咱们从零开始,聊聊 Java 这门语言到底是怎么来的,它有什么牛逼的地方,以及学了 Java 能干啥。
1. Java 的历史
- 发布时间:1995 年,由 Sun 公司发布。
- 创始人:James Gosling(詹姆斯·高斯林),被称为“Java 界的祖师爷”。
- 起源故事:最初是 Sun 公司的内部项目“Green 计划”,旨在开发智能家电的编程语言。随着互联网时代的到来,Java 迅速走红。
- 命名由来:取自印度尼西亚爪哇岛(Java Island)的英文名,那里盛产咖啡。因此,Java 的 Logo 是一杯冒着热气的咖啡。
2. 版本说明
- 当前版本:已更新至 Java 21+。
- LTS 版本:企业中最常用的是长期支持版本(LTS),如 Java 8、Java 11、Java 17 等。
- 本教程基础:Java 8。因为它最经典、最稳定、企业应用最广。
3. Java 的核心特性
Java 之所以长盛不衰,主要归功于以下五大特性:
- 核心思想:将现实世界抽象为代码中的“对象”(如人、车、猫)。
- 三大特征:封装、继承、多态(后续章节详细讲解)。
- 原理:依靠 JVM (Java Virtual Machine)。
- 优势:Windows 上写的代码无需修改,可直接在 Linux/Mac 上运行。C 语言则需针对不同系统重新编译。
- 语法类似 C++,但去除了指针、手动内存管理、多继承等复杂概念。
- 自动垃圾回收 (GC):无需手动释放内存,降低入门门槛。
- 拥有沙箱安全模型等机制,防止恶意代码破坏系统。早期广泛用于浏览器端。
- 内置多线程支持,允许程序同时执行多个任务,提高效率。
4. Java 的应用领域
- 后端开发:网站/App 后台服务(主流方向)。
- 安卓开发:原生 App 开发语言。
- 大数据:Hadoop、Spark 等框架底层多用 Java/Scala。
- 游戏开发:例如《我的世界》(Minecraft)。
- 企业级应用:银行、金融、电商等大型系统。
第 2 期:跨平台原理与 JVM 虚拟机详解
上期提到 Java 最牛的特性是跨平台,核心在于 JVM (Java 虚拟机)。
1. 跨平台原理类比
- 传统方式 (如 C):像作家写书,需要翻译成各种语言(中文、英文、日文)才能给不同国家的人看。对应代码:Windows 编译的 exe 只能在 Windows 跑,Linux 需重新编译。
- Java 方式:
- 先翻译成一种“中间语言”——字节码 (Bytecode)。
- 每个平台都有一个“翻译官”——JVM,将统一的字节码翻译成该平台能懂的机器码。
2. Java 执行流程
关键点:.class 字节码是平台无关的;JVM 是平台相关的(Windows/Mac/Linux 各有专属 JVM)。
3. JVM 的主要功能
JVM 是一个虚拟计算机,包含栈、堆、方法区等内存结构。主要职责包括:
- 类加载:加载 .class 文件到内存。
- 字节码验证:检查合法性与安全性。
- 解释执行:逐条翻译字节码为机器码。
- 即时编译 (JIT):将热点代码直接编译成本地机器码,提升效率(Java 并非纯解释型语言,效率接近 C++)。
- 垃圾回收 (GC):自动管理内存。
第 3 期:JDK、JRE 与 JVM 的关系图解
初学者常混淆 JDK、JRE、JVM,三者关系如下:
1. 概念定义
| JVM | Java Virtual Machine | Java 虚拟机 | 运行字节码的核心引擎。 |
| JRE | Java Runtime Environment | Java 运行环境 | JVM + 核心类库。用于运行 Java 程序,不能开发。 |
| JDK | Java Development Kit | Java 开发工具包 | JRE + 开发工具 (javac, java, javadoc 等)。用于开发 Java 程序。 |
2. 包含关系图
┌─────────────────────────────┐
│ JDK │
│ ┌───────────────────────┐ │
│ │ JRE │ │
│ │ ┌─────────────────┐ │ │
│ │ │ JVM │ │ │
│ │ └─────────────────┘ │ │
│ │ + 核心类库 │ │
│ └───────────────────────┘ │
│ + 开发工具(javac等) │
└─────────────────────────────┘
- 关系:JDK ⊃ JRE ⊃ JVM
3. 生活化比喻
- JVM = 汽车发动机(核心动力,但无法单独驾驶)。
- JRE = 完整的汽车(有发动机+座椅轮胎,可驾驶/运行,但不能造车/开发)。
- JDK = 车库+车间(有一辆完整汽车+工具,可造车/开发)。
4. 安装建议
- 仅运行程序:装 JRE。
- 开发 Java 程序:必须装 JDK(内含 JRE,无需单独装 JRE)。
- JVM:无需单独安装,包含在 JRE/JDK 中。
第 4 期:下载与安装 JDK 环境(Windows/Mac)
1. 下载渠道
- Oracle JDK:官方原版。Java 8+ 商业用途需付费,个人学习免费。
- OpenJDK:开源免费,企业常用。
- 其他发行版:Amazon Corretto, 阿里 Dragonwell, 华为 OpenJDK 等。
- 推荐:初学者使用 Oracle JDK 8 或 OpenJDK 8。务必选择 LTS 版本。
2. Windows 安装步骤
3. Mac 安装步骤
4. 注意事项
- 安装路径不要包含中文、空格或特殊字符。
- 下载对应系统位数的版本(通常选 x64)。
- 记住安装路径,后续配置环境变量需用。
第 5 期:配置环境变量(JAVA_HOME 与 Path)
安装后直接在命令行输入 java 报错,是因为系统找不到命令路径。需配置环境变量。
1. 关键变量
- JAVA_HOME:指向 JDK 安装根目录(不含 bin)。
- 作用:方便其他软件(Tomcat, Maven, IDEA 等)统一读取 JDK 位置。
- Path:指向 JDK 的 bin 目录。
- 作用:让系统在任意目录下都能找到 java, javac 等命令。
2. Windows 配置步骤
- 右键“此电脑” → 属性 → 高级系统设置 → 环境变量。
- 系统变量 → 新建:变量名 JAVA_HOME,变量值 C:\\Java\\jdk1.8.0_202 (示例路径)。
- 系统变量 → 找到 Path → 编辑。
- 新建条目:%JAVA_HOME%\\bin。
- 重要:将此条目移动到列表最上方,确保优先使用自己安装的 JDK。
3. Mac 配置步骤
export PATH=$JAVA_HOME/bin:$PATH
第 6 期:验证安装与命令行常用指令
1. 验证安装
打开命令行 (Win: cmd, Mac: Terminal),输入以下命令:
# 查看 Java 版本
java -version
# 查看编译器版本
javac -version
- 成功标志:显示版本号 (如 1.8.0_202)。
- 失败处理:若提示“不是内部或外部命令”,请检查环境变量配置。
2. 常用命令行指令
| 查看当前目录 | cd (无参) | pwd |
| 切换目录 | cd 路径 | cd 路径 |
| 返回上一级 | cd .. | cd .. |
| 列出文件 | dir | ls |
| 创建文件夹 | mkdir 名 | mkdir 名 |
| 删除文件 | del 文件名 | rm 文件名 |
| 清屏 | cls | clear |
- 技巧:使用 Tab 键可进行文件名/目录名自动补全。
- 路径概念:
- 绝对路径:从根目录开始的完整路径 (如 C:\\Java\\…)。
- 相对路径:相对于当前目录的路径 (如 ./src)。
第 7 期:编写第一个 HelloWorld 程序
1. 创建文件
- 新建文本文件,命名为 HelloWorld.java。
- 注意:必须显示文件扩展名。Windows 需在“查看”中勾选“文件扩展名”。
- 严禁:文件名为 HelloWorld.java.txt。
2. 编写代码
使用记事本或编辑器打开,输入:
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello World!");
}
}
3. 关键规范
第 8 期:编译与运行 Java 程序 (javac 与 java)
1. 操作流程
- 成功:无输出,目录下生成 HelloWorld.class。
- 失败:根据错误信息修正拼写、符号或文件名问题。
- 注意:只写类名,不要加 .java 或 .class。
- 结果:控制台输出 Hello World!。
2. 常见错误
- java HelloWorld.class:报错,因为 JVM 寻找的是类名而非文件名。
- 中文乱码:编译时指定编码 javac -encoding UTF-8 HelloWorld.java。
第 9 期:HelloWorld 程序逐行代码剖析
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello World!");
}
}
1. 第一行:类的定义
public class HelloWorld {
- public:访问修饰符,表示公开可见。
- class:关键字,定义一个类。
- HelloWorld:类名。
- {:类体开始。
2. 第二行:主方法 (入口)
public static void main(String[] args) {
- public:公共权限,供 JVM 调用。
- static:静态方法,无需实例化对象即可调用。
- void:无返回值。
- main:方法名,固定写法,JVM 识别的入口。
- (String[] args):参数列表,接收命令行字符串数组(初学者暂忽略)。
3. 第三行:输出语句
System.out.println("Hello World!");
- System:系统类。
- out:标准输出流对象。
- println():打印并换行方法。
- "Hello World!":字符串常量。
- ;:语句结束符。
4. 总结
- Java 基本单位是类。
- 程序入口是固定的 main 方法。
- 输出使用 System.out.println。
第 10 期:开发工具 IDEA 的安装与基础配置
为了提升开发效率,建议使用集成开发环境 (IDE),首选 IntelliJ IDEA。
1. 版本选择
- Community (社区版):免费,支持 Java SE,适合初学者。
- Ultimate (旗舰版):收费,支持 Web/EE 等企业级开发。
2. 安装步骤
- Create Desktop Shortcut (桌面快捷方式)
- Update PATH Variable (命令行可用 idea 命令)
- Update Context Menu (右键菜单集成)
- .java 关联 (默认用 IDEA 打开)
3. 首次启动与界面
- 新建项目 (New Project):选择 Java 语言,指定 JDK 8。
- 界面区域:
- 左侧:项目结构。
- 中间:代码编辑区。
- 下方:控制台/终端。
- 顶部:菜单栏/工具栏。
4. 推荐基础配置 (File → Settings)
Java 入门到精通 (第 11-20 期)
简介:本阶段从 IDE 实操入手,深入讲解 Java 基础语法核心概念,包括关键字、标识符、注释、常量、基本数据类型(整数、浮点、字符、布尔)。
第 11 期:IDEA 中创建项目与运行程序
1. 创建新项目
打开 IntelliJ IDEA,点击 New Project,配置如下:
- Name:项目名称,如 JavaStudy。
- Location:项目路径,避免中文和空格。
- Language:选择 Java。
- JDK:选择已安装的 JDK 8(若未检测到需手动添加)。
- Build system:选择 IntelliJ(初学者暂不使用 Maven/Gradle)。
2. 创建类
- 在左侧项目结构的 src 文件夹上右键 → New → Java Class。
- 输入类名(如 HelloWorld),回车。
- IDEA 会自动生成类结构:public class HelloWorld {
}
3. 编写代码与快捷键
- 生成 main 方法:输入 psvm 然后按 Tab 键。public static void main(String[] args) {
} - 生成输出语句:在 main 方法中输入 sout 然后按 Tab 键。System.out.println();
- 完整示例:public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello IDEA!");
}
}
4. 运行程序
有以下几种方式运行:
- 结果查看:下方控制台输出 Hello IDEA! 及 Process finished with exit code 0(0 表示正常退出)。
- 优势:IDEA 自动完成编译和运行,无需手动敲命令。
5. 常用操作
- 新建包:src 右键 → New → Package(用于分类管理代码)。
- 重命名:选中文件 → Shift+F6(IDEA 会自动更新所有引用)。
- 代码提示:输入部分字母后,IDEA 会自动补全,善用此功能提高效率。
第 12 期:IDEA 常用快捷键与调试技巧入门
熟练掌握快捷键可大幅提升开发效率。
1. 核心快捷键速查表
| 生成代码 | psvm + Tab | psvm + Tab | 生成 main 方法 |
| sout + Tab | sout + Tab | 生成 System.out.println() | |
| 编辑 | Ctrl + D | Cmd + D | 复制当前行到下一行 |
| Ctrl + Y | Cmd + Backspace | 删除当前行 | |
| Ctrl + / | Cmd + / | 单行注释/取消注释 | |
| Ctrl + Shift + / | Cmd + Shift + / | 多行注释/取消注释 | |
| Ctrl + Alt + L | Option + Cmd + L | 格式化代码(自动对齐) | |
| Alt + Enter | Option + Enter | 万能修复键(导包、修错、提取方法等) | |
| Ctrl + Alt + V | Option + Cmd + V | 自动生成变量 | |
| 查找 | Ctrl + F | Cmd + F | 当前文件查找 |
| Ctrl + Shift + F | Cmd + Shift + F | 全局查找 | |
| 双击 Shift | 双击 Shift | 万能搜索(搜类、文件、设置等) | |
| 运行/调试 | Ctrl + Shift + F10 | Control + R | 运行当前程序 |
| Ctrl + Shift + F9 | Control + D | 调试运行当前程序 | |
| F8 | F8 | 单步执行(不进入方法内部) | |
| F7 | F7 | 单步执行(进入方法内部) | |
| F9 | F9 | 继续执行(至下一个断点) | |
| Ctrl + F2 | Cmd + F2 | 停止程序 |
2. 调试 (Debug) 技巧入门
调试是定位 Bug 的核心手段,让程序一步步执行并观察变量值。
- F7:Step Into(进入方法)。
- F8:Step Over(跳过方法,不进入)。
- F9:Resume Program(继续运行直到下一个断点)。
第 13 期:关键字与保留字的区别
1. 关键字 (Keyword)
- 定义:Java 语言中被赋予特殊含义的单词。
- 特点:
- 全部为小写。
- 具有特定用途(如定义类、变量、流程控制等)。
- 在 IDEA 中通常显示为蓝色或紫色高亮。
- 不能用作类名、方法名、变量名。
- 常见分类:
- 修饰符:public, private, protected, static, final, abstract
- 流程控制:if, else, switch, case, while, for, break, continue, return
- 数据类型:int, long, double, char, boolean, true, false, null
- 面向对象:class, interface, extends, implements, new, this, super
- 异常处理:try, catch, finally, throw, throws
- 其他:package, import, synchronized, volatile, enum
2. 保留字 (Reserved Word)
- 定义:Java 目前未使用,但预留供未来版本可能使用的关键字。
- 数量:仅两个 —— const 和 goto。
- 原因:
- const:Java 使用 final 定义常量,故保留 const。
- goto:Java 认为 goto 容易导致代码逻辑混乱,故废弃,但仍保留该词禁止使用。
3. 总结
- 共同点:都不能作为标识符(名字)使用。
- 判断方法:在 IDEA 中输入,若变色则为关键字/保留字;若为黑色则不是。
- 注意:true, false, null 虽常被视为字面量,但在标识符规则中同样不可用。
第 14 期:标识符的命名规则与规范
标识符是给类、方法、变量等起的名字。
1. 命名规则(必须遵守,否则报错)
示例:
- ✅ age, studentName, _hello, $money, abc123
- ❌ 123abc (数字开头), class (关键字), my-name (含减号)
- ⚠️ 我的名字 (合法但不推荐,易导致编码问题和可读性差)
2. 命名规范(建议遵守,体现专业性)
| 类名/接口名 | 大驼峰 (UpperCamelCase) | 每个单词首字母大写 | HelloWorld, UserService |
| 方法名/变量名 | 小驼峰 (lowerCamelCase) | 第一个单词小写,后续单词首字母大写 | main, userName, getInfo |
| 常量名 | 全大写 | 全部大写,单词间用下划线分隔 | MAX_SIZE, PI, DEFAULT_VALUE |
| 包名 | 全小写 | 全部小写,用点分隔层级 | com.example.demo, java.util |
3. 核心原则:见名知意
- 好例子:age, studentName, calculateSum
- 坏例子:a, b, test, helloworld (类名未大写)
- 理由:代码是给人看的,清晰的名字能像文章一样顺畅阅读,减少维护成本。
第 15 期:注释的三种方式
注释是写给程序员看的说明,编译器会忽略。
1. 单行注释
- 语法:// 注释内容
- 用法:解释单行代码或行尾补充说明。
- 快捷键:Ctrl + / (Win) / Cmd + / (Mac)
// 这是一个单行注释
int age = 18; // 定义年龄变量
2. 多行注释
- 语法:/* 注释内容 */
- 用法:注释大块代码或多行说明。注意:不能嵌套使用。
- 快捷键:Ctrl + Shift + / (Win) / Cmd + Shift + / (Mac)
/*
这是一个多行注释
可以包含多行文字
*/
3. 文档注释 (Javadoc)
- 语法:/** 注释内容 */
- 用法:专门用于生成 API 文档,写在类、方法、字段上方。
- 常用标签:
- @author:作者
- @version:版本号
- @param:参数说明
- @return:返回值说明
- @deprecated:已废弃
/**
* 计算圆的面积
* @param radius 半径
* @return 面积
*/
public double calculateArea(double radius) { ... }
4. 注释原则
- 要写:类和方法的文档注释、复杂业务逻辑的解释、特殊处理的“坑”。
- 不要写:废话注释(如 int a = 1; // a等于1)、过时注释(代码改了注释没改比没注释更坑)。
第 16 期:常量的定义与 final 关键字
1. 常量分类
- 字面常量:直接写出的固定值,如 123, "Hello", true, null。
- 自定义常量:使用 final 关键字修饰的变量,值一旦确定不可更改。
2. final 关键字
- 修饰变量:变为常量,初始化后不可重新赋值。
- 修饰方法:不能被重写(Override)。
- 修饰类:不能被继承(Extend)。
3. 定义与使用
// 语法:final 数据类型 常量名 = 值;
final double PI = 3.1415926;
final int MAX_SIZE = 100;
// 错误示范
final int age = 18;
age = 20; // 编译报错!Cannot assign a value to final variable 'age'
4. 使用场景
- Bad: if (age > 18)
- Good: final int ADULT_AGE = 18; if (age > ADULT_AGE)
5. 注意事项
- 局部常量必须在定义时初始化。
- 常量名规范:全大写 + 下划线。
第 17 期:基本数据类型分类(8 大基本类型)
Java 数据类型分为基本类型和引用类型。本期重点讲解 8 大基本数据类型,口诀:“四类八种”。
1. 整数类型 (4 种)
| byte | 1 | -128 ~ 127 | 0 | 极少用,节省空间场景 |
| short | 2 | -32768 ~ 32767 | 0 | 极少用 |
| int | 4 | ±21亿 | 0 | 最常用 |
| long | 8 | ±900亿亿 | 0L | 大数、时间戳 |
- 注意:整数默认类型为 int。定义 long 需加后缀 L。
2. 浮点类型 (2 种)
| float | 4 | 6-7位有效数字 | 0.0f | 需加后缀 f |
| double | 8 | 15-16位有效数字 | 0.0 | 最常用 |
- 注意:小数默认类型为 double。定义 float 需加后缀 f。
- 警告:浮点数运算存在精度误差,严禁用于金额计算(需用 BigDecimal)。
3. 字符类型 (1 种)
- char: 2 字节,Unicode 编码,取值 0~65535。
- 示例:'a', '中', '\\n'
4. 布尔类型 (1 种)
- boolean: 1 字节(规范未严格规定),只有 true 或 false。
- 用于条件判断。
第 18 期:整数类型详解 (byte, short, int, long)
1. 各类型详解
- byte: 最小单位,常用于网络传输、文件读写底层数据。
- short: 介于 byte 和 int 之间,现代开发中很少使用。
- int: 主力军。绝大多数计数、索引、ID 都使用 int。
- long: 当数值超过 int 范围时使用。
- 典型场景:System.currentTimeMillis() 返回的时间戳。
- 写法:long num = 10000000000L; (务必加 L,防止溢出)。
2. 进制表示
Java 支持多种进制的字面量表示,打印时默认转为十进制:
- 十进制:123
- 二进制:0b1010 (以 0b 开头)
- 八进制:012 (以 0 开头)
- 十六进制:0xA (以 0x 开头)
3. 数字可视化增强 (Java 7+)
可以在数字中间加下划线 _ 提高可读性,编译器会自动忽略:
int million = 1_000_000; // 100万
long billion = 1_000_000_000L; // 10亿
4. 经验总结
第 19 期:浮点类型详解 (float vs double)
1. 核心区别
- precision (精度): double > float。
- memory (内存): double 占用更多内存。
- 默认类型: 代码中写的小数默认是 double。
2. 定义语法
float f = 3.14f; // 必须加 f/F
double d = 3.14; // 默认就是 double,d/D 可选
double d2 = 3.14d;
3. 精度陷阱 (重要!)
计算机使用二进制存储小数,部分十进制小数无法精确表示(类似 1/3 = 0.333…)。
System.out.println(0.1 + 0.2);
// 输出: 0.30000000000000004
- 结论:涉及金钱、精确计算的场景,绝对禁止使用 float/double,必须使用 java.math.BigDecimal。
4. 特殊值
- Infinity: 正无穷 (1.0 / 0)
- -Infinity: 负无穷 (-1.0 / 0)
- NaN: 非数字 (0.0 / 0.0)。注意:NaN == NaN 结果为 false,判断需用 Double.isNaN()。
5. 科学计数法
double e2 = 3.14e2; // 314.0
double eNeg3 = 3.14e-3; // 0.00314
第 20 期:字符类型 char 与 Unicode 编码
1. char 基础
- 占位:2 字节 (16 bit),范围 0~65535。
- 语法:单引号 ' ',只能存一个字符。char c = 'a';
char c2 = '中';
// char c3 = "ab"; // 错误,这是字符串
2. char 的本质:数字
char 在底层存储的是字符对应的 Unicode 编码值。
- 隐式转换:char 参与算术运算时,自动提升为 int。char c = 'a'; // ASCII 97
System.out.println(c + 1); // 输出 98 - 强制转换:可以将数字赋给 char,显示对应字符。char c = 97;
System.out.println(c); // 输出 a
3. 关键编码值 (ASCII)
记住以下常用字符的编码,有助于理解字符运算:
- '0' = 48
- 'A' = 65
- 'a' = 97
- 大小写差值:32 ('a' – 'A' = 32)
4. 转义字符 ($
用于表示特殊控制字符:
| \\n | 换行 | "Line1\\nLine2" |
| \\t | 制表符 | "Name\\tAge" |
| \\\\ | 反斜杠 | "C:\\\\Path" |
| \\' | 单引号 | char c = '\\''; |
| \\" | 双引号 | String s = "\\"Hello\\""; |
| \\uXXXX | Unicode 字符 | '\\u4e2d' (中) |
5. 常见应用
- 大小写转换:利用 ASCII 差值 32。char upper = (char)(c – 32); // 小转大
char lower = (char)(c + 32); // 大转小 - 类型判断:通过范围判断字符类型。if (c >= '0' && c <= '9') { /* 数字 */ }
else if (c >= 'a' && c <= 'z') { /* 小写字母 */ }
Java 入门到精通 (第 21-30 期)
简介:本阶段深入讲解基本数据类型的细节、变量管理、类型转换、字符串操作、Scanner 输入处理以及格式化输出,并通过综合案例巩固基础语法。
第 21 期:布尔类型 boolean 的特殊性
1. 基本用法
boolean 类型只有两个值:true 和 false。
- 注意:必须小写,它们是关键字,不能作为标识符。
- 示例:boolean flag = true;
System.out.println(flag); // 输出 true
2. 特殊性
与其他基本类型相比,boolean 有以下特点:
- ❌ int a = true; (编译报错)
- ❌ boolean b = 1; (编译报错)
3. 使用场景
- 条件判断:if, while, for 的条件表达式必须是 boolean 类型。
- 方法返回值:用于返回逻辑判断结果(如 isEven(), isValid())。
- 标志位:记录状态(如 isFinished, isVisible)。
4. 命名建议
变量名建议以 is, has, can, should 开头,见名知意。
- ✅ isAdult, hasNext, canRun
第 22 期:变量的声明与初始化
1. 概念
变量是内存中的一块存储空间,具有类型、名字和值。
2. 语法步骤
double salary;
3. 注意事项
第 23 期:变量的作用域与生命周期
1. 作用域 (Scope)
变量能起作用的范围,即代码中可以访问该变量的区域。
- 规则:
- 变量在其所在的大括号 {} 内有效。
- 内部可以访问外部变量。
- 外部不能访问内部变量。
- 不同作用域可以有同名变量(但不建议,易混淆)。
public class ScopeTest {
public static void main(String[] args) {
int a = 10;
if (a > 5) {
int b = 20;
System.out.println(a); // 合法,访问外部变量
System.out.println(b); // 合法,访问内部变量
}
// System.out.println(b); // 报错!b 已超出作用域
}
}
2. 生命周期 (Life Cycle)
变量从创建到销毁的时间段。
- 局部变量:
- 创建:执行到声明行时分配内存。
- 销毁:离开作用域(如方法结束)时内存被回收。
- 每次调用方法都会重新创建新的局部变量。
第 24 期:自动类型转换(隐式转换)
1. 定义
当小范围类型的数据赋值给大范围类型的变量时,Java 自动完成转换,无需人工干预。
2. 转换顺序(由小到大)
byte → short → int → long → float → double
↑
char
- 注意:
- boolean 不参与任何类型转换。
- float 虽然只有 4 字节,但表示范围比 long (8 字节) 大,所以 long 可自动转为 float。
- char 可自动转为 int 及以上类型。
3. 表达式中的类型提升
- 规则 1:byte, short, char 参与运算时,自动提升为 int。byte a = 10;
byte b = 20;
// byte c = a + b; // 报错!结果是 int
int c = a + b; // 正确 - 规则 2:混合运算中,整个表达式提升为精度/范围最大的类型。int a = 10;
double b = 3.14;
double c = a + b; // a 自动转为 double,结果为 double
第 25 期:强制类型转换(显式转换)与精度丢失
1. 定义
将大范围类型转换为小范围类型时,必须手动指定,称为强制类型转换。
- 语法:(目标类型) 值
int a = 10;
byte b = (byte) a; // 强制转换
2. 风险:精度丢失与溢出
- 数值溢出:如果值超出目标类型范围,会发生截断,导致结果错误。int c = 200;
byte d = (byte) c; // 输出 -56 (溢出) - 小数截断:浮点转整数时,直接丢弃小数部分,不是四舍五入。double d = 3.99;
int i = (int) d; // 结果为 3
3. 特殊场景
- byte/short 运算:结果默认为 int,赋值回原类型需强转。byte c = (byte)(a + b); // 必须加括号整体强转
- 常量优化:如果赋值的是常量且值在范围内,编译器会自动处理,无需强转。byte b = 100; // 合法,100 在 byte 范围内
int a = 100;
// byte b2 = a; // 报错,变量 a 的值不确定
第 26 期:字符串 String 的基础入门
1. 基本概念
- String 是引用类型(类),不是基本类型。
- 用双引号 "" 表示。
- 不可变性:字符串对象一旦创建,内容不可改变。修改操作(如 toUpperCase)返回的是新字符串对象。
2. 定义方式
String s1 = "Hello"; // 推荐:直接量
String s2 = new String("Hello"); // 较少用
3. 常用方法
| length() | 获取长度 | "abc".length() -> 3 |
| charAt(index) | 获取指定位置字符 | "abc".charAt(0) -> ‘a’ |
| equals(obj) | 比较内容是否相等 | "abc".equals("abc") -> true |
| toUpperCase() | 转大写 | "abc".toUpperCase() -> “ABC” |
| toLowerCase() | 转小写 | "ABC".toLowerCase() -> “abc” |
| contains(s) | 是否包含子串 | "hello".contains("ell") -> true |
| trim() | 去除首尾空格 | " ab ".trim() -> “ab” |
4. 重要提醒
- 比较内容:务必使用 .equals(),不要使用 ==(== 比较的是内存地址)。
- 拼接:+ 号可用于拼接,其他类型会自动转为字符串。
- 注意运算顺序:1 + 2 + "abc" -> "3abc";"abc" + 1 + 2 -> "abc12"。
第 27 期:Scanner 类实现键盘录入
1. 使用步骤
2. 常用读取方法
- nextInt(): 读取整数。
- nextDouble(): 读取小数。
- nextBoolean(): 读取布尔值 (true/false)。
- next(): 读取一个单词(遇到空格停止)。
- nextLine(): 读取一整行(遇到回车停止)。
3. 常见坑:nextInt 与 nextLine 混用
- 问题:nextInt() 只读取数字,不读取末尾的换行符 \\n。紧接着调用 nextLine() 会读到空字符串。
- 解决方案:
- 在 nextInt() 后额外调用一次 sc.nextLine() 消耗掉换行符。
- 全部使用 nextLine() 读取,再通过 Integer.parseInt() 转换。
第 28 期:Scanner 综合练习
案例 1:用户信息录入
结合 nextLine, nextInt, nextDouble 录入姓名、年龄、身高。
import java.util.Scanner;
public class UserInfo {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.print("请输入姓名:");
String name = sc.nextLine(); // 姓名可能含空格,用 nextLine
System.out.print("请输入年龄:");
int age = sc.nextInt(); // 年龄是整数
System.out.print("请输入身高:");
double height = sc.nextDouble(); // 身高是小数
System.out.println("信息:" + name + ", " + age + "岁, " + height + "米");
sc.close();
}
}
案例 2:简易加法计算器
import java.util.Scanner;
public class SimpleCalculator {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.print("请输入第一个数:");
int num1 = sc.nextInt();
System.out.print("请输入第二个数:");
int num2 = sc.nextInt();
System.out.println("结果:" + (num1 + num2));
sc.close();
}
}
第 29 期:System.out.printf 格式化输出
1. 基本语法
System.out.printf("格式字符串", 参数1, 参数2...);
2. 常用占位符
| %d | 整数 | 十进制 |
| %f | 浮点 | 默认 6 位小数 |
| %s | 字符串 | |
| %c | 字符 | |
| %b | 布尔 | |
| %x | 整数 | 十六进制 |
| %n | – | 换行 |
| %% | – | 百分号本身 |
3. 高级控制
- 保留小数:%.2f (保留两位,四舍五入)。
- 宽度对齐:
- %10d: 右对齐,宽度 10。
- %-10d: 左对齐,宽度 10。
- 补零:%04d (不足 4 位前面补 0,如 0005)。
- 千分位:%,d (如 1,234,567)。
4. 字符串格式化
String.format("格式", 参数) 返回格式化后的字符串,不直接打印。
第 30 期:基础语法综合练习:简易计算器
需求
实现支持 +, -, *, / 的计算器,要求:
完整代码
import java.util.Scanner;
public class Calculator {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("=== 简易计算器 ===");
System.out.print("请输入第一个数:");
double num1 = sc.nextDouble();
System.out.print("请输入运算符 (+ – * /):");
String op = sc.next();
System.out.print("请输入第二个数:");
double num2 = sc.nextDouble();
double result = 0;
boolean valid = true;
// 判断运算符
if (op.equals("+")) {
result = num1 + num2;
} else if (op.equals("-")) {
result = num1 – num2;
} else if (op.equals("*")) {
result = num1 * num2;
} else if (op.equals("/")) {
if (num2 == 0) {
System.out.println("错误:除数不能为 0!");
valid = false;
} else {
result = num1 / num2;
}
} else {
System.out.println("错误:无效的运算符!");
valid = false;
}
// 输出结果
if (valid) {
System.out.printf("%.2f %s %.2f = %.2f%n", num1, op, num2, result);
}
sc.close();
}
}
知识点回顾
- Scanner: 数据输入。
- String: equals() 比较字符串。
- Math: 算术运算。
- Control Flow: if-else 分支判断(虽本期未详细讲语法,但已在代码中使用)。
- Formatting: printf 格式化输出。
Java 入门到精通 (第 31-40 期)
简介:本阶段深入讲解 Java 运算符体系,包括算术、字符串拼接、自增自减、赋值、关系、逻辑(含短路)、位运算、位移以及三元运算符。重点解析易错点和面试考点。
第 31 期:算术运算符 (+, -, *, /, %)
1. 基本算术运算符
| + | 加法 | 5 + 3 | 8 |
| – | 减法 | 5 – 3 | 2 |
| * | 乘法 | 5 * 3 | 15 |
| / | 除法 | 5 / 3 | 1 (整数截断) |
| % | 取模(余数) | 5 % 3 | 2 |
2. 整数除法陷阱
- 规则:两个整数相除,结果仍为整数,直接截断小数部分(非四舍五入)。System.out.println(5 / 3); // 输出 1
- 获取小数结果:确保至少有一个操作数是浮点数。System.out.println(5.0 / 3); // 1.666…
System.out.println((double)5 / 3); // 1.666…
3. 取模运算 (%) 的应用
- 判断奇偶:num % 2 == 0 为偶数。
- 获取低位:num % 10 获取个位数。
- 范围控制:生成随机数时常用 random() % n。
4. 其他特性
- 正负号:+5, -3 为一元运算符。
- 字符运算:char 参与运算时自动转为 ASCII/Unicode 码值。char c = 'a';
System.out.println(c + 1); // 98 ('b'的编码) - 字符串拼接:+ 号若有一边是字符串,则执行拼接而非加法。
第 32 期:字符串拼接与 + 号重载
1. 运算符重载
Java 中只有 + 号支持重载:
- 两边都是数字 → 加法。
- 有一边是字符串 → 拼接。
2. 拼接顺序与括号
+ 号从左往右计算,类型决定行为。
System.out.println(1 + 2 + "abc"); // "3abc" (先算 1+2=3,再拼)
System.out.println("abc" + 1 + 2); // "abc12" (先拼 "abc"+1="abc1",再拼)
// 使用括号改变优先级
System.out.println("结果:" + (1 + 2)); // "结果:3"
3. 底层原理
- 常量拼接:编译器优化为直接拼接 "HelloWorld"。
- 变量拼接:底层使用 StringBuilder.append()。
- 注意:在循环中大量使用 + 拼接字符串会频繁创建对象,导致性能下降。建议改用 StringBuilder。
4. 特殊值处理
- 空字符串 "":拼接后无变化。
- null:拼接时会转换为字符串 "null",不会报空指针异常。String s = null;
System.out.println(s + "abc"); // 输出 "nullabc"
第 33 期:自增自减运算符 (++ / –)
1. 前缀式 vs 后缀式
- 前缀式 (++a, –a):先变后用。先让变量加减 1,再使用新值。
- 后缀式 (a++, a–):先用后变。先使用原值,再让变量加减 1。
2. 示例对比
int a = 10;
int b = a++; // b=10, a=11 (后缀:先用10赋值给b,a再变11)
int c = 10;
int d = ++c; // d=11, c=11 (前缀:c先变11,再用11赋值给d)
3. 注意事项
- 单独使用无区别:a++; 和 ++a; 效果相同。
- 仅作用于变量:5++ 或 (a+b)++ 非法。
- 面试题陷阱:int a = 10;
a = a++;
System.out.println(a); // 输出 10
// 解析:后缀式先用原值10,a变为11,最后把原值10赋回给a,所以a还是10。 - 开发建议:避免在复杂表达式中混用自增自减,保持代码可读性。
第 34 期:赋值与扩展赋值运算符
1. 基本赋值
= :将右侧值赋给左侧变量。左侧必须是变量。
2. 扩展赋值运算符
包括 +=, -=, *=, /=, %= 等。
int a = 10;
a += 5; // 等价于 a = a + 5;
3. 隐藏的类型转换 (重要!)
扩展赋值运算符会自动进行强制类型转换。
byte b = 10;
// b = b + 1; // 报错!b+1结果是int,不能直接赋给byte
b += 1; // 正确!等价于 b = (byte)(b + 1)
- 风险:如果结果超出目标类型范围,会发生溢出,但编译器不会报错。byte b = 127;
b += 1; // 结果为 -128 (溢出)
第 35 期:关系运算符
1. 常用符号
== (等于), != (不等于), > (大于), < (小于), >= (大于等于), <= (小于等于)。
- 结果:均为 boolean 类型。
2. 常见错误:= vs ==
- = 是赋值。
- == 是比较。
- 隐蔽 Bug:boolean flag = false;
if (flag = true) { ... } // 这是赋值,永远成立!
// 建议写法:if (true == flag) { … } // 写反了编译报错,便于发现
3. 字符串比较
- 严禁使用 == 比较内容:== 比较的是内存地址。
- 必须使用 .equals():String s1 = new String("hello");
String s2 = new String("hello");
System.out.println(s1 == s2); // false (地址不同)
System.out.println(s1.equals(s2)); // true (内容相同)
第 36 期:逻辑运算符 (&, |, ^, !)
1. 基础逻辑运算
| & | 与 | 全真才真 | 有假则假 |
| ` | ` | 或 | 有真则真 |
| ^ | 异或 | 不同为真 | 同假异真 |
| ! | 非 | 取反 | 真假互换 |
2. 优先级
! > & > ^ > |
3. 双重身份
& 和 | 既可用于逻辑运算(操作数为 boolean),也可用于位运算(操作数为整数)。
第 37 期:短路逻辑运算符 (&&, ||)
1. 短路机制
- && (短路与):若左边为 false,右边不执行,直接返回 false。
- || (短路或):若左边为 true,右边不执行,直接返回 true。
- 对比 & 和 |:无论左边结果如何,右边一定执行。
2. 为什么用短路?
// if (s != null && s.length() > 0) // 安全:s!=null为false,跳过length()调用
// if (s != null & s.length() > 0) // 危险:右边仍执行,抛出 NullPointerException
3. 开发规范
实际业务开发中,逻辑判断几乎全部使用 && 和 ||。& 和 | 主要用于位运算。
第 38 期:位运算符基础 (&, |, ^, ~)
1. 按位与 &
- 规则:对应位都为 1 则为 1。
- 应用:判断奇偶 (num & 1)、清零、提取指定位。
2. 按位或 |
- 规则:对应位有一个为 1 则为 1。
- 应用:设置特定位为 1。
3. 按位异或 ^
- 规则:对应位不同则为 1。
- 特性:
- a ^ a = 0
- a ^ 0 = a
- 满足交换律和结合律。
- 经典应用:无需临时变量交换两个整数。a = a ^ b;
b = a ^ b;
a = a ^ b;
4. 按位取反 ~
- 规则:0 变 1,1 变 0。
- 注意:涉及补码,~x 通常等于 -x – 1。
5. 适用范围
仅适用于整数类型 (byte, short, int, long, char)。运算时小类型自动提升为 int。
第 39 期:位移运算符 (<<, >>, >>>)
1. 左移 <<
- 规则:低位补 0,高位溢出丢弃。
- 意义:x << n 等价于 x * 2^n。
- 风险:容易溢出(特别是符号位移入数据位)。
2. 有符号右移 >>
- 规则:高位补符号位(正数补 0,负数补 1)。
- 意义:x >> n 等价于 x / 2^n (向下取整)。
- 注意:负数右移结果与整数除法略有不同(如 -5 >> 1 为 -3,而 -5 / 2 为 -2)。
3. 无符号右移 >>>
- 规则:高位恒补 0。
- 意义:负数无符号右移会变成很大的正数。常用于处理无符号数据(如 IP 地址)。
4. 注意事项
- 位移量超过类型位数时,会对位数取模(如 int 移 32 位相当于移 0 位)。
- 实际业务中较少使用,多见于算法和底层优化。
第 40 期:三元运算符 (? 😃
1. 语法结构
条件表达式 ? 表达式1 : 表达式2
- 条件为 true → 结果为表达式 1 的值。
- 条件为 false → 结果为表达式 2 的值。
2. 特点
- 表达式而非语句:必须有返回值,且该返回值必须被使用(赋值或打印)。// int a = 10;
// a > 5 ? a++ : b++; // 报错!结果未使用 - 类型一致性:表达式 1 和 2 的类型应一致,否则会自动类型提升。
3. 适用场景
- 简单二选一取值。
- 替代简单的 if-else。String status = age >= 18 ? "成年" : "未成年";
- 不建议:嵌套过深或逻辑复杂时使用,会降低可读性,此时应使用 if-else。
结语:恭喜!你已经掌握了 Java 中所有的基础运算符。从算术运算到位运算,再到逻辑控制和条件选择,这些运算符构成了 Java 程序逻辑的基石。下一期我们将进入流程控制的核心——分支结构 (if-else, switch)。
Java 入门到精通 (第 41-50 期)
简介:本阶段深入讲解运算符的高级用法(嵌套、优先级、求值顺序)、Java 10+ 新特性 var,并正式进入流程控制的核心——选择结构(if 语句的三种格式、嵌套、switch 语句)。
第 41 期:三元运算符的嵌套使用
1. 什么是嵌套
三元运算符的表达式部分可以再次包含三元运算符,形成嵌套结构。
- 语法:条件 ? 表达式1 : (条件2 ? 表达式2 : 表达式3)
- 示例:判断正数、负数或零。String result = num > 0 ? "正数" : (num < 0 ? "负数" : "零");
2. 经典案例:三个数求最大值
int max = a > b ? (a > c ? a : c) : (b > c ? b : c);
// 逻辑:先比 a 和 b,胜者再与 c 比较
3. 结合性与可读性
- 右结合性:a > b ? a : b > c ? b : c 等价于 a > b ? a : (b > c ? b : c)。
- 建议:虽然支持嵌套,但超过一层嵌套后可读性急剧下降。强烈建议使用括号明确意图,或者直接使用 if-else if-else 结构。
第 42 期:运算符的优先级与结合性
1. 优先级 (Precedence)
决定不同运算符谁先计算。
- 最高:. (成员访问), [] (数组), () (括号/方法调用)。
- 高:一元运算符 (!, ~, ++, –, +, -)。
- 中:算术 (*, /, %) → 位移 (<<, >>) → 关系 (<, >) → 相等 (==, !=) → 位与 (&) → 位异或 (^) → 位或 (|) → 逻辑与 (&&) → 逻辑或 (||)。
- 低:三元 (? :)。
- 最低:赋值 (=, += 等)。
2. 结合性 (Associativity)
当优先级相同时,决定从左往右还是从右往左算。
- 左结合:大多数运算符(如 +, *, = 以外的运算)。
- 右结合:一元运算符、三元运算符、赋值运算符。
- 例:a = b = c 等价于 a = (b = c)。
3. 核心原则
- 括号优先级最高:不确定时,永远加括号 ( )。
- 可读性第一:复杂表达式拆分为中间变量,不要考验读者的记忆力。
第 43 期:表达式求值顺序详解
1. 优先级 vs 求值顺序
- 优先级:决定运算符的结合顺序(先乘除后加减)。
- 求值顺序:决定操作数的计算顺序。
2. Java 的规则:从左到右
Java 规定操作数的求值顺序始终是从左到右。
int b = (a++) + (a * 2);
// 1. 计算左边 a++:取原值 10,a 变为 11
// 2. 计算右边 a*2:此时 a 已是 11,结果为 22
// 3. 相加:10 + 22 = 32
3. 最佳实践
- 避免副作用:不要在表达式中依赖求值顺序(如混用自增和乘法)。
- 拆分代码:有副作用的操作应单独成行,使用中间变量,提高可读性和可维护性。
第 44 期:类型推断与 var 关键字 (Java 10+)
1. 概念
var 是 Java 10 引入的关键字,用于局部变量类型推断。编译器根据右侧初始化值自动推导左侧变量类型。
var list = new ArrayList<String>(); // 编译器推断 list 为 ArrayList<String>
2. 本质
- 非动态类型:编译后类型依然确定,Java 仍是强类型语言。
- 语法糖:仅为了减少重复代码,不改变运行时行为。
3. 使用规则
- ✅ 可用:局部变量,且必须声明时初始化。
- ❌ 不可用:
- 成员变量(字段)。
- 方法参数。
- 方法返回值。
- 只声明不初始化 (var a;)。
- 初始值为 null。
4. 注意事项
- 见名知意:由于类型隐藏,变量名必须清晰表达其含义(如 var userList 而非 var a)。
- 适度使用:若右侧类型不明显或降低可读性,不建议使用。
- 基本类型注意:整数默认推断为 int,小数默认推断为 double。
第 45 期:运算符综合实战练习
通过以下案例巩固运算符知识:
- 临时变量法(推荐)。
- 加减法(有溢出风险)。
- 异或法(面试常见,实际少用)。
第 46 期:if 语句的第一种格式
1. 语法
if (条件表达式) {
语句体;
}
- 执行流:条件为 true 执行语句体;false 跳过。
2. 关键注意点
第 47 期:if…else 语句的第二种格式
1. 语法
if (条件表达式) {
语句体1;
} else {
语句体2;
}
- 执行流:二选一。条件成立执行语句体1,否则执行语句体2。
2. 与三元运算符对比
- 三元运算符:适用于简单的取值操作(表达式,有返回值)。
- if…else:适用于复杂的逻辑执行(语句,无返回值,可执行多条语句)。
第 48 期:if…else if…else 第三种格式
1. 语法
if (条件1) { ... }
else if (条件2) { ... }
else if (条件3) { ... }
else { ... }
2. 执行逻辑
- 从上往下依次判断。
- 短路特性:一旦某个条件成立,执行对应语句块后,整个结构结束,不再判断后续条件。
- 最多执行一个分支。
3. 重要原则
- 顺序至关重要:范围判断应从大到小或从小到大排列,避免“大区间”在前导致“小区间”永远无法命中。
- 适用场景:多分支互斥判断(如成绩等级、月份季节)。
第 49 期:if 语句的嵌套使用
1. 概念
在 if 或 else 的代码块内部再编写 if 语句。
2. else 配对规则
- 就近原则:else 总是与最近且未配对的 if 配对。
- 建议:务必使用大括号 {} 明确作用域,避免歧义。
3. 设计建议
- 层级限制:嵌套层数尽量不超过 3 层。
- 优先合并:能用逻辑运算符 (&&, ||) 合并条件的,尽量不用嵌套。
- 优先 else-if:多个并列条件判断,优先使用 else if 链,而非深层嵌套。
第 50 期:switch 语句的基础语法
1. 语法结构
switch (表达式) {
case 常量1:
语句体1;
break; // 跳出 switch
case 常量2:
语句体2;
break;
default:
语句体n;
break;
}
2. 执行流程
3. switch vs if
| 适用场景 | 等值判断 (值不多) | 范围判断, 复杂布尔逻辑 |
| 数据类型 | byte, short, int, char, String, enum | 任意类型 |
| 性能 | 底层可能优化为跳转表,效率较高 | 逐条判断 |
4. 注意事项
结语:恭喜!你已经掌握了 Java 基础语法的两大支柱:运算符和选择结构。现在你可以编写具有逻辑判断能力的程序了。接下来的课程将进入循环结构(while, for, do-while),让程序能够处理重复性任务。
Java 入门到精通 (第 51-60 期)
简介:本阶段深入讲解 switch 语句的细节(break、穿透、数据类型),对比 if 与 switch 的使用场景,并系统学习循环结构(while, do-while, for),包括嵌套循环、图形打印以及控制跳转语句 break 和 continue。
第 51 期:switch 语句中 break 的作用与 Case 穿透
1. break 的作用
- 功能:跳出 switch 语句块,终止后续执行。
- 缺失后果:如果没有 break,程序会发生 Case 穿透 (Fall-through)。
2. Case 穿透
- 现象:从匹配的 case 开始,一直向下执行所有语句,直到遇到 break 或 switch 结束。
- 示例:int week = 2;
switch (week) {
case 1: System.out.println("周一"); // 不执行
case 2: System.out.println("周二"); // 执行
case 3: System.out.println("周三"); // 穿透执行
default: System.out.println("其他"); // 穿透执行
}
// 输出:周二\\n周三\\n其他
3. 合理利用穿透
当多个 case 需要执行相同的代码逻辑时,可以利用穿透简化代码:
int day = 3;
switch (day) {
case 1:
case 2:
case 3:
case 4:
case 5:
System.out.println("工作日");
break;
case 6:
case 7:
System.out.println("休息日");
break;
default:
System.out.println("无效");
}
4. 注意事项
- 默认建议写 break:除非有意利用穿透,否则每个 case 后都应加 break。
- 最后一个 break:default 或最后一个 case 后的 break 可省略(因为后面没代码了),但为了规范建议保留。
第 52 期:switch 支持的数据类型
1. 支持的类型
Java switch 表达式支持以下类型:
- 基本类型:byte, short, int, char。
- 不支持:long (太大), float/double (精度问题), boolean (只有两个值)。
- 引用类型:
- String (Java 7+)。
- enum (枚举, Java 5+)。
- 包装类:Byte, Short, Integer, Character (自动拆箱)。
2. 为什么不支持 Long/Double?
- switch 底层基于整数值比较实现。
- long 范围超出 int,转换可能丢失精度。
- float/double 存在精度误差,相等性判断不可靠。
3. String 的注意事项
- case 中的字符串不能为 null,否则会抛出 NullPointerException。
- 使用 equals() 进行内部比较,性能优于多次 if-else。
4. Java 12+ 新特性 (Switch 表达式)
Java 12 引入箭头语法 ->,无需 break,避免穿透:
String result = switch (day) {
case 1, 2, 3, 4, 5 -> "工作日";
case 6, 7 -> "休息日";
default -> "无效";
};
第 53 期:if 与 switch 的选择与区别
| 适用场景 | 范围判断、复杂逻辑、布尔判断 | 等值判断、分支较多且固定 |
| 支持类型 | 任意类型 (结果需为 boolean) | byte, short, int, char, String, enum |
| 灵活性 | 高 | 低 (仅限等值) |
| 效率 | 分支多时逐条判断 | 底层优化为跳转表,效率较高 |
| 可读性 | 分支多时代码冗长 | 结构清晰,易于维护 |
选择建议
- 范围判断 (>, <, >=)。
- 复杂条件组合 (&&, ||)。
- 判断类型不支持 switch (long, double, boolean)。
- 字符串包含、前缀等非精确匹配。
第 54 期:while 循环的基本结构
1. 语法
while (条件表达式) {
循环体;
}
- 流程:先判断条件,若为 true 则执行循环体,再回到判断。
2. 循环三要素
3. 常见错误
- 死循环:缺少“状态改变”步骤。
- 分号误用:while(cond); {} 会导致空循环,大括号内代码只执行一次或不执行。
- 省略大括号:建议始终保留 {} 以避免逻辑错误。
第 55 期:do…while 循环与 while 的区别
1. 语法
do {
循环体;
} while (条件表达式); // 注意末尾必须有分号!
2. 核心区别
- while:先判断,后执行。条件不满足时,循环体一次都不执行。
- do…while:先执行,后判断。无论条件如何,循环体至少执行一次。
3. 使用场景
- do…while:适用于必须至少执行一次的场景,如菜单选择、用户输入验证。
- while:适用于不确定次数,且可能一次都不执行的场景。
第 56 期:for 循环的标准格式
1. 语法
for (初始化; 条件判断; 迭代更新) {
循环体;
}
2. 优势
- 结构清晰:三要素集中在一行,便于阅读和维护。
- 防止遗漏:不易忘记更新循环变量。
- 作用域限制:在初始化部分定义的变量仅在循环内有效。
3. 执行流程
4. 省略部分
三个部分均可省略,但分号必须保留。
- for (;;) { … } 等价于 while(true) 死循环。
- 不建议完全省略,会降低可读性。
第 57 期:for 循环的执行流程图解
1. 流程详解
- 初始化:只在循环开始时执行一次。
- 判断:每次进入循环体前检查。
- 循环体:执行具体逻辑。
- 更新:在循环体执行完毕后执行,然后再次判断。
2. 关键点
- i++ 的位置:在循环体之后执行。
- 循环结束后的变量值:通常为导致条件失败的那个值。
- 例:for(int i=1; i<=3; i++),循环结束后 i 为 4。
3. 与 while 的等价性
// for 循环
for (init; cond; update) { body; }
// 等价于 while 循环
init;
while (cond) {
body;
update;
}
第 58 期:循环嵌套:打印乘法口诀表
1. 嵌套规则
- 外层循环控制行数。
- 内层循环控制每行的列数(内容)。
- 执行节奏:外层走一步,内层走完一圈。
2. 乘法口诀表实现
for (int i = 1; i <= 9; i++) { // 外层:行
for (int j = 1; j <= i; j++) { // 内层:列,不超过当前行号
System.out.print(j + "*" + i + "=" + (j*i) + "\\t");
}
System.out.println(); // 每行结束换行
}
3. 练习建议
- 打印三角形、菱形等图形是锻炼嵌套循环逻辑的好方法。
- 关键在于找出行数 i 与 列数/空格数 之间的数学关系。
第 59 期:循环嵌套:打印各种图形
通过调整内层循环的条件,可以打印不同图案:
- 空格数:rows – i
- 星号数:2 * i – 1
第 60 期:break 跳出循环语句
1. break 的作用
- 功能:立即终止当前所在的循环或 switch。
- 位置:通常放在 if 条件中,满足特定条件时触发。
2. 在循环中的应用
- 提前退出:例如查找目标值,找到后立即 break,节省时间。
- 死循环控制:在 while(true) 中通过条件判断 break 退出。
3. 嵌套循环中的行为
- break 只能跳出最近的一层循环(内层)。
- 若要跳出多层循环,需使用带标签的 break (Labelled Break),见下一期。
4. 注意事项
- 作用域限制:只能在循环或 switch 中使用。
- 后续代码不可达:break 之后的代码无法执行,编译器会报错。
- 与 return 区别:
- break:跳出循环,继续执行方法中剩余代码。
- return:结束整个方法,返回调用者。
5. 经典案例:素数判断
boolean isPrime = true;
for (int i = 2; i < n; i++) {
if (n % i == 0) {
isPrime = false;
break; // 发现因子,不是素数,直接退出
}
}
结语:恭喜!你已经掌握了 Java 流程控制的两大核心支柱:选择结构 (if/switch) 和 循环结构 (while/do-while/for)。现在你可以编写具有复杂逻辑和重复处理能力的程序了。接下来的课程将讲解 continue 语句以及更高级的数组和面向对象编程基础。
Java 入门到精通 (第 61-70 期)
简介:本阶段深入讲解循环控制语句(continue、带标签的 break/continue)、死循环的应用,并系统介绍了 Random 和 Math 类。最后通过一系列经典算法案例(猜数字、闰年、水仙花数、斐波那契数列等)综合应用流程控制知识。
第 61 期:continue 结束单次循环
1. 作用
- continue:跳过本次循环中剩余的代码,直接进入下一次循环的判断或更新步骤。
- 对比 break:break 是彻底终止整个循环;continue 只是跳过当前这一次迭代。
2. 示例:打印 1-10,跳过 5
for (int i = 1; i <= 10; i++) {
if (i == 5) {
continue; // 跳过打印,直接进行 i++
}
System.out.println(i);
}
// 输出:1, 2, 3, 4, 6, 7, 8, 9, 10
3. 注意事项:while 中的死循环陷阱
在 while 循环中使用 continue 时,必须确保改变循环条件的语句位于 continue 之前,否则会导致死循环。
int i = 0;
while (i < 10) {
if (i == 5) {
continue; // 跳过了下面的 i++,导致 i 永远为 5,死循环!
}
System.out.println(i);
i++;
}
- 建议:在 for 循环中使用 continue 更安全,因为 i++ 在循环头中,一定会执行。
4. 嵌套循环中的行为
continue 只能作用于当前所在层的循环。内层的 continue 不会影响外层循环的执行。
第 62 期:带标签的 break 与 continue
1. 什么是标签
给循环起一个名字,格式为 标签名: 循环语句。
outer: for (int i = 0; i < 10; i++) { ... }
2. 功能
- break outer;:直接跳出名为 outer 的外层循环,而不仅仅是当前内层循环。
- continue outer;:直接跳到名为 outer 的外层循环的下一次迭代。
3. 使用场景
主要用于多层嵌套循环中需要一次性跳出所有层级时。但在实际开发中较少使用,通常可以通过标志变量或方法返回来实现,以保持代码可读性。
4. 面试考点
Java 中有 goto 关键字吗?
- 有保留字 goto,但未实现。
- 带标签的 break/continue 实现了类似 goto 的功能,但仅限于循环内部跳转。
第 63 期:死循环的概念与应用
1. 定义
条件永远为真,循环无法自然结束的循环。
while (true) { ... }
for (;;) { ... }
do { ... } while (true);
2. 是 Bug 吗?
不一定。许多程序需要长期运行,故意使用死循环,并在内部通过 break 或 return 控制退出。
- 常见场景:服务器监听、游戏主循环、菜单系统、监控程序。
3. 意外死循环的原因
4. 如何终止
- 命令行:Ctrl + C
- IDE:点击停止按钮
第 64 期:随机数 Random 类的生成
1. 基本用法
import java.util.Random;
Random r = new Random();
int num = r.nextInt(10); // 生成 [0, 10) 之间的整数,即 0-9
2. 生成指定范围 [min, max] 的随机整数
通用公式:
int num = r.nextInt(max – min + 1) + min;
- 例如:生成 1-100 → r.nextInt(100) + 1
- 例如:生成 5-15 → r.nextInt(11) + 5
3. 其他方法
- nextDouble(): [0.0, 1.0)
- nextBoolean(): true 或 false
- nextInt(): 任意 int 范围
4. 伪随机数
- Random 生成的是伪随机数,基于种子计算。
- 默认构造函数使用当前时间作为种子,保证每次运行结果不同。
- 若指定种子 new Random(100),则每次生成的序列相同。
第 65 期:Math 类中的常用数学方法
1. 特点
- 位于 java.lang 包,无需导包。
- 所有方法均为 static,直接通过 Math.方法名() 调用。
- 类本身是 final,不可继承,构造私有。
2. 常用方法表
| abs(a) | 绝对值 | Math.abs(-5) → 5 |
| max(a, b) / min(a, b) | 最大值/最小值 | Math.max(3, 5) → 5 |
| pow(a, b) | a 的 b 次幂 | Math.pow(2, 3) → 8.0 |
| sqrt(a) | 平方根 | Math.sqrt(16) → 4.0 |
| ceil(a) | 向上取整 | Math.ceil(3.2) → 4.0 |
| floor(a) | 向下取整 | Math.floor(3.8) → 3.0 |
| round(a) | 四舍五入 | Math.round(3.5) → 4 (long) |
| random() | 随机 double | [0.0, 1.0) |
3. Math.random() vs Random 类
- Math.random() 底层调用 Random,仅返回 [0.0, 1.0) 的 double。
- 生成整数需手动转换:(int)(Math.random() * 100) + 1。
- 推荐使用 Random 类,API 更清晰直观。
4. 常量
- Math.PI: 圆周率 π
- Math.E: 自然常数 e
第 66 期:流程控制案例——猜数字游戏
1. 需求
- 系统生成 1-100 随机数。
- 用户有 10 次机会猜测。
- 提示“猜大了”、“猜小了”。
- 猜对胜利,用完次数失败。
- 支持一局结束后选择是否重玩。
2. 核心逻辑结构
// 外层:控制是否再玩一局 (do-while)
do {
// 初始化本轮游戏
int target = r.nextInt(100) + 1;
boolean win = false;
// 内层:控制猜测次数 (for)
for (int i = 1; i <= 10; i++) {
// 获取输入
// 判断大小 (if-else)
if (guess == target) {
win = true;
break; // 猜对,跳出内层循环
}
}
// 判断输赢
if (!win) System.out.println("失败");
// 询问是否重玩
} while (继续游戏);
3. 知识点综合运用
- Random: 生成目标数。
- Scanner: 接收用户输入。
- for/while: 循环控制。
- if-else: 逻辑判断。
- break: 提前退出循环。
- boolean 标记位: 记录游戏结果状态。
第 67 期:流程控制案例——判断闰年
1. 闰年规则
2. 代码实现
推荐写法(逻辑表达式):
boolean isLeap = (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
替代写法(嵌套 if):
if (year % 4 == 0) {
if (year % 100 == 0) {
if (year % 400 == 0) isLeap = true;
else isLeap = false;
} else {
isLeap = true;
}
} else {
isLeap = false;
}
3. 扩展:判断月份天数
结合 switch 和闰年判断,确定某年某月的天数。
- 1,3,5,7,8,10,12月 → 31天
- 4,6,9,11月 → 30天
- 2月 → 闰年29天,平年28天
第 68 期:流程控制案例——水仙花数
1. 定义
三位数中,各位数字的立方和等于其本身的数。
- 例:153=13+53+33153 = 1^3 + 5^3 + 3^3153=13+53+33
2. 解题思路
- 个位:n % 10
- 十位:n / 10 % 10
- 百位:n / 100
3. 代码实现
for (int i = 100; i <= 999; i++) {
int ge = i % 10;
int shi = i / 10 % 10;
int bai = i / 100;
if (ge*ge*ge + shi*shi*shi + bai*bai*bai == i) {
System.out.println(i);
}
}
// 结果:153, 370, 371, 407
4. 扩展:取任意位数字
公式:num / (int)Math.pow(10, n) % 10 (n 从右向左索引,0开始)。
第 69 期:流程控制案例——统计位数与求和
1. 需求
输入一个整数,输出:
2. 核心算法:循环取余
利用 while 循环,每次取个位并除以 10,直到数为 0。
int temp = Math.abs(num); // 处理负数和0
int count = 0;
int sum = 0;
int reverse = 0;
if (temp == 0) {
count = 1; // 特殊处理0
} else {
while (temp > 0) {
int digit = temp % 10;
count++;
sum += digit;
reverse = reverse * 10 + digit; // 拼接逆序数
temp /= 10;
}
}
3. 扩展:回文数判断
若 reverse == Math.abs(num),则为回文数。
第 70 期:流程控制案例——斐波那契数列
1. 定义
数列:1, 1, 2, 3, 5, 8, 13…
规律:F(n)=F(n−1)+F(n−2)F(n) = F(n-1) + F(n-2)F(n)=F(n−1)+F(n−2) (n≥3n \\ge 3n≥3)
2. 迭代法实现(推荐)
使用三个变量滚动更新,空间复杂度 O(1)O(1)O(1)。
int n = 20;
int a = 1, b = 1;
System.out.print(a + " " + b);
for (int i = 3; i <= n; i++) {
int c = a + b;
System.out.print(" " + c);
a = b; // 前移
b = c; // 更新当前
}
3. 其他实现方式
- 数组存储:适合需要保存历史数据的场景。int[] fib = new int[n];
fib[0] = 1; fib[1] = 1;
for(int i=2; i<n; i++) fib[i] = fib[i–1] + fib[i–2]; - 递归:代码简洁但效率低(重复计算),不推荐用于大数值。
4. 注意事项
- 数据溢出:int 最多支持到第 46 项左右,更大需用 long 或 BigInteger。
- 应用场景:爬楼梯问题、兔子繁殖、动态规划入门。
结语:恭喜!你已经完成了 Java 基础语法中流程控制的所有核心内容。从简单的分支判断到复杂的循环嵌套,再到经典的算法案例,你的逻辑思维能力和编程实战能力得到了显著提升。接下来,我们将进入数据结构的基础——数组的学习。
Java 入门到精通 (第 71-80 期)
简介:本阶段系统讲解 Java 数组(Array)。从一维数组的概念、内存模型、声明初始化、遍历操作,到常见的算法案例(最大值、求和、逆序),再到二维数组的结构与遍历。重点解析数组的底层原理及常见异常处理。
第 71 期:数组的概念与内存分配原理
1. 为什么需要数组
- 痛点:存储大量相同类型数据时,定义多个变量繁琐且难以管理(如求平均分)。
- 解决方案:数组是一个容器,可以一次性存储多个相同类型的数据。
2. 数组的核心特点
- 类型一致:所有元素必须是同一种数据类型。
- 长度固定:创建后长度不可改变。
- 索引访问:通过从 0 开始的整数索引访问元素。
- 连续存储:在内存中占据连续的空间。
3. 内存模型
Java 内存主要分为栈(Stack)和堆(Heap):
- 栈(Stack):存储局部变量(包括引用变量的地址)。
- 堆(Heap):存储 new 出来的对象(包括数组对象本身)。
引用类型机制:
- 数组变量(如 int[] arr)存储在栈中,保存的是指向堆中数组对象的地址。
- 数组的实际元素数据存储在堆中。
栈内存 堆内存
+——+ +———–+
| arr | ———–> | int[5] |
+——+ +———–+
(地址) | 0: 90 |
| 1: 85 |
| … |
+———–+
4. 默认初始化值
| 整数 (byte, short, int, long) | 0 |
| 浮点数 (float, double) | 0.0 |
| 字符 (char) | '\\u0000' |
| 布尔 (boolean) | false |
| 引用类型 (String, 对象等) | null |
第 72 期:一维数组的声明与动态初始化
1. 数组声明
推荐写法:数据类型[] 数组名;
- 注意:声明时不能指定长度。
2. 动态初始化
只指定长度,由系统赋予默认值。
// 格式:new 数据类型[长度]
int[] arr = new int[5];
3. 数组访问与长度
- 访问元素:数组名[索引]。索引范围:0 到 length – 1。
- 获取长度:数组名.length。
- 重要区别:length 是属性(无括号),String 的 length() 是方法(有括号)。
4. 数组遍历
使用 for 循环,索引从 0 遍历至 arr.length – 1。
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
5. 常见错误
- ArrayIndexOutOfBoundsException:索引越界(访问了 <0 或 >=length 的索引)。
- NullPointerException:数组变量为 null 时访问元素。
第 73 期:一维数组的静态初始化
1. 静态初始化
创建数组时直接指定具体元素值,系统自动计算长度。
2. 两种格式
- 标准格式:int[] arr = new int[]{1, 2, 3};
- 简化格式(推荐):int[] arr = {1, 2, 3};
- 注意:简化格式只能在声明同时使用,不能用于后续赋值(如 arr = {1,2} 是错误的)。
3. 动态 vs 静态选择
- 知道元素内容 → 用静态初始化。
- 只知道长度,内容未知或后续生成 → 用动态初始化。
4. 注意事项
- 数组一旦创建,长度不可变。
- 直接打印数组对象输出的是哈希地址(如 [I@1b6d3586),需遍历或使用 Arrays.toString() 查看内容。
第 74 期:数组的遍历与 length 属性
1. 两种遍历方式对比
| 语法 | for(int i=0; i<arr.length; i++) | for(type var : arr) |
| 是否有索引 | 有 (i) | 无 |
| 能否修改元素 | 能 (arr[i] = val) | 不能 (修改变量副本不影响原数组) |
| 适用场景 | 需要索引、修改元素、查找位置 | 仅读取元素、简单遍历 |
2. 典型应用
- 求和:累加所有元素。
- 求平均值:sum / length。
- 陷阱:注意整数除法精度丢失,建议先转换为 double:(double) sum / length。
3. 注意事项
- 空数组 (length=0) 遍历时循环体不执行,安全。
- null 数组遍历时会报 NullPointerException。
第 75 期:数组的最小值与最大值获取
1. 核心思路(擂台法)
2. 关键细节
- 初始值必须来自数组:不能设为 0,否则全负数数组结果错误。
- 起始索引:从 1 开始比较(因为 0 已作为初始值)。
- 同时求最值:在一次遍历中同时更新 max 和 min,提高效率。
3. 获取最值的索引
记录最大/最小值的索引 index,当发现更优值时更新 index。
第 76 期:数组元素的求和与平均值
1. 求和与平均值代码模板
int sum = 0;
for (int num : arr) {
sum += num;
}
double avg = (double) sum / arr.length; // 强制转换防止整数截断
2. 进阶统计
- 单次遍历:可同时计算总和、最大值、最小值。
- 多次遍历:若需统计“大于平均值的个数”,需先算出平均值,再二次遍历统计。
3. 溢出风险
- 若数组元素很大或数量很多,sum 可能超出 int 范围,建议使用 long 或 double 存储累加和。
第 77 期:数组的逆序操作
1. 概念
- 逆序:修改原数组,使元素顺序颠倒(原地交换)。
- 倒着打印:仅改变输出顺序,不修改原数组。
2. 实现逻辑
首尾交换:索引 i 与 length – 1 – i 交换。
只需遍历到 length / 2。
3. 代码实现
方法一:For 循环
for (int i = 0; i < arr.length / 2; i++) {
int temp = arr[i];
arr[i] = arr[arr.length – 1 – i];
arr[arr.length – 1 – i] = temp;
}
方法二:双指针 While 循环
int left = 0;
int right = arr.length – 1;
while (left < right) {
int temp = arr[left];
arr[left] = arr[right];
arr[right] = temp;
left++;
right—;
}
第 78 期:数组的拷贝与扩容
1. 为什么需要扩容
数组长度固定。若需增加容量,需新建更大数组并拷贝数据。
2. 四种拷贝方式
3. 扩容策略
通常按倍数扩容(如 1.5 倍或 2 倍),以减少频繁扩容带来的性能开销(时间换空间)。
第 79 期:数组的常见异常
1. ArrayIndexOutOfBoundsException (索引越界)
- 原因:访问索引 < 0 或 >= length。
- 常见场景:循环条件写错 (<=)、索引计算错误。
- 预防:坚持使用 i < arr.length。
2. NullPointerException (空指针)
- 原因:数组变量为 null 时访问属性或方法。
- 常见场景:只声明未初始化、手动赋值为 null、引用类型数组元素默认为 null。
- 预防:使用前检查 if (arr != null);引用类型数组元素使用前需确认非 null。
3. 调试技巧
学会阅读异常堆栈信息,定位报错行数和具体原因。
第 80 期:二维数组的理解与内存结构
1. 概念
- 定义:数组的数组。
- 结构:像表格一样,有行(Row)和列(Column)。
- 本质:Java 中没有真正的多维数组,二维数组是一个一维数组,其每个元素又是一个一维数组的引用。
2. 内存结构
- 外层数组(行)存储在堆中,其元素存储的是内层数组(列)的地址。
- 支持不规则数组(锯齿数组),即每行的长度可以不同。
3. 声明与初始化
- 规则二维数组:int[][] arr = new int[3][5]; (3行5列)
- 不规则二维数组:int[][] arr = new int[3][]; // 指定行数
arr[0] = new int[3]; // 第一行3个元素
arr[1] = new int[5]; // 第二行5个元素
4. 访问与遍历
- 访问:arr[行索引][列索引]。
- 长度:
- arr.length:行数。
- arr[i].length:第 i 行的列数。
- 遍历:双重循环。
- 外层循环控制行 (i < arr.length)。
- 内层循环控制列 (j < arr[i].length)。
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr[i].length; j++) {
System.out.print(arr[i][j] + " ");
}
System.out.println();
}
结语:恭喜!你已经掌握了 Java 中最重要的数据结构之一——数组。从一维到二维,从基础操作到内存原理,这些知识是后续学习集合框架(Collection)和面向对象编程的基石。接下来,我们将进入面向对象编程 (OOP) 的世界,探索类与对象的核心概念。
Java 入门到精通 (第 81-90 期)
简介:本阶段深入讲解二维数组及其操作,介绍 Arrays 工具类与经典算法(二分查找、冒泡/选择/插入排序),并通过综合案例(抽奖系统、成绩管理系统)巩固数组应用。最后引入“方法”概念,详解方法的定义、调用及参数传递机制(值传递)。
第 81 期:二维数组的初始化与遍历
1. 静态初始化
直接指定元素值,支持规则和不规则(锯齿)数组。
// 标准格式
int[][] arr = new int[][]{{1, 2}, {3, 4, 5}};
// 简化格式(推荐)
int[][] arr = {{1, 2}, {3, 4, 5}};
2. 遍历
- 普通 For 循环:需两层循环,外层控制行,内层控制列。注意使用 arr[i].length 获取每行长度。
- 增强 For 循环:更简洁。for (int[] row : arr) {
for (int num : row) {
System.out.print(num + " ");
}
System.out.println();
}
3. 经典案例:杨辉三角
- 规律:每行首尾为 1;中间元素 arr[i][j] = arr[i-1][j-1] + arr[i-1][j]。
- 实现:先动态创建每一行的数组(长度为 i+1),填充数据后打印。
第 82 期:Arrays 工具类常用方法
java.util.Arrays 提供了丰富的数组操作静态方法。
| toString(arr) | 转字符串,方便打印一维数组 | [1, 2, 3] |
| deepToString(arr) | 转字符串,用于多维数组 | [[1, 2], [3, 4]] |
| sort(arr) | 升序排序 | Arrays.sort(arr) |
| binarySearch(arr, key) | 二分查找(数组必须有序) | 返回索引或负数 |
| copyOf(arr, len) | 复制数组,可指定新长度 | 短则截断,长则补默认值 |
| fill(arr, val) | 填充数组所有元素 | Arrays.fill(arr, 0) |
| equals(a1, a2) | 比较内容是否相同 | true/false |
第 83 期:二分查找算法实现
1. 前提条件
- 数组必须是有序的。
2. 核心逻辑
- 计算中点 mid = left + (right – left) / 2(防止溢出)。
- 若 arr[mid] == target,返回 mid。
- 若 arr[mid] < target,说明目标在右侧,left = mid + 1。
- 若 arr[mid] > target,说明目标在左侧,right = mid – 1。
3. 复杂度
- 时间复杂度:O(logn)O(\\log n)O(logn),效率极高。
- 空间复杂度:O(1)O(1)O(1)。
第 84 期:冒泡排序算法详解
1. 思路
重复遍历数组,比较相邻元素,若顺序错误则交换。每一轮将最大元素“冒泡”至末尾。
2. 代码结构
for (int i = 0; i < arr.length – 1; i++) { // n-1 轮
for (int j = 0; j < arr.length – 1 – i; j++) { // 每轮减少比较次数
if (arr[j] > arr[j + 1]) {
// 交换
}
}
}
3. 优化
添加 boolean swapped 标志位。若某轮无交换,说明已有序,提前终止。
4. 特性
- 时间复杂度:平均 O(n2)O(n^2)O(n2),最好 O(n)O(n)O(n)(已有序)。
- 稳定性:稳定。
- 空间复杂度:O(1)O(1)O(1)。
第 85 期:选择排序算法详解
1. 思路
每一轮从待排序部分选出最小元素,与待排序部分的第一个元素交换。
2. 代码结构
for (int i = 0; i < arr.length – 1; i++) {
int minIndex = i;
for (int j = i + 1; j < arr.length; j++) {
if (arr[j] < arr[minIndex]) {
minIndex = j;
}
}
if (minIndex != i) {
// 交换 arr[i] 和 arr[minIndex]
}
}
3. 对比冒泡
- 交换次数少:每轮最多交换 1 次,优于冒泡。
- 稳定性:不稳定(相等元素相对位置可能改变)。
- 时间复杂度始终为 O(n2)O(n^2)O(n2)。
第 86 期:插入排序算法详解
1. 思路
类似打扑克牌理牌。将数组分为已排序和待排序两部分,每次取出待排序第一个元素,插入到已排序部分的正确位置。
2. 代码结构
for (int i = 1; i < arr.length; i++) {
int temp = arr[i];
int j = i – 1;
// 边找边移
while (j >= 0 && arr[j] > temp) {
arr[j + 1] = arr[j];
j—;
}
arr[j + 1] = temp;
}
3. 特性
- 最好情况:O(n)O(n)O(n)(已有序)。
- 最坏情况:O(n2)O(n^2)O(n2)(完全逆序)。
- 稳定性:稳定。
- 适用场景:数据量小或基本有序时效率很高,是实际开发中常用的基础排序(如 JDK 源码中的混合排序)。
第 87 期:数组综合案例——抽奖系统
1. 需求
- 奖池抽取奖品,不重复。
- 查看剩余奖品、已中奖品。
2. 核心技巧:模拟删除
由于数组长度固定,无法直接删除元素。
- 策略:用变量 count 记录有效奖品数量。
- 移除逻辑:随机抽取索引 index 的元素,将最后一个有效元素 (prizes[count-1]) 复制到 index 位置,然后 count–。
- 优点:无需移动大量元素,效率高(适用于不关心顺序的场景)。
第 88 期:数组综合案例——成绩管理系统
1. 功能模块
- 增:添加到数组末尾,增加计数。
- 查:遍历打印。
- 统:一次遍历计算总分、最高、最低。
- 排:使用 Arrays.copyOf 复制副本后排序,保留原顺序。
- 搜:遍历查找或二分查找(需先排序)。
- 段:统计各分数段人数。
2. 设计模式
- 定长数组 + 计数器:这是使用原生数组模拟动态列表的标准做法。
- 菜单驱动:while(true) + switch 实现交互界面。
第 89 期:方法(Method)的定义与调用
1. 概念
方法是完成特定功能的代码块,实现代码复用和模块化。
2. 定义格式
修饰符 返回值类型 方法名(参数列表) {
方法体;
return 返回值; // void 类型可不写或只写 return
}
3. 调用
- 无返回值:方法名(实参);
- 有返回值:数据类型 变量 = 方法名(实参);
4. 注意事项
- 方法必须在类中定义,不可嵌套。
- 参数个数、类型、顺序需匹配。
- return 语句立即结束方法执行。
第 90 期:方法的参数传递(值传递)
Java 中只有值传递,没有引用传递。
1. 基本类型参数
- 传递的是值的副本。
- 方法内修改形参,不影响外部实参。
2. 引用类型参数(如数组、对象)
- 传递的是地址值的副本。
- 形参和实参指向堆内存中的同一个对象。
- 修改对象内容(如 arr[0] = 100):会影响外部实参(因为操作的是同一个对象)。
- 重新赋值(如 arr = new int[]{…}):不会影响外部实参(因为只是改变了副本的指向,实参仍指向原对象)。
3. 总结口诀
- 改值不改址,外面看不见(基本类型)。
- 改址不改值,外面也看不见(引用类型重新 new)。
- 既改值又改址(通过引用来修改内部状态),外面看得到。
结语:恭喜!你已经掌握了 Java 基础语法的核心支柱:数组与方法。数组让你能够批量处理数据,方法让你能够组织和管理逻辑。接下来,我们将进入面向对象编程(OOP)的世界,学习如何封装数据与行为,构建更复杂的程序结构。
Java 入门到精通 (第 91-100 期)
简介:本阶段深入讲解方法的高级特性(重载、可变参数、递归),介绍常用类库(String, StringBuilder, Date/Time, System),并回顾整个基础语法阶段的学习路线,为进入面向对象编程(OOP)打下坚实基础。
第 91 期:方法的重载(Overload)
1. 概念
在同一个类中,允许存在一个以上的同名方法,只要它们的参数列表不同即可。
- 目的:功能相似的方法使用相同名称,提高代码可读性和易用性(如 System.out.println)。
2. 判定标准
- 必须满足:
- 同一个类中。
- 方法名相同。
- 参数列表不同(参数个数、参数类型、参数顺序不同)。
- 无关因素:
- 返回值类型不同(不算重载,编译器无法区分调用哪个)。
- 修饰符不同。
- 参数名不同。
3. 方法签名
- 定义:方法名 + 参数列表。
- 作用:唯一标识一个方法。重载即意味着方法签名不同。
第 92 期:可变参数(VarArgs)的使用
1. 概念
方法的参数个数可以变化,支持 0 个、1 个或多个参数。
2. 语法
public static void methodName(Type... paramName) { ... }
- 本质是一个数组。在方法内部,paramName 被视为 Type[] 数组。
3. 规则
4. 示例
public static int sum(int... nums) {
int sum = 0;
for (int num : nums) sum += num;
return sum;
}
// 调用:sum(), sum(1), sum(1, 2, 3) 均合法
第 93 期:方法的返回值与 void 关键字
1. 返回值类型
- 有返回值:指定具体数据类型(基本类型或引用类型)。方法体中必须包含 return 值;,且所有执行路径都必须有返回。
- 无返回值:使用 void。方法体中可以没有 return,或者仅写 return; 用于提前结束方法。
2. return 的作用
3. 选择原则
- 需要获取计算结果 → 有返回值。
- 仅执行操作(如打印、修改对象状态) → void。
第 94 期:方法的递归调用
1. 概念
方法在执行过程中调用自身。
2. 核心要素
3. 经典案例
- 阶乘:n!=n×(n−1)!n! = n \\times (n-1)!n!=n×(n−1)!,出口 1!=11! = 11!=1。
- 斐波那契数列:F(n)=F(n−1)+F(n−2)F(n) = F(n-1) + F(n-2)F(n)=F(n−1)+F(n−2),出口 F(1)=1,F(2)=1F(1)=1, F(2)=1F(1)=1,F(2)=1。
- 注意:朴素递归实现斐波那契效率低(重复计算),大数据量下建议使用循环或记忆化搜索。
4. 优缺点
- 优点:代码简洁,逻辑清晰,适合解决天然递归问题(如树遍历、汉诺塔)。
- 缺点:性能开销大(栈空间占用),可能存在重复计算,调试困难。
- 建议:能用循环解决的优先用循环;特殊场景用递归。
第 95 期:Debug 调试模式进阶
1. 断点(Breakpoint)
程序运行到标记位置暂停,便于观察状态。
- 行断点:常规断点。
- 条件断点:满足特定条件(如 i == 50)时才暂停,极大提高循环调试效率。
2. 常用快捷键
| F8 | Step Over | 单步执行,不进入方法内部 |
| F7 | Step Into | 单步执行,进入方法内部 |
| Shift+F8 | Step Out | 跳出当前方法,回到调用处 |
| F9 | Resume | 继续运行至下一个断点 |
3. 调试面板
- Variables:查看当前变量值,支持修改变量测试。
- Frames (Call Stack):查看方法调用栈,回溯调用来源。
第 96 期:String 类的常用方法详解
String 是不可变字符序列,所有修改操作均返回新字符串。
1. 获取与判断
- length(): 长度。
- charAt(index): 获取指定索引字符。
- indexOf(str): 查找首次出现索引,未找到返回 -1。
- equals(obj): 比较内容(严禁使用 == 比较 String 内容)。
- contains(str): 是否包含子串。
- startsWith(str) / endsWith(str): 前缀/后缀判断。
- isEmpty(): 是否为空串(长度为0)。
2. 转换与截取
- toUpperCase() / toLowerCase(): 大小写转换。
- toCharArray(): 转为字符数组。
- substring(beginIndex) / substring(begin, end): 截取子串(左闭右开 [begin, end))。
- replace(old, new): 替换所有匹配项。
- split(regex): 分割字符串,返回数组。
- trim(): 去除首尾空格。
3. 注意事项
- 频繁拼接字符串不要用 +,会导致大量临时对象产生,应使用 StringBuilder。
- 常量池优化:编译期确定的字符串常量会复用。
第 97 期:StringBuilder 与 StringBuffer
1. 为什么需要可变字符串
String 不可变,多次拼接会产生大量垃圾对象,效率极低。StringBuilder 底层基于可扩容的字符数组,修改原对象,效率高。
2. 常用方法
- append(…): 追加内容(支持链式调用)。
- insert(index, str): 插入内容。
- delete(start, end): 删除内容。
- replace(start, end, str): 替换内容。
- reverse(): 反转字符串。
- toString(): 转换为 String 对象。
3. StringBuilder vs StringBuffer
- StringBuilder: 非线程安全,效率高。单线程首选。
- StringBuffer: 线程安全(synchronized 修饰),效率较低。多线程场景使用。
4. 性能对比
在循环中进行大量字符串拼接时,StringBuilder 比 String + 快数个数量级。
第 98 期:日期时间类
1. 历史演进
- 第一代:Date (java.util.Date),设计缺陷多,大部分方法已废弃。
- 第二代:Calendar,复杂且月份从 0 开始,易出错。
- 第三代(推荐):Java 8 API (java.time 包)。
2. Java 8 新 API 核心类
- LocalDate: 只包含日期(年月日)。
- LocalTime: 只包含时间(时分秒)。
- LocalDateTime: 包含日期和时间。
- 特点:不可变、线程安全、API 直观、月份从 1 开始。
3. 常用操作
- 获取:now(), of(year, month, day…)。
- 修改:plusDays(n), minusMonths(n), withYear(n)(返回新对象)。
- 格式化:DateTimeFormatter(线程安全)。
- 计算间隔:Duration (时间差), Period (日期间隔)。
第 99 期:System 类常用方法
System 是系统工具类,所有成员均为静态。
1. 输入输出
- System.out: 标准输出(控制台)。
- System.err: 标准错误输出(通常红色显示)。
- System.in: 标准输入流。
2. 时间与性能
- currentTimeMillis(): 毫秒级时间戳,常用于计算代码执行耗时。
- nanoTime(): 纳秒级高精度计时,用于微基准测试。
3. 其他实用方法
- arraycopy(src, srcPos, dest, destPos, length): 高效数组拷贝。
- getProperty(key): 获取系统属性(如 OS 名称、Java 版本)。
- exit(status): 退出 JVM(0 正常,非 0 异常)。
- gc(): 建议 JVM 执行垃圾回收(仅为建议,不保证立即执行)。
第 100 期:基础语法阶段总结与回顾
1. 学习路线回顾
- 环境搭建:JDK/JRE/JVM 关系,Hello World。
- 基础语法:变量、数据类型、运算符、Scanner。
- 流程控制:if/switch 分支,while/for/do-while 循环,break/continue。
- 数组:一维/二维数组,Arrays 工具类,排序算法(冒泡/选择/插入),二分查找。
- 方法与常用类:方法定义/重载/递归,String/StringBuilder,日期时间,System。
2. 核心思想提升
- 模块化:将大问题拆解为小方法。
- 复用意识:避免重复代码。
- 调试能力:熟练使用 IDEA Debug 工具定位 Bug。
- 算法思维:掌握基础的数据处理逻辑。
3. 纯过程式 vs 面向对象
- 目前所学均为纯过程式编程(数据与方法分离)。
- 随着项目规模扩大,过程式编程维护成本增加。
- 下一步:进入面向对象编程 (OOP),学习类、对象、封装、继承、多态,这是 Java 的核心灵魂。
4. 结语
恭喜完成 Java 基础语法 100 期学习!地基已打牢,接下来将开启面向对象的新篇章。
祝贺! 你已经完成了 Java 基础语法的全面学习。现在你具备了编写结构化程序的能力。请准备好迎接下一阶段的挑战:面向对象编程 (OOP)。
网硕互联帮助中心






评论前必须登录!
注册