Java 面向对象 · 交互课程

Java 语言基础

Java 语言基础

模块 2 · 课次 04 + 实验1 / 教材第 2 章(2.1 语法基础、2.2 运算符与表达式、2.3 流程控制、2.4 数组、2.5 字符串) 目标:掌握标识符与关键字;数据类型、变量与常量、类型转换与作用域;运算符与优先级;分支/循环与 break/continue/return;一维与多维数组;String/StringBuffer;并初步建立「程序 = 数据结构 + 算法」的抽象认识,为第 3 章面向对象铺垫。

〇、引入:程序 = 数据结构 + 算法

现实世界程序世界
具体事物的属性及行为对象
同类事物共同特性(属性、行为)的抽象类

贯穿示例:素材/Erase-JAVA/ 的 com.zmofun.erase.protogame.ErasGame 类,就是把「擦除小游戏」这一类事物(属性:棋盘、分数;行为:擦除、计分、过关)抽象成类,再实例化为对象——下文会持续引用它的真实代码。

一、2.1 语法基础

标识符与关键字

数据类型

Java 是强类型语言:每个变量都有确定的类型;各类型占用固定内存长度,与具体软硬件平台无关;每种类型有缺省值,变量取值总是确定的。

类别类型大小默认值(成员变量)示例
字符型char2 字节'\u0000''A'、'中'
布尔型boolean1 位falsetrue / false
整型byte1 字节0(byte) 127
整型short2 字节0(short) 30000
整型int4 字节0100(整型字面量默认)
整型long8 字节0L9223372036854775807L
浮点型float4 字节0.0f3.14f
浮点型double8 字节0.0d3.14159(小数默认)
引用型String 等对象头+内容null"hello"

类型转换:

int a = 10;            // int 字面量
long b = 10L;          // long 必须加 L
float f = 3.14f;       // float 必须加 f
double d = 3.14;       // double 默认
byte y = (byte) a;     // int → byte 需强转(可能溢出)

交互实验:数据类型速查表(DataTypeTable)

点行查看每个基本类型的范围与默认值;再用下面的「转换检查器」判断两个类型之间能否自动扩宽。

类别类型大小默认值(成员变量)范围示例
字符型char2 字节'\u0000'\u0000 (0) ~ \uFFFF (65535)'A'
布尔型boolean1 字节false—true
整型byte1 字节0-128 ~ 127(byte) 127
整型short2 字节0-32768 ~ 32767(short) 30000
整型int4 字节0-2147483648 ~ 2147483647100
整型long8 字节0L-9223372036854775808L ~ 9223372036854775807L9223372036854775807L
浮点型float4 字节0.0f≈ ±1.4E-45 ~ ≈ ±3.4E383.14f
浮点型double8 字节0.0d≈ ±4.9E-324 ~ ≈ ±1.7E3083.14159

int:整型,4 字节;默认值 0;范围 -2147483648 ~ 2147483647

→

✅ byte → int 自动扩宽:范围变大,安全,编译器自动完成。

自动扩宽链:byte < short < int < long < float < double(char 可扩到 int/long/float/double);反向必须显式强转。

变量与常量、作用域

作用域可见范围例子
类作用域整个类体(含所有方法)成员变量 score
方法作用域单个方法内参数、方法内局部变量
语句块作用域{ } 块内for (int i = 0; ...) 的 i
class Game {
    static final int MAX_ROWS = 18;   // 类级常量:所有对象共享、不可改
    int score = 0;                    // 成员变量,默认 0

    void add(int n) {
        int local = n;                // 局部变量,必须初始化
        score += local;
    }
}

二、2.2 运算符与表达式

类别运算符说明
算术+ - * / %% 取余;结果类型随操作数提升
单目+ - ~ ! ++ --重点:++/-- 前缀与后缀的区别(见下)
关系&gt; &lt; &gt;= &lt;= == !=结果为 boolean
逻辑&&、||、!重点:&& 与 &、|| 与 | 的区别(短路求值)
位运算&、|、^、~、&lt;&lt;、&gt;&gt;以二进制位为单位,操作数与结果为整型;移位运算是难点
三目cond ? a : b条件表达式
复合赋值+= -= *= /= %= &= |= ^= &lt;&lt;= &gt;&gt;=a += 1 等价 a = a + 1
instanceofobj instanceof Type判断对象是否为某类型的实例(第 3 章常用)

++/-- 前缀 vs 后缀:

int i = 5;
int a = i++;    // 先使用(a=5),再自增 → i=6
int b = ++i;    // 先自增(i=8),再使用 → b=8

短路求值(&& vs &):

// && 短路:左边为 false,右边根本不求值
if (a > 0 && b(a) > 1) { ... }   // a<=0 时 b(a) 不会被调用
// & 不短路:两边都求值(位运算语义)

移位运算符:<< 左移补 0(×2),>> 右移带符号(÷2),>>> 无符号右移。

优先级从高到低(同层默认左结合,拿不准就加括号):

级别运算符结合性
1( )、前缀 ++ -- ! ~、后缀 ++ --右 / 特殊
2* / %左结合
3+ -左结合
4&lt;&lt; &gt;&gt; &amp; ^ |左结合
5&gt; &lt; &gt;= &lt;= instanceof左结合
6== !=左结合
7&&左结合(短路)
8||左结合(短路)
9? :右结合
10= += -= *= /= 等赋值右结合

注释:// 单行;/* … */ 与 /** … */(文档注释)多行。

交互实验:表达式求值台(OperatorLab)

输入算术表达式,实时看 Java 的求值顺序:* / % 先于 + -,括号最高。 点击下面的「求值步骤」可高亮对应子表达式。

87+83-65*2
87+83-65*2= 40

注意 Java 语义:两个整数做 / 向零截断(7/2 = 3),% 符号随被除数(-7 % 3 = -1)。

三、2.3 流程控制语句

分支:

if (score >= 90) {
    System.out.println("优秀");
} else if (score >= 60) {
    System.out.println("及格");
} else {
    System.out.println("不及格");
}

switch (day) {               // int / char / String 都可作 case
    case 1: case 2: case 3: case 4: case 5:
        System.out.println("工作日"); break;   // 漏 break 会「贯穿」到下一 case
    default:
        System.out.println("周末");
}

循环:

int i = 0;
while (i < 5) {              // 先判断后执行:可能一次都不跑
    i++;
}

do {                          // 先执行后判断:至少跑一次
    i--;
} while (i > 0);

for (int j = 0; j < 10; j++) {   // 初始化; 条件; 步进
    sum += j;
}

// 增强 for(foreach):遍历数组/集合,不用下标
for (String s : names) {
    System.out.println(s);
}

三种循环可互相改写;选型原则:次数已知用 for,未知次数用 while,至少要执行一次用 do-while。

跳转语句:

语句作用
break跳出当前循环 / switch 分支块
continue结束本轮循环,进入下一轮
return从方法返回值(同时结束方法)
for (int n = 2; n < 20; n++) {
    if (n % 2 == 0) continue;   // 偶数跳过
    if (n > 15) break;          // 超过 15 直接结束循环
    System.out.println(n);      // 只打印 3,5,7,...,13
}

交互实验:控制流步进器(ControlFlowVisualizer)

逐行观察 if / while / for 的执行路径:哪条分支被进入、循环每轮的条件判断、break / continue 如何改变走向。

score =
score = 85;
if (score >= 90) {
System.out.println("优秀");
} else if (score >= 60) {
System.out.println("及格");
} else {
System.out.println("不及格");
第 0 / 5 步

点「▶ 下一步」开始执行。

变量

(尚未执行)

输出

注意:条件判断每轮都会执行一次——循环退出那一步的条件结果是 false,这就是 while/for 的终止检查。

四、2.4 数组

数组是同类型元素的定长序列:声明与实例化分离,索引从 0 开始,合法下标 0 ~ length-1,越界抛 ArrayIndexOutOfBoundsException。

// 一维:声明 + 实例化
String[] names = new String[5];      // 5 个 null
names[0] = "Alice";

// 二维:new String[5][5] —— 5 行 × 5 列,每行是一个 String[]
String[][] grid = new String[5][5];
grid[2][3] = "X";

// 声明并初始化(长度由元素个数决定)
int[] keya = { 87, 83, 65, 68 };     // Erase Game.java:W/S/A/D 的键码

贯穿示例:单机版 ERASE 的 Game.java 用两个数组记住键盘映射——int[] keya = { 87, 83, 65, 68 };(W/S/A/D)与 int[] keyb = { 38, 40, 37, 39 };(方向键)。棋盘本身就是二维数组:行×列每个格子存一个方块编号,这正是「数据结构」的最直观形态。

交互实验:数组内存地图(ArrayMemoryMap)

数组在堆上是连续空间:a[i] 的地址 = 起始地址 + i × 元素大小。点单元格看地址公式;引用数组里每个格子装的是引用,对象另在堆上。

int[] a = {10, 20, 30};

教学简化:int 4 字节、char 2 字节、引用 8 字节;Erase 的 keya 存的是 W/S/A/D 四个字符,每个占 2 字节。

五、2.5 字符串

String 不可变:任何「修改」方法都返回新串,原串不变——这是学生最容易踩的坑(s.toUpperCase() 不接返回值就等于没改)。

方法作用
length()字符个数
charAt(i) / substring(a, b)取字符 / 子串
startsWith(s) / endsWith(s)前缀/后缀判断
indexOf(s) / lastIndexOf(s)首次/末次出现位置(找不到 -1)
equals(o) / equalsIgnoreCase(o)内容比较(不要用 ==)
compareTo(o)字典序比较
toLowerCase() / toUpperCase()大小写转换(返回新串)
replace(a, b) / replaceAll(r, s)替换(字面/正则)
split(d)按分隔符切成 String[]

频繁拼接用 StringBuffer(可变,避免每次 new 一个 String):append()、insert()、setCharAt()。

String s = "hello,world";
System.out.println(s.split(","));      // [hello, world]
System.out.println(s.equals("Hello")); // false(区分大小写)

StringBuffer sb = new StringBuffer();
sb.append("score=").append(100);       // 反复追加不产生中间串

六、方法:参数传递

void swap(int a, int b) {   // 基本类型:交换无效
    int t = a; a = b; b = t;
}
int x = 1, y = 2;
swap(x, y);                 // x、y 仍是 1、2

class Box { int v; }
void setV(Box box, int n) { // 引用类型:能改对象内容
    box.v = n;
}
Box b = new Box();
setV(b, 99);                // b.v 变成 99 —— 因为传的是引用地址的拷贝

贯穿示例:Erase 里 Game、GameBox、NewShape 各管一块——Game 持有棋盘数组与分数(数据结构),GameBox 负责画格子,键码数组驱动移动逻辑(算法)。结构化语法到 OOP 的过渡,就是「把变量、方法、控制结构装进类」。

思考题

  1. 结合 Erase-JAVA 项目的 ErasGame 类,用自己的话说明「类是对同类事物共同特性的抽象」这句话的含义;
  2. String 为什么设计成不可变?如果它是可变的,s.toUpperCase() 这类方法会遇到什么问题?(提示:多个变量可能共享同一个串)
  3. 用 for、while、do-while 三种方式分别写一个「找出 2~20 之间所有奇数」的循环,比较哪种写法最清晰;
  4. 以 ERASE 棋盘为例说明「数据结构是对现实世界的抽象」:二维数组的每个格子对应游戏里的什么?分数、关卡又该用什么数据组织?

学习小测验

学习小测验

Q1

Java 中整型常量(字面量)的默认类型是?

Q2

下列哪个不是 Java 的流程控制语句?

Q3

执行 final double PI = 3.14; 之后再写 PI = 3.14159; 会发生什么?

Q4

下列哪种循环「至少会执行一次」循环体?

Q5

String[] s = new String[5]; 下列哪个索引会抛 ArrayIndexOutOfBoundsException?

Q6

a = 0 时,表达式 a > 0 && b() 中方法 b() 会被调用几次?

Q7

方法 void f(int n) 内部执行 n = n * 2;,调用后实参的值会改变吗?