你好,欢迎继续 Java 零基础学习之旅!
在前三篇中,我们完成了环境搭建、第一个程序以及 DOS 命令、注释等基础准备。从今天开始,我们将正式进入 Java 编程的核心——语法基础。
这一篇我们会学习 变量(存储数据的容器)、数据类型(定义变量能存什么数据)、运算符(对数据进行运算)以及 字符编码(计算机如何表示字符)。掌握了这些,你就能编写出真正有逻辑的程序了!
1. 变量:程序中的“收纳盒”
1.1 什么是变量?
变量是用来存储数据的一个命名内存空间。你可以把它想象成一个有标签的收纳盒:
- 盒子能装东西(数据)
- 盒子有名字(变量名)
- 盒子有特定的类型(能装什么类型的东西)
在 Java 中,每个变量都必须先声明后使用。
1.2 变量的声明与初始化
声明变量的语法:
数据类型 变量名;
例如:
int age; // 声明一个整数类型的变量 age
double price; // 声明一个浮点数类型的变量 price
String name; // 声明一个字符串类型的变量 name(String 是引用类型)
给变量赋初始值(初始化):
age = 18; // 将 18 赋值给 age
price = 99.5; // 将 99.5 赋值给 price
name = "张三"; // 将 "张三" 赋值给 name
也可以在声明的同时初始化:
int age = 18;
double price = 99.5;
String name = "张三";
1.3 变量的命名规则
- 必须以字母、下划线
_或美元符号$开头(不能以数字开头)。 - 后续可以是字母、数字、下划线或美元符号。
- 严格区分大小写(
age和Age是不同的变量)。 - 不能使用 Java 关键字(如
public、class、int等)。 - 建议使用小驼峰命名法:首字母小写,后续单词首字母大写,例如
studentName、totalScore。
合法变量名示例:age、_name、$value、studentAge、count1
不合法变量名示例:123abc(数字开头)、my-name(连字符不允许)、class(关键字)
2. 数据类型:变量的“种类”
Java 是强类型语言,每个变量必须声明类型,编译器会检查类型是否匹配。数据类型分为两大类:基本数据类型 和 引用数据类型。
2.1 基本数据类型(Primitive Types)
Java 定义了 8 种基本数据类型,分为四类:整型、浮点型、字符型、布尔型。
| 类型 | 关键字 | 占用字节 | 取值范围/说明 | 默认值 |
|---|---|---|---|---|
| 整型 | byte | 1 | -128 ~ 127 | 0 |
short | 2 | -2^15 ~ 2^15-1(约 -3.2万 ~ 3.2万) | 0 | |
int(默认) | 4 | -2^31 ~ 2^31-1(约 -21亿 ~ 21亿) | 0 | |
long | 8 | -2^63 ~ 2^63-1 | 0L | |
| 浮点型 | float | 4 | 约 ±3.4E38(7位有效数字) | 0.0f |
double(默认) | 8 | 约 ±1.8E308(15位有效数字) | 0.0d | |
| 字符型 | char | 2 | 0 ~ 65535,表示一个 Unicode 字符 | '\u0000' |
| 布尔型 | boolean | 未明确定义(通常1字节) | true 或 false | false |
注意:
- 整数直接量(如
100)默认是int类型,如果赋值给long类型需要在数字后加L(推荐大写):long num = 100L;- 浮点数直接量(如
3.14)默认是double类型,如果赋值给float需要在数字后加f或F:float f = 3.14f;char类型用单引号表示,例如'A'、'中'。
深入理解:1 字节(byte)的取值范围为什么是 -128 ~ 127?
计算机中所有数据最终都以二进制形式存储。一个字节(byte)由 8 个二进制位(bit)组成,每一位只能是 0 或 1。那么 8 位二进制可以表示多少种不同的组合呢?
- 2^8 = 256 种组合。
问题在于如何用这 256 种组合表示正数和负数。Java 中的 byte 采用有符号整数表示法,使用补码(Two's Complement)编码。规则如下:
- 最高位(最左边的一位)作为符号位:0 表示正数,1 表示负数。
- 剩下 7 位表示数值部分。
正数的范围
当符号位为 0 时,剩下 7 位可以表示 0 到 2^7-1,即 0 ~ 127。
- 例如:
0000 0001表示 1,0111 1111表示 127。
负数的表示(补码)
负数的补码表示法特点是:一个负数的补码等于其绝对值的二进制表示取反后加 1。这种设计使得加法和减法可以使用同一套电路,无需单独处理符号。
以 -1 为例:
- 先取 1 的二进制(7 位数值部分):
000 0001。 - 按位取反:
111 1110。 - 加 1:
111 1111。 - 加上符号位 1,得到
1111 1111,这就是 -1 的补码表示。
为什么最小是 -128?
- 正数最大是 127(
0111 1111)。 - 负数中,当符号位为 1,数值部分全为 0 时,即
1000 0000,这个编码表示什么?
按照补码转换规则反向推导:1000 0000减 1 得0111 1111,取反得1000 0000(即 128),所以它代表 -128。
也就是说,-128 的补码表示就是1000 0000。
因此,8 位有符号整数的范围是从 -128(1000 0000)到 127(0111 1111),一共 256 个数。
2.2 引用数据类型(Reference Types)
除了 8 种基本类型,其他都是引用类型,例如:
- 类(
String等) - 接口
- 数组
- 枚举
引用类型变量存储的是对象的地址(引用),而不是对象本身。后面我们会详细学习,现在先知道有这个概念即可。
2.3 类型转换
自动类型转换(隐式转换)
当把一个小范围的数据赋值给大范围的变量时,Java 会自动完成转换,无需特殊处理。
转换顺序(箭头方向表示自动转换允许的方向):
byte → short → int → long → float → double
↗
char
注意:char 可以自动转换为 int 及以上,但 byte 和 short 不能自动转换为 char(因为 char 是无符号的)。
示例:
int i = 100;
long l = i; // 自动转换:int → long
double d = l; // 自动转换:long → double
char c = 'A';
int code = c; // 自动转换:char → int,code 变为 65('A' 的 ASCII 码)
强制类型转换(显式转换)
当把大范围的数据赋值给小范围的变量时,必须进行强制转换,但可能造成精度损失或数据溢出。
语法:(目标类型) 值
精度损失示例
double pi = 3.14159;
int intPi = (int) pi; // 强制转换,intPi 变为 3(小数部分被截断)
数据溢出示例
数据溢出是指转换后的结果超出了目标类型的取值范围,导致高位数据丢失,结果变得“意想不到”。
我们以 int 转 byte 为例。int 占 4 字节,byte 占 1 字节。当我们将一个较大的 int 强制转为 byte 时,只会保留最低的 8 位(最后一个字节),而高位的 24 位被丢弃。这 8 位被解释为补码表示的 byte 值。
举例:
int big = 130; // 二进制:00000000 00000000 00000000 10000010
byte small = (byte) big; // 结果是多少?
计算过程:
130的 int 二进制:00000000 00000000 00000000 10000010。- 强制转 byte:只取最低 8 位
10000010。 - 这 8 位作为 byte 的补码:最高位是 1,表示负数。计算其真值:
- 先减 1:
10000001 - 取反(除符号位):
11111110(即 -126?等一下,重新算准确) 更规范的方法:负数的补码10000010转原码:先减 1 得10000001,再取反(符号位不变)得11111110,即 -126。
或者直接用公式:补码表示的负数值 = -(2^7) + 剩余7位的值 = -128 + 2 = -126。
- 先减 1:
- 所以
small的值是 -126。
可以看到,130 在正常范围内,但强制转 byte 后变成了 -126,这就是数据溢出。
再试一个更大的数:
int big = 300; // 二进制:00000000 00000000 00000001 00101100
byte small = (byte) big; // 取低8位:00101100 = 44(正数)
这里低8位 00101100 最高位是 0,所以结果为正 44。
重要:强制转换要谨慎,确保不会丢失重要数据。如果无法确定是否溢出,可以先判断原值是否在目标类型的范围内。
3. 运算符:数据的“加工厂”
运算符用于对数据进行运算操作。Java 提供了丰富的运算符,我们分类学习。
3.1 算术运算符
| 运算符 | 含义 | 示例 | 结果 |
|---|---|---|---|
+ | 加法 | 5 + 3 | 8 |
- | 减法 | 5 - 3 | 2 |
* | 乘法 | 5 * 3 | 15 |
/ | 除法 | 5 / 2 | 2(整数除法,结果取整) |
% | 取余(模) | 5 % 2 | 1 |
++ | 自增(加1) | a++ 或 ++a | 见下文 |
-- | 自减(减1) | a-- 或 --a | 见下文 |
注意:
- 整数除法结果还是整数,会舍去小数部分。
++和--放在变量前(前缀)和放在变量后(后缀)有区别:- 前缀:先自增/自减,再使用变量的值。
- 后缀:先使用变量的值,再自增/自减。
示例:
int a = 5;
int b = a++; // b = 5, a = 6(先用 a 的原值赋值给 b,然后 a 自增)
int c = ++a; // a = 7, c = 7(a 先自增,然后将新值赋值给 c)
3.2 赋值运算符
| 运算符 | 含义 | 示例 | 等价于 |
|---|---|---|---|
= | 直接赋值 | x = 5 | |
+= | 加后赋值 | x += 3 | x = x + 3 |
-= | 减后赋值 | x -= 3 | x = x - 3 |
*= | 乘后赋值 | x *= 3 | x = x * 3 |
/= | 除后赋值 | x /= 3 | x = x / 3 |
%= | 取余后赋值 | x %= 3 | x = x % 3 |
3.3 比较运算符(关系运算符)
比较运算符的结果是布尔值 true 或 false。
| 运算符 | 含义 | 示例 | 结果 |
|---|---|---|---|
== | 等于 | 5 == 3 | false |
!= | 不等于 | 5 != 3 | true |
> | 大于 | 5 > 3 | true |
< | 小于 | 5 < 3 | false |
>= | 大于等于 | 5 >= 5 | true |
<= | 小于等于 | 5 <= 3 | false |
注意:判断相等要用
==,不要误写成=(赋值)。
3.4 逻辑运算符
用于连接布尔表达式,结果也是布尔值。
| 运算符 | 含义 | 用法 | 说明 | ||||
|---|---|---|---|---|---|---|---|
&& | 短路与 | 条件1 && 条件2 | 两个都为 true 才 true;如果条件1 为 false,条件2 不会执行 | ||||
| ` | ` | 短路或 | `条件1 | 条件2` | 只要一个为 true 就 true;如果条件1 为 true,条件2 不会执行 | ||
! | 逻辑非 | !条件 | 取反:true 变 false,false 变 true | ||||
& | 非短路与 | 条件1 & 条件2 | 无论条件1 是啥,条件2 都会执行(一般位运算用) | ||||
| ` | ` | 非短路或 | `条件1 | 条件2` | 无论条件1 是啥,条件2 都会执行(一般位运算用) | ||
^ | 逻辑异或 | 条件1 ^ 条件2 | 相同为 false,不同为 true |
示例:
int age = 20;
boolean isStudent = true;
boolean result = (age > 18) && isStudent; // true && true = true
3.5 位运算符(了解即可)
位运算符直接对整数的二进制位进行操作。初学者先简单了解,后面用到再深入。
| 运算符 | 含义 | 示例 | |||
|---|---|---|---|---|---|
& | 按位与 | 5 & 3(0101 & 0011 = 0001 = 1) | |||
| ` | ` | 按位或 | `5 | 3`(0101 | 0011 = 0111 = 7) |
^ | 按位异或 | 5 ^ 3(0101 ^ 0011 = 0110 = 6) | |||
~ | 按位取反 | ~5 | |||
<< | 左移 | 5 << 1(0101 左移1位 = 1010 = 10) | |||
>> | 右移(带符号) | 5 >> 1(0101 右移1位 = 0010 = 2) | |||
>>> | 无符号右移 | -5 >>> 1(高位补0) |
3.6 三元运算符(条件运算符)
语法:条件 ? 表达式1 : 表达式2
如果条件为 true,则整个表达式的结果为表达式1,否则为表达式2。
示例:
int a = 10, b = 20;
int max = (a > b) ? a : b; // max = 20
3.7 运算符优先级
当多个运算符出现在一个表达式中时,优先级决定了计算顺序。可以通过下表记忆(优先级从高到低):
| 优先级 | 运算符分类 | 运算符 | ||
|---|---|---|---|---|
| 1 | 后缀 | expr++ expr-- | ||
| 2 | 单目 | ++expr --expr +expr -expr ~ ! | ||
| 3 | 乘除 | * / % | ||
| 4 | 加减 | + - | ||
| 5 | 移位 | << >> >>> | ||
| 6 | 关系 | < > <= >= instanceof | ||
| 7 | 相等 | == != | ||
| 8 | 按位与 | & | ||
| 9 | 按位异或 | ^ | ||
| 10 | 按位或 | ` | ` | |
| 11 | 逻辑与 | && | ||
| 12 | 逻辑或 | ` | ` | |
| 13 | 三元 | ? : | ||
| 14 | 赋值 | = += -= *= /= %= &= ^= ` | = <<= >>= >>>=` |
如果不确定优先级,可以使用圆括号
()明确指定运算顺序,这样代码也更易读。
4. 字符编码:计算机如何表示文字
我们在屏幕上看到的文字,在计算机内部都是二进制数字。字符编码就是一套规则,用来将字符映射为数字(码点),反之亦然。
4.1 ASCII 码
早期美国发明了 ASCII(American Standard Code for Information Interchange),用 7 位二进制(0-127)表示 128 个字符,包括英文字母、数字、标点和控制字符。例如:
'A'→ 65'a'→ 97'0'→ 48
ASCII 只能表示英文,无法表示中文等复杂文字。
4.2 Unicode
为了统一全球所有文字,出现了 Unicode 字符集。它为每个字符分配一个唯一的码点(Code Point),范围从 U+0000 到 U+10FFFF,可以容纳超过 100 万个字符。
Java 的 char 类型就是基于 Unicode 的,占用 2 个字节,可以表示 U+0000 到 U+FFFF 范围内的字符(基本多语言平面,BMP)。超过此范围的字符(如一些生僻汉字、Emoji)需要用两个 char 表示(代理对)。
4.3 常见的编码方案
Unicode 只是一个字符集,具体怎么存储(用几个字节)由编码方案决定:
- UTF-8:可变长度编码,英文占 1 字节,中文通常占 3 字节。是网络传输和文件存储最常用的编码。
- UTF-16:Java 内部字符串使用 UTF-16 编码,每个字符用 2 或 4 字节。
- GBK/GB2312:中国国家标准,中文占 2 字节,英文占 1 字节(兼容 ASCII)。在中文 Windows 系统中常见。
4.4 Java 中的字符与编码
char类型变量存储的是 Unicode 码点(数值),可以直接用整数赋值:
char ch1 = 'A'; // 字符直接量
char ch2 = 65; // 十进制,等价于 'A'
char ch3 = '\u0041'; // Unicode 转义,也是 'A'
- 当从控制台读取输入或读写文件时,可能会遇到编码问题。比如用
System.out.println输出中文,如果控制台编码与源码编码不一致,可能显示乱码。
建议:在编写 Java 程序时,将源代码文件保存为 UTF-8 格式,并确保编译和运行环境也使用 UTF-8(可以通过
-encoding UTF-8选项指定)。
5. 综合示例:应用变量、数据类型与运算符
下面我们写一个程序,将今天学到的知识综合运用起来:计算圆的面积和周长,并判断一个年份是否为闰年。
/**
* 综合示例:演示变量、数据类型、运算符和字符编码
*/
public class ComprehensiveDemo {
public static void main(String[] args) {
// 1. 变量与数据类型
double radius = 5.5; // 半径,double 类型
final double PI = 3.14159; // 常量,用 final 修饰,全大写命名
// 2. 算术运算
double area = PI * radius * radius; // 面积
double circumference = 2 * PI * radius; // 周长
// 3. 输出结果,使用转义字符 \n 换行
System.out.println("半径为 " + radius + " 的圆:");
System.out.println("面积 = " + area + "\n周长 = " + circumference);
// 4. 比较与逻辑运算:判断闰年
int year = 2024;
// 闰年规则:能被4整除但不能被100整除,或者能被400整除
boolean isLeap = (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
System.out.println(year + "年是闰年吗? " + isLeap);
// 5. 字符与编码
char ch = '中';
int code = ch; // char 自动转换为 int
System.out.println("字符 '" + ch + "' 的 Unicode 码点是:" + code + "(十六进制:\\u" + Integer.toHexString(code) + ")");
// 6. 三元运算符
int score = 85;
String grade = (score >= 60) ? "及格" : "不及格";
System.out.println("分数 " + score + ":" + grade);
}
}
运行结果:

6. 常见问题与提示
- 整数除法结果不对:比如
5/2结果是 2,不是 2.5。如果想得到小数,至少让其中一个操作数为浮点数,如5/2.0或5.0/2。 - 类型转换丢失精度:大范围转小范围要小心,最好先判断范围是否允许。
- 强制转换数据溢出:如前面示例,130 转 byte 变成 -126,是因为高位被截断,剩余 8 位被解释为补码。使用时务必注意。
- 浮点数比较:直接使用
==可能因为精度问题出错,建议使用差值小于某个极小值的方式判断。 - 字符编码乱码:确保源码文件编码与运行环境一致,推荐统一使用 UTF-8。
7. 总结与下期预告
今天的内容是 Java 编程的基石:
- 变量:数据存储的基本单元。
- 数据类型:8 种基本类型 + 引用类型,以及类型转换规则(包括字节取值来源和溢出详解)。
- 运算符:对数据进行各种运算,包括算术、赋值、比较、逻辑、位和三元。
- 字符编码:理解计算机如何表示文字,避免乱码问题。
下一篇文章我们将学习 进制转换——基本功。
动手实践:
- 编写程序,声明各种基本类型变量,打印它们的值、大小和默认值。
- 练习类型转换,观察强制转换后的数据变化,尝试不同数值的溢出效果。
- 用运算符计算一个三位数的个位、十位、百位数字。
- 尝试使用三元运算符求三个数中的最大值。
如果你在练习中遇到问题,欢迎在评论区留言。我们下期见! 🚀