管理主页分类

拖动分类调整顺序,并选择是否在主页显示。设置仅保存在当前浏览器。

  • 微服务课件 31
  • Java 基础 28
  • Java Web 开发 25
  • 多来买 18
  • LeetCode 题解 8
  • 开发工具 8
  • 网络工具 2
  • 黑马头条 2
  • Java 记忆恢复 1

Day 01:Java 程序结构、方法与基础控制流程

8386 字
42 分钟
Day 01:Java 程序结构、方法与基础控制流程

Day 01:Java 程序结构、方法与基础控制流程#

项目内容
分类Java 基础
系列Java 记忆恢复
整理日期2026-08-06
适用环境JDK 17
文档用途知识复习、发布与打印

状态说明:阅读和理解本文不等于已经独立掌握,掌握程度仍需以后通过复述、代码阅读和间隔练习验证。

学习目标#

完成本篇复习后,应当能够逐步理解:

  • 普通 Java 应用为什么使用 main 作为入口。
  • publicprivatestatic 分别解决什么问题。
  • 方法如何接收参数、返回结果,以及调用时如何执行。
  • intlong 的区别,以及整数溢出为什么会产生错误结果。
  • forwhiledo...while 的基本流程。
  • breakcontinuereturn 分别会结束什么。
  • 如何使用 Scanner 读取控制台输入。
  • nextLine()trim()Integer.parseInt() 如何配合。
  • try/catch 如何处理 NumberFormatException
  • 局部变量、成员变量和作用域的基本区别。
  • 自动类型转换、强制类型转换和表达式类型提升的基本规则。

第一部分:已讲知识整理#

1. Java 程序为什么从 main 方法开始#

1.1 main 是普通 Java 应用的标准入口#

在 JDK 17 中,运行一个普通 Java 应用时,通常会执行:

Terminal window
java HelloJava

Java 启动器需要知道从哪个位置把程序控制权交给我们编写的代码。为此,Java 约定使用下面的方法作为标准入口:

public static void main(String[] args) {
System.out.println("程序开始");
}

大致执行过程如下:

  1. Java 启动器创建 JVM。
  2. JVM 加载命令中指定的类。
  3. JVM完成必要的类初始化。
  4. Java 启动器找到符合规定签名的 main 方法。
  5. main 被调用,程序开始执行我们编写的流程。

Java 不会在所有类中随意寻找一个看起来能运行的方法。统一的入口约定可以让启动过程明确、稳定。

1.2 main 方法各部分的含义#

public static void main(String[] args)
部分含义
publicJava 启动器可以从类的外部访问这个方法
static不需要先创建当前类的对象就能调用
void方法不向调用者返回一个普通 Java 值
mainJava 约定的入口方法名
String[] args用来接收命令行参数的字符串数组

参数名不一定必须叫 args,下面的写法同样有效:

public static void main(String[] values) {
}

也可以写成可变参数形式:

public static void main(String... args) {
}

1.3 main 不一定是 JVM 执行的第一段代码#

“程序从 main 开始”是一种便于理解的说法。更准确地说,main 是普通 Java 应用把控制权交给业务代码时使用的标准入口。

类的静态初始化可能先于 main 执行:

public class Demo {
static {
System.out.println("静态初始化");
}
public static void main(String[] args) {
System.out.println("main 方法");
}
}

输出顺序是:

静态初始化
main 方法

1.4 常见错误#

缺少 static

public void main(String[] args) {
}

返回类型错误:

public static int main(String[] args) {
return 0;
}

方法名大小写错误:

public static void Main(String[] args) {
}

Java 区分大小写,Mainmain 是两个不同的方法名。

一句话记忆#

main 不是 JVM 启动时执行的全部代码中的第一行,而是普通 Java 应用交给我们控制时使用的标准入口。


2. static:属于类,而不是属于某个对象#

2.1 类成员与对象成员#

static 表示成员属于类本身。没有 static 的实例成员通常属于具体对象。

public class Student {
static String school = "第一中学";
String name;
}

创建两个对象:

Student student1 = new Student();
student1.name = "小明";
Student student2 = new Student();
student2.name = "小红";

这里:

  • name 属于具体对象,两个学生可以保存不同姓名。
  • school 属于 Student 类,所有 Student 对象共享同一份数据。

访问静态成员时,推荐通过类名访问:

System.out.println(Student.school);

2.2 静态方法#

静态方法不依赖某个具体对象,可以直接通过类调用:

public class Calculator {
public static int add(int a, int b) {
return a + b;
}
}

调用:

int result = Calculator.add(10, 20);

Java 标准库中的常见静态方法包括:

Math.max(10, 20);
Integer.parseInt("123");

这些方法只依赖传入的数据,不需要保存某个对象的个人状态。

2.3 为什么 main 使用 static#

程序刚启动时,Java 启动器还没有创建当前类的对象。如果入口是实例方法,启动器就需要额外决定:

  • 应该调用哪个构造方法。
  • 构造方法需要传入什么参数。
  • 应该创建几个对象。

main 声明为 static 后,可以直接通过类调用,不需要先创建对象。

2.4 静态方法的限制#

静态方法不能直接访问实例成员:

public class Student {
String name;
public static void showName() {
System.out.println(name); // 编译错误
}
}

原因是调用 showName() 时,不一定存在某个具体 Student 对象。即使存在多个对象,Java 也不知道这里需要读取哪一个对象的 name

可以显式传入对象:

public static void showName(Student student) {
System.out.println(student.name);
}

静态方法中也不能直接使用 this,因为 this 表示“当前对象”,而静态方法没有固定的当前对象。

2.5 适用场景#

适合使用 static

  • 程序入口,例如 main
  • 不依赖对象状态的工具方法。
  • 所有对象需要共享的数据。
  • 配合 final 定义类级别常量。
public static final int MAX_SCORE = 100;

不适合仅仅为了省略创建对象,就把所有字段和方法都改成 static。姓名、年龄、余额等属于具体对象的数据通常不应共享。

2.6 常见误区#

  • static 不表示数据不能修改;防止重新赋值通常还需要 final
  • static 不等于“永远不会释放”或“永远存在”,它仍由 JVM 的类加载和生命周期管理。
  • 虽然可以通过对象访问静态成员,但这样容易让人误以为它属于对象,推荐使用类名访问。
  • 可修改的静态字段会被所有对象共享,使用不当可能造成相互影响。

一句话记忆#

普通成员属于具体对象,static 成员属于类本身。


3. private:限制访问范围#

3.1 private 解决什么问题#

private 表示成员只能在声明它的类内部直接访问。

public class Student {
private int score;
}

其他类不能直接修改:

Student student = new Student();
student.score = -100; // 编译错误

这样可以避免外部代码绕过类自己的规则,随意写入不合理数据。

3.2 private 与封装#

更合适的做法是通过方法控制数据如何修改:

public class Student {
private int score;
public void setScore(int score) {
if (score >= 0 && score <= 100) {
this.score = score;
}
}
public int getScore() {
return score;
}
}

外部代码只能通过类允许的方式操作数据:

Student student = new Student();
student.setScore(80);
System.out.println(student.getScore());

这种做法体现了封装:

  • 数据和操作数据的方法放在同一个类中。
  • 不应由外部直接操作的细节隐藏在类内部。
  • 对外只提供经过控制的使用方式。

3.3 私有方法#

私有方法通常用于隐藏类内部的辅助逻辑:

private boolean isValidScore(int score) {
return score >= 0 && score <= 100;
}
public void setScore(int score) {
if (isValidScore(score)) {
this.score = score;
}
}

外部使用者只需要知道 setScore(),不需要依赖内部如何判断分数是否合法。

3.4 privatestatic 的区别#

修饰符回答的问题
static这个成员属于类,还是属于具体对象?
private哪些代码可以直接访问这个成员?

它们可以组合使用:

private static int count;

含义是:

  • staticcount 属于类,所有对象共享。
  • private:只有当前类内部可以直接访问。

3.5 常见访问范围#

写法基本访问范围
private当前类内部
不写访问修饰符同一个包内
protected同一个包内,以及符合条件的子类
public所有能够引用这个类的位置

初学阶段可以先采用一个实用原则:字段通常优先使用 private,需要对外提供的能力再通过方法开放。

但不要机械地为所有字段生成 getter 和 setter。如果某个数据不应由外部修改,就不应该随意提供 setter。

3.6 常见误区#

  • private 不表示变量不能修改,只表示类外不能直接访问。
  • private 的范围是整个类,不是当前方法。
  • private 不是数据加密或安全存储机制,它主要是代码层面的访问控制。
  • 顶层类不能声明为 private;顶层类通常是 public 或不写访问修饰符。

一句话记忆#

private 把实现细节留在类内部,外部只能通过类允许的方式使用它。


4. 方法、参数与返回值#

4.1 方法是什么#

方法是一段具有名称、可以调用和复用的代码。一个方法通常负责完成一个明确的小任务。

基本结构:

访问修饰符 static 返回类型 方法名(参数列表) {
方法体
return 返回值;
}

例如:

private static int add(int a, int b) {
return a + b;
}
部分含义
private只能在当前类内部直接调用
static不需要创建对象即可调用
int方法最终返回一个整数
add方法名
int a, int b方法需要接收的参数
return a + b把计算结果交还给调用者

4.2 形参与实参#

方法声明中的参数叫形参:

private static int add(int a, int b)

这里的 ab 是形参。

调用时传入的数据叫实参:

int result = add(10, 20);

这里的 1020 是实参。

可以先把调用过程理解为:

a 接收到 10
b 接收到 20
计算 a + b
把 30 返回给调用位置

参数让同一个方法可以处理不同数据:

add(10, 20);
add(5, 8);
add(100, 200);

4.3 返回值#

返回值是方法执行完毕后交给调用者的结果:

private static boolean isPassed(int score) {
return score >= 60;
}

调用:

boolean passed = isPassed(80);

执行过程:

  1. 把实参 80 交给形参 score
  2. 计算 score >= 60
  3. 得到 true
  4. true 返回给调用位置。
  5. 将结果保存到变量 passed

返回值类型必须与声明的返回类型兼容:

private static int getNumber() {
return 10;
}

下面的写法类型不匹配:

private static int getNumber() {
return "10"; // 编译错误
}

4.4 void#

void 表示方法执行后不返回一个结果值:

private static void printMessage() {
System.out.println("你好");
}

调用时直接执行:

printMessage();

不能把 void 方法赋值给变量:

String message = printMessage(); // 编译错误

void 方法可以使用单独的 return 提前结束:

private static void printScore(int score) {
if (score < 0) {
return;
}
System.out.println(score);
}

这里的 return 只结束方法,不返回数据。

4.5 方法调用过程#

public class ScoreService {
public static void main(String[] args) {
int score = 80;
boolean passed = isPassed(score);
printResult(passed);
}
private static boolean isPassed(int score) {
return score >= 60;
}
private static void printResult(boolean passed) {
System.out.println(passed);
}
}

执行顺序:

  1. Java 启动器调用 main
  2. 创建局部变量 score,值为 80
  3. main 暂停在调用位置,进入 isPassed(80)
  4. isPassed 返回 true
  5. 回到 main,把结果保存到 passed
  6. 调用 printResult(true)
  7. 输出 true
  8. main 执行结束。

4.6 打印和返回不是一回事#

只打印结果:

private static void add(int a, int b) {
System.out.println(a + b);
}

返回结果:

private static int add(int a, int b) {
return a + b;
}

返回结果通常更灵活:

int result = add(10, 20);
System.out.println(result);
int doubled = result * 2;

一般可以让计算方法负责返回结果,让显示代码决定如何输出。这样更方便复用和测试。

4.7 常见错误#

  • 调用时参数数量不正确。
  • 实参类型与形参类型不兼容。
  • 声明了非 void 返回类型,却没有覆盖所有必要的返回路径。
  • 方法已经返回结果,但调用者没有保存或使用它。
  • 把局部变量误认为方法结束后仍然可以访问。

一句话记忆#

参数负责把数据传进方法,return 负责把结果带出方法,返回类型规定带出的数据类型。


5. intlong 与整数溢出#

5.1 基本范围#

intlong 都用于保存整数,主要区别是可表示范围不同。

类型位数取值范围
int32 位-2,147,483,6482,147,483,647
long64 位-9,223,372,036,854,775,8089,223,372,036,854,775,807

普通整数通常使用 int

int age = 18;
int score = 100;

可能超过 int 范围时需要考虑 long

long population = 8_000_000_000L;

数字中的下划线只用于提高可读性,不影响数值。

5.2 L 后缀#

整数常量通常先按照 int 处理。超出 int 范围的整数常量需要添加 L

long number = 3_000_000_000L;

推荐使用大写 L,因为小写 l 容易看成数字 1

long sum = 0L;

0L 表示这是一个 long 类型的零。

5.3 整数溢出#

当计算结果超出类型能够表示的范围时,会发生溢出:

int number = 2_147_483_647;
number = number + 1;
System.out.println(number);

输出:

-2147483648

Java 的普通整数运算默认不会因为溢出而自动抛出异常。程序可能继续运行,但结果已经错误。

5.4 为什么返回 long 仍然可能出错#

private static long calculateSum(int number) {
int sum = 0;
for (int i = 1; i <= number; i++) {
sum += i;
}
return sum;
}

虽然方法返回类型是 long,但累加过程使用的是 int。如果 sum 已经溢出,最后只是把错误的 int 结果转换成 long

正确方向是让中间结果也使用 long

private static long calculateSum(int number) {
long sum = 0L;
for (int i = 1; i <= number; i++) {
sum += i;
}
return sum;
}

5.5 表达式可能在赋值前溢出#

long result = 100_000 * 100_000;

右侧两个操作数都是 int,所以会先执行 int 乘法。乘法可能已经溢出,然后才把错误结果赋给 long

正确写法:

long result = 100_000L * 100_000;

其中一个操作数是 long 后,整个表达式会使用 long 运算。

int + int → int
long + int → long
long + long → long

5.6 主动检测溢出#

希望溢出时直接抛出异常,可以使用:

int sum = Math.addExact(a, b);
long product = Math.multiplyExact(x, y);

如果整数可能超过 long,可以进一步了解 BigInteger。基础练习中,先学会根据输入范围判断中间结果和最终结果需要什么类型。

5.7 常见错误#

  • 方法返回 long,但累加变量仍然是 int
  • 最终变量是 long,但右侧表达式先以 int 完成计算。
  • 认为强制类型转换一定安全。
  • 认为使用 long 后永远不会溢出。
  • 只根据测试样例选择类型,没有考虑允许输入的最大范围。

一句话记忆#

最终变量是 long 还不够;参与计算的中间变量和表达式也必须在溢出前变成 long


6. 循环与流程控制#

6.1 for 循环#

for 通常适合循环次数或计数范围较明确的场景:

for (int i = 1; i <= 5; i++) {
System.out.println(i);
}

执行顺序:

  1. 执行 int i = 1,只执行一次。
  2. 判断 i <= 5
  3. 条件成立,执行循环体。
  4. 执行 i++
  5. 回到条件判断。

6.2 while 循环#

while 会先判断条件,再决定是否执行循环体:

int i = 1;
while (i <= 5) {
System.out.println(i);
i++;
}

如果第一次判断时条件就不成立,循环体一次也不会执行。

6.3 do...while 循环#

do...while 先执行循环体,再判断是否继续,因此至少执行一次:

int i = 1;
do {
System.out.println(i);
i++;
} while (i <= 5);

6.4 while (true)#

while (true) {
// 重复执行
}

条件永远是 true,因此循环本身没有结束条件。它适合“不知道需要尝试多少次”的场景,例如不断读取输入,直到获得合法数据。

while (true) 内部应存在清晰、可达到的退出路径,否则程序会一直运行。

6.5 break#

break 立即结束距离它最近的一层循环:

while (true) {
String input = scanner.nextLine();
if (!input.isEmpty()) {
break;
}
}
System.out.println("循环已经结束");

执行 break 后,从循环后面的代码继续执行。

6.6 continue#

continue 跳过当前这一轮剩余代码,直接进入下一轮:

for (int i = 1; i <= 5; i++) {
if (i == 3) {
continue;
}
System.out.println(i);
}

输出:

1
2
4
5

6.7 return#

return 结束的是整个当前方法,而不只是循环:

private static int readPositiveNumber(Scanner scanner) {
while (true) {
int number = Integer.parseInt(scanner.nextLine());
if (number > 0) {
return number;
}
}
}

执行 return number 后:

  1. while 结束。
  2. 当前方法结束。
  3. number 被交给调用者。

6.8 三者对比#

语句作用
continue跳过本轮剩余代码,进入下一轮循环
break结束当前循环,从循环后继续
return结束整个当前方法,回到调用位置

6.9 常见错误#

忘记更新循环条件:

int i = 1;
while (i <= 5) {
System.out.println(i);
// 忘记 i++,循环无法结束
}

更新之前执行 continue

int i = 1;
while (i <= 5) {
if (i == 3) {
continue; // i 会一直等于 3
}
i++;
}

循环条件后误加分号:

while (i <= 5); {
System.out.println(i);
}

这里的分号形成了一个空循环,后面的代码块并不属于该循环。

普通流程中也不要用 System.exit(0) 代替 breakreturnSystem.exit(0) 会终止整个 JVM,而不只是结束循环或方法。

一句话记忆#

continue 跳过本轮,break 跳出循环,return 离开整个方法。


第二部分:Day 01 补全知识#

7. 使用 Scanner 读取控制台输入#

7.1 Scanner 是什么#

Scanner 是 Java 标准库提供的输入读取工具。使用前需要导入:

import java.util.Scanner;

创建读取控制台输入的对象:

Scanner scanner = new Scanner(System.in);

这里:

  • System.in 表示标准输入流,普通控制台程序中通常对应键盘输入。
  • new Scanner(System.in) 创建一个能够解析输入内容的 Scanner 对象。
  • scanner 是保存这个对象引用的变量。

7.2 使用 nextLine() 读取一整行#

System.out.print("请输入年龄:");
String input = scanner.nextLine();

nextLine() 会读取用户输入的一整行文本,并返回 String

即使用户输入:

18

程序最初得到的仍然是字符串 "18",不是整数 18

7.3 nextInt()nextLine() 的选择#

也可以直接读取整数:

int age = scanner.nextInt();

但是把 nextInt()nextLine() 混合使用时,容易遇到换行符尚未被读取的问题:

int age = scanner.nextInt();
String name = scanner.nextLine();

此时 name 可能立即得到一个空字符串,因为 nextLine() 读取了前一次整数输入后留下的换行。

在需要统一校验输入的基础程序中,可以采用一种更容易控制的方式:

  1. 始终使用 nextLine() 读取整行字符串。
  2. 清理字符串。
  3. 使用解析方法转换类型。
  4. 转换失败时统一处理异常。

7.4 关闭 Scanner#

可以使用 try-with-resources:

try (Scanner scanner = new Scanner(System.in)) {
String input = scanner.nextLine();
System.out.println(input);
}

代码块结束后,Scanner 会被自动关闭。

关闭包装 System.inScanner 也会关闭底层标准输入。因此在较大的程序中,不要在多个方法中反复创建并关闭不同的控制台 Scanner。通常在入口创建一次,再把它作为参数传给需要读取输入的方法。

7.5 常见错误#

  • 忘记导入 java.util.Scanner
  • 认为 nextLine() 返回的是整数。
  • 混用 nextInt()nextLine(),却没有理解换行处理。
  • 在多个方法中反复创建和关闭控制台 Scanner
  • 把输入、转换、业务计算和输出全部塞进 main,导致流程难以理解。

一句话记忆#

Scanner 负责读取输入;nextLine() 读取到的是一整行字符串。


8. trim()Integer.parseInt()#

8.1 清理字符串#

用户可能在数字前后输入空格:

[空格][空格][空格]18[空格][空格][空格]

可以使用 trim() 去除字符串首尾的常见空白字符:

String input = scanner.nextLine().trim();

如果需要更完整地处理 Unicode 空白字符,JDK 11 及以上还可以使用:

String input = scanner.nextLine().strip();

基础控制台程序中,trim() 已经足以说明“读取后先清理首尾空白”的过程。

trim() 不会修改原来的字符串,而是返回一个新字符串:

String original = " 18 ";
String cleaned = original.trim();

8.2 把字符串转换为整数#

String input = "18";
int age = Integer.parseInt(input);

Integer.parseInt() 的工作是把符合整数格式的字符串解析为 int

它不会把任意字符串都转换成整数:

Integer.parseInt("abc");
Integer.parseInt("12.5");
Integer.parseInt("");

这些输入都不是合法的 int 文本,会产生 NumberFormatException

8.3 parseInt() 与强制类型转换不是一回事#

解析字符串:

int number = Integer.parseInt("123");

数值类型转换:

double value = 123.9;
int number = (int) value;

前者把文本解释成数字,后者在已有数值类型之间转换。它们解决的是不同问题。

8.4 读取更大的整数#

如果输入本身可能超过 int 范围,可以使用:

long number = Long.parseLong(input);

选择 Integer.parseInt() 还是 Long.parseLong(),应当根据允许的输入范围决定。

8.5 常见错误#

  • 忘记接收 trim() 的返回值,以为原字符串会被原地修改。
  • 输入带有无法识别的内容,却认为 parseInt() 会自动返回默认值。
  • 12.5 当作合法整数文本。
  • 只把最终变量改成 long,输入仍然使用只能解析 int 的方法,却没有明确这是有意限制输入范围。

一句话记忆#

nextLine() 得到文本,trim() 清理首尾空白,parseInt() 再把合法的整数字符串解析为 int


9. try/catchNumberFormatException#

9.1 异常是什么#

异常表示程序执行过程中出现了需要处理的非正常情况。

例如:

int number = Integer.parseInt("abc");

"abc" 无法解析为整数,因此会产生 NumberFormatException

如果没有处理,当前方法的正常流程会被打断,异常继续向调用者传播,普通控制台程序最终可能直接结束并打印错误信息。

9.2 使用 try/catch#

try {
int number = Integer.parseInt(input);
System.out.println(number);
} catch (NumberFormatException e) {
System.out.println("输入不是合法整数");
}

执行逻辑:

  • try 中没有异常:继续执行 try 后面的正常流程,跳过对应的 catch
  • try 中产生匹配的 NumberFormatException:立即停止 try 中剩余代码,进入 catch
  • catch 执行结束后:从整个 try/catch 后面继续。

9.3 与循环组合#

while (true) {
String input = scanner.nextLine().trim();
try {
int number = Integer.parseInt(input);
if (number <= 0) {
System.out.println("请输入正整数");
continue;
}
return number;
} catch (NumberFormatException e) {
System.out.println("输入不是合法整数");
}
}

不同输入对应不同流程:

输入情况程序行为
不能解析为整数进入 catch,提示后开始下一轮
能解析但小于等于 0执行 continue,开始下一轮
合法正整数执行 return,返回数据并结束方法

9.4 为什么不要随意捕获所有异常#

下面的写法范围过大:

try {
int number = Integer.parseInt(input);
} catch (Exception e) {
System.out.println("发生错误");
}

它会把许多不同问题都归为同一种情况,容易隐藏真正的程序错误。

已经明确知道需要处理的是整数格式问题时,更适合捕获具体异常:

catch (NumberFormatException e)

9.5 catch 参数#

catch (NumberFormatException e)

其中:

  • NumberFormatException 是需要捕获的异常类型。
  • e 是保存本次异常对象的局部变量。

如果确实需要诊断,可以读取异常信息。但面向普通用户时,通常应该输出清晰的业务提示,而不是直接展示完整堆栈。

9.6 常见错误#

  • 把正常的条件判断全部写成异常处理。
  • 捕获异常后什么也不做,导致问题被静默忽略。
  • 使用过于宽泛的 catch (Exception e) 隐藏真正错误。
  • 认为进入 catch 后会自动重新执行 try;只有外层循环才会让程序再次尝试。
  • try 范围写得过大,导致难以判断究竟哪一步可能失败。

一句话记忆#

try 放可能失败的操作,catch 处理指定异常;是否重新尝试由循环决定,不是由 catch 自动决定。


10. 局部变量、成员变量与作用域#

10.1 作用域是什么#

作用域表示一个变量名在哪一段代码中可以被访问。

局部变量只在声明它的代码块内部有效:

public static void main(String[] args) {
int score = 80;
System.out.println(score);
}

离开 main 后,其他方法不能直接使用这里的 score

10.2 代码块作用域#

if (true) {
int number = 10;
System.out.println(number);
}
System.out.println(number); // 编译错误

number 只存在于 if 的代码块中。

循环变量也有自己的作用域:

for (int i = 0; i < 5; i++) {
System.out.println(i);
}
System.out.println(i); // 编译错误

10.3 成员变量#

声明在类中、方法外的变量叫字段或成员变量:

public class Student {
private int score;
}

实例字段通常跟随对象保存状态。静态字段属于类:

public class Student {
private static int count;
private int score;
}

10.4 默认值与初始化#

字段会获得类型对应的默认值,例如:

  • 整数类型默认是 0
  • boolean 默认是 false
  • 引用类型默认是 null

局部变量不会自动获得可直接使用的默认值:

int number;
System.out.println(number); // 编译错误:可能尚未初始化

需要先明确赋值:

int number = 0;
System.out.println(number);

10.5 同名变量与 this#

public class Student {
private int score;
public void setScore(int score) {
this.score = score;
}
}

这里:

  • this.score 表示当前对象的字段。
  • 右侧 score 表示方法参数。

参数名遮蔽了同名字段,因此需要用 this 明确指定对象字段。

10.6 参数与字段如何选择#

  • 数据只在一次方法调用中临时使用:优先使用参数或局部变量。
  • 数据需要成为对象持续保存的状态:考虑实例字段。
  • 数据需要由整个类共享:谨慎考虑静态字段。

不要为了让多个方法都能访问某个临时数据,就随意把局部变量改成静态字段。这样会扩大状态范围,增加代码之间的相互影响。

一句话记忆#

变量只能在自己的作用域内使用;临时数据用局部变量,需要保存的对象状态才使用字段。


11. 基本类型转换与表达式类型#

11.1 自动扩大转换#

范围较小的整数类型放入范围较大的类型时,通常可以自动转换:

int small = 100;
long large = small;

这个方向不会因为整数范围缩小而丢失数据。

常见的扩大方向可以先简单理解为:

byte → short → int → long → float → double

这只是帮助记忆的基础顺序。不同运算还会受到具体类型提升规则影响。

11.2 强制缩小转换#

把范围较大的类型放入范围较小的类型,需要显式强制转换:

long large = 3_000_000_000L;
int small = (int) large;

这段代码能够编译,但结果会失真,因为原值超出了 int 范围。

强制转换表达的是“允许执行转换”,不代表转换后的结果一定安全。

11.3 浮点数转整数#

double price = 19.99;
int whole = (int) price;

结果是:

19

强制转换会直接舍弃小数部分,不会自动四舍五入。需要四舍五入时,应根据需求使用 Math.round() 等明确方法。

11.4 表达式类型#

int a = 100_000;
int b = 100_000;
long result = a * b;

a * b 会先按 int 运算。即使接收变量是 long,也不能改变已经完成的表达式类型。

可以先把一个操作数变成 long

long result = (long) a * b;

或者使用 long 字面量:

long result = 100_000L * 100_000;

11.5 整数除法#

两个整数相除,结果仍然是整数:

int result = 5 / 2;

结果是 2,小数部分被舍弃。

希望得到小数,需要让至少一个操作数成为浮点类型:

double result = 5.0 / 2;

结果是 2.5

11.6 常见错误#

  • 认为接收变量的类型会提前改变右侧表达式的计算类型。
  • 认为强制类型转换一定不会丢失数据。
  • 认为整数除法会自动保留小数。
  • 为了消除编译错误随意添加强制转换,却没有检查允许的数值范围。

一句话记忆#

类型转换不仅要看最终变量,还要看参与运算的每个操作数以及表达式在哪个类型中完成。


第三部分:综合代码阅读示例#

12. 正整数输入与累加#

下面的完整示例用于串联本篇知识点。它是阅读材料,不是必须立即完成的作业。

import java.util.Scanner;
public class PositiveSumCalculator {
public static void main(String[] args) {
try (Scanner scanner = new Scanner(System.in)) {
int upperBound = readPositiveInt(scanner);
long sum = calculateSum(upperBound);
System.out.println("1 到 " + upperBound + " 的整数和为:" + sum);
}
}
private static int readPositiveInt(Scanner scanner) {
while (true) {
System.out.print("请输入一个正整数:");
String input = scanner.nextLine().trim();
try {
int number = Integer.parseInt(input);
if (number <= 0) {
System.out.println("输入必须大于 0,请重新输入。");
continue;
}
return number;
} catch (NumberFormatException e) {
System.out.println("输入不是合法整数,请重新输入。");
}
}
}
private static long calculateSum(int upperBound) {
long sum = 0L;
for (long current = 1L; current <= upperBound; current++) {
sum += current;
}
return sum;
}
}

12.1 程序结构#

  • main 是入口。
  • readPositiveInt() 负责读取和校验输入。
  • calculateSum() 负责计算。
  • main 负责组织调用并输出最终结果。

每个方法只承担相对明确的职责,输入、计算和显示没有全部堆在同一个代码块中。

12.2 输入流程#

读取整行字符串
→ 清理首尾空白
→ 尝试解析为 int
→ 检查是否大于 0
→ 合法时返回
→ 不合法时提示并继续循环

12.3 为什么累加器和循环变量使用 long#

upperBound 的输入范围被限制为 int,但从 1 累加到较大的 int 时,结果可能超过 int

因此:

long sum = 0L;

累加过程在 long 中完成。

循环变量也使用 long

for (long current = 1L; current <= upperBound; current++)

这样即使 upperBound 接近 Integer.MAX_VALUE,循环变量递增时也不会先发生 int 回绕。实际执行如此巨大的循环会非常耗时,但类型边界仍然保持正确。

12.4 异常后的流程#

Integer.parseInt(input) 失败时:

  1. try 中剩余代码停止执行。
  2. 进入 catch (NumberFormatException e)
  3. 输出提示。
  4. catch 执行结束。
  5. 到达 while 循环体末尾。
  6. 开始下一轮输入。

不是 catch 自动重试,而是外层 while (true) 让程序重新执行。


第四部分:打印速查#

13. 核心概念速查表#

知识点核心含义
main普通 Java 应用的标准入口
static成员属于类,不依赖某个具体对象
private成员只能在当前类内部直接访问
参数调用者传入方法的数据
返回值方法处理后交还给调用者的数据
void方法不返回一个结果值
int32 位有符号整数
long64 位有符号整数
0Llong 类型的整数零
溢出结果超过类型范围后产生错误回绕
break结束当前循环
continue跳过本轮剩余代码
return结束整个当前方法
nextLine()读取一整行字符串
trim()返回去除首尾常见空白后的新字符串
parseInt()把合法整数字符串解析为 int
try/catch尝试执行可能失败的操作并处理指定异常
作用域变量名能够被访问的代码范围

14. 常见错误检查表#

  • main 是否写成 public static void main(String[] args)
  • 静态方法是否错误地直接访问了实例字段或 this
  • 字段是否因为图方便而被随意声明成 public
  • 方法的参数数量、类型和返回类型是否匹配?
  • 需要后续使用的计算结果是否真正通过 return 返回?
  • 累加器和中间表达式是否可能先以 int 溢出?
  • while 循环是否存在明确的退出路径?
  • continue 是否跳过了循环变量更新?
  • nextLine() 得到的字符串是否需要清理和解析?
  • parseInt() 失败时是否只捕获了需要处理的具体异常?
  • 局部变量是否在使用前明确初始化?
  • 强制类型转换是否可能丢失数据?
  • 整数除法是否错误地期待得到小数结果?

15. 关键记忆句#

  1. main 是普通 Java 应用的标准入口。
  2. static 回答“成员属于类还是对象”。
  3. private 回答“哪些代码可以直接访问成员”。
  4. 参数把数据传入方法,return 把结果带出方法。
  5. 返回类型是 long,不代表中间计算自动使用 long
  6. continue 跳过本轮,break 跳出循环,return 离开方法。
  7. nextLine() 读取文本,trim() 清理文本,parseInt() 解析文本。
  8. catch 负责处理异常,循环负责决定是否重新尝试。
  9. 临时数据使用局部变量,需要保存的对象状态才考虑字段。
  10. 类型安全要同时检查变量、操作数和整个表达式。

第五部分:下一阶段预告#

Day 02 将继续恢复以下知识:

  • 数组的声明、创建和初始化。
  • 数组下标为什么从 0 开始。
  • array.length 与数组遍历。
  • 数组越界异常。
  • String 的基本特点。
  • 字符串为什么使用 .equals() 比较内容。
  • == 比较基本类型与引用类型时分别意味着什么。

这些内容将在下一篇中完整展开,本篇只保留学习路线,不提前混入更多细节。

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

Day 01:Java 程序结构、方法与基础控制流程
https://firefly-mu-weld.vercel.app/posts/java-memory-recovery-day01/
作者
Daisy
发布于
2026-08-07
许可协议
CC BY-NC-SA 4.0
Profile Image of the Author
Daisy
Hello, I'm Daisy.
公告
欢迎来到我的博客!这是一则示例公告。
分类
标签

文章目录