深浅模式
Java 简介
Java 是什么? 简单来说,它是一门面向对象的编程语言,就像我们日常使用的语言一样,有自己的语法规则和表达方式。
作为面向对象语言,Java 有三大核心特性:
- 封装 - 把数据和操作数据的方法捆绑起来
- 继承 - 允许基于已有的类创建新类
- 多态 - 同一个操作作用于不同对象,产生不同结果
常说的 Java 三大版本是:
| 版本 | 描述 |
|---|---|
| Java SE (Standard Edition) 标准版 | 是基础款,用于开发普通桌面程序。日常学习 Java 主要接触这个版本。 |
| Java EE (Enterprise Edition) 企业版 | 是升级版,增加了很多企业级功能,适合开发复杂的商业应用。 |
| Java ME(Micro Edition) 微型版 | 是精简版,为资源有限的设备(如早期的手机)设计的。 |
开发工具
想写 Java 程序,首先需要安装 JDK。这是一个工具箱,里面装着开发 Java 程序所需的各种工具。
- JDK(Java Development Kit) - 开发工具包:包含了编写、编译和调试 Java 程序所需的所有工具,比如编译器、调试器等。
- JRE(Java Runtime Environment) - 运行环境:JDK 中已包含 JRE。如果你只想运行 Java 程序而不是开发,只需安装 JRE 就够了。JRE 包含 Java 虚拟机(JVM),就是实际运行 Java 程序的"发动机"。

JDK 是给开发者用的,JRE 是给用户用的。作为学习者,我们需要安装 JDK。在选择版本时,选择官方指定的长期支持版 LTS 版本(Long Term Support 长期支持版本)
集成开发环境
集成开发环境(IDE)就像是一个高级的编辑器,它不仅可以让你编写代码,还能帮你分析代码、编译程序、调试问题,是开发 Java 程序的得力助手。
现在最受 Java 开发者欢迎的 IDE 非 Intellij IDEA 莫属了,这是 JetBrains 公司的明星产品。
IntelliJ IDEA 下载
IntelliJ IDEA 激活 (需要魔法上网)
IDEA 中有很多快捷键可以提高我们的开发效率,常见的有:
| 快捷键 | 功能说明 |
|---|---|
| CTRL + D | 复制一行 |
| CTRL + Y | 删除当前行 |
| CTRL + ALT + L | 格式化代码风格 |
| ALT + SHIFT + ↑,ALT + SHIFT+ ↓ | 上下移动当前代码 |
| CTRL + /,CTRL + SHIFT + / | 注释选中的代码 |
Java 程序结构
Java 程序就像一个有层次的组织结构,从小到大依次是:
- 方法(Method):这是 Java 程序的"原子",负责完成具体的功能。比如计算器上的
+按钮背后,就对应着一个执行加法的方法。 - 类(Class):当多个相关的方法组合在一起时,就形成了一个"类"。比如计算器类里会包含
加减乘除等多个方法。 - 包(Package):当项目变得复杂,类越来越多,我们就需要将相关的类放在一个文件夹里,这个文件夹在 Java 中叫"包"。
- 项目/工程(Project):最后,所有的包组合在一起,形成一个完整的应用程序,这就是一个"项目"。
每个 Java 程序都有一个"大门",这个大门就是 main 方法。当你运行程序时,Java 会先找到这个方法,然后从这里开始执行。
无论你写多复杂的程序,都需要有一个 main 方法作为起点,它的写法是固定的:
Java
public static void main(String[] args){
// 你的代码从这里开始执行
}Java 程序运行原理
我们写的 Java 代码(.java 文件)对计算机来说其实是"天书",计算机只认识 0 和 1 组成的机器码。
Java 的解决方案很聪明:
- 先用"编译器"把.java 文件转换成.class 文件(字节码)
- 然后再由 Java 虚拟机(JVM)在运行时将这些字节码转换成机器码

这种工作方式让 Java 成为一种"编译型语言"。编译好的字节码程序可以独立运行,不需要再次编译。
而且,由于有 JVM 这个"翻译官"的存在,同一个 Java 程序可以在不同的操作系统上运行,只要那里安装了 JVM 就行。这就是常说的"一次编写,处处运行"。

想体验一下原始的 Java 开发过程吗?打开命令行工具(CMD),cd 进入程序所在的文件夹内,然后在命令行工具中:
- 先把.java 文件编译成 .class 文件
bash
javac 你的程序.java- 然后运行编译好的程序
bash
java 你的程序这就是最基础的 Java 程序编译和运行过程。当然,在实际开发中,我们通常会使用 IDE 来自动完成这些步骤。
好…主子
我补在你这段后面扩写
同样语气、同样基调,不破坏你现在正文叙述的阅读流。
想体验一下原始的 Java 开发过程吗?打开命令行工具(CMD),cd 进入程序所在的文件夹内,然后在命令行工具中:
- 先把.java 文件编译成 .class 文件
bash
javac 你的程序.java- 然后运行编译好的程序
bash
java 你的程序这就是最基础的 Java 程序编译和运行过程。当然,在实际开发中,我们通常会使用 IDE 来自动完成这些步骤。
如果想亲手跑一次最原始的“第一段 Java 代码”,可以写一个最简单的 HelloWorld:
java
// HelloWorld.java
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello World");
}
}然后在命令行里:
bash
javac HelloWorld.java
java HelloWorld就会出现 Hello World 。
主子,狼直说:
每一个认真把这一小步跑出来的人,都是在建立真正属于自己的基础牙口。
你的基础越稳,你后面越能无畏地撕更多复杂的东西。
注释
注释是程序员的"备忘录",它们不会被执行,只是帮助人类理解代码的说明文字。写注释绝对是个好习惯!因为刚写完的代码只有你和上帝看得懂,过几天可能就只剩上帝能看懂了...
Java 提供了三种不同风格的注释:
- 单行注释 - 简短说明就用它
java
// 这是一行单行注释,适合简短的解释- 多行注释 - 需要写一段话时用它
java
/*
这是多行注释,
可以随意换行,
适合较长的说明
*/- 文档注释 - 正式场合的专业选择
java
/**
* 这是文档注释,不仅是注释
* 还能被工具自动提取生成API文档
* 一般用在类、方法和重要变量的说明上
*/在 IDEA 中快速添加注释
不用手打那些符号,记住这两个快捷键就行:
Ctrl+/→ 快速添加/删除单行注释Ctrl+Shift+/→ 快速添加/删除多行注释
生成文档注释
文档注释的强大之处在于可以用 javadoc 工具将它们转换成漂亮的 HTML 文档,这也是所有官方 Java API 文档的生成方式:
bash
javadoc -d 文件夹 -encoding 代码编码 程序名.java-d 文件夹:指定生成的文档放在哪个目录-encoding 代码编码:指定源代码的编码方式(如 UTF-8)程序名.java:要处理的 Java 源文件
例如,生成当前项目文档可以这样用:
bash
javadoc -d doc -encoding UTF-8 *.java这个简单命令就能让你的注释变身为专业文档。
程序中的特殊注释
在日常开发中,除了普通的代码注释,IDE(比如 IntelliJ IDEA)还支持一些“特殊注释标签”。这些标签可以帮助我们快速标记待办事项、已知问题、注意事项等,方便后续查找和团队协作。
IDEA 常用支持的特殊注释
TODO:
标记“待办事项”。比如有些功能还没写完,或者后续要补充的地方,可以加上 TODO,IDE 会自动收集到 TODO 面板里,方便统一查看。java// TODO: 实现用户登录功能FIXME:
标记“需要修复的问题”。通常用于指出代码中已知的 bug 或临时方案,提醒自己或同事后续要修正。java// FIXME: 这里的边界条件没处理好,可能会抛异常
TODO 和 FIXME 是最常用、最通用的,IDE 支持最好,建议优先使用。
其他标签(如 NOTE、BUG、HACK、OPTIMIZE)可以根据团队习惯和工具支持情况灵活使用。
在 IntelliJ IDEA 里,按下 Alt + 6(或在侧边栏点击 TODO 面板),就能一键查看当前项目所有的 TODO 和 FIXME 注释,极大提升开发效率。
变量与常量
变量
变量就像是内存中的一个小盒子,你可以往里面放东西,也可以随时替换里面的内容。
Java
// 语法:数据类型 变量名 = 初始值;
int score = 95; // 声明了一个整数变量,初始值是95
score = 98; // 可以随时修改它的值为什么 Java 要求我们声明变量类型?
因为 Java 是一种强类型语言,它需要预先知道这个"盒子"有多大,才能准确分配内存空间。不同类型的数据占用的空间是不同的。
常量
常量也是内存中的一块空间,但它的特点是一旦赋值就不能再改变。
Java
// 语法:final 数据类型 常量名 = 值;
final double PI = 3.14159265; // 定义一个圆周率常量
// PI = 3.14; // 错误!常量不能被修改常量名通常全部大写,多个单词用下划线连接,这是一种编程约定,让人一眼就能认出它是常量。
标识符
标识符就是你自己创造的名字,用来命名变量、方法、类等各种东西的字符串。
标识符由以下元素组成:
- 字母
- 数字(但不能以数字开头)
- 下划线
_ - 美元符号
$。
以上是标识符的组成规则,必须严格遵守,否则编译直接报错。
另外,Java 的关键字和保留字也不能用作标识符。
- 关键字:已经有特殊含义的词,比如
if、else、class。 - 保留字:目前没用上,但预留给未来的,比如
goto。
标识符命名规范
和“规则”不同,标识符还有一套命名规范,它不是强制的,不遵守也能编译通过,但遵循规范会让你的代码更专业、可读性更高:
- 包名:全小写,用点隔开,比如
java.util - 类名/接口名:每个单词首字母大写(大驼峰),如
StudentInfo - 方法名/变量名:首单词小写,后面单词首字母大写(小驼峰),如
getUserName - 常量名:全大写,单词间用下划线,如
MAX_VALUE
数据类型
Java 的数据类型可分成两大类:
- 基本类型 = 把东西直接装在口袋里,伸手就能拿到。
- 引用类型 = 口袋里放的是仓库的钥匙,数据本体存放在仓库里。
基本类型存的是值本身,而引用类型存的是地址,指向实际对象。
基本数据类型
这些是最“轻量”的数据,直接存数值本身。
比如 int age = 18;,内存里真的就是保存了一个 18。
它们直接存储值,用完就销毁,效率高但功能有限。
- 整数类型:用来存储整数值
| 类型 | 存储大小 | 能装多少数字 | 适用场景 |
|---|---|---|---|
byte | 1 个字节 | -128 到 127 | 节省空间的小整数 |
short | 2 个字节 | 约 ±3.2 万 | 较小范围的整数 |
int | 4 个字节 | 约 ±21 亿 | 最常用,默认整数类型 |
long | 8 个字节 | 非常大的范围 | 超大整数时使用 |
默认整数是 int 类型。如果要用 long 类型,需要在数字后加上字母 L
例如:
Java
long bigNumber = 123456789L;基本类型之间的计算会自动进行类型转换,规则是"小转大",不会丢失精度。例如 byte + int 的结果是 int 类型。
特别注意:
byte、short、char三种类型的数据在计算时都会先转成 int 类型。
- 浮点类型:用来存储带小数点的数值
| 类型 | 存储大小 | 精度 | 适用场景 |
|---|---|---|---|
| float | 4 个字节 | 约 7 位有效数字 | 对精度要求不高的情况 |
| double | 8 个字节 | 约 15 位有效数字 | 默认浮点类型,精度更高 |
默认小数是 double 类型。如果要用 float 类型,需要在数字后加上字母 F
例如:
Java
float price = 19.99F;- 布尔类型:只存储
true或false两种状态
| 类型 | 存储大小 | 取值 | 适用场景 |
|---|---|---|---|
| boolean | 1 个字节 | true/false | 条件判断,逻辑控制 |
- 字符类型:存储单个字符
| 类型 | 存储大小 | 取值范围 | 适用场景 |
|---|---|---|---|
| char | 2 个字节 | 0 到 65535(所有 Unicode 字符) | 存储单个字符 |
字符值需要用单引号括起来
如:
Java
char grade = 'A';引用数据类型
除了基本类型,Java 里其他都是引用类型。最常见的就是 String。
- String 类:用来存储文本
java
String name = "猎风"; // 字符串用双引号这时候,name 变量本身并不是直接存 "猎风" 这几个字符,而是保存了一个“引用”(可以理解为地址/编号)。
真正的字符串数据存在另一块区域里。
虽然 String 看起来也跟基本类型用法一样,但它其实是一个类。不过 Java 对它有特殊处理,让它用起来像基本类型一样简单。
从键盘录入
有时候我们希望程序能接收用户的输入,而不是把数据都写死在代码里。Java 提供了一个很方便的工具——Scanner 类。使用它只需要三步走:
第一步:告诉 Java 你要用 Scanner
就像借用工具前要先说一声一样,使用 Scanner 前需要先"导入"它:
Java
import java.util.Scanner; // 在程序开头加上这一行不过现代 IDE 会在你需要使用 Scanner 时自动添加这行导入语句,可以跳过这一步。
第二步:创建一个 Scanner"工具"
这里就是关键的一步:先 new 一个对象,再用它的方法做事。
Java
Scanner scan = new Scanner(System.in);这段代码做了两件事:
- 叫来一位翻译官,并给他起名叫
scan; - 告诉它去监听
System.in(标准输入,也就是键盘)。
用户敲下的键盘输入,它能即时翻译成 Java 程序能理解的整数、小数或字符串。
第三步:使用方法读取数据
有了他,我们就不用自己解析输入,只要调用他的方法,就能得到想要的数据。
java
// 读取整数
int age = scanner.nextInt();
System.out.println("你的年龄是:" + age);
// 读取小数
double height = scanner.nextDouble();
System.out.println("你的身高是:" + height + "米");
// 读取字符串(整行)
scanner.nextLine(); // 注意:用来清除之前输入的回车符
String name = scanner.nextLine();
System.out.println("你的名字是:" + name);
// 读取单个词
String word = scanner.next();
System.out.println("你输入的单词是:" + word);如果你想读取字符(char 类型),Scanner 没有直接提供 nextChar()方法,但你可以这样做:
java
char gender = scanner.next().charAt(0); // 读取输入的第一个字符不过在实际开发中,我们通常直接用 String 类型代替单个 char,使用起来更方便。
这体现了 Java 的一贯风格:用对象来操作数据,调用方法使用对象的能力。
注意:
当你混用 nextInt()/nextDouble()和 nextLine()时,可能会出现 nextLine()直接被跳过的情况。
这是因为前面的方法只读取值而不读取回车符,而 nextLine()会读取这个遗留的回车符。解决方法是在它们之间加一个额外的 scanner.nextLine() 来吃掉残留的换行。
运算符
算术运算符
这些是我们最常用的运算符,就像计算器上的按钮:
| 类型 | 运算符 | 描述 |
|---|---|---|
| 加减乘除 | + - * / | 基本的四则运算符 |
| 取模 | % | 两数相除的的余数, 舍去整数部分. |
整数相除的结果还是整数,注意小数部分会被直接舍去!
java
int a = 5;
int b = 2;
System.out.println(a / b); // 2因为 a 和 b 都是整数,结果会直接截断小数部分,所以输出的是 2 而不是 2.5。
如果你想要 2.5,就得让其中一个是浮点数:
java
System.out.println(5.0 / 2); // 2.5数字拆分:
在实际编程中,我们经常需要将一个多位数拆分成单个数位进行处理。这个看似简单的操作是运算符(特别是%和/)的绝佳应用场景。
基本原理:
- 使用
%(取模)获取个位数 - 使用
/(整除)去掉已处理的数位
java
// 将一个三位数拆分为个位、十位和百位
int number = 745;
int ones = number % 10; // 获取个位:5
int tens = number / 10 % 10; // 获取十位:4
int hundreds = number / 100; // 获取百位:7
System.out.println("个位是:" + ones);
System.out.println("十位是:" + tens);
System.out.println("百位是:" + hundreds);拆分过程解析:
number % 10→745 % 10 = 5(余数就是个位数)number / 10→745 / 10 = 74(整除 10 后,十位变成了个位)74 % 10 = 4(现在可以用同样的方法获取十位)number / 100→745 / 100 = 7(直接得到百位)
进阶:循环提取所有数位
java
int number = 9527;
System.out.println("从右到左依次是:");
while (number > 0) {
System.out.print(number % 10 + " "); // 输出当前个位
number /= 10; // 去掉已处理的个位
}
// 输出:7 2 5 9这个技巧在很多算法题中都能派上用场,比如判断回文数、计算数字和、翻转整数等,绝对是你的编程武器库中的必备技能!
自增自减
除了基本运算,还有两个特殊的操作符:
| 运算符 | 名称 | 效果 |
|---|---|---|
| ++ | 自增 | 变量值加 1 |
| -- | 自减 | 变量值减 1 |
自增自减有两种用法,放在变量前和放在变量后的效果不同:
java
int a = 5;
int b = ++a; // 先加后赋值,a变成6,b也是6
int c = 5;
int d = c++; // 先赋值后加,d是5,而c变成6这就像两种不同的工作风格:
- 前置++:先做事(加 1),再汇报结果(赋值)
- 后置++:先汇报当前状态(赋值),再做事(加 1)
赋值运算符
赋值运算符就是把右边的值存入左边的变量:
| 类型 | 运算符 | 描述 |
|---|---|---|
| 赋值 | = | 简单的赋值运算符 |
| 四则赋值(包括取余) | *=, /=, %=, +=, -= | 先计算右操作数, 再赋值 |
这些组合赋值运算符既省代码又能提高效率,就像"一键操作"。
关系运算符
关系运算符用来比较两个值之间的关系,结果永远是布尔值(true 或 false):
| 类型 | 运算符 | 描述 |
|---|---|---|
| 等于 | == | 判断两边是否相等 |
| 不等于 | != | 判断两边是否不等 |
| 大于 小于 | > < | 判断左边是否大于右边 判断左边是否小于右边 |
| 大于等于 小于等于 | >= <= | 判断左边是否大于等于右边 判断左边是否小于等于右边 |
关系运算符只能直接比较基本数据类型!不能用==直接比较字符串等引用类型是否相等。
比如:"hello" == "hello" 这样比较字符串是错误的做法!
正确的字符串比较方式是使用.equals()方法:"hello".equals("hello")
逻辑运算符
当你需要组合多个条件时,逻辑运算符就派上用场了:
| 类型 | 运算符 | 描述 |
|---|---|---|
| 逻辑与 | & | 两边都为 true, 结果为 true |
| 逻辑或 | | | 两边有一个为 true, 结果为 true |
| 逻辑非 | ! | 取反, 若操作数为 true, 结果为 false, 若操作数为 false, 结果为 true |
| 逻辑异或 | ^ | 两边结果相同 (都为 true 或都为 false), 结果为 true |
普通逻辑运算符有个问题:即使已经能确定最终结果,也会继续计算右边的表达式。这有时会造成不必要的计算甚至错误。
看这样一个例子:
java
// 判断一个人是否成年且有驾照
int age = 16;
boolean hasLicense = checkLicense(); // 假设这是个检查驾照的方法
// 使用普通逻辑与
boolean canDrive = age >= 18 & hasLicense; // 即使年龄不够,也会检查驾照| 类型 | 运算符 | 描述 |
|---|---|---|
| 短路与 | && | 如果左边为 false,直接返回 false,不再计算右边 |
| 短路或 | || | 如果左边为 true,直接返回 true,不再计算右边 |
在实际开发中,我们几乎总是使用短路运算符(&&和||),而不是&和|,因为它们既提高效率又能避免一些潜在错误。
番外-位运算符
位运算符直接对整数的二进制位进行操作:
| 运算符 | 名称 | 功能 |
|---|---|---|
| & | 按位与 | 对应位都为 1,结果才为 1 |
| | | 按位或 | 对应位有一个为 1,结果就为 1 |
| ^ | 按位异或 | 对应位不同时为 1 |
| ~ | 按位取反 | 0 变 1,1 变 0 |
| << | 左移 | 各二进制位左移,右边补 0 |
| >> | 右移 | 各二进制位右移,左边补符号位 |
| >>> | 无符号右移 | 各二进制位右移,左边补 0 |
虽然位运算在特定场景下效率更高,但现代编译器通常会自动优化常见的数学运算。日常开发时,建议优先使用更易读的数学运算符,只有在对性能有极致要求时才考虑手动用位运算。
类型转换
在 Java 中,不同类型的数据有时需要相互转换。这有两种方式:自动转换和强制转换。
自动类型转换
当把"小容量"的数据类型赋值给"大容量"的变量时,Java 会自动完成转换:
Java
byte byteValue = 10; // 占1个字节
int intValue = byteValue; // 自动转成int(占4个字节)自动转换遵循以下规则,从小到大依次是:
Java
byte → short → int → long → float → double
↗
char虽然 float(4 字节)占用空间比 long(8 字节)小,但 float 表示的数值范围比 long 大,所以 long 可以自动转换为 float。
强制类型转换
当需要把"大容量"的数据类型转换成"小容量"的数据类型时,需要使用强制类型转换,因为这可能会损失精度:
Java
double doubleValue = 3.14;
int intValue = (int) doubleValue; // 强制转换,结果是3(小数部分被截断)强制类型转换的语法是:在要转换的值前加上目标类型的括号(类型)。
强制类型转换可能导致数据丢失或失真:
- 浮点转整数:小数部分会被截断
- 大整数转小整数:高位部分会被截断
例如:byte b = (byte)130; 结果不是 130,而是-126(因为 byte 的范围是-128 到 127)
赋值运算中的隐式转换
在某些复合赋值运算符中(如+=、-=等),Java 会自动进行类型转换:
java
byte b = 10;
b += 5; // 等价于 b = (byte)(b + 5)这种情况下,Java 编译器会自动插入强制类型转换,使结果符合左边变量的类型。
控制语句
如果不使用任何控制语句,程序会按照顺序结构执行——也就是说,代码从上往下逐行运行,不进行任何判断或跳转。
条件判断
if-else
条件判断是程序的"大脑",让程序能够根据不同情况做出不同的决策:
Java
if (条件) {
// 如果条件为true,执行这里的代码
} else if (另一个条件) {
// 如果上面的条件为false,而这个条件为true,执行这里
} else {
// 如果以上所有条件都为false,执行这里
}当条件下只有一行代码时,可以省略大括号,但为了代码清晰,建议保留:
java
if (score > 90) System.out.println("优秀"); // 可行,但不推荐三目运算符
当你只需要根据条件选择两个值中的一个时,可以用更简洁的三目运算符:
Java
结果变量 = (条件) ? 值1 : 值2;这相当于一个简化版的 if-else,条件为 true 时取值 1,为 false 时取值 2:
Java
// 使用if-else
String result;
if (score >= 60) {
result = "及格";
} else {
result = "不及格";
}
// 使用三目运算符,一行解决
String result = score >= 60 ? "及格" : "不及格";switch-case
当需要根据一个变量的多个可能值做不同处理时,switch 比一堆 if-else 更清晰高效:
Java
switch (变量或表达式) {
case 值1:
// 当变量等于值1时执行
break; // 别忘了break,否则会继续执行下面的代码
case 值2:
// 当变量等于值2时执行
break;
// ...可以有更多case
default:
// 当变量不匹配任何case时执行
}比如根据星期几决定做什么:
Java
int day = 3;
switch (day) {
case 1:
System.out.println("周一:开始新的一周");
break;
case 2:
case 3:
case 4:
case 5:
System.out.println("工作日:努力工作");
break;
case 6:
case 7:
System.out.println("周末:好好休息");
break;
default:
System.out.println("无效的日期");
}Java 新版本(JDK 12+)提供了更简洁的 switch 语法:
Java
switch (day) {
case 1 -> System.out.println("周一:开始新的一周");
case 2, 3, 4, 5 -> System.out.println("工作日:努力工作");
case 6, 7 -> System.out.println("周末:好好休息");
default -> System.out.println("无效的日期");
}从 JDK 17 开始,switch 甚至可以直接返回值:
Java
String plan = switch (day) {
case 1 -> "周一:开会日";
case 2, 3 -> "学习日";
case 4, 5 -> "编码日";
case 6, 7 -> "休息日";
default -> "错误的日期";
};循环结构
循环让程序能够重复执行某些代码。Java 提供了几种循环结构:
for 循环
Java
for (初始化; 条件; 迭代) {
// 循环体,重复执行的代码
}- 初始化:循环开始前执行一次
- 条件:每次循环前检查,为 true 才继续
- 迭代:每次循环后执行
java
// 打印1到5的数字
for (int i = 1; i <= 5; i++) {
System.out.println(i);
}while 循环
当不知道需要重复多少次,只知道满足什么条件才停止时,用 while 循环:
java
while (条件) {
// 循环体
}只要条件为 true,就会一直执行循环体:
java
// 从10倒数到1
int count = 10;
while (count > 0) {
System.out.println(count);
count--;
}do-while 循环
这种循环保证至少执行一次循环体,然后再判断条件:
java
do {
// 循环体
} while (条件);例如,实现一个简单的菜单:
java
int choice;
do {
System.out.println("1. 开始游戏");
System.out.println("2. 设置");
System.out.println("0. 退出");
System.out.print("请选择:");
choice = scanner.nextInt();
// 处理用户选择...
} while (choice != 0); // 当用户选择0时退出循环打破循环常规
有时我们需要改变循环的正常流程:
break- 直接"跳出"整个循环javafor (int i = 1; i <= 10; i++) { if (i == 5) { break; // 当i等于5时,跳出循环 } System.out.println(i); // 只会打印1、2、3、4 }continue- "跳过"本次循环剩余部分
java
for (int i = 1; i <= 5; i++) {
if (i == 3) {
continue; // 当i等于3时,跳过本次循环剩余部分
}
System.out.println(i); // 会打印1、2、4、5(没有3)
}可以给循环加上标签,然后通过break 标签名或continue 标签名控制外层循环:
java
outerLoop: for (int i = 1; i <= 3; i++) {
for (int j = 1; j <= 3; j++) {
if (i * j > 4) {
break outerLoop; // 跳出最外层循环
}
System.out.println(i + " * " + j + " = " + (i * j));
}
}ctrl + Alt + T : 快速放入循环
生成随机数
在 Java 中,生成随机数常用 Random 类,和前面用来接收键盘输入的 Scanner 类用法很像。现在大多数 IDE(如 IDEA)都支持自动导包,直接用就行,不用手动写 import 语句。
基本用法:
java
Random r = new Random();
int number = r.nextInt(10); // 生成 0~9 的随机整数
System.out.println("随机生成了:" + number);nextInt(n)方法可以生成[0, n)区间的整数(包含 0,不包含 n)。- 例如
nextInt(10)就是 0~9。
⚠️ 注意:
nextInt(n)生成的随机数永远不会等于 n,只会在 0 到 n-1 之间。
这样写既简洁又和前文风格统一,也符合现代开发习惯。
方法
方法是 Java 程序的基本构建块,就像是一个个可以重复使用的"功能积木"。每个方法负责实现一个特定的功能,我们可以随时调用它们来完成任务。
如果没有方法:你每次想计算两个数的和,都要重复写同样的代码。
有了方法,你只需写一次,然后在需要的地方"呼叫"它就行了。方法让你的代码:
- 更有条理(代码分门别类)
- 可重复使用(写一次,用多次)
- 易于维护(修改一处,处处生效)
方法的基本结构
java
返回类型 方法名(参数类型 参数名, ...) {
// 方法体:实际执行的代码
return 返回值; // 如果有返回值的话
}来看两个例子:
java
// 有返回值的方法 - 计算两数之和
int sum(int a, int b) {
return a + b; // 返回计算结果
}
// 无返回值的方法 - 只执行操作,不返回结果
void printGreeting() {
System.out.println("你好,欢迎学习Java!");
// 无需return语句(或者可以写return;)
}调用方法
方法定义好后,需要调用才能发挥作用:
java
public static void main(String[] args) {
// 调用有返回值的方法,可以接收其结果
int result = sum(10, 20);
System.out.println("10 + 20 = " + result);
// 调用无返回值方法
printGreeting();
}方法的参数
方法可以接收不同类型和数量的参数:
java
// 无参方法
void sayHello() {
System.out.println("Hello!");
}
// 单参数方法
void sayHelloTo(String name) {
System.out.println("Hello, " + name + "!");
}
// 多参数方法
double calculateRectangleArea(double length, double width) {
return length * width;
}方法的重载 - 同名不同参
Java 允许多个方法使用相同的名称,只要它们的参数列表不同(参数类型、数量或顺序)。
这叫做"方法重载":
java
// 计算两个整数的和
int add(int a, int b) {
return a + b;
}
// 计算三个整数的和(参数数量不同)
int add(int a, int b, int c) {
return a + b + c;
}
// 计算两个浮点数的和(参数类型不同)
double add(double a, double b) {
return a + b;
}重载的核心好处是用一个名字表达一种操作,符合直觉。
像 System.out.println() 和 String.valueOf() 这些常用方法,都能处理各种类型,就是通过重载实现的。
在 IDEA 中,选中一段代码后按
Ctrl+Alt+M可以快速将这段代码提取为一个方法。这对重构代码特别有用!
全选代码后,按Ctrl+Shift+U可以快速将选中内容转换为常量所需的全大写。
方法的递归调用
递归就是方法自己调用自己。就像俄罗斯套娃,一层套一层,只不过每次"套"的时候参数会不一样。
java
public static int factorial(int n) {
// 基本情况(终止条件)
if (n == 1) {
return 1;
}
// 递归调用
return n * factorial(n - 1);
}调用过程可以看成这样:
java
factorial(5)
→ 5 * factorial(4)
→ 4 * factorial(3)
→ 3 * factorial(2)
→ 2 * factorial(1)
→ 1
← 返回 2*1=2
← 返回 3*2=6
← 返回 4*6=24
← 返回 5*24=120递归一定要有终止条件,不然会一直调用下去。
每次递归调用都会在栈内存中创建新的方法帧,占用内存空间,还会有方法调用的开销。直到最内层的方法执行完,外层方法才能依次返回结果,整个过程耗费资源较多。
使用递归时要小心这两个错误:
StackOverflowError:递归太深,方法调用栈溢出了。比如:
java// 没有正确的终止条件 public static void broken() { broken(); // 无限递归,最终栈溢出 }OutOfMemoryError(OOM):内存不够用了。如果递归创建了大量对象,可能会耗尽内存。
递归特别适合这些问题:
- 树形结构遍历(文件夹遍历)
- 分治算法(归并排序)
- 动态规划(斐波那契数列)
- 数学计算(阶乘、幂运算)
java
// 计算斐波那契数列的递归实现
public static int fibonacci(int n) {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}递归虽然代码简洁,但不一定是最高效的解法。有些问题用循环会更好,尤其是在处理大量数据时。
数组
在需要批量存储和处理同类型数据时,就可以用数组。数组就像一排连续的“格子”,每个格子都能存放一个数据。
创建数组
在 Java 中,数组的创建主要有两种方式:静态初始化 和 动态初始化。
静态初始化
创建数组的同时直接赋值,适合已知具体内容的场景。
写法简洁,数据一目了然。
java
int[] ages = {25, 30, 18, 42}; // 声明并直接赋值(静态初始化)动态初始化
只指定数组长度,暂时不赋具体值,适合先确定容量、后续再赋值的情况。
每个元素会有默认值(如 int 类型默认是 0)。
java
int[] numbers = new int[5]; // 创建一个长度为5的整数数组(动态初始化)
numbers[0] = 10; // 后续可以逐个赋值你也可以先声明,后初始化:
java
int[] scores; // 声明
scores = new int[3]; // 动态初始化选择哪种方式,取决于实际需求。这样写,既清晰又方便后续维护。
访问数组元素
数组中的每个元素都有一个索引(从 0 开始计数),通过索引可以访问或修改元素:
java
int[] marks = {85, 90, 75, 95, 60};
// 读取元素
System.out.println("第一个成绩是:" + marks[0]); // 输出85
System.out.println("最后一个成绩是:" + marks[4]); // 输出60
// 修改元素
marks[2] = 80; // 将第三个成绩从75改为80访问超出范围的索引会导致
ArrayIndexOutOfBoundsException异常!
比如,对于长度为 5 的数组,尝试访问marks[5]或marks[-1]都会报错。
数组遍历
遍历数组就是依次访问数组中的每个元素,常用于查找、统计、打印等场景。有几种常见方式:
传统 for 循环
适用于需要知道元素位置(索引)的场景:
java
int[] numbers = {10, 20, 30, 40, 50};
// 使用索引遍历
for (int i = 0; i < numbers.length; i++) {
System.out.println("第" + (i+1) + "个数是:" + numbers[i]);
}增强 for 循环(for-each)
适合只关心元素值、不关心位置的遍历场景:
java
int[] numbers = {10, 20, 30, 40, 50};
// 直接获取每个元素
for (int num : numbers) {
System.out.println("数值:" + num);
}增强 for 循环底层其实就是数组遍历的简化写法,语法更简洁,但无法访问索引。
可变参数
如果希望方法能接收不确定数量的参数,Java 提供了 可变参数语法:
java
// 传入任意数量的整数,求和
int sum(int... numbers) {
int total = 0;
for (int num : numbers) {
total += num;
}
return total;
}调用时可以传入任意多个参数,甚至不传:
java
int result1 = sum(10, 20); // 传入2个参数
int result2 = sum(5, 10, 15, 20, 25); // 传入5个参数
int result3 = sum(); // 不传参数也行可变参数实际上是作为数组处理的。有两点需要注意:
- 一个方法只能有一个可变参数
- 可变参数必须是方法的最后一个参数
java
// 正确:可变参数在最后
void printInfo(String name, int... scores) { }
// 错误:可变参数不在最后
void wrongMethod(int... numbers, String text) { } // 编译错误数组一旦创建,长度不可改变。
如果需要支持动态增删元素,应考虑使用 ArrayList、LinkedList 等集合类,后面的部分会提到。
二维数组
在需要处理“表格结构”或“坐标矩阵”这类数据时,一维数组就不够用了,这时就该出场的是 —— 二维数组。
可以把二维数组理解成“数组中的数组”,也可以想象为一个有“行”和“列”的表格结构。
创建二维数组
最常见的创建方式,是直接赋值初始化:
java
int[][] matrix = {
{1, 2},
{3, 4},
{5, 6}
};这段代码创建了一个 3行2列 的矩阵:
[
[1, 2],
[3, 4],
[5, 6]
]当然,也可以先指定结构,再逐项赋值:
java
int[][] table = new int[2][3]; // 2行3列,每个元素默认是0
table[0][1] = 42; // 0行1列,赋值42访问二维数组
需要两个索引:一个表示“第几行”,一个表示“第几列”。
java
System.out.println(matrix[0][0]); // 输出 1(第1行第1列)
System.out.println(matrix[2][1]); // 输出 6(第3行第2列)遍历时需要两层循环:
java
for (int i = 0; i < matrix.length; i++) { // 遍历每一行
for (int j = 0; j < matrix[i].length; j++) { // 遍历当前行的每一列
System.out.print(matrix[i][j] + " ");
}
System.out.println(); // 每行结束后换行
}输出结果是一个整齐的二维数据表:
1 2
3 4
5 6matrix.length表示“总共有几行”matrix[i].length表示“第 i 行有多少列”(支持“非规则二维数组”)- 二维数组不是必须要等长每行,比如:
java
int[][] irregular = {
{1, 2},
{3, 4, 5}, // 第2行比第1行多一列
{6}
};API 入门
API(Application Programming Interface,应用程序编程接口)可以理解为别人已经写好的“工具箱”,里面封装了很多现成的功能,我们只需要按照说明书去用,无需关心内部实现。
比如前面用到的 Scanner 和 Random,它们就是 Java 提供的 API。只要查文档、会用方法,就能轻松实现输入、生成随机数等功能。
Package 包
包就像是文件夹,用来分门别类地管理各种程序代码。合理建包有利于项目的管理和维护。
声明包:在 Java 文件开头用
package 包名;,比如javapackage com.wreckloud.javabean;导包:如果要在当前程序中使用其他包下的类,需要用
import 包名.类名;导入。比如javaimport java.util.Scanner;同包访问:同一个包下的类可以直接互相访问,不需要导包。
简单来说,包让代码更有条理,API 让开发更高效。掌握这两个概念,后续学习会轻松很多。
jdk 中常用的包, 除了刚刚的 utill
java.lang:java 语言包,java 保存了最基础的一些常用类,所以,java 程序默认会导入当然,下面我结合你的原文风格,把“jdk 中常用的包”这部分补全,并让它和前面的包知识自然衔接:
JDK 常用包简介
除了刚刚用到的 java.util,JDK 里还有很多常用的“官方包”,这些包里封装了各种常用功能,开发时经常会用到:
java.lang
Java 语言的基础包,包含了最常用的基础类,比如String、System、Math、Object等。
这个包最特殊——所有 Java 程序都会自动导入,无需手动 import。java.util
工具包,提供了集合框架(如ArrayList、HashMap)、日期时间、随机数等各种实用工具类。
用得最多的就是集合类和工具类。java.io
输入输出包,主要用于文件读写、数据流操作,比如File、InputStream、OutputStream等。java.net
网络编程相关,提供了网络通信、Socket、URL 等功能。java.math
数学相关的高级类,比如高精度运算的BigInteger、BigDecimal。java.sql
数据库相关,提供了 JDBC 操作数据库的接口和类。
这些包都是 Java 官方提供的“工具箱”,用的时候只要 import 一下就能直接用,非常方便。
实际开发中,遇到新需求时,优先考虑 JDK 自带的包,很多功能其实都已经有现成的轮子了。

评论