Skip to content

switch and try

Himxh edited this page Oct 7, 2026 · 2 revisions

HimDSL 语法教程:try-catch-finally 与 switch-case

HimDSL Tutorial: try-catch-finally and switch-case


一、前言

1. Introduction

本教程面向已经能写出基础 HimDSL 脚本的用户。 This tutorial targets users who can already write basic HimDSL scripts.

我们假设你熟悉变量、函数、if、for、while 的写法。 We assume you are familiar with variables, functions, if, for, and while.

本教程覆盖三部分:try-catch-finally、switch-case、break 与 continue。 This tutorial covers three parts: try-catch-finally, switch-case, and break/continue.


二、try-catch-finally

2. try-catch-finally

2.1 为什么需要它

2.1 Why You Need It

脚本运行中会出现各种意外:玩家掉线、文件不存在、参数非法。 Many unexpected things happen during script execution: players disconnect, files go missing, arguments are invalid.

如果不做处理,任何异常都会中止整个脚本。 Without handling, any exception will abort the entire script.

try-catch-finally 让你可以捕获错误、优雅降级、并确保清理逻辑一定执行。 try-catch-finally lets you catch errors, degrade gracefully, and guarantee cleanup logic always runs.

2.2 基本语法

2.2 Basic Syntax

try 块放可能出错的代码。 The try block holds code that may fail.

catch 块在捕获到异常时执行。 The catch block runs when an exception is caught.

finally 块无论是否出错都会执行。 The finally block always runs regardless of errors.

2.3 catch 的两种写法

2.3 Two Forms of catch

形式一:catch (e) —— 捕获一切异常。 Form 1: catch (e) — catches any exception.

形式二:catch (ExceptionType e) —— 只捕获指定类型。 Form 2: catch (ExceptionType e) — catches only the specified type.

类型匹配遵循 Java 规则,父类能捕获子类。 Type matching follows Java rules: a parent type catches its subclasses.

2.4 异常对象暴露的字段

2.4 Fields Exposed by the Exception Object

e.message:异常的消息文本,永不为 null。 e.message: the exception message text, never null.

e.type:异常的简单类名,例如 RuntimeException。 e.type: the exception's simple class name, e.g. RuntimeException.

e.fulltype:异常的全限定名。 e.fulltype: the exception's fully qualified name.

e.stacktrace:完整的堆栈字符串。 e.stacktrace: the complete stack trace string.

e.printStackTrace():把堆栈打印到服务器日志。 e.printStackTrace(): prints the stack trace to the server log.

2.5 finally 的用途

2.5 The Purpose of finally

finally 用于释放资源,比如关闭文件句柄。 finally is used to release resources, such as closing file handles.

即使 try 里 return 了,finally 依然会先执行。 Even if try returns, finally still executes first.

void readConfig() {
    int fh = 0;
    try {
        fh = fopen("wwwwwwwwwconfig.json", "r");
        var content = fread(fh);
    } catch (e) {
        ply.sendMessage("读取失败: " + e.message);
    } finally {
        if (fh > 0) {
            fclose(fh);
        }
    }
}

2.6 finally 覆盖 return 的规则

2.6 Rule: finally Overrides return

如果 finally 里有 return,它会覆盖 try 的返回值。 If finally contains a return, it overrides the try's return value.

这是 Java 的语义,HimDSL 完全照抄。 This is Java semantics, and HimDSL follows it exactly.

int tricky() {
    try {
        return 1;
    } finally {
        return 2;
    }
}

// 调用 tricky() 得到 2,不是 1
// Calling tricky() yields 2, not 1

三、try-catch 的坑点

3. Pitfalls of try-catch

3.1 坑点一:权限异常捕获不到

3.1 Pitfall 1: Permission Exceptions Cannot Be Caught

当脚本使用当前级别禁止的能力时,会抛出 PermissionDeniedException。 When a script uses a capability forbidden at its level, a PermissionDeniedException is thrown.

这个异常无法被任何 catch 捕获。 This exception cannot be caught by any catch.

finally 会正常执行,但 catch 里的代码不会运行。 finally still runs, but the catch body does not.

try {
    runcmd("CONSOLE", "op someone");   // sandbox 下会被拒
} catch (e) {
    // 这里的代码永远不会执行
    // Code here never runs
} finally {
    // 这里的代码依然会执行
    // Code here still runs
}

这么设计是为了防止脚本吞掉权限日志。 This design prevents scripts from swallowing permission logs.

3.2 坑点二:catch 里再抛异常会覆盖原异常

3.2 Pitfall 2: Rethrowing Inside catch Overrides the Original

如果 catch 块自己抛异常,原异常会丢失。 If the catch block itself throws, the original exception is lost.

上层看到的将是 catch 里抛出的那个。 The upper layer sees the exception thrown inside catch.

建议在 catch 中避免调用可能失败的操作。 Avoid calling failure-prone operations inside catch.

3.3 坑点三:catch 顺序很关键

3.3 Pitfall 3: catch Order Matters

catch 从上到下匹配,第一个命中即执行。 catch matches top-to-bottom; the first match wins.

子类异常必须写在父类前面。 Subclass exceptions must be written before parent exceptions.

写反了会导致子类的 catch 永远不生效。 If reversed, the subclass catch will never trigger.

3.4 坑点四:finally 里的 return 会吞掉异常

3.4 Pitfall 4: return in finally Swallows Exceptions

如果 finally 里写了 return,即使 try 抛了异常也会被吞掉。 If finally contains a return, any exception from try is swallowed.

这通常不是你想要的行为,请谨慎使用。 This is usually not what you want; use it with caution.


四、switch-case-default

4. switch-case-default

4.1 为什么需要它

4.1 Why You Need It

当你需要根据一个值分支到多种情况时,switch 比连续 if-else 更清晰。 When you need to branch on a value into multiple cases, switch is clearer than a chain of if-else.

它也更接近配置驱动的思维方式。 It also matches a configuration-driven mindset better.

4.2 基本语法

4.2 Basic Syntax

switch 后面的括号里放被比较的值。 The parentheses after switch hold the value being compared.

case 是具体的匹配值,default 是兜底分支。 case holds a specific match value; default is the fallback branch.

void handleItem(string type) {
    player p = @p;
    switch (type) {
        case "apple":
            p.sendMessage("你拿到了苹果");
            break;
        case "diamond":
            p.sendMessage("闪闪发光");
            break;
        default:
            p.sendMessage("普通物品: " + type);
    }
}

4.3 fall-through:故意贯穿

4.3 fall-through: Intentional Passing

如果 case 后不写 break,会继续执行下一个 case。 If you omit break after a case, execution continues into the next case.

这叫做 fall-through,是 Java 的经典语义。 This is called fall-through, classic Java semantics.

多个 case 共享同一段代码时非常有用。 It is very useful when several cases share the same code.

void combo(int code) {
    int bonus = 0;
    switch (code) {
        case 1:
        case 2:
            bonus = bonus + 10;
        case 3:
            bonus = bonus + 5;
            break;
        case 4:
            bonus = 100;
            break;
    }
    Bukkit.getServer().getLogger().info("奖励: " + bonus);
}

code 为 1 或 2 时会先加 10 再加 5,最终 15。 When code is 1 or 2, it adds 10 then 5, ending at 15.

code 为 3 时只加 5。 When code is 3, it adds only 5.

4.4 case 支持的类型

4.4 Types Supported by case

数字:整数和浮点数都可以比较。 Numbers: both integers and floats can be compared.

字符串:直接比较文本内容。 Strings: compares text content directly.

枚举:可以和枚举常量匹配,也可以和字符串比较。 Enums: can match enum constants, or compare with strings.


五、switch-case 的坑点

5. Pitfalls of switch-case

5.1 坑点一:忘写 break 会贯穿

5.1 Pitfall 1: Forgetting break Causes Fall-through

这是最常见的错误。 This is the most common mistake.

每个 case 后面如果没有 break,会继续往下执行。 Without break after each case, execution flows downward.

有时候这是故意的,但更多时候是 bug。 Sometimes intentional, but more often a bug.

5.2 坑点二:case 表达式只求值一次

5.2 Pitfall 2: case Expressions Evaluate Only Once

case 后面可以写任意表达式,不只是常量。 Any expression can follow case, not just constants.

但匹配扫描阶段只求值一次,命中后不再计算后续 case。 But evaluation happens only during the match scan; after a match, later case expressions are not evaluated.

这意味着 case 里的函数调用不会反复执行。 This means function calls inside case won't run repeatedly.

5.3 坑点三:default 可以不放最后

5.3 Pitfall 3: default Need Not Be Last

default 的位置不影响匹配逻辑。 The position of default does not affect matching logic.

匹配失败时会跳到 default 的位置,然后从上往下执行。 On match failure, execution jumps to default's position, then runs downward.

如果 default 不在最后,可能会 fall-through 到它后面的 case。 If default is not last, it may fall through to the case after it.

5.4 坑点四:switch 里 continue 会穿透到外层循环

5.4 Pitfall 4: continue inside switch Reaches the Enclosing Loop

switch 本身不是循环,continue 会往上找最近的循环。 switch itself is not a loop; continue looks upward for the nearest loop.

如果没有外层循环,continue 什么也不做。 If there is no enclosing loop, continue does nothing.

for (int i = 0; i < 10; i = i + 1) {
    switch (i) {
        case 5:
            continue;   // 跳到 for 的下一轮
        default:
            ply.sendMessage("i = " + i);
    }
}

5.5 坑点五:switch 里不能像 Java 14 那样用箭头语法

5.5 Pitfall 5: No Arrow Syntax Like Java 14

本语言不支持 case 1 -> ... 这种写法。 This language does not support case 1 -> ....

也不支持 yield。 Nor does it support yield.

请老老实实用传统的 case ... : ... break; 结构。 Please use the classic case ... : ... break; structure.


六、break 与 continue

6. break and continue

6.1 语义说明

6.1 Semantics

break 跳出最近的循环或 switch。 break exits the nearest loop or switch.

continue 跳到最近的循环的下一轮。 continue jumps to the next iteration of the nearest loop.

在 switch 里使用 break,只会跳出 switch 本身。 Using break inside switch exits only the switch itself.

6.2 嵌套 switch 中的 break

6.2 break in Nested switch

void nested(int a, int b) {
    switch (a) {
        case 1:
            switch (b) {
                case 10:
                    ply.sendMessage("内部命中");
                    break;   // 只跳出内层
            }
            ply.sendMessage("继续外层");
            break;           // 跳出外层
    }
}

内层 break 不会穿透到外层。 The inner break does not leak to the outer switch.


八、最佳实践

8. Best Practices

在可能失败的操作外层包 try。 Wrap potentially failing operations in try.

把资源释放放在 finally。 Put resource release in finally.

switch 每个 case 后要么 break,要么明确注释说明贯穿是故意的。 After each case in switch, either break or add a comment explaining the intentional fall-through.

catch 里不要忽略异常,至少记录日志。 Do not silently ignore exceptions in catch; at least log them.

嵌套太深的 try 或 switch 应该拆成函数。 Deeply nested try or switch blocks should be split into functions.


九、常见问题

9. FAQ

问:try 里可以有 return 吗? Q: Can try contain a return?

答:可以,finally 会先执行,然后才是 return。 A: Yes; finally runs first, then the return happens.

问:catch 里可以有 return 吗? Q: Can catch contain a return?

答:可以,finally 依然会执行。 A: Yes; finally still runs.

问:没有 catch 只有 finally 合法吗? Q: Is it legal to have finally without catch?

答:合法,等价于 try { ... } finally { ... }。 A: Yes, equivalent to try { ... } finally { ... }.

问:switch 里能声明变量吗? Q: Can I declare variables inside switch?

答:可以,作用域从声明处到 switch 结束。 A: Yes; the scope runs from the declaration point to the end of the switch.

问:能否 break 出多层循环? Q: Can I break out of multiple loops at once?

答:不支持带标签的 break,需要用一个标志变量模拟。 A: Labeled break is not supported; simulate it with a flag variable.


十、总结

10. Summary

try-catch-finally 让你的脚本能优雅地处理错误。 try-catch-finally lets your scripts handle errors gracefully.

switch-case 让多分支逻辑更清晰。 switch-case makes multi-branch logic clearer.

break 和 continue 控制循环与分支的流向。 break and continue control the flow of loops and branches.

把它们组合起来,可以写出健壮、可维护的脚本。 Combining them lets you write robust, maintainable scripts.

祝你编写愉快。 Happy scripting.

HimDSL 文档 / Documentation

入门 / Getting Started

语言基础 / Language Basics

控制流 / Control Flow

函数 / Functions

数据结构 / Data Structures

自定义类型 / Custom Types

Minecraft 集成 / Minecraft Integration

事件与调度 / Events & Scheduling

GUI 系统 / GUI System

内置函数参考 / Built-ins Reference

进阶 / Advanced

扩展 API / Extension API

你真的需要 API 吗?/ Do you really need the API?

Clone this wiki locally