00java语法基础
Java 第一讲《Java 语法基础》:动手动脑问题与课后实验整理
目录
1. 动手动脑 1:运行 EnumTest.java,枚举类型怎么用?
题目
仔细阅读示例 EnumTest.java,运行它并分析运行结果:你能得到什么结论?你掌握枚举类型的基本用法了吗?
代码
enum Size { SMALL, MEDIUM, LARGE } // 枚举类型
public class EnumTest {
public static void main(String[] args) {
Size s = Size.SMALL; // 直接取值
Size t = Size.valueOf("SMALL"); // 从字符串转枚举
System.out.println(s); // SMALL
System.out.println(s == t); // true
System.out.println(s.equals(t)); // true
// 枚举的 foreach 遍历
for (Size v : Size.values()) {
System.out.println(v);
}
}
}
结论
-
枚举类型是引用类型,不属于原始数据类型;
-
它的每个具体值都引用一个特定的对象,相同的值引用同一个对象;
-
因此枚举变量可以直接用
==比较,此时==和equals()结果等价; -
枚举可以用
values()遍历,也可以用在switch语句中。
2. 动脑动手 2:原码、反码、补码与位运算
题目
查阅资料弄清原码、反码、补码的概念,然后编写示例程序,对正数、负数进行各种位操作(&、|、^、~、<<、>>),观察输出并与手工计算结果比对,看看 Java 中的数到底采用哪种码表示。
代码
public class BitTest {
public static void main(String[] args) {
int a = 5; // 二进制 000...0101
int b = -5;
System.out.println("a = " + a);
System.out.println("b = " + b);
System.out.println("a & b = " + (a & b));
System.out.println("a | b = " + (a | b));
System.out.println("a ^ b = " + (a ^ b));
System.out.println("~a = " + (~a)); // -6
System.out.println("a << 1 = " + (a << 1)); // 10,左移一位相当于×2
System.out.println("a >> 1 = " + (a >> 1)); // 2,右移一位相当于÷2
// 直接看二进制表示(32 位)
System.out.println("5 的二进制 : " + Integer.toBinaryString(a));
System.out.println("-5 的二进制: " + Integer.toBinaryString(b));
}
}
结论
运行后 -5 的二进制串是 11111111111111111111111111111011—— 高位全是 1,这正是补码的形式:
-
原码:最高位是符号位,其余位表示绝对值;
-
反码:正数不变,负数在原码基础上符号位不变、其余位取反;
-
补码:正数不变,负数在反码基础上 +1。
Java 内部对有符号整数统一使用补码存储。补码的好处是:0 的表示唯一,且加减法可以统一处理(减法变成加上负数的补码),这也是为什么 ~5 = -6、-5 高位全 1。
3. 动脑动手 3:同名变量的 "屏蔽原则"(变量作用域)
题目
Java 变量遵循 "同名变量的屏蔽原则"。请自己编写测试代码,像示例一样有意识地在不同地方定义同名变量,看看输出的到底是哪个值。
代码
public class ScopeTest {
public static void main(String[] args) {
int x = 10;
System.out.println("外层 x = " + x); // 10
{
int x = 20; // 内层块里定义同名变量,屏蔽外层的 x
System.out.println("内层 x = " + x); // 20
}
System.out.println("外层 x = " + x); // 10,内层的 x 已失效
}
}
如果有成员变量和局部变量同名,局部变量同样会 "屏蔽" 成员变量:
class Demo {
int x = 100; // 成员变量
void show() {
int x = 50; // 局部变量,屏蔽成员变量
System.out.println(x); // 50;要用成员变量需写 this.x
}
}
结论
-
每个变量都有它的作用域(一对花括号
{}内),出了作用域变量即失效; -
当内层作用域定义了与外层同名的变量时,内层变量会 ** 屏蔽(shadow)** 外层变量,在内层看到的是就近定义的那个;
-
这就是 "同名变量的屏蔽原则"—— 使用变量时遵循就近匹配。
4. 动手动脑 4:基本类型转换图能得出什么结论?
题目
课件给出了一张类型自动转换方向图(标注了 "无精度损失" 和 "有精度损失" 两条路径)。查阅 Java 各基本数据类型所占位数和数值范围,你能得出什么结论?
各类型位数与范围
| 类型 | 位数 | 范围(约) |
|---|---|---|
| byte | 8 | −128 ~ 127 |
| short | 16 | ±3.2 万 |
| int | 32 | ±21 亿 |
| long | 64 | ±9.2×10¹⁸ |
| float | 32 | ±3.4×10³⁸(有效约 7 位) |
| double | 64 | ±1.8×10³⁰⁸(有效约 15 位) |
| char | 16 | 0 ~ 65535 |
自动转换方向(无精度损失)
byte → short → int → long → float → double
char ↗
结论
-
小范围 → 大范围可以自动转换(如
int → long → double),编译器自动完成,无精度损失; -
大范围 → 小范围必须强制类型转换(如
double → int),可能丢失信息:
double d = 1234567890;
float f = (float) d;
System.out.println(f); // 1.23456794E9,末尾已经不准了
- 一个容易踩的坑:
long是 64 位、float只有 32 位,但long → float是自动转换的 —— 因为 float 表示范围更大,但尾数精度只有 23 位,大整数转成 float 时仍可能损失精度。所以 "自动转换" 不等于 "绝对安全"。
5. 动手实验 5:TestDouble.java 为什么算出 "意外" 结果?
题目
运行示例 TestDouble.java:你看到了什么输出?意外吗?为什么 double 运算得不到 "数学上精确" 的结果?怎样处理?
代码
public class TestDouble {
public static void main(String[] args) {
System.out.println(0.07 + 0.01);
// 你以为是 0.08,实际输出:0.07999999999999999
}
}
原因
计算机用 IEEE 754 浮点数标准存储 float/double,很多十进制小数(如 0.07、0.1)在二进制下是无限循环小数,只能被截断近似存储。因此用 double 做十进制精确运算(如金额)时,结果会有微小误差。
解决:BigDecimal(且要用字符串构造)
import java.math.BigDecimal;
public class TestBigDecimal {
public static void main(String[] args) {
// 正确:用字符串构造,才能保留十进制精度
BigDecimal a = new BigDecimal("0.07");
BigDecimal b = new BigDecimal("0.01");
System.out.println(a.add(b)); // 0.08 精确
// 错误:用 double 构造,传入的 double 本身已经不精确
BigDecimal bad = new BigDecimal(0.07);
System.out.println(bad); // 0.0699999999999999977...
}
}
课件特别提醒:构造
BigDecimal时应传
字符串
而不是
double,否则 double 本身的近似误差会被带进来。
6. 动手动脑 6:"X+Y="+X+Y 的输出是什么?
题目
int X = 100;
int Y = 200;
System.out.println("X+Y=" + X + Y);
System.out.println(X + Y + "=X+Y");
这两行分别输出什么?为什么?
分析与答案
+ 有两种含义:算术加法、字符串连接。只要 + 两边有一个是字符串,它就变成字符串连接;运算符结合性是从左到右。
-
第一行
"X+Y=" + X:字符串 + int →"X+Y=100";再+ Y→"X+Y=100200" -
第二行
X + Y先算(两个 int,算术加)=300;再+ "=X+Y"→"300=X+Y"
所以输出为:
X+Y=100200
300=X+Y
如果希望第一行输出 X+Y=300,应给加法加括号:
System.out.println("X+Y=" + (X + Y)); // X+Y=300
小结
| 知识点 | 对应问题 |
|---|---|
| 枚举类型(引用类型、== 与 equals 等价) | 动手动脑 1 |
| 原码 / 反码 / 补码、位运算(Java 用补码) | 动脑动手 2 |
| 变量作用域、同名变量屏蔽(就近原则) | 动脑动手 3 |
| 基本类型位数、自动 / 强制类型转换 | 动手动脑 4 |
| double 精度误差、BigDecimal 精确计算 | 动手实验 5 |
+ 的双重含义、字符串连接从左到右 |
动手动脑 6 |
这些问题串起来正好覆盖了本讲主线:变量怎么存(数据类型与内存)、数据怎么算(运算符与类型转换)、数据怎么进出程序(输入输出)—— 把这三件事弄清楚,就迈出了 Java 语法基础的第一步。

浙公网安备 33010602011771号