飞龙在天乔帮主
83.33M · 2026-02-05
你已经学过对象将状态存储在**字段(Field)**中。
int cadence = 0;
int speed = 0;
int gear = 1;
在「什么是对象?」章节中介绍了字段,但你可能还有一些疑问:字段命名有哪些规则和约定?除了 int,还有哪些数据类型?字段声明时必须初始化吗?如果没有显式初始化,字段会被赋默认值吗?
在探索这些问题之前,你需要先了解一些技术上的区别。在 Java 中,"字段"和"变量"这两个术语都会用到,这常常让新手感到困惑,因为它们似乎指的是同一个东西。
Java 定义了以下几种变量:
从技术角度讲,对象将各自的状态存储在"非静态字段"中,即不带 static 关键字声明的字段。非静态字段也称为实例变量(Instance Variable),因为它们的值对于类的每个实例(即每个对象)都是独立的。一辆自行车的 currentSpeed 与另一辆自行车的 currentSpeed 是相互独立的。
通俗理解:实例变量就像每个人的身份证号,每个人都有自己独立的一份,互不影响。
**类变量(Class Variable)**是用 static 修饰符声明的字段。这告诉编译器:无论这个类被实例化多少次,这个变量只存在一份。例如,定义某种自行车齿轮数量的字段可以标记为 static,因为所有实例概念上都使用相同的齿轮数。代码 static int numGears = 6; 就创建了这样一个静态字段。此外,还可以添加 final 关键字表示齿轮数永远不会改变。
通俗理解:类变量就像班级的班牌,全班同学共用一个,不管来了多少学生,班牌只有一块。
类似于对象在字段中存储状态,方法通常在局部变量(Local Variable)中存储临时状态。声明局部变量的语法与声明字段类似(例如 int count = 0;)。没有特殊关键字将变量指定为局部变量,这完全取决于变量声明的位置——在方法的开括号和闭括号之间。因此,局部变量只对声明它们的方法可见,类的其他部分无法访问。
你已经见过参数的例子,包括 Bicycle 类和"Hello World!"程序的 main 方法。回想一下 main 方法的签名:public static void main(String[] args)。这里的 args 变量就是这个方法的参数。重要的是,参数始终被归类为"变量"而非"字段"。这同样适用于其他接受参数的结构(如构造函数和异常处理器),你将在后面的教程中学到。
️ 对比学习:四种变量类型
| 类型 | 声明位置 | 作用域 | 生命周期 | 默认值 |
|---|---|---|---|---|
| 实例变量 | 类中,方法外 | 整个类 | 随对象创建/销毁 | 有 |
| 类变量(静态) | 类中,带 static | 整个类 | 随类加载/卸载 | 有 |
| 局部变量 | 方法内 | 方法内 | 方法执行期间 | 无 |
| 参数 | 方法签名中 | 方法内 | 方法执行期间 | 无 |
每种编程语言都有自己的命名规则和约定,Java 也不例外。变量命名规则和约定如下:
规则(必须遵守):
$ 或下划线 _ 开头约定(建议遵守):
$ 或 _$(自动生成的名称可能包含它)_ 开头cadence、speed、gear 比 c、s、g 更直观)speedgearRatio、currentGearstatic final int NUM_GEARS = 6常见错误
// 错误示例
int 2count = 0; // 不能以数字开头
int my-var = 0; // 不能包含连字符
int class = 0; // 不能使用关键字
// 正确示例
int count2 = 0;
int myVar = 0;
int myClass = 0;
Java 是静态类型语言,这意味着所有变量在使用前必须先声明。声明需要指定变量的类型和名称:
int gear = 1;
这告诉程序存在一个名为 gear 的字段,保存数值数据,初始值为 1。变量的数据类型决定了它可以包含的值以及可以对它执行的操作。
除了 int,Java 还支持其他七种基本数据类型(Primitive Data Type)。基本类型是语言预定义的,用保留关键字命名。基本类型的值不与其他基本类型的值共享状态。
| 类型 | 位数 | 范围 | 默认值 | 说明 |
|---|---|---|---|---|
byte | 8位 | -128 ~ 127 | 0 | 节省内存,用于大数组 |
short | 16位 | -32,768 ~ 32,767 | 0 | 节省内存,用于大数组 |
int | 32位 | -2³¹ ~ 2³¹-1 | 0 | 最常用的整数类型 |
long | 64位 | -2⁶³ ~ 2⁶³-1 | 0L | 需要比 int 更大范围时使用 |
float | 32位 | IEEE 754 | 0.0f | 节省内存,不用于精确值 |
double | 64位 | IEEE 754 | 0.0d | 小数的默认选择,不用于精确值 |
boolean | - | true / false | false | 简单的真/假标志 |
char | 16位 | 'u0000' ~ 'uffff' | 'u0000' | 单个 Unicode 字符 |
️ 注意:float 和 double 不能用于精确值计算(如货币)。需要精确计算时,请使用 java.math.BigDecimal 类。
除了八种基本类型,Java 还通过 java.lang.String 类提供对字符串的特殊支持。用双引号包围字符串会自动创建 String 对象:
String s = "this is a string";
String 对象是不可变的(Immutable),一旦创建,其值就不能改变。虽然 String 严格来说不是基本类型,但由于语言对它的特殊支持,你可能会把它当作基本类型来看待。
面试考点
Q:String 为什么设计成不可变的?
A:主要有三个原因:
字段声明时不一定要赋值。未初始化的字段会被编译器设置为合理的默认值,通常是零或 null。但依赖默认值通常被认为是不好的编程风格。
| 数据类型 | 默认值 |
|---|---|
| byte | 0 |
| short | 0 |
| int | 0 |
| long | 0L |
| float | 0.0f |
| double | 0.0d |
| char | 'u0000' |
| boolean | false |
| String(或任何对象) | null |
️ 注意:局部变量没有默认值!编译器不会为未初始化的局部变量赋默认值。访问未初始化的局部变量会导致编译时错误。
常见错误
// 编译错误:局部变量未初始化
public void method() {
int count;
System.out.println(count); // 错误!
}
// 正确:先初始化再使用
public void method() {
int count = 0;
System.out.println(count);
}
你可能注意到初始化基本类型变量时没有使用 new 关键字。基本类型是内置于语言中的特殊数据类型,不是从类创建的对象。**字面量(Literal)**是固定值在源代码中的表示形式,直接写在代码中,无需计算。
boolean result = true;
char capitalC = 'C';
byte b = 100;
short s = 10000;
int i = 100000;
整数字面量如果以 L 或 l 结尾则为 long 类型,否则为 int 类型。建议使用大写 L,因为小写 l 容易与数字 1 混淆。
整数字面量可以用以下进制表示:
// 十进制:26
int decVal = 26;
// 十六进制:26(0x 前缀)
int hexVal = 0x1a;
// 二进制:26(0b 前缀,Java SE 7+)
int binVal = 0b11010;
浮点数字面量以 F 或 f 结尾为 float 类型,否则为 double 类型(可选地以 D 或 d 结尾)。
double d1 = 123.4;
double d2 = 1.234e2; // 科学计数法,等于 123.4
float f1 = 123.4f;
char 和 String 字面量可以包含任何 Unicode(UTF-16)字符。如果编辑器和文件系统允许,可以直接在代码中使用这些字符;否则可以使用 Unicode 转义,如 'u0108'(带抑扬符的大写 C)。
转义序列:
| 转义序列 | 含义 |
|---|---|
b | 退格 |
t | 制表符 |
n | 换行 |
f | 换页 |
r | 回车 |
" | 双引号 |
' | 单引号 |
\ | 反斜杠 |
char 字面量用单引号,String 字面量用双引号。
还有一个特殊的 null 字面量,可以赋给任何引用类型变量(基本类型除外)。null 常用作标记,表示某个对象不可用。
还有一种特殊的类字面量(Class Literal),由类型名后加 .class 构成,例如 String.class,它引用代表该类型本身的 Class 对象。
Java SE 7 及更高版本允许在数字字面量的数字之间使用任意数量的下划线 _,可以提高代码可读性:
long creditCardNumber = 1234_5678_9012_3456L;
long socialSecurityNumber = 999_99_9999L;
float pi = 3.14_15F;
long hexBytes = 0xFF_EC_DE_5E;
long hexWords = 0xCAFE_BABE;
byte nybbles = 0b0010_0101;
long bytes = 0b11010010_01101001_10010100_10010010;
下划线不能放在以下位置:
F、L 等后缀之前0x 之后)// 无效
float pi1 = 3_.1415F; // 小数点旁边
float pi2 = 3._1415F; // 小数点旁边
long ssn = 999_99_9999_L; // L 后缀之前
int x2 = 52_; // 结尾
int x4 = 0_x52; // 0x 之间
int x5 = 0x_52; // 0x 之后
// 有效
int x1 = 5_2;
int x3 = 5_______2;
int x6 = 0x5_2;
数组(Array)是一个容器对象,保存固定数量的单一类型的值。数组的长度在创建时确定,之后不可更改。
索引: 0 1 2 3 4 5 6 7 8 9
┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┐
值: │ │ │ │ │ │ │ │ │ │ │
└─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┘
数组中的每个元素称为元素(Element),通过数字**索引(Index)**访问。索引从 0 开始,因此第 9 个元素的索引是 8。
通俗理解:数组就像一排储物柜,每个柜子都有编号(从 0 开始),每个柜子只能放同类型的东西。柜子数量在建造时就固定了。
以下程序创建一个整数数组,放入一些值,然后打印每个值:
class ArrayDemo {
public static void main(String[] args) {
// 声明一个整数数组
int[] anArray;
// 分配 10 个整数的内存
anArray = new int[10];
// 初始化元素
anArray[0] = 100;
anArray[1] = 200;
// ... 依此类推
anArray[9] = 1000;
System.out.println("Element at index 0: " + anArray[0]);
System.out.println("Element at index 1: " + anArray[1]);
// ... 依此类推
System.out.println("Element at index 9: " + anArray[9]);
}
}
在实际编程中,通常会使用循环结构遍历数组元素,而不是逐行编写。
// 声明一个整数数组(推荐写法)
int[] anArray;
// 声明各种类型的数组
byte[] anArrayOfBytes;
short[] anArrayOfShorts;
long[] anArrayOfLongs;
float[] anArrayOfFloats;
double[] anArrayOfDoubles;
boolean[] anArrayOfBooleans;
char[] anArrayOfChars;
String[] anArrayOfStrings;
数组类型写作 type[],其中 type 是元素的数据类型。数组大小不是类型的一部分(所以方括号是空的)。声明本身不会创建数组,只是告诉编译器这个变量将保存指定类型的数组。
// 也可以把方括号放在变量名后(不推荐)
float anArrayOfFloats[];
方式一:使用 new 运算符
// 创建数组
anArray = new int[10];
// 初始化元素
anArray[0] = 100;
anArray[1] = 200;
anArray[2] = 300;
// 访问元素
System.out.println("Element at index 0: " + anArray[0]);
如果缺少创建语句,编译器会报错:
ArrayDemo.java:4: Variable anArray may not have been initialized.
方式二:使用简写语法(推荐)
int[] anArray = {
100, 200, 300,
400, 500, 600,
700, 800, 900, 1000
};
数组长度由花括号内的值的数量决定。
使用两个或更多方括号声明多维数组:
String[][] names = {
{"Mr. ", "Mrs. ", "Ms. "},
{"Smith", "Jones"}
};
// Mr. Smith
System.out.println(names[0][0] + names[1][0]);
// Ms. Jones
System.out.println(names[0][2] + names[1][1]);
在 Java 中,多维数组是数组的数组。与 C 或 Fortran 不同,Java 的多维数组各行长度可以不同。
使用内置的 length 属性获取数组大小:
System.out.println(anArray.length); // 输出: 10
System 类提供了 arraycopy 方法高效地复制数组数据:
public static void arraycopy(Object src, int srcPos,
Object dest, int destPos, int length)
参数说明:
src:源数组srcPos:源数组起始位置dest:目标数组destPos:目标数组起始位置length:复制的元素数量class ArrayCopyDemo {
public static void main(String[] args) {
String[] copyFrom = {
"Affogato", "Americano", "Cappuccino", "Corretto", "Cortado",
"Doppio", "Espresso", "Frappucino", "Freddo", "Lungo", "Macchiato",
"Marocchino", "Ristretto" };
String[] copyTo = new String[7];
System.arraycopy(copyFrom, 2, copyTo, 0, 7);
for (String coffee : copyTo) {
System.out.print(coffee + " ");
}
}
}
java.util.Arrays 类提供了常用的数组操作方法:
// 使用 copyOfRange(不需要预先创建目标数组)
String[] copyTo = java.util.Arrays.copyOfRange(copyFrom, 2, 9);
注意:copyOfRange 的第二个参数是起始索引(包含),第三个参数是结束索引(不包含)。
Arrays 类其他常用方法:
| 方法 | 功能 |
|---|---|
binarySearch | 在已排序数组中二分查找 |
equals | 比较两个数组是否相等 |
fill | 用指定值填充数组 |
sort | 排序(升序) |
parallelSort | 并行排序(Java SE 8+,大数组更快) |
stream | 创建流(Java SE 8+) |
toString | 转为字符串 |
// 转为字符串
System.out.println(java.util.Arrays.toString(copyTo));
// 输出: [Cappuccino, Corretto, Cortado, Doppio, Espresso, Frappucino, Freddo]
// 使用 Stream API(Java SE 8+)
java.util.Arrays.stream(copyTo).map(coffee -> coffee + " ").forEach(System.out::print);
现在你已经学会了如何声明和初始化变量,接下来学习如何使用它们。**运算符(Operator)是对一个、两个或三个操作数(Operand)**执行特定操作并返回结果的特殊符号。
下表按优先级从高到低列出了 Java 运算符。优先级高的运算符先求值。同一行的运算符优先级相同。
| 类别 | 运算符 | ||
|---|---|---|---|
| 后缀 | expr++ expr-- | ||
| 一元 | ++expr --expr +expr -expr ~ ! | ||
| 乘除 | * / % | ||
| 加减 | + - | ||
| 移位 | << >> >>> | ||
| 关系 | < > <= >= instanceof | ||
| 相等 | == != | ||
| 按位与 | & | ||
| 按位异或 | ^ | ||
| 按位或 | ` | ` | |
| 逻辑与 | && | ||
| 逻辑或 | ` | ` | |
| 三元 | ? : | ||
| 赋值 | = += -= *= /= %= &= ^= ` | = <<= >>= >>>=` |
最常见的运算符是简单赋值运算符 =,它将右边的值赋给左边的操作数:
int cadence = 0;
int speed = 0;
int gear = 1;
该运算符也可用于对象,赋值对象引用。
| 运算符 | 描述 |
|---|---|
+ | 加法(也用于字符串连接) |
- | 减法 |
* | 乘法 |
/ | 除法 |
% | 取余(模运算) |
class ArithmeticDemo {
public static void main (String[] args) {
int result = 1 + 2;
System.out.println("1 + 2 = " + result); // 3
int original_result = result;
result = result - 1;
System.out.println(original_result + " - 1 = " + result); // 2
original_result = result;
result = result * 2;
System.out.println(original_result + " * 2 = " + result); // 4
original_result = result;
result = result / 2;
System.out.println(original_result + " / 2 = " + result); // 2
original_result = result;
result = result + 8;
System.out.println(original_result + " + 8 = " + result); // 10
original_result = result;
result = result % 7;
System.out.println(original_result + " % 7 = " + result); // 3
}
}
复合赋值运算符:x += 1; 等价于 x = x + 1;
字符串连接:+ 运算符也用于连接字符串:
class ConcatDemo {
public static void main(String[] args){
String firstString = "This is";
String secondString = " a concatenated string.";
String thirdString = firstString + secondString;
System.out.println(thirdString);
}
}
输出:This is a concatenated string.
一元运算符只需要一个操作数:
| 运算符 | 描述 |
|---|---|
+ | 一元正号(表示正值,通常省略) |
- | 一元负号(取反) |
++ | 自增(加 1) |
-- | 自减(减 1) |
! | 逻辑非(取反布尔值) |
class UnaryDemo {
public static void main(String[] args) {
int result = +1;
System.out.println(result); // 1
result--;
System.out.println(result); // 0
result++;
System.out.println(result); // 1
result = -result;
System.out.println(result); // -1
boolean success = false;
System.out.println(success); // false
System.out.println(!success); // true
}
}
自增/自减运算符可以放在操作数前面(前缀)或后面(后缀)。result++ 和 ++result 都会让 result 加 1,但它们的返回值不同:
++result:返回增加后的值result++:返回原来的值面试考点
Q:++i 和 i++ 有什么区别?
class PrePostDemo {
public static void main(String[] args){
int i = 3;
i++;
System.out.println(i); // 4
++i;
System.out.println(i); // 5
System.out.println(++i); // 6(先加后用)
System.out.println(i++); // 6(先用后加)
System.out.println(i); // 7
}
}
A:单独使用时效果相同。在表达式中,++i 先自增再返回新值,i++ 先返回原值再自增。
️ 对比学习:++i vs i++
| 表达式 | 操作顺序 | 返回值 |
|---|---|---|
++i | 先加 1,再返回 | 新值 |
i++ | 先返回,再加 1 | 原值 |
| 运算符 | 描述 |
|---|---|
== | 等于 |
!= | 不等于 |
> | 大于 |
>= | 大于等于 |
< | 小于 |
<= | 小于等于 |
️ 注意:测试两个值是否相等时必须用 ==,而不是 =!
class ComparisonDemo {
public static void main(String[] args){
int value1 = 1;
int value2 = 2;
if(value1 == value2)
System.out.println("value1 == value2");
if(value1 != value2)
System.out.println("value1 != value2");
if(value1 > value2)
System.out.println("value1 > value2");
if(value1 < value2)
System.out.println("value1 < value2");
if(value1 <= value2)
System.out.println("value1 <= value2");
}
}
面试考点
Q:== 和 equals() 有什么区别?
A:
==:比较基本类型的值,或比较引用类型的内存地址equals():比较对象的内容(需要类正确重写该方法)String s1 = new String("hello");
String s2 = new String("hello");
System.out.println(s1 == s2); // false(不同对象)
System.out.println(s1.equals(s2)); // true(内容相同)
| 运算符 | 描述 | ||
|---|---|---|---|
&& | 条件与(短路) | ||
| ` | ` | 条件或(短路) | |
? : | 三元运算符 |
&& 和 || 具有短路行为:如果第一个操作数已经能确定结果,就不会计算第二个操作数。
class ConditionalDemo1 {
public static void main(String[] args){
int value1 = 1;
int value2 = 2;
if((value1 == 1) && (value2 == 2))
System.out.println("value1 is 1 AND value2 is 2");
if((value1 == 1) || (value2 == 1))
System.out.println("value1 is 1 OR value2 is 1");
}
}
三元运算符是 if-then-else 的简写形式:
result = someCondition ? value1 : value2;
含义:如果 someCondition 为 true,将 value1 赋给 result;否则将 value2 赋给 result。
class ConditionalDemo2 {
public static void main(String[] args){
int value1 = 1;
int value2 = 2;
int result;
boolean someCondition = true;
result = someCondition ? value1 : value2;
System.out.println(result); // 1
}
}
通俗理解:三元运算符就像做选择题,条件为真选 A,为假选 B。当表达式简洁且没有副作用时,用它比 if-then-else 更简洁。
instanceof 运算符将对象与指定类型进行比较,可以测试对象是否是某个类的实例、子类的实例,或实现了某个接口的类的实例。
class InstanceofDemo {
public static void main(String[] args) {
Parent obj1 = new Parent();
Parent obj2 = new Child();
System.out.println("obj1 instanceof Parent: " + (obj1 instanceof Parent)); // true
System.out.println("obj1 instanceof Child: " + (obj1 instanceof Child)); // false
System.out.println("obj1 instanceof MyInterface: " + (obj1 instanceof MyInterface)); // false
System.out.println("obj2 instanceof Parent: " + (obj2 instanceof Parent)); // true
System.out.println("obj2 instanceof Child: " + (obj2 instanceof Child)); // true
System.out.println("obj2 instanceof MyInterface: " + (obj2 instanceof MyInterface)); // true
}
}
class Parent {}
class Child extends Parent implements MyInterface {}
interface MyInterface {}
️ 注意:null 不是任何类型的实例,null instanceof AnyType 始终返回 false。
这些运算符对整数类型执行位级操作,使用频率较低:
| 运算符 | 描述 | |
|---|---|---|
~ | 按位取反 | |
<< | 有符号左移 | |
>> | 有符号右移 | |
>>> | 无符号右移 | |
& | 按位与 | |
^ | 按位异或 | |
| ` | ` | 按位或 |
class BitDemo {
public static void main(String[] args) {
int bitmask = 0x000F;
int val = 0x2222;
System.out.println(val & bitmask); // 输出: 2
}
}
表达式(Expression)由变量、运算符和方法调用组成,求值得到一个单一的值。
int cadence = 0; // 赋值表达式,返回 int
anArray[0] = 100; // 赋值表达式
System.out.println("..."); // 方法调用表达式
int result = 1 + 2; // result 现在是 3
if (value1 == value2) // 比较表达式,返回 boolean
表达式返回值的数据类型取决于表达式中使用的元素。cadence = 0 返回 int,因为赋值运算符返回与左操作数相同类型的值。
复合表达式可以由多个小表达式组成:
1 * 2 * 3 // 乘法顺序无关
x + y / 100 // 有歧义!
(x + y) / 100 // 明确:先加后除
x + (y / 100) // 明确:先除后加(默认行为)
️ 注意:编写复合表达式时,建议使用括号明确运算顺序,这样代码更易读、易维护。
**语句(Statement)**大致相当于自然语言中的句子,构成一个完整的执行单元。
表达式语句:以下表达式加上分号 ; 就构成语句:
++ 或 -- 的使用// 赋值语句
aValue = 8933.234;
// 自增语句
aValue++;
// 方法调用语句
System.out.println("Hello World!");
// 对象创建语句
Bicycle myBike = new Bicycle();
声明语句:声明变量。
// 声明语句
double aValue = 8933.234;
控制流语句:控制语句的执行顺序,下一节详细介绍。
**块(Block)**是一对花括号 {} 之间的零个或多个语句,可以用在任何允许单个语句的地方。
class BlockDemo {
public static void main(String[] args) {
boolean condition = true;
if (condition) { // 块 1 开始
System.out.println("Condition is true.");
} // 块 1 结束
else { // 块 2 开始
System.out.println("Condition is false.");
} // 块 2 结束
}
}
通俗理解:块就像一个盒子,把相关的语句打包在一起。if-else 每个分支都可以用一个块来包含多条语句。
源文件中的语句通常按出现顺序从上到下执行。控制流语句(Control Flow Statement)通过决策、循环和分支来打破这种流程,使程序能够有条件地执行特定代码块。
Java 支持以下控制流语句:
if-then、if-then-else、switchfor、while、do-whilebreak、continue、returnif-then 语句是最基本的控制流语句。它告诉程序仅当特定条件为 true 时才执行某段代码。
void applyBrakes() {
// "if" 子句:自行车必须在移动
if (isMoving) {
// "then" 子句:减速
currentSpeed--;
}
}
如果条件为 false,控制跳到 if-then 语句之后。
如果 "then" 子句只有一条语句,可以省略花括号:
void applyBrakes() {
if (isMoving)
currentSpeed--;
}
️ 注意:省略花括号会使代码更脆弱。如果后来添加第二条语句,很容易忘记加花括号。编译器不会捕获这种错误,你只会得到错误的结果。建议始终使用花括号。
当 "if" 条件为 false 时,if-then-else 语句提供备选执行路径。
void applyBrakes() {
if (isMoving) {
currentSpeed--;
} else {
System.err.println("The bicycle has already stopped!");
}
}
多重条件判断(else if):
class IfElseDemo {
public static void main(String[] args) {
int testscore = 76;
char grade;
if (testscore >= 90) {
grade = 'A';
} else if (testscore >= 80) {
grade = 'B';
} else if (testscore >= 70) {
grade = 'C';
} else if (testscore >= 60) {
grade = 'D';
} else {
grade = 'F';
}
System.out.println("Grade = " + grade);
}
}
输出:Grade = C
注意:虽然 76 同时满足 >= 70 和 >= 60,但一旦条件满足,相应语句执行后,剩余条件不再检查。
与 if-then 和 if-then-else 不同,switch 语句可以有多个可能的执行路径。
switch 适用于以下类型:
byte、short、char、intString(Java SE 7+)Character、Byte、Short、Integerpublic class SwitchDemo {
public static void main(String[] args) {
int month = 8;
String monthString;
switch (month) {
case 1: monthString = "January";
break;
case 2: monthString = "February";
break;
case 3: monthString = "March";
break;
case 4: monthString = "April";
break;
case 5: monthString = "May";
break;
case 6: monthString = "June";
break;
case 7: monthString = "July";
break;
case 8: monthString = "August";
break;
case 9: monthString = "September";
break;
case 10: monthString = "October";
break;
case 11: monthString = "November";
break;
case 12: monthString = "December";
break;
default: monthString = "Invalid month";
break;
}
System.out.println(monthString);
}
}
输出:August
️ 对比学习:if-else vs switch
| 特性 | if-else | switch |
|---|---|---|
| 条件类型 | 任意布尔表达式、范围 | 单个值(整数、枚举、String) |
| 可读性 | 条件少时清晰 | 多个等值判断时更清晰 |
| 性能 | 顺序判断 | 可能使用跳转表优化 |
每个 break 语句终止当前 switch 语句。没有 break,语句会"穿透(Fall Through)"——继续执行后面所有 case 的语句,直到遇到 break。
public class SwitchDemoFallThrough {
public static void main(String[] args) {
java.util.ArrayList<String> futureMonths = new java.util.ArrayList<String>();
int month = 8;
switch (month) {
case 1: futureMonths.add("January");
case 2: futureMonths.add("February");
case 3: futureMonths.add("March");
case 4: futureMonths.add("April");
case 5: futureMonths.add("May");
case 6: futureMonths.add("June");
case 7: futureMonths.add("July");
case 8: futureMonths.add("August");
case 9: futureMonths.add("September");
case 10: futureMonths.add("October");
case 11: futureMonths.add("November");
case 12: futureMonths.add("December");
break;
default: break;
}
if (futureMonths.isEmpty()) {
System.out.println("Invalid month number");
} else {
for (String monthName : futureMonths) {
System.out.println(monthName);
}
}
}
}
通俗理解:穿透就像多米诺骨牌,一旦触发,后面的都会倒下,直到遇到 break 这个"挡板"。
class SwitchDemo2 {
public static void main(String[] args) {
int month = 2;
int year = 2000;
int numDays = 0;
switch (month) {
case 1: case 3: case 5:
case 7: case 8: case 10:
case 12:
numDays = 31;
break;
case 4: case 6:
case 9: case 11:
numDays = 30;
break;
case 2:
if (((year % 4 == 0) && !(year % 100 == 0)) || (year % 400 == 0))
numDays = 29;
else
numDays = 28;
break;
default:
System.out.println("Invalid month.");
break;
}
System.out.println("Number of Days = " + numDays);
}
}
输出:Number of Days = 29
public class StringSwitchDemo {
public static int getMonthNumber(String month) {
int monthNumber = 0;
if (month == null) {
return monthNumber;
}
switch (month.toLowerCase()) {
case "january":
monthNumber = 1;
break;
case "february":
monthNumber = 2;
break;
// ... 其他月份
case "august":
monthNumber = 8;
break;
// ...
default:
monthNumber = 0;
break;
}
return monthNumber;
}
public static void main(String[] args) {
String month = "August";
int returnedMonthNumber = StringSwitchDemo.getMonthNumber(month);
if (returnedMonthNumber == 0) {
System.out.println("Invalid month");
} else {
System.out.println(returnedMonthNumber);
}
}
}
输出:8
switch 表达式中的 String 使用 String.equals 方法与每个 case 标签比较。
️ 注意:确保 switch 表达式不为 null,否则会抛出 NullPointerException。
while 语句在条件为 true 时持续执行语句块。
while (expression) {
statement(s)
}
class WhileDemo {
public static void main(String[] args){
int count = 1;
while (count < 11) {
System.out.println("Count is: " + count);
count++;
}
}
}
无限循环:
while (true) {
// 代码
}
do-while 语句在循环底部求值表达式,因此循环体至少执行一次。
do {
statement(s)
} while (expression);
class DoWhileDemo {
public static void main(String[] args){
int count = 1;
do {
System.out.println("Count is: " + count);
count++;
} while (count < 11);
}
}
️ 对比学习:while vs do-while
| 特性 | while | do-while |
|---|---|---|
| 条件检查位置 | 循环前 | 循环后 |
| 最少执行次数 | 0 次 | 1 次 |
| 适用场景 | 可能不需要执行 | 至少需要执行一次 |
for 语句提供了一种紧凑的方式来迭代一系列值。
for (initialization; termination; increment) {
statement(s)
}
false 时循环终止class ForDemo {
public static void main(String[] args){
for(int i = 1; i < 11; i++){
System.out.println("Count is: " + i);
}
}
}
通俗理解:for 循环就像设定好的闹钟,你告诉它从几点开始(初始化)、到几点结束(终止条件)、每次走多少(增量),它就自动按规律执行。
变量作用域:在初始化表达式中声明的变量,其作用域从声明处延伸到 for 语句结束。如果循环外不需要该变量,最好在初始化表达式中声明它。
无限循环:
for ( ; ; ) {
// 代码
}
专为遍历集合和数组设计的简洁形式:
class EnhancedForDemo {
public static void main(String[] args){
int[] numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
for (int item : numbers) {
System.out.println("Count is: " + item);
}
}
}
建议尽可能使用增强 for 循环,代码更简洁易读。
break 有两种形式:无标签和有标签。
无标签 break:终止最内层的 switch、for、while 或 do-while 语句。
class BreakDemo {
public static void main(String[] args) {
int[] arrayOfInts = { 32, 87, 3, 589, 12, 1076, 2000, 8, 622, 127 };
int searchfor = 12;
int i;
boolean foundIt = false;
for (i = 0; i < arrayOfInts.length; i++) {
if (arrayOfInts[i] == searchfor) {
foundIt = true;
break; // 找到后退出循环
}
}
if (foundIt) {
System.out.println("Found " + searchfor + " at index " + i);
} else {
System.out.println(searchfor + " not in the array");
}
}
}
输出:Found 12 at index 4
有标签 break:终止带有指定标签的外层语句。
class BreakWithLabelDemo {
public static void main(String[] args) {
int[][] arrayOfInts = {
{ 32, 87, 3, 589 },
{ 12, 1076, 2000, 8 },
{ 622, 127, 77, 955 }
};
int searchfor = 12;
int i;
int j = 0;
boolean foundIt = false;
search: // 标签
for (i = 0; i < arrayOfInts.length; i++) {
for (j = 0; j < arrayOfInts[i].length; j++) {
if (arrayOfInts[i][j] == searchfor) {
foundIt = true;
break search; // 跳出外层循环
}
}
}
if (foundIt) {
System.out.println("Found " + searchfor + " at " + i + ", " + j);
} else {
System.out.println(searchfor + " not in the array");
}
}
}
输出:Found 12 at 1, 0
️ 注意:break 终止带标签的语句,控制流转移到该语句之后的语句,不是转移到标签处。
continue 跳过当前迭代的剩余部分。
无标签 continue:跳到最内层循环的下一次迭代。
class ContinueDemo {
public static void main(String[] args) {
String searchMe = "peter piper picked a peck of pickled peppers";
int max = searchMe.length();
int numPs = 0;
for (int i = 0; i < max; i++) {
if (searchMe.charAt(i) != 'p')
continue; // 不是 'p',跳过
numPs++;
}
System.out.println("Found " + numPs + " p's in the string.");
}
}
输出:Found 9 p's in the string.
有标签 continue:跳过带标签的外层循环的当前迭代。
class ContinueWithLabelDemo {
public static void main(String[] args) {
String searchMe = "Look for a substring in me";
String substring = "sub";
boolean foundIt = false;
int max = searchMe.length() - substring.length();
test: // 标签
for (int i = 0; i <= max; i++) {
int n = substring.length();
int j = i;
int k = 0;
while (n-- != 0) {
if (searchMe.charAt(j++) != substring.charAt(k++)) {
continue test; // 不匹配,尝试下一个位置
}
}
foundIt = true;
break test;
}
System.out.println(foundIt ? "Found it" : "Didn't find it");
}
}
输出:Found it
️ 对比学习:break vs continue
| 语句 | 作用 | 有标签版本 |
|---|---|---|
break | 完全退出循环 | 退出指定标签的外层循环 |
continue | 跳过本次迭代,继续下一次 | 跳过外层循环的本次迭代 |
return 语句退出当前方法,控制流返回到方法被调用的地方。
// 返回值
return ++count;
// 无返回值(void 方法)
return;
返回值的数据类型必须与方法声明的返回类型匹配。
变量类型
| 类型 | 位置 | 特点 |
|---|---|---|
| 实例变量 | 类中,无 static | 每个对象一份 |
| 类变量 | 类中,有 static | 所有对象共享一份 |
| 局部变量 | 方法中 | 方法结束即销毁,无默认值 |
| 参数 | 方法签名 | 调用时传入 |
基本数据类型
| 类型 | 位数 | 范围 | 默认值 |
|---|---|---|---|
| byte | 8 | -128 ~ 127 | 0 |
| short | 16 | -32768 ~ 32767 | 0 |
| int | 32 | -2³¹ ~ 2³¹-1 | 0 |
| long | 64 | -2⁶³ ~ 2⁶³-1 | 0L |
| float | 32 | IEEE 754 | 0.0f |
| double | 64 | IEEE 754 | 0.0d |
| boolean | - | true/false | false |
| char | 16 | 0 ~ 65535 | 'u0000' |
控制流语句
| 类型 | 语句 | 用途 |
|---|---|---|
| 决策 | if-then, if-then-else, switch | 条件执行 |
| 循环 | for, while, do-while | 重复执行 |
| 分支 | break, continue, return | 跳转控制 |