变量

你已经学过对象将状态存储在**字段(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 也不例外。变量命名规则和约定如下:

规则(必须遵守)

  • 变量名区分大小写
  • 变量名可以是任意合法标识符——由 Unicode 字母和数字组成的无限长度序列,以字母、美元符号 $ 或下划线 _ 开头
  • 不允许有空格
  • 不能是关键字或保留字

约定(建议遵守)

  • 始终以字母开头,而非 $_
  • 避免使用美元符号 $(自动生成的名称可能包含它)
  • 不鼓励以下划线 _ 开头
  • 使用完整单词而非缩写(cadencespeedgearcsg 更直观)
  • 单个单词全小写:speed
  • 多个单词采用驼峰命名:gearRatiocurrentGear
  • 常量全大写,单词间用下划线:static 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)。基本类型是语言预定义的,用保留关键字命名。基本类型的值不与其他基本类型的值共享状态。

八种基本数据类型

类型位数范围默认值说明
byte8位-128 ~ 1270节省内存,用于大数组
short16位-32,768 ~ 32,7670节省内存,用于大数组
int32位-2³¹ ~ 2³¹-10最常用的整数类型
long64位-2⁶³ ~ 2⁶³-10L需要比 int 更大范围时使用
float32位IEEE 7540.0f节省内存,不用于精确值
double64位IEEE 7540.0d小数的默认选择,不用于精确值
boolean-true / falsefalse简单的真/假标志
char16位'u0000' ~ 'uffff''u0000'单个 Unicode 字符

注意floatdouble 不能用于精确值计算(如货币)。需要精确计算时,请使用 java.math.BigDecimal 类。

String 类型

除了八种基本类型,Java 还通过 java.lang.String 类提供对字符串的特殊支持。用双引号包围字符串会自动创建 String 对象:

String s = "this is a string";

String 对象是不可变的(Immutable),一旦创建,其值就不能改变。虽然 String 严格来说不是基本类型,但由于语言对它的特殊支持,你可能会把它当作基本类型来看待。

面试考点

Q:String 为什么设计成不可变的?

A:主要有三个原因:

  1. 安全性:String 常用作参数(如网络连接、文件路径),不可变防止被恶意修改
  2. 线程安全:不可变对象天然线程安全,可在多线程间共享
  3. 字符串常量池:不可变性使得字符串可以被缓存复用,节省内存

默认值

字段声明时不一定要赋值。未初始化的字段会被编译器设置为合理的默认值,通常是零或 null。但依赖默认值通常被认为是不好的编程风格

数据类型默认值
byte0
short0
int0
long0L
float0.0f
double0.0d
char'u0000'
booleanfalse
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;

整数字面量

整数字面量如果以 Ll 结尾则为 long 类型,否则为 int 类型。建议使用大写 L,因为小写 l 容易与数字 1 混淆。

整数字面量可以用以下进制表示:

// 十进制:26
int decVal = 26;
// 十六进制:26(0x 前缀)
int hexVal = 0x1a;
// 二进制:26(0b 前缀,Java SE 7+)
int binVal = 0b11010;

浮点数字面量

浮点数字面量以 Ff 结尾为 float 类型,否则为 double 类型(可选地以 Dd 结尾)。

double d1 = 123.4;
double d2 = 1.234e2;    // 科学计数法,等于 123.4
float f1 = 123.4f;

字符和字符串字面量

charString 字面量可以包含任何 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;

下划线不能放在以下位置

  • 数字的开头或结尾
  • 小数点旁边
  • FL 等后缀之前
  • 需要数字串的位置(如 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 开始),每个柜子只能放同类型的东西。柜子数量在建造时就固定了。

ArrayDemo 示例

以下程序创建一个整数数组,放入一些值,然后打印每个值:

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:++ii++ 有什么区别?

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;

含义:如果 someConditiontrue,将 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

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-thenif-then-elseswitch
  • 循环语句forwhiledo-while
  • 分支语句breakcontinuereturn

if-then 和 if-then-else 语句

if-then 语句

if-then 语句是最基本的控制流语句。它告诉程序仅当特定条件为 true 时才执行某段代码。

void applyBrakes() {
    // "if" 子句:自行车必须在移动
    if (isMoving) { 
        // "then" 子句:减速
        currentSpeed--;
    }
}

如果条件为 false,控制跳到 if-then 语句之后。

如果 "then" 子句只有一条语句,可以省略花括号:

void applyBrakes() {
    if (isMoving)
        currentSpeed--;
}

注意:省略花括号会使代码更脆弱。如果后来添加第二条语句,很容易忘记加花括号。编译器不会捕获这种错误,你只会得到错误的结果。建议始终使用花括号

if-then-else 语句

当 "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,但一旦条件满足,相应语句执行后,剩余条件不再检查

switch 语句

if-thenif-then-else 不同,switch 语句可以有多个可能的执行路径

switch 适用于以下类型:

  • 基本类型:byteshortcharint
  • 枚举类型
  • String(Java SE 7+)
  • 包装类:CharacterByteShortInteger
public 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-elseswitch
条件类型任意布尔表达式、范围单个值(整数、枚举、String)
可读性条件少时清晰多个等值判断时更清晰
性能顺序判断可能使用跳转表优化

break 语句的重要性

每个 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 这个"挡板"。

多个 case 共享代码

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

在 switch 中使用 String(Java SE 7+)

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 和 do-while 语句

while 语句

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-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

特性whiledo-while
条件检查位置循环循环
最少执行次数0 次1 次
适用场景可能不需要执行至少需要执行一次

for 语句

for 语句提供了一种紧凑的方式来迭代一系列值。

for (initialization; termination; increment) {
    statement(s)
}
  • initialization:初始化表达式,循环开始时执行一次
  • termination:终止表达式,为 false 时循环终止
  • increment:每次迭代后执行,可以递增或递减
class ForDemo {
    public static void main(String[] args){
         for(int i = 1; i < 11; i++){
              System.out.println("Count is: " + i);
         }
    }
}

通俗理解for 循环就像设定好的闹钟,你告诉它从几点开始(初始化)、到几点结束(终止条件)、每次走多少(增量),它就自动按规律执行。

变量作用域:在初始化表达式中声明的变量,其作用域从声明处延伸到 for 语句结束。如果循环外不需要该变量,最好在初始化表达式中声明它。

无限循环

for ( ; ; ) {
    // 代码
}

增强 for 语句(for-each)

专为遍历集合和数组设计的简洁形式:

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 有两种形式:无标签有标签

无标签 break:终止最内层的 switchforwhiledo-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 跳过当前迭代的剩余部分。

无标签 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 语句退出当前方法,控制流返回到方法被调用的地方。

// 返回值
return ++count;

// 无返回值(void 方法)
return;

返回值的数据类型必须与方法声明的返回类型匹配。


变量类型

类型位置特点
实例变量类中,无 static每个对象一份
类变量类中,有 static所有对象共享一份
局部变量方法中方法结束即销毁,无默认值
参数方法签名调用时传入

基本数据类型

类型位数范围默认值
byte8-128 ~ 1270
short16-32768 ~ 327670
int32-2³¹ ~ 2³¹-10
long64-2⁶³ ~ 2⁶³-10L
float32IEEE 7540.0f
double64IEEE 7540.0d
boolean-true/falsefalse
char160 ~ 65535'u0000'

控制流语句

类型语句用途
决策if-then, if-then-else, switch条件执行
循环for, while, do-while重复执行
分支break, continue, return跳转控制

本站提供的所有下载资源均来自互联网,仅提供学习交流使用,版权归原作者所有。如需商业使用,请联系原作者获得授权。 如您发现有涉嫌侵权的内容,请联系我们 邮箱:alixiixcom@163.com