外观
第 4 章 面向对象编程基础:创造你的第一个机器人
阅读目标
- 理解面向对象编程(OOP)的核心思想:将世界看作对象的集合。
- 掌握类与对象的概念,并能创建和使用它们。
- 学习方法的定义、调用,以及成员变量与局部变量的区别。
- 深入理解封装、继承、多态三大特性如何协同工作。
- 掌握构造器的使用方法,为对象进行初始化。
4.1 思想的转变:从“过程”到“对象”
在之前的学习中,我们写的代码像一份菜谱:第一步做什么,第二步做什么……严格按照顺序执行。这叫“面向过程”。
现在,我们要学习一种更强大、更接近现实世界的思想:面向对象编程 (Object-Oriented Programming, OOP)。
想象一下真实的 FTC 赛场,我们不会去想“让左前轮的电机转动3圈,同时让右前轮的电机转动3圈...”。我们的大脑会直接下达一个指令:“机器人,前进!”
这就是面向对象的思想:我们把程序世界看作一个由许多高智能的对象组成的团队。每个对象都有它自己的属性(它是什么样的)和行为(它能做什么)。我们通过指挥这些对象协同工作来完成任务。
核心思想转变
- 面向过程:关心“第一步做什么,第二步做什么”。像一份菜谱,严格按照步骤一步步执行。
- 面向对象:关心“谁能做什么事”。像一个团队,每个成员(对象)有自己的职责,你只需要告诉“谁”去做“什么事”。
4.2 类和对象:从机器人蓝图到实体机器人
在我们正式开始学习代码之前,让我们先玩一个思维游戏。环顾一下你的房间,或者想象一下你熟悉的任何一个地方,比如学校或公园。
你能发现哪些具体的东西?可能有一张桌子、一把椅子、一个水杯,或者窗外的一棵树、一辆汽车。 现在,我们来试着描述其中一样东西,比如“一辆汽车”。它有哪些特征呢?
- 属性 (它是什么样的): 它有颜色(红色)、品牌(比如特斯拉)、四个轮子。
- 行为 (它能做什么): 它能前进、能后退、能鸣笛、能开关车门。
在面向对象的世界里,有两个核心概念:类 (Class) 和 对象 (Object)。
- 类 (Class):是一份蓝图或模板,它定义了一类事物应该具有的通用属性和行为。
一个类就是一份蓝图,这份蓝图主要由三个部分构成:
- 成员变量 (Fields):描述这类事物有什么样的属性。
- 构造器 (Constructors):描述当创造一个新对象时,如何进行初始化。
- 方法 (Methods):描述这类事物能做些什么样的行为。
- 对象 (Object):是根据这个蓝图创造出来的一个具体的、真实的个体。
类与对象的关系
- 类:就像一份“FTC机器人设计图纸”。图纸上画着机器人应该有轮子、有机械臂,并且能前进、能抬手。
- 对象:就是根据这份图纸,在工厂里生产出来的“每一台具体的机器人”。
- 我们可以用同一份图纸(同一个类),造出成千上万台独立工作的机器人(对象)。
┌────────────────────────────┐
│ Robot Class (机器人蓝图) │
│ ┌────────────────────┐ │
│ │ 成员变量 │ │
│ │ - 名字 (name) │ │
│ │ - 电量 (battery) │ │
│ └────────────────────┘ │
│ ┌────────────────────┐ │
│ │ 构造器 │ │
│ │ + Robot(initialName)│ │
│ │ (如何制造一台新机器人)│ │
│ └────────────────────┘ │
│ ┌────────────────────┐ │
│ │ 方法 │ │
│ │ + drive() │ │
│ │ + charge() │ │
│ │ (机器人能做什么) │ │
│ └────────────────────┘ │
└────────────────────────────┘1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
java
/**
* Robot.java
* 定义一个“机器人”类(FTC机器人蓝图)
*/
public class Robot {
// 属性 (成员变量):描述这个机器人有什么
String name;
double power; // 动力,范围 0.0 (停止) 到 1.0 (全速)
// 行为 (方法):描述这个机器人能做什么
public void drive() {
System.out.println(name + "号机器人正在以 " + (power * 100) + "% 的动力前进!");
}
}
/**
* Test.java
* 这里是我们的机器人测试场,我们在这里创造并指挥机器人
*/
public class Test {
public static void main(String[] args) {
// 1. 创建对象:根据 Robot 蓝图,用 `new` 关键字创造一个机器人对象
Robot robot1 = new Robot();
// 2. 设置属性:给这台机器人赋值
robot1.name = "先锋号";
robot1.power = 0.8; // 设置 80% 的动力
// 3. 调用行为:命令它执行任务
robot1.drive(); // 输出:先锋号机器人正在以 80.0% 的动力前进!
// 我们可以根据同一份蓝图,再造一台完全独立的机器人
Robot robot2 = new Robot();
robot2.name = "守护者";
robot2.power = 0.5;
robot2.drive(); // 输出:守护者号机器人正在以 50.0% 的动力前进!
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
4.3 方法:对象的超能力
方法 (Method) 定义了对象能执行的操作或行为,是对象的“技能”。
一个方法由四个核心部分组成:
| 组成部分 | 例子 public void drive(double seconds) | 说明 |
|---|---|---|
| 返回类型 | void | 方法执行完毕后返回的数据类型。void 表示不返回任何值。 |
| 方法名 | drive | 方法的名称,遵循驼峰命名法。 |
| 参数列表 | (double seconds) | 调用方法时需要传入的数据,像技能的“施法材料”。 |
| 方法体 | { ... } | 方法的具体实现代码,用花括号包裹。 |
java
/**
* Robot.java (带方法的版本)
*/
public class Robot {
String name;
/**
* 这是一个没有返回值(void)且有参数的方法
* @param seconds 需要行驶的秒数
*/
public void driveFor(double seconds) {
System.out.println(this.name + " 开始前进,持续 " + seconds + " 秒。");
}
/**
* 这是一个有返回值(String)且没有参数的方法
* @return 返回机器人的状态报告
*/
public String getStatus() {
// 使用 return 关键字返回一个字符串结果
return "机器人 " + this.name + " 当前状态正常。";
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
4.4 成员变量 vs 局部变量
变量根据定义位置的不同,可以分为两种:
| 特性 | 成员变量 (对象的属性) | 局部变量 (方法的临时工) |
|---|---|---|
| 定义位置 | 类的内部,方法的外部 | 方法的内部或参数列表里 |
| 作用范围 | 在整个类中都有效 | 只在定义它的那个方法内有效 |
| 生命周期 | 随对象的创建而生,随对象的销毁而亡 | 随方法的调用而生,随方法的结束而亡 |
| 初始值 | 有默认值 (int为0, boolean为false) | 没有默认值,必须先赋值再使用 |
java
public class Robot {
// name 是成员变量,代表机器人的核心属性,它的生命周期和机器人对象一样长
private String name = "先锋号";
public void driveFor(double seconds) {
// distance 是局部变量,它只是为了完成本次driveFor任务临时使用的“草稿纸”
// 一旦 driveFor 方法执行完毕,distance 就会被销毁
double distance = 0.5 * seconds;
System.out.println(name + " 行驶了 " + distance + " 米。");
}
public void test() {
// System.out.println(distance); // 错误!这里无法访问 driveFor 方法的局部变量
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
为什么要区分成员变量和局部变量?
就像你的“书包”(对象)里放着“课本”(成员变量),而课堂上用的“草稿纸”(局部变量)用完就扔掉。所有东西都塞进书包会让书包变得混乱不堪。各司其职能让程序更清晰、更安全。
4.5 封装:为你的机器人装上智能保护壳
我们的机器人 power 属性可以直接被赋值,如果有人不小心写了 robot1.power = 999;,这在现实中可能会烧毁电机!
封装 (Encapsulation) 就是为了解决这个问题。它就像给机器人的核心部件装上一个保护壳,并提供一些安全的按钮(方法)来操作它。
我们使用 private (私有的) 和 public (公共的) 关键字来实现封装:
private:将属性设为私有,像日记本一样,只有机器人自己能访问。public:将方法设为公共,像操作按钮一样,任何人都可以调用。
java
/**
* Robot.java (封装升级版)
*/
public class Robot {
// 1. 将属性设为私有,外部无法直接访问
private String name;
private double power;
// ... 构造器将在后面介绍 ...
/**
* 2. 提供一个公共的、安全的方法来设定动力 (Setter方法)
*/
public void setPower(double p) {
// 在公共方法里设置“安全门卫”,检查传入值的合法性
if (p >= 0.0 && p <= 1.0) {
this.power = p; // 合法,接受赋值
} else {
System.out.println("错误:动力值 " + p + " 超出范围 (0.0 - 1.0)!");
}
}
/**
* 3. 提供一个公共方法让外部读取动力值 (Getter方法)
*/
public double getPower() {
return this.power;
}
// ... 其他方法 ...
}
/**
* Test.java
*/
public class Test {
public static void main(String[] args) {
Robot myRobot = new Robot();
// myRobot.power = 999; // 编译错误!power 是 private 的,无法直接访问。
// 必须通过我们提供的安全按钮来操作
myRobot.setPower(0.75); // 安全的操作
myRobot.setPower(999); // 危险的操作被安全卫士阻止了
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
`this` 关键字
this 代表“当前这个对象自己”。当方法参数名和成员变量名可能冲突时,this.power 能明确地指向“我这个对象的 power 属性”。
4.6 构造器:机器人的诞生仪式
构造器(Constructor) 是一个特殊的方法,它在对象被创建的那一刻(new的时候)自动被调用,专门用来做“初始化”工作,比如给对象的属性赋上初始值。
- 默认构造器:如果你不写任何构造器,Java 会送你一个看不见的、没有参数的空构造器。
new Robot(); - 有参构造器:我们通常会自定义有参构造器,强制在创建对象时就提供必要的初始信息。
💡 **构造器的特点**
- 方法名必须与类名完全相同。
- 没有返回类型(连
void也没有)。 - 创建对象时(
new的时候)会自动调用,且只调用一次。
每次创建一个新机器人都得手动 pioneer.name = "先锋号" 太麻烦了。我们希望在它“诞生”时就必须给它一个名字。
java
/**
* Robot.java (带有构造器)
*/
public class Robot {
private String name;
private double power;
/**
* 这是 Robot 类的构造器。
* 在 `new Robot(...)` 的时候自动运行。
* @param robotName 这是给新机器人起的名字
*/
public Robot(String robotName) {
this.name = robotName; // 将传入的名字赋给 name 属性
this.power = 0.0; // 默认初始动力为 0
System.out.println("一台名为【" + this.name + "】的机器人被制造出来了!");
}
// ... 其他 getter/setter 方法 ...
}
/**
* Test.java
*/
public class Test {
public static void main(String[] args) {
// 创建对象时,必须在括号里提供构造器需要的参数
Robot robot1 = new Robot("闪电号"); // 调用构造器
// Robot robot2 = new Robot(); // 错误!因为已经定义了有参构造器,默认的无参构造器就失效了。
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
深入构造器:有参数的构造器和无参数的构造器之间有什么关系?
java
public class Robot {
private String name;
private double power;
// 1. 无参数的构造器
public Robot() {
// this(...) 可以调用本类中其他的构造器,必须放在第一行!
// 这里调用了下面的有参构造器,给了一个默认名字
this("无名氏");
}
// 2. 有参数的构造器
public Robot(String name) {
this.name = name;
this.power = 0; // 初始化动力
System.out.println("【" + this.name + "】诞生了!");
}
}
public class Test {
public static void main(String[] args) {
Robot r1 = new Robot(); // 调用无参构造器
Robot r2 = new Robot("探路者"); // 调用有参构造器
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
对象创建全过程
执行 Robot r2 = new Robot("探路者"); 时发生了什么?
- 分配内存:Java 在内存(堆)中为新的
Robot对象分配一块空间。 - 默认初始化:对象的成员变量被赋予默认值(
name为null,power为0.0)。 - 调用构造器:匹配的
Robot(String name)构造器被调用。 - 执行构造器代码:
this.name = "探路者"和this.power = 0被执行,成员变量被赋予了初始值。 - 返回引用:对象的内存地址被返回,并赋值给
r2这个引用变量。
4.7 继承:站在巨人的肩膀上
在FTC中,我们有驱动轮电机、机械臂电机、爪子电机等。它们都是“电机”,都有端口号、都能设置功率。我们可以创建一个通用的 Motor 父类来包含这些共性,然后让具体的电机类来继承 (Inheritance) 它。
- 父类 (Superclass): 包含通用属性和行为的类 (如
Motor)。 - 子类 (Subclass): 继承父类并可以添加自己特性的类 (如
DriveMotor)。
java
/**
* Motor.java (父类)
*/
public class Motor {
// protected 关键字表示这个属性可以被自己和所有子类访问
protected int port;
protected double power;
public Motor(int port) {
this.port = port;
System.out.println("端口 " + port + " 上的电机已初始化。");
}
public void setPower(double power) {
this.power = power;
System.out.println("端口 " + port + " 电机功率设为 " + power);
}
}
/**
* DriveMotor.java (子类)
* extends 关键字表示 DriveMotor 继承自 Motor
*/
public class DriveMotor extends Motor {
/**
* 子类的构造器
* @param port 电机连接的端口号
*/
public DriveMotor(int port) {
// super() 用来调用父类的构造器,必须放在第一行
super(port);
}
// 子类可以有自己的专属方法
public void goForward() {
// setPower 方法是从父类 Motor 继承来的
setPower(1.0);
System.out.println("驱动电机全速前进!");
}
}
/**
* Test.java
*/
public class Test {
public static void main(String[] args) {
// 创建一个驱动电机对象
DriveMotor leftMotor = new DriveMotor(0);
leftMotor.setPower(0.8); // 调用继承来的方法
leftMotor.goForward(); // 调用自己的方法
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
什么是 `extends` 和 `super`?
extends(扩展)是Java中用于声明继承关系的关键字,class A extends B 的意思就是“A类继承自B类”。super(超级)则是一个指向父类的“代词”。super(参数) 用于在子类的构造器里调用父类的构造器,确保父类的部分也被正确初始化。super.方法名() 则可以让你在子类中,调用那个被你不小心“覆盖”掉的父类原始方法。
4.8 多态:一个遥控器控制所有设备
多态 (Polymorphism) 是面向对象最强大的特性。它的意思是“多种形态”。
想象一下,你有一个万能遥控器,上面有个红色的“启动”按钮。当你用它对着电视按,电视就打开了;对着空调按,空调就开始吹风。这个“启动”按钮的行为,作用在不同对象上,产生了不同的结果。这就是多态!
实现多态的三要素:
- 继承:必须有父子类关系。
- 方法重写 (Override):子类要重新实现父类的方法。
什么是重写?
重写就是子类重新实现父类的方法,这样调用时,会调用子类的方法(如果子类没有实现该方法,则调用父类的方法)。
重写和重载的区别在于,重写是子类重新实现父类的方法,而重载是同一个类中,方法名相同,参数列表不同。
- 父类引用指向子类对象:
父类类型 变量 = new 子类对象();
【生活中的例子:谁在叫?】
java
class Animal {
public void makeSound() {
System.out.println("动物发出声音...");
}
}
class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("小狗汪汪叫!");
}
}
class Cat extends Animal {
@Override
public void makeSound() {
System.out.println("小猫喵喵叫!");
}
}
public class Zoo {
// 这个方法可以接收任何“动物”
public static void hearSound(Animal animal) {
animal.makeSound();
}
public static void main(String[] args) {
// 父类引用 指向 子类对象
Animal myDog = new Dog();
Animal myCat = new Cat();
myDog.makeSound(); // 虽然myDog是Animal类型,但它实际指向Dog,所以执行Dog的方法
myCat.makeSound(); // 同理,执行Cat的方法
System.out.println("--- 使用一个方法处理不同对象 ---");
hearSound(new Dog()); // 传入狗对象
hearSound(new Cat()); // 传入猫对象
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
hearSound 方法根本不关心传进来的是Dog还是Cat,它只知道这是个Animal,并且它有makeSound的功能。这就是多态的威力:屏蔽差异,统一接口。
【FTC机器人例子:统一控制所有部件】
我们的机器人上有很多可以“激活”的部件,比如爪子(Claw)、发射器(Launcher)。我们可以定义一个统一的Attachment(附件)父类。
java
/**
* Attachment.java (父类/接口)
* 代表所有可以被“激活”的机器人附件
*/
public class Attachment {
public void activate() {
System.out.println("一个附件被激活了...");
}
}
/**
* Claw.java (子类1:爪子)
* @Override 注解告诉编译器,我们要重写父类的方法
*/
public class Claw extends Attachment {
@Override
public void activate() {
System.out.println("爪子闭合,夹取成功!");
}
}
/**
* Launcher.java (子类2:发射器)
*/
public class Launcher extends Attachment {
@Override
public void activate() {
System.out.println("发射器启动,发射像素!");
}
}
/**
* Test.java (万能遥控器)
*/
public class Test {
public static void main(String[] args) {
// 创建一个爪子和一个发射器
Claw myClaw = new Claw();
Launcher myLauncher = new Launcher();
// --- 这就是多态的体现 ---
// pressButton 方法接收的是通用的 Attachment 类型
// 就像万能遥控器的“启动”按钮
pressButton(myClaw); // 传入爪子对象
pressButton(myLauncher); // 传入发射器对象
}
/**
* 这个方法就是我们的“万能遥控器按钮”
* 它不关心具体是什么附件,只知道它是一个 Attachment,并且能被 activate()
* @param attachment 任何继承了 Attachment 的对象
*/
public static void pressButton(Attachment attachment) {
System.out.print("按下按钮 -> ");
attachment.activate();
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
多态的核心价值
多态实现了接口的统一。pressButton 方法不需要为 Claw 写一个,再为 Launcher 写一个。它只需要和通用的 Attachment 父类打交道,这让我们的程序拥有了极强的可扩展性。未来即使我们新增一个 Drill (钻头) 附件,pressButton 方法也无需任何修改就能直接使用它!
4.7 课堂练习
练习 1:定义一个“手机”类 (巩固封装)
- 创建一个
Phone类。 - 为它添加私有属性:品牌 (
brand, String)、价格 (price, double)。 - 提供一个构造器,在创建对象时可以初始化品牌和价格。
- 为这两个属性提供公共的
getter和setter方法。 - 添加一个公共方法
call(String contact),调用时打印 "正在给 [联系人姓名] 打电话..."。 - 创建一个测试类,创建
Phone对象并测试其所有方法。
参考答案:
java
/**
* Phone.java
*/
public class Phone {
// 1. 私有属性
private String brand;
private double price;
// 2. 构造器
public Phone(String brand, double price) {
this.brand = brand;
this.price = price;
}
// 3. brand 的 getter 和 setter
public String getBrand() {
return this.brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
// 4. price 的 getter 和 setter
public double getPrice() {
return this.price;
}
public void setPrice(double price) {
if (price > 0) { // setter 里可以加一些保护逻辑
this.price = price;
} else {
System.out.println("价格无效!");
}
}
// 5. 公共方法
public void call(String contact) {
System.out.println("正在给 " + contact + " 打电话...");
}
}
/**
* Test.java
*/
public class Test {
public static void main(String[] args) {
// 创建 Phone 对象
Phone myPhone = new Phone("华为", 5999.0);
// 测试 getter
System.out.println("手机品牌:" + myPhone.getBrand() + ", 价格:" + myPhone.getPrice());
// 测试 call 方法
myPhone.call("张三");
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
练习 2:FTC爪子控制器 (综合练习)
- 创建一个
RobotClaw类。 - 添加一个私有属性
private boolean isOpen;来表示爪子的开合状态。 - 创建一个构造器,在创建爪子对象时,让它默认为打开状态 (
isOpen = true;)。 - 创建两个公共方法:
public void open()用来打开爪子,public void close()用来闭合爪子。 - 创建一个公共方法
public void printStatus(),根据isOpen的值,打印 "爪子状态:打开" 或 "爪子状态:关闭"。 - 创建一个测试类,创建
RobotClaw对象,并测试它的所有功能。
参考答案:
java
/**
* RobotClaw.java
*/
public class RobotClaw {
// 1. 私有属性
private boolean isOpen;
// 2. 构造器
public RobotClaw() {
this.isOpen = true; // 默认是打开状态
System.out.println("爪子已创建,初始状态为:打开");
}
// 3. 打开方法
public void open() {
this.isOpen = true;
System.out.println("动作:打开爪子");
}
// 4. 关闭方法
public void close() {
this.isOpen = false;
System.out.println("动作:关闭爪子");
}
// 5. 打印状态方法
public void printStatus() {
if (this.isOpen) {
System.out.println("爪子当前状态:打开");
} else {
System.out.println("爪子当前状态:关闭");
}
}
}
/**
* Test.java
*/
public class Test {
public static void main(String[] args) {
// 创建爪子对象
RobotClaw claw = new RobotClaw();
// 测试初始状态
claw.printStatus();
// 测试关闭和打印状态
claw.close();
claw.printStatus();
// 测试打开和打印状态
claw.open();
claw.printStatus();
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
