[JavaSE] 02 - Java 基本语法
2.1 - 变量和常量
2.1.1 - 字面量
字面量(Literal)指的是源代码中直接写出来的、固定不变的值。
整数字面量可以使用十进制、十六进制、八进制和二进制表示。在不带 L 后缀时,其类型通常为 int;使用 L 后缀则表示 long。字面量必须符合相应类型的取值范围。
| 类型 | 示例 | 说明 |
|---|---|---|
| 十进制 | 10、100_000 | 默认的 int 类型,:c20[可用下划线增强可读性,但不可在首尾]。 |
| 长整型 | 100L、100l | 使用 L 或 l 后缀表示 long 类型(建议用大写 L 避免与数字 1 混淆)。 |
| 十六进制 | 0xFF、0x1A | 以 0x 或 0X 开头。 |
| 八进制 | 012、0777 | 以 0 开头(现代开发中较少使用)。 |
| 二进制 | 0b1010、0B1111 | 以 0b 或 0B 开头(Java 7+)。 |
用于表示带小数点的数字或科学计数法,默认类型为 double。对于 范围内的小数可省略 0,如 .5 表示 0.5。
| 类型 | 示例 | 说明 |
|---|---|---|
| 双精度(默认) | 3.14、2.5e3 | :c20[默认是 double 类型。]支持科学计数法(e 表示 10 的幂)。 |
| 双精度(显式) | 3.14D、2.5e3d | 使用 D 或 d 后缀显式声明为 double(非必须)。 |
| 单精度 | 3.14F、2.5e3f | 使用 F 或 f 后缀声明为 float。 |
| 十六进制浮点 | 0x1.8p1 | 以 0x 开头,p 表示 2 的幂(此例等于 3.0)。 |
用于表示单个字符或文本。
| 类型 | 示例 | 说明 |
|---|---|---|
| 字符 | 'A'、'文' | 使用单引号包裹单个字符,本质是 16 位无符号整数。 |
| 转义字符 | '\n'、'\t'、'\\'、'\'' | 表示换行、制表符、反斜杠本身、单引号本身。 |
| Unicode 字符 | '\u0041' | 表示 Unicode 字符(此例为 'A')。 |
| 字符串 | "Hello" | 使用双引号包裹,表示文本序列。 |
| 文本块 | - | 使用三个双引号,支持多行字符串(Java 15+)。 |
布尔和空字面量
| 类型 | 示例 | 说明 |
|---|---|---|
| 布尔字面量 | true、false | 表示逻辑真或假,属于 boolean 类型。 |
| 空字面量 | null | 表示空引用,只能赋值给对象引用类型(不能赋给基本数据类型如 int)。 |
值得注意的地方:
-
Java 中没有无符号(unsigned)整数字面量。
-
char类型的字面量可以参与算术运算,例如'A' + 1的结果是整数66。 -
null不是关键字,也不是基本类型,它是一个特殊的字面量,表示“没有对象被引用”。
2.1.2 - 变量的分类和作用域
Java 变量按声明位置分为四大类:局部变量、实例变量、静态变量和参数。其中实例变量和静态变量统称为字段(field),或成员变量。
| 变量类型 | 声明位置 | 所属关系 | 默认值 |
|---|---|---|---|
| 局部变量 | 方法、构造器或语句块等内部 | 不属于类或对象的字段 | 无 |
| 实例变量 | 类中,方法外,未使用 static | 每个对象各有一份 | 有 |
| 静态变量 | 类中,使用 static | 属于类,通常由该类的实例共享 | 有 |
| 参数 | 方法、构造器等的参数列表 | 接收调用时传入的值 | 不适用 |
class Student {
String name; // 实例变量
static int count = 0; // 静态变量
void study(int hours) { // hours:参数
int minutes = hours * 60; // 局部变量
}
}关于不同类型变量的作用域(Scope):
- 局部变量:作用域仅限于定义它的那一对
{}内部; - 实例变量和静态变量(字段):受访问修饰符的影响(将在面向对象部分介绍);
- 参数:作用域仅限于方法内部。
2.1.3 - 变量的声明、初始化和赋值
一、声明、初始化和赋值
Java 是强类型语言,每个变量都有固定的数据类型。
Java 变量的使用通常包括三个操作:声明(Declaration)、初始化(Initialization)和赋值(Assignment)。
- 声明:必须指定变量的类型与名称,可不给初始值;
- 初始化:初始化指变量首次获得初始值的过程;
- 赋值:与 C 一致,对于普通变量,可以通过赋值运算符
=修改其保存的值。
int age;
double score = 95.5;
char gender = 'M';
// 允许在一条声明语句中声明多个相同类型的变量
int a = 10, b, c = 30;变量名属于标识符(Identifier),必须符合 Java 的标识符规则:
- 可以使用 Unicode 字母、数字及其他允许的字符;
- 不允许包含空格、运算符、分隔符、引号、反斜杠、@、#;
- 不允许以数字开头;
- 不允许使用 Java 关键字。
二、确定赋值规则
对于局部变量,因为无默认值,所以 Java 要求在读取变量之前,必须能够确定该变量已经被赋值。这称为确定赋值(Definite Assignment)。
public class Main {
public static void main(String[] args) {
int age;
if (Math.random() > 0.5) {
age = 20;
}
System.out.println(age); // 编译错误
}
}age 是局部变量,无初始值;虽然代码中存在 age = 20,但是并非所有执行路径都能保证它获得值,不属于确定赋值,因此编译器会报错。
int age;
if (Math.random() > 0.5) {
age = 20;
} else {
age = 30;
}
System.out.println(age); // 正确编译器能够确定无论条件是否成立,变量在使用前都已经被赋值。所以可编译成功。
public class Main {
static int age;
public static void main(String[] args) {
System.out.println(age); // 输出 0
}
}此处 age 是静态字段,Java 会为未显式初始化的字段(成员变量)提供默认值
| 类型 | 默认值 |
|---|---|
| byte / short / int / long | 0 |
| float / double | 0.0 |
| char | '\u0000' |
| boolean | false |
| 引用类型(对象、数组、String 等) | null |
2.1.4 - 局部变量类型推断(var)
从 Java 10 开始,Java 支持使用 var 声明具有类型推断能力的局部变量。
- 必须在声明时提供初始化表达式。
- 不能用
null作为唯一的初始化表达式。 - 不能用来声明普通字段或方法参数。
- 一次
var声明只能声明一个变量。 - 推断后的类型是固定的,并不是动态类型。
var x = 10; // int
var y = 10L; // long
var z = 10.0; // double
var price = 19.99f; // float
var name = "Alice"; // String
var obj = null; // 错误:无法推断类型
var score; // 错误:缺少初始化表达式
var a = 1, b = 2; // 错误:不能这样同时声明两个变量2.1.5 - 常量
常量是用 final 修饰的变量,表示在程序执行过程中只能被赋值一次。
final int MAX_SIZE = 100;-
final:表示该变量只能被赋值一次(可以不立即赋值)。 -
int:变量的数据类型。 -
MAX_SIZE:变量名;一般情况下全部字母大写,单词之间使用下划线分隔,不过局部常量仍可以使用小驼峰命名法。 -
100:初始值,也是一个整数字面量。
与 C 语言中 const 的区别
与 C 不同的是:
- C 语言的
const表达的含义是“只读”; - 而 Java 的
final表示该变量只能被赋值一次(允许声明时不赋值)。
const 是 Java 中未被使用的保留关键字,以后也大概率不会使用。
2.2 - 数据类型
数据类型(Data Type)用于规定一个变量能够保存什么样的数据,以及可以对这些数据进行哪些操作。
Java 是一种静态类型语言(Statically Typed Language),变量的类型在编译时就需要确定,不能随意改变。
Java 的数据类型可以分为两大类:
- 基本数据类型(Primitive Types):直接表示数值、字符或布尔值,共有 8 种。
- 引用数据类型(Reference Types):通过引用访问对象或数组,例如类、接口、数组等。
2.2.1 - Java 基本数据类型
| 类型 | 关键字 | 字节数 | 主要用途 | 取值范围 (精简) |
|---|---|---|---|---|
| 字节型 | byte | 1 | 小范围整数 | -128 ~ 127 |
| 短整型 | short | 2 | 小范围整数 | -32768 ~ 32767 |
| 整型 | int | 4 | 最常用的整数类型 | $-2^{31} \sim 2^{31}-1$ |
| 长整型 | long | 8 | 大范围整数 | $-2^{63} \sim 2^{63}-1$ |
| 单精度浮点型 | float | 4 | 小数 | ±3.4e38 (约6~7位有效数字) |
| 双精度浮点型 | double | 8 | 最常用的浮点类型 | ±1.8e308 (约15~16位有效数字) |
| 字符型 | char | 2 | UTF-16 代码单元* | 0 ~ 65535 (即 0 ~ 2^16-1) |
| 布尔型 | boolean | 不确定** | 真或假 | true / false |
注释信息
*Java 的 char 并不保证可以表示一个完整的 Unicode 字符。例如,一些表情符号需要使用两个 char(代理对)来表示。
String emoji = "😀";
System.out.println(emoji.length()); // 2**JVM 规范没有明确规定 boolean 占用多少个字节,在数组中通常是 1 字节,而单独的布尔变量可能占用 4 字节(因为 JVM 的具体实现可能将其作为 int 处理,但与 C/C++ 不同,它在源代码中它不能与数字互相转换)。
int a = 1;
if (a) { // 编译错误
System.out.println("Hello");
}2.2.2 - Java 引用数据类型
除了 8 种基本类型,Java 中的其他大多数数据类型都是引用类型,包括字符串。
引用类型变量保存的是对对象的引用(Reference),而不是像基本类型变量一样直接表示基本类型的值。
- 引用类型变量可以保存
null,表示当前没有引用任何对象; - 引用类型赋值 = 引用的复制(指向同一对象)
- Java 只有值传递(pass by value),在给方法传参时,传入的是引用的副本(可改变所指对象的内容,但无法修改该指向)。
int[] arr1 = {1, 2, 3};
int[] arr2 = arr1; // arr1 和 arr2 指向同一个数组对象
arr2[0] = 99;
System.out.println(arr1[0]); // 99
String s1 = new String("hi");
String s2 = new String("hi");
s1 == s2 // false(不同对象)
s1.equals(s2) // true (内容相同)
String s = "a";
String t = s;
s = s + "b"; // s + "b" 是一个新的对象,因为 s 所指的对象 "a" 无法修改,t 仍是 "a"2.3 - 基本数据类型的类型转换
2.3.1 - 赋值和传参时的转换
Java 中,赋值和传参时的基本数据类型的类型转换可以分为两种:
- 自动类型转换(Widening Primitive Conversion)
- 强制类型转换(Narrowing Primitive Conversion)
一、自动类型转换(隐式 / 拓宽转换)
当把一个表数范围小的数值或变量赋值给表数范围大的变量时,系统自动完成类型转换。
-
char可以自动转int及更宽类型; -
byte/short不能自动转char,因为char是无符号的; -
int转float、long转float、long转double可能丢失精度。
int a = 100;
long b = a; // int 自动转换为 long
double d = a; // int 自动转换为 double虚线:可能发生精度损失的自动转换
二、强制类型转换(显式 / 窄化转换)
当目标类型不能通过自动转换得到时(需要将宽类型放入窄类型变量时),通常需要显式指定目标类型,进行强制转换。
-
语法:
(目标类型) 变量名。 -
转换方法:整数直接截断高位、浮点转整数直接丢弃小数部分。
-
boolean不能与任何数值基本类型进行类型转换。
double d = 99.99;
int i = (int) d; // 小数部分直接被舍弃,i 的值为 99
int bigNum = 300;
byte b = (byte) bigNum; // byte 范围是 -128 ~ 127,转换后会发生溢出,结果为 442.3.2 - 表达式中的类型提升
除了赋值时的类型转换,Java 还会在算术运算等表达式中自动进行数值类型提升(Numeric Promotion),直接决定了表达式结果的数据类型。
一、一元数值提升(Unary Numeric Promotion)
当 byte、short 或 char 参与某些一元运算时,会先提升为 int(例如一元正号 +)。
byte a = 10;
int b = +a; // 正确
byte c = +a; // 编译错误,因为 +a 的结果是 int 类型| 操作数类型 | 提升后类型 |
|---|---|
byte、short、char | :c10[提升为 `int`] |
int | 保持 int |
long | 保持 long |
float | 保持 float |
double | 保持 double |
二、二元数值提升(Binary Numeric Promotion)
适用于大部分二元运算符:
-
算术运算符:
+ - * / % -
比较运算符:
<、<=、>、>= -
位运算符:
& | ^ -
数值相等运算符:
==、!=
规则顺序:
-
如果任一操作数是
double,另一个操作数转换为double; -
否则,如果任一操作数是
float,另一个操作数转换为float; -
否则,如果任一操作数是
long,另一个操作数转换为long;
- 否则,两个操作数都转换为
int(也就是在处理byte/short的运算时,必须转为int,这是因为 JVM 并没有byte/short的算术指令)
byte a = 1;
byte b = 2;
byte c = a + b; // 编译错误
byte c = (byte)(a + b); // 编译成功
int d = 10;
long e = 20L;
long f = d + e; // 结果类型为 long
float g = 3.14F;
double h = 2.0;
double i = g + h; // 结果类型为 double需特别注意整数除法:
int a = 5;
int b = 2;
double c = a / b; // 2.0
/**
因为 a / b 的两个操作数都是 int,所以它执行整数除法,结果是整数 2。
之后,再将 2 自动转换为 double,得到 2.0。
**/
double c = (double) a / b; // 2.52.4 - 运算符
Java 的运算符与 C 语言有很多相似之处,但在整数除法、字符串拼接、逻辑运算、位运算、类型转换和对象比较等方面,有一些值得特别注意的规则。
2.4.1 - 表达式和运算符概述
一、表达式的组成
int result = a + b;-
a、b:操作数(Operand) -
+:运算符(Operator) -
a + b:表达式(Expression) -
=:赋值运算符,将右侧表达式的结果赋给左侧变量(整个赋值也是一个表达式)
二、运算符的分类
| 类型 | 说明 | 示例 |
|---|---|---|
| 一元运算符(Unary) | 作用于一个操作数 | -a、++a、!flag |
| 二元运算符(Binary) | 作用于两个操作数 | a + b、a > b |
| 三元运算符(Ternary) | 作用于三个操作数 | a > b ? a : b |
| 分类 | 运算符 |
|---|---|
| 算术运算符 | + - * / % |
| 自增、自减运算符 | ++ -- |
| 赋值运算符 | = += -= *= /= %= 等 |
| 关系运算符 | > < >= <= == != |
| 逻辑运算符 | && || ! & | ^ |
| 位运算符 | & | ^ ~ << >> >>> |
| 条件运算符 | ? : |
| 类型判断运算符 | instanceof |
除此之外,之前已经介绍过,Java 还有用于类型转换的 (type)、对象创建的 new 等语法。
2.4.2 - 算术运算符
算术运算符:+ 加法、- 减法、* 乘法、/ 除法、% 取余
- 运算结果取决于参与运算的操作数类型,而不是接收结果的变量类型;
- Java 取余运算: (期中
/是向 0 截断的整数除法); - 与 C 不同,Java 的
%还可以用于浮点数。
System.out.println(10 / 3); // 3
System.out.println(10.0 / 3); // 3.3333333333333335
System.out.println(-10 / 3); // -3,向 0 截断,而不是向下取整或四舍五入
double a = 10 / 3;
double b = 10.0 / 3;
System.out.println(a); // 3.0
System.out.println(b); // 3.3333333333333335
System.out.println(10 % 3); // 1
System.out.println(-10 % 3); // -1
System.out.println(10 % -3); // 1
System.out.println(-10 % -3); // -1
System.out.println(5.5 % 2.0); // 1.52.4.3 - 字符串拼接
Java 中的 + 还可用于字符串拼接。
System.out.println("Hello" + " World");
// Hello World
System.out.println(1 + 2 + "3"); // "33"
System.out.println("1" + 2 + 3); // "123",因为按顺序会先得到“12”
System.out.println("1" + (2 + 3)); // "15"2.4.4 - 自增和自减运算符
自增、自减运算符:++a(先加再用)、--a(先减再用)、a++(先用再加)、a--(先用再减);
Java 中,自增和自减运算可以作用于 byte、short、char 等类型,并且不需要显式类型转换:
- 但 Java 本身无法对这三类变量进行算术运算,所以会先将其自动提升为 int 类型;
- 运算结束后,再自动将结果强制转换回原本的类型;
- 所以,即使运算结果超过该类型的表数范围,也不产生编译错误,而是溢出。
byte b = 10;
b = b + 1; // 编译错误
b++; // 编译成功,等价于 b = (byte)(b + 1);2.4.5 - 赋值运算符
一、基本赋值运算符
基本赋值运算符 = 将右侧表达式的结果赋给左侧变量。并且,在 Java 中赋值不是单纯的语句,而是表达式。
- 赋值表达式的类型是左侧变量的类型;赋值表达式的值是赋值完成后左侧变量的值。;
- 并且 Java 的赋值运算符都具有右结合性(运算符的结合性决定同优先级运算符连续出现时如何分组),所以 Java 支持连续赋值。
a = b = c;
// 等价于
a = (b = c);
// 而不是 (a = b) = c;
// 因为 (a = b) 是一个赋值表达式的结果,不是变量,不能作为赋值左操作数。二、复合赋值运算符
复合赋值运算符(Compound Assignment Operators)将运算与赋值合并,所有的四则运算和二元位运算都有复合赋值运算符。
与自增、自减运算符类似,Java 的复合赋值运算符隐含了向左侧变量类型的强制转换。
byte a = 10;
a = a + 5; // 编译错误
a += 5; // 正确,相当于 a = (byte) (a + 5);