深浅模式
Java 简介
Java 是什么? 简单来说,它是一门面向对象的编程语言,就像我们日常使用的语言一样,有自己的语法规则和表达方式。
作为面向对象语言,Java 有三大核心特性:
- 封装 - 把数据和操作数据的方法捆绑起来
- 继承 - 允许基于已有的类创建新类
- 多态 - 同一个操作作用于不同对象,产生不同结果
常说的 Java 三大版本是:
版本 | 描述 |
---|---|
Java SE (Standard Edition) 标准版 | 是基础款,用于开发普通桌面程序。日常学习 Java 主要接触这个版本。 |
Java EE (Enterprise Edition) 企业版 | 是升级版,增加了很多企业级功能,适合开发复杂的商业应用。 |
Java ME(Micro Edition) 微型版 | 是精简版,为资源有限的设备(如早期的手机)设计的。 |
开发工具
想写 Java 程序,你首先需要安装 JDK(Java Development Kit)。这就像是一个工具箱,里面装着开发 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),在命令行工具中:
- 先把.java 文件编译成 .class 文件
bash
javac 你的程序.java
- 然后运行编译好的程序
bash
java 你的程序
这就是最基础的 Java 程序编译和运行过程。当然,在实际开发中,我们通常会使用 IDE 来自动完成这些步骤。
注释
注释是程序员的"备忘录",它们不会被执行,只是帮助人类理解代码的说明文字。
写注释绝对是个好习惯!因为刚写完的代码只有你和上帝看得懂,过几天可能就只剩上帝能看懂了...
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 已经"预定"的词(关键字或保留字)作标识符。
- 关键字: Java 已经赋予特殊含义的词,比如 if、else、class 等,这些词是 Java 的"保留地",不能用来命名你自己的东西。
- 保留字: 现在 Java 还没用上,但未来可能会用的词。也不建议你拿来用。
标识符命名规范
编程世界有一套大家都认可的命名习惯,遵循这些习惯会让你的代码更加专业:
- 包名:全小写,用点隔开,比如
java.util
- 类名/接口名:每个单词首字母大写(大驼峰),如
StudentInfo
- 方法名/变量名:首单词小写,后面单词首字母大写(小驼峰),如
getUserName
- 常量名:全大写,单词间用下划线,如
MAX_VALUE
数据类型
基本数据类型
Java 的基本数据类型各有各的用途:
- 整数类型:用来存储整数值
类型 | 存储大小 | 能装多少数字 | 适用场景 |
---|---|---|---|
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 = "张三"; // 字符串用双引号
虽然 String 看起来很基本,但它其实不是基本类型,而是一个类。不过 Java 对它有特殊处理,让它用起来像基本类型一样简单。
从键盘录入
想让你的程序接收用户输入的数据?Java 提供了一个很方便的工具——Scanner 类。使用它只需要三步走:
第一步:告诉 Java 你要用 Scanner
就像借用工具前要先说一声一样,使用 Scanner 前需要先"导入"它:
Java
import java.util.Scanner; // 在程序开头加上这一行
不用担心记不住这行代码,现代 IDE(如 IDEA)会在你需要使用 Scanner 时自动添加这行导入语句。
第二步:创建一个 Scanner"工具"
导入后,还需要创建一个 Scanner 对象才能使用它:
Java
Scanner scanner = new Scanner(System.in);
这行代码的意思是:创建一个名为 scanner 的扫描器,它会从 System.in(也就是键盘)获取输入。
这个 scanner 就像是你和键盘之间的"翻译官",负责把用户敲击键盘的内容转换成 Java 程序能理解的数据。
第三步:使用 Scanner 获取不同类型的输入
Scanner 提供了多种方法来读取不同类型的数据:
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,使用起来更方便。
注意:
当你混用nextInt()
/nextDouble()
和nextLine()
时,可能会出现nextLine()
直接被跳过的情况。这是因为前面的方法只读取值而不读取回车符,而 nextLine()会读取这个遗留的回车符。解决方法是在它们之间加一个额外的 scanner.nextLine()来"吃掉"这个回车符。
运算符
算术运算符
这些是我们最常用的运算符,就像计算器上的按钮:
类型 | 运算符 | 描述 |
---|---|---|
加减乘除 | + - * / | 基本的四则运算符 |
取模 | % | 两数相除的的余数, 舍去整数部分. |
注意: 整数相除的结果还是整数,小数部分会被直接舍去!
例如 5/2 得到的是 2 而不是 2.5。
如果你需要得到小数结果,至少有一个操作数必须是小数,比如 5.0/2 或 5/2.0。
数字拆分
在实际编程中,我们经常需要将一个多位数拆分成单个数位进行处理。这个看似简单的操作是运算符(特别是%
和/
)的绝佳应用场景。
基本原理:
- 使用
%
(取模)获取个位数 - 使用
/
(整除)去掉已处理的数位
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 6
matrix.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 自带的包,很多功能其实都已经有现成的轮子了。
评论