🌟 想系统化学习 Java 编程?看看这个:[编程基础] Java · 学习手册
0x01:变量 & 常量 —— 概念引入
无论是使用 Java 还是其他编程语言,我们都需要对数据进行处理。那么这些数据应该存储到什么地方呢?从编程的角度来看,就是存储到变量和常量中。变量和常量,它们相当于就是存储数据的一个容器。
为了方便我们定位容器(然后获取容器中的数据),我们通常还需要给变量 & 常量进行命名。而这个命名也不是随意起的,需要符合一定的规则(这个笔者在前面章节讲过了):
基础回顾 🚀:Java · 初窥门径 —— 标识符 & 保留字
0x02:Java · 初窥门径 —— 常量
常量通常是指代一个固定的值,可以理解为,通常不变的量。
Java 中常量一般分为两类:
-
字面常量:例如:1、2、3、’a’、’b’、true、false、”helloWorld”(部分情况可修改)。
-
符号常量:被关键字
final
修饰的常量就是符号常量,这种值你改不了。
对于符号常量,笔者会在后续的笔记中讲解,本章我们关注的是 “字面常量”。即 ”字面意义上的常量“。以下是一些常见的字面常量:
0x0201:整型常量
整型常量是整数类型的数据, 有二进制、八进制、十进制和十六进制 4 种表示形式,具体如下:
-
二进制:由数字 0 和 1 组成的数字序列。从 JDK 7 开始,允许使用字面值表示二进制数,前面要以 0b 或 0B 开头,目的是为了和十进制数区分,如
0b01101100
、0B10110101
。 -
八进制:以 0 开头并且其后由 0~7(包括 0 和 7 )的整数组成的数字序列,如
0342
。 -
十进制:由数字 0~9(包括 0 和 9)的整数组成的数字序列,如
198
。 -
十六进制:以 0x 或者 0X 开头并且其后由 0~9、A~F(包括 0 和 9、A 和 F,字母不区分大小写)组成的数字序列,如
0x25AF
。
0x0202:浮点数常量
浮点数常量就是在数学中用到的小数。
Java 中的浮点数分为单精度浮点数(float)和双精度浮点数(double)两种类型。其中,单精度浮点数后面以F
或f
结尾,而双精度浮点数则以D
或d
结尾。当然,在使用浮点数时也可以在结尾处不加任何后缀,此时 JVM 会默认浮点数为 double 类型,浮点数常量还可以通过指数形式表示。
浮点数常量具体示例如下:
2e3f
3.6d
0f
3.84d
5.022e+23f // 指数形式 100 => 1e2
0x0203:字符常量
字符常量用于表示一个字符,一个字符常量要用一对英文半角格式的单引号(''
)括起来。字符常量可以是英文字母、数字、标点符号以及由转义序列表示的特殊字符。具体示例如下:
'a'
'1'
'&'
'\r'
'\u0000'
上面示例中,'\u0000'
表示一个空白字符,即在单引号之间没有任何字符。之所以能这样表示,是因为 Java 采用的是 Unicode 字符集,Unicode 字符以\u
开头,空白字符在 Unicode 码表中对应的值为'\u0000'
。
0x0204:字符串常量
字符串常量用于表示一串连续的字符,一个字符串常量要用一对英文半角格式的双引号(""
)括起来,具体示例如下:
"HelloWorld"
"123"
"Welcome \n XXX"
""
一个字符串可以包含一个字符或多个字符,也可以不包含任何字符,即长度为 0。
0x0205:布尔常量
布尔常量即布尔型的值,用于区分事物的真与假。布尔常量有 true 和 false 两个值。
0x0206:null 常量
null 常量只有一个值 null,表示对象的引用为空。
其实笔者觉得这部分有点混乱,本来应该是只讲讲常量的,但是写着写着就牵扯到了 ”数据类型“。而且感觉字符常量笔者也没讲明白(读者把握核心,通常不变的量即可)。
0x03:Java · 初窥门径 —— 变量
0x0301:变量的引入
在程序运行期间,随时可能产生一些临时数据,应用程序会将这些数据保存在内存单元中,每个内存单元都用一个标识符标识,这些用于标识内存单元的标识符就称为变量,内存单元中存储的数据就是变量的值。
0x0302:变量的使用流程
其实在讲解变量的时候,还应该讲解一下变量的类型(和上面常量一样,变量也有 ”整型“、”浮点型“、”字符型“ 。。。),但是这又是一个大的知识点,笔者决定在下一章讲解。那么本章,我们先来掌握变量的使用流程(以 int
型,即整数类型举例)。
1. 变量的声明 & 赋值
要想使用一个变量,我们就得先对变量进行声明,下面是 Java 程序中声明一个变量的基本格式:
// 英文版: [ ]中的内容为可选项,即可有可无
type identifier [ = value][, identifier [= value] ...] ;
// 中文对照版
数据类型 变量名 [=初始值] [,变量名 [=初始值]...];
下面是一个 Java 变量声明与赋值(给变量赋予一个值)的示例:
public class HelloWorld {
public static void main(String[] args) {
// 我们可以在定义变量的同时给变量赋值
int age = 10; // 年龄 = 10 岁, 数据类型 => int;变量名 => age;初始值 => 10;
// 我们也可以以一行定义多个变量并赋值
int age2 = 10, width = 100, height;
// 我们还可以先定义变量后赋值
int a;
a = 100;
// 如果一个变量只是定义了,未被赋值,就等于未定义
int b;
}
}
上面这个示例比较 Easy,展示了各种定义变量以及赋值的方法。这里笔者再简单讲解一下关于变量声明的一个注意点:
如果你只声明一个变量,没有给变量进行赋值的话,那么其实这个变量相当于没有定义。
我们可以尝试运行上面的代码,运行后会生成一个字节码文件,下面是笔者产生的字节码文件的内容与原文件内容的对比:
另外,再提一点,Java 中不允许对一个变量进行二次定义:
2. 变量的使用
变量的使用包括对变量的重新赋值(处理),或者打印变量,看下面这个示例:
// FileName: HelloWorld.java
public class HelloWorld {
public static void main(String[] args) {
int age = 1;
System.out.println("2025 今年我 " + age + " 岁了");
age += 1; // 过了一年
System.out.println("2026 今年我 " + age + " 岁了");
age += 1; // 过了一年
System.out.println("2027 今年我 " + age + " 岁了");
}
}
0x0303:拓展 — 变量的内存分析(了解)
本小节就是一个拓展,我们从内存角度来分析变量的使用(理解底层,有助于我们后期的学习)。
1. 变量内存分析 — 加法的执行流程
先看下面这个代码,是一个简单的加法运算(其他运算都差不多):
// FileName: printMotto.java
public class printMotto {
public static void main(String[] args) {
int a = 10;
int b = 20;
int c = a + b;
System.out.print(c);
}
}
我们以内存角度分析一下程序的运行流程:
在上图中,我们提到了一个内存地址(房间号)的概念,我们在高级语言中可以通过 a
、b
这样非常形象的标识符来标记变量,然而其底层逻辑,实际是 a
是一个指向某个内存的指针,你访问 a
其实就是访问 a
指向的那个内存单元格中的内容。
有点抽象是吧,没关系(以后会懂的,就是不懂也没啥关系),笔者下面来尝试借助函数打印一波内存的地址单元(你说函数咋获得?直接搜):
如上 System.identityHashCode()
可以获取变量的内存地址,我们实操一下:
// FileName: printMotto.java
public class printMotto {
public static void main(String[] args) {
int a = 10;
int b = 20;
int c = a + b;
System.out.println("变量 a 的内存地址: " + System.identityHashCode(a));
System.out.println("变量 b 的内存地址: " + System.identityHashCode(b));
System.out.println("变量 c 的内存地址: " + System.identityHashCode(c));
}
}
如上,上面打印的那堆贼拉长的数,就是我们的内存房间号。
2. 变量内存分析 — 内存节省策略
这部分更是拓展中的拓展,我们在前面内存分析中写道 “住户 30 住在 c 号房”,问题来了,如果来了两个 30,那么它们在内存中是咋样的呢?是新分配一个房间还是住一起呢?
看下面的代码,我们新增了一个变量 d
,且该变量的值与 c
一致,此时我们看看 d
和 c
的内存地址:
// FileName: printMotto.java
public class printMotto {
public static void main(String[] args) {
int a = 10;
int b = 20;
int c = a + b;
int d = 30;
System.out.println("变量 a 的内存地址: " + System.identityHashCode(a));
System.out.println("变量 b 的内存地址: " + System.identityHashCode(b));
System.out.println("变量 c 的内存地址: " + System.identityHashCode(c));
System.out.println("变量 d 的内存地址: " + System.identityHashCode(d));
}
}
如上,可以看到,值相同,打印的内存地址也一样。看来 Java 是把值一样的客人都放一起了,这样就能省开几个房间了,大大节省了我们内存空间的消耗。
0x0304:拓展 — 变量的作用域(掌握)
变量需要在它的作用范围内才可以被使用,这个作用范围被称为变量的作用域。在 Java 程序中,变量一定会被定义在一对大括号中,该大括号所包裹的代码区域便是该变量的作用域(后期会涉及更加复杂的情况,暂时读者可以先以 “{}
” 作为作用域的划定)。
在讲解变量的作用域前,我们先来了解一下可以定义变量的地方,或者变量的分类:
-
局部变量: 在一个函数(方法)或一个代码块内声明的变量。
-
成员变量: 声明在类中,且在方法外的变量。
1. 案例演示 — 局部变量作用域
看下面这个代码,变量 A
被定义在 main()
方法中,此时我们可以在 main()
方法中输出变量 A:
public class printMotto {
public static void main(String[] args) {
int A = 10;
System.out.println(A); // 这里能输出 A 的值
}
}
main()
就是一个方法,OK,那么现在我们再创建一个 test()
方法,问,这里面能输出 A 的值吗(关于方法我们以后会讲到):
public class printMotto {
public static void main(String[] args) {
int A = 10;
System.out.println(A); // 这里能输出 A 的值
}
public static void test() {
System.out.print(A);
}
}
如上,还没运行,IDE 就给我们标红了,证明那里有语法问题。其实这就是变量的作用域问题,一个大括号就是一个作用域,作用域内,我玩我的,作用域外,就不关我事了。
2. 案例演示 — 成员变量作用域
继续,看下面这个案例,我们将变量 A 声明在 printMotto
类(被 class
修饰的就是类)中,但是没有声明在任意一个方法中,问,在 main()
方法与 test()
方法中我们能调用 A 吗(static
代表静态,也是后面的内容 😂,暂时不做掌握):
public class printMotto {
static int A = 10; // 成员变量 A
public static void main(String[] args) {
System.out.println(A); // 这里能输出 A 的值
}
public static void test() {
System.out.print(A); // 这里也能输出 A 的值
}
}
一样,直接看 IDE,如下,没有报错,证明是可以运行的:
原谅笔者这里真的不知道举啥例子,讲的有些抽象了,而且涉及到了很多后面才会学的知识,但是不讲又不行。对小白而言,如果看不懂也请记住这两个案例,我们在后期的编程中肯定会用到,届时有了基础的你,理解这两份代码一定会非常轻松。