零基础学Java|第四篇:变量、数据类型、运算符与字符编码

0 阅读15分钟

你好,欢迎继续 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 变量的命名规则

  • 必须以字母、下划线 _ 或美元符号 $ 开头(不能以数字开头)。
  • 后续可以是字母、数字、下划线或美元符号。
  • 严格区分大小写ageAge 是不同的变量)。
  • 不能使用 Java 关键字(如 publicclassint 等)。
  • 建议使用小驼峰命名法:首字母小写,后续单词首字母大写,例如 studentNametotalScore

合法变量名示例age_name$valuestudentAgecount1
不合法变量名示例123abc(数字开头)、my-name(连字符不允许)、class(关键字)


2. 数据类型:变量的“种类”

Java 是强类型语言,每个变量必须声明类型,编译器会检查类型是否匹配。数据类型分为两大类:基本数据类型引用数据类型

2.1 基本数据类型(Primitive Types)

Java 定义了 8 种基本数据类型,分为四类:整型、浮点型、字符型、布尔型。

类型关键字占用字节取值范围/说明默认值
整型byte1-128 ~ 1270
short2-2^15 ~ 2^15-1(约 -3.2万 ~ 3.2万)0
int(默认)4-2^31 ~ 2^31-1(约 -21亿 ~ 21亿)0
long8-2^63 ~ 2^63-10L
浮点型float4约 ±3.4E38(7位有效数字)0.0f
double(默认)8约 ±1.8E308(15位有效数字)0.0d
字符型char20 ~ 65535,表示一个 Unicode 字符'\u0000'
布尔型boolean未明确定义(通常1字节)truefalsefalse

注意

  • 整数直接量(如 100)默认是 int 类型,如果赋值给 long 类型需要在数字后加 L(推荐大写):long num = 100L;
  • 浮点数直接量(如 3.14)默认是 double 类型,如果赋值给 float 需要在数字后加 fFfloat 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. 先取 1 的二进制(7 位数值部分):000 0001
  2. 按位取反:111 1110
  3. 加 1:111 1111
  4. 加上符号位 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 会自动完成转换,无需特殊处理。

转换顺序(箭头方向表示自动转换允许的方向):

byteshortintlongfloatdoublechar

注意:char 可以自动转换为 int 及以上,但 byteshort 不能自动转换为 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(小数部分被截断)
数据溢出示例

数据溢出是指转换后的结果超出了目标类型的取值范围,导致高位数据丢失,结果变得“意想不到”。

我们以 intbyte 为例。int 占 4 字节,byte 占 1 字节。当我们将一个较大的 int 强制转为 byte 时,只会保留最低的 8 位(最后一个字节),而高位的 24 位被丢弃。这 8 位被解释为补码表示的 byte 值。

举例:

int big = 130;        // 二进制:00000000 00000000 00000000 10000010
byte small = (byte) big;  // 结果是多少?

计算过程:

  1. 130 的 int 二进制:00000000 00000000 00000000 10000010
  2. 强制转 byte:只取最低 8 位 10000010
  3. 这 8 位作为 byte 的补码:最高位是 1,表示负数。计算其真值:
    • 先减 1:10000001
    • 取反(除符号位):11111110(即 -126?等一下,重新算准确) 更规范的方法:负数的补码 10000010 转原码:先减 1 得 10000001,再取反(符号位不变)得 11111110,即 -126。
      或者直接用公式:补码表示的负数值 = -(2^7) + 剩余7位的值 = -128 + 2 = -126。
  4. 所以 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 + 38
-减法5 - 32
*乘法5 * 315
/除法5 / 22(整数除法,结果取整)
%取余(模)5 % 21
++自增(加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 += 3x = x + 3
-=减后赋值x -= 3x = x - 3
*=乘后赋值x *= 3x = x * 3
/=除后赋值x /= 3x = x / 3
%=取余后赋值x %= 3x = x % 3

3.3 比较运算符(关系运算符)

比较运算符的结果是布尔值 truefalse

运算符含义示例结果
==等于5 == 3false
!=不等于5 != 3true
>大于5 > 3true
<小于5 < 3false
>=大于等于5 >= 5true
<=小于等于5 <= 3false

注意:判断相等要用 ==,不要误写成 =(赋值)。

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)
``按位或`53`(01010011 = 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+0000U+10FFFF,可以容纳超过 100 万个字符。

Java 的 char 类型就是基于 Unicode 的,占用 2 个字节,可以表示 U+0000U+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);
    }
}

运行结果: image.png

6. 常见问题与提示

  • 整数除法结果不对:比如 5/2 结果是 2,不是 2.5。如果想得到小数,至少让其中一个操作数为浮点数,如 5/2.05.0/2
  • 类型转换丢失精度:大范围转小范围要小心,最好先判断范围是否允许。
  • 强制转换数据溢出:如前面示例,130 转 byte 变成 -126,是因为高位被截断,剩余 8 位被解释为补码。使用时务必注意。
  • 浮点数比较:直接使用 == 可能因为精度问题出错,建议使用差值小于某个极小值的方式判断。
  • 字符编码乱码:确保源码文件编码与运行环境一致,推荐统一使用 UTF-8。

7. 总结与下期预告

今天的内容是 Java 编程的基石:

  • 变量:数据存储的基本单元。
  • 数据类型:8 种基本类型 + 引用类型,以及类型转换规则(包括字节取值来源和溢出详解)。
  • 运算符:对数据进行各种运算,包括算术、赋值、比较、逻辑、位和三元。
  • 字符编码:理解计算机如何表示文字,避免乱码问题。

下一篇文章我们将学习 进制转换——基本功。

动手实践

  1. 编写程序,声明各种基本类型变量,打印它们的值、大小和默认值。
  2. 练习类型转换,观察强制转换后的数据变化,尝试不同数值的溢出效果。
  3. 用运算符计算一个三位数的个位、十位、百位数字。
  4. 尝试使用三元运算符求三个数中的最大值。

如果你在练习中遇到问题,欢迎在评论区留言。我们下期见! 🚀