第8章 - 最佳实践与常见坑
嗨,朋友!我是长安。
这是最后一章了!我把这些年在实际项目中踩过的坑和总结的经验,全部整理出来给你。这一章是"避坑指南",建议收藏!
🕳️ Java 程序员最容易踩的 10 个坑
坑1:数组索引从 1 开始
-- ❌ Java 思维
local arr = {"a", "b", "c"}
print(arr[0]) -- nil!不是 "a"!
-- ✅ Lua 正确写法
print(arr[1]) -- a
严重程度:⭐⭐⭐⭐⭐
这是 Java 程序员写 Lua 最最最容易犯的错误。一定要刻在脑子里:Lua 索引从 1 开始!
坑2:不等于是 ~= 不是 !=
-- ❌ Java 思维
if a != b then -- 语法错误!
-- ✅ Lua 正确写法
if a ~= b then
严重程度:⭐⭐⭐⭐⭐
写了十年 != 的 Java 程序员,转到 Lua 几乎每次都会写错。
坑3:0 和空字符串是真值
-- ❌ Java/C 思维
local count = 0
if count then
print("这会执行!") -- 会执行!0 是真值!
end
-- ✅ 正确的判断方式
if count ~= 0 then
print("count 不为零")
end
-- 同样,空字符串也是真值
local s = ""
if s then
print("空字符串也是真的!") -- 会执行!
end
if #s > 0 then
print("字符串不为空") -- 正确判断方式
end
严重程度:⭐⭐⭐⭐⭐
只有 nil 和 false 是假值,其他一切都是真值。这和 Java/JavaScript/Python 都不一样!
坑4:忘记写 local
-- ❌ 忘记 local,变成全局变量
function process()
result = "hello" -- 这是全局变量!会污染全局环境
end
-- ✅ 始终使用 local
function process()
local result = "hello" -- 局部变量,安全
end
严重程度:⭐⭐⭐⭐
Lua 默认是全局变量,和 Java(默认局部)完全相反。养成习惯,每个变量前面都加 local。
坑5:没有 continue
-- ❌ Java 思维
for i = 1, 10 do
if i % 2 == 0 then
continue -- 语法错误!Lua 没有 continue
end
print(i)
end
-- ✅ 方案一:goto(推荐)
for i = 1, 10 do
if i % 2 == 0 then goto continue end
print(i)
::continue::
end
-- ✅ 方案二:if 包裹
for i = 1, 10 do
if i % 2 ~= 0 then
print(i)
end
end
坑6:没有 ++ 和 +=
-- ❌ Java 思维
count++ -- 错误!
count += 1 -- 错误!
-- ✅ Lua 写法
count = count + 1
坑7:字符串拼接用 .. 不是 +
-- ❌ Java 思维
local s = "Hello" + " World" -- 错误!+ 是算术运算
-- ✅ Lua 写法
local s = "Hello" .. " World"
-- 更要小心的是:
print("count: " + 5) -- 报错!"count: " 不能转成数字
print("count: " .. 5) -- 正确:count: 5
print("10" + 5) -- 15(字符串自动转数字做运算)
坑8:/ 是浮点除法
-- ❌ Java 思维(以为是整数除法)
print(10 / 3) -- 3.3333...(不是 3!)
-- ✅ 整数除法用 //
print(10 // 3) -- 3
坑9:逻辑运算符是单词不是符号
-- ❌ Java 思维
if a && b then -- 错误!
if a || b then -- 错误!
if !a then -- 错误!
-- ✅ Lua 写法
if a and b then
if a or b then
if not a then
坑10:Table 是引用类型,赋值不复制
-- ❌ 以为是值复制
local a = {1, 2, 3}
local b = a -- b 和 a 指向同一个 table!
b[1] = 100
print(a[1]) -- 100(a 也被改了!)
-- ✅ 手动复制
local function copyTable(t)
local copy = {}
for k, v in pairs(t) do
copy[k] = v
end
return copy
end
local c = copyTable(a)
Java 程序员应该理解
这其实和 Java 的引用类型一样。List<Integer> b = a; 也是引用赋值。但因为 Lua 没有类型声明,更容易犯这个错。
✅ Lua 编码规范
命名规范
-- 局部变量和函数:小驼峰(camelCase)
local userName = "长安"
local function getUserInfo() end
-- 常量:全大写 + 下划线
local MAX_RETRY = 3
local DEFAULT_TIMEOUT = 5000
-- 模块名:小写
local mymodule = require("mymodule")
-- "类"名:大驼峰(PascalCase)
local Animal = {}
local HttpClient = {}
-- 私有变量/函数:下划线前缀
local _privateVar = "secret"
local function _internalProcess() end
代码风格
-- 1. 始终使用 local
local x = 10
-- 2. 函数定义用 local function
local function foo()
end
-- 3. 字符串优先用双引号
local name = "长安"
-- 4. 表的最后一个元素也加逗号(方便增删)
local config = {
host = "localhost",
port = 8080,
debug = true, -- 最后也加逗号
}
-- 5. 善用提前返回,减少嵌套
local function validate(data)
if not data then return false, "data is nil" end
if not data.name then return false, "name is required" end
if #data.name < 3 then return false, "name too short" end
return true
end
🚀 性能优化技巧
1. 缓存全局函数到局部变量
-- ❌ 每次调用都要查全局表
for i = 1, 1000000 do
math.sin(i) -- 每次查找 math 表 → sin 函数
end
-- ✅ 缓存到局部变量(性能提升 30%+)
local sin = math.sin
for i = 1, 1000000 do
sin(i) -- 直接调用局部变量
end
2. 避免频繁创建临时 Table
-- ❌ 循环中创建大量临时 table
for i = 1, 100000 do
local point = {x = i, y = i * 2} -- 每次都创建新 table
process(point)
end
-- ✅ 复用 table
local point = {}
for i = 1, 100000 do
point.x = i
point.y = i * 2
process(point)
end
3. 字符串拼接用 table.concat
-- ❌ 大量字符串拼接(每次创建新字符串,类似 Java 的 String +)
local result = ""
for i = 1, 10000 do
result = result .. "line " .. i .. "\n"
end
-- ✅ 用 table.concat(类似 Java 的 StringBuilder)
local parts = {}
for i = 1, 10000 do
parts[#parts + 1] = "line " .. i
end
local result = table.concat(parts, "\n")
4. 预分配 Table 大小
-- 如果你知道 table 的大致大小,可以预先创建
-- LuaJIT 的 table.new 可以指定数组和哈希部分的大小
local ffi = require("ffi")
local tab_new = require("table.new")
local t = tab_new(1000, 0) -- 预分配 1000 个数组位置
for i = 1, 1000 do
t[i] = i
end
📋 Java ↔ Lua 速查对照表
| 操作 | Java | Lua |
|---|---|---|
| 打印 | System.out.println() | print() |
| 变量声明 | int x = 1 | local x = 1 |
| 字符串拼接 | + | .. |
| 不等于 | != | ~= |
| 逻辑与 | && | and |
| 逻辑或 | || | or |
| 逻辑非 | ! | not |
| 自增 | i++ | i = i + 1 |
| 整数除法 | 10 / 3 → 3 | 10 // 3 → 3 |
| 数组长度 | arr.length | #arr |
| 字符串长度 | str.length() | #str |
| 空值 | null | nil |
| 注释 | // | -- |
| 多行注释 | /* */ | --[[ ]] |
| 代码块 | { } | do ... end 或 then ... end |
| for 循环 | for(int i=0;i<n;i++) | for i=1,n do |
| for-each | for(var x : list) | for k,v in pairs(t) do |
| 导入 | import xxx | require("xxx") |
| 三元运算符 | a ? b : c | a and b or c |
| 异常处理 | try-catch | pcall/xpcall |
| 创建对象 | new Object() | setmetatable({}, Class) |
| this 引用 | this | self(通过 : 语法) |
💡 长安的终极建议
- 先学好基础语法,再去碰 Redis Lua 和 OpenResty
- 多写多练,不要只看不动手
- 习惯差异,接受 Lua 和 Java 的不同(索引从1开始、~= 等)
- 善用 Table,它是 Lua 的万能工具
- 注意性能,缓存全局函数、用 table.concat 拼接字符串
- 安全第一,在 Java 中嵌入 Lua 一定要做沙箱
- 保持简单,Lua 的设计哲学就是简单,不要把它写成 Java
📝 全教程总结
恭喜你完成了整个 Lua 教程!🎉 让我们回顾一下你学到了什么:
基础部分
- ✅ Lua 简介与环境搭建
- ✅ 变量、数据类型、运算符
- ✅ 流程控制(if/for/while)
- ✅ 函数(多返回值、闭包)
- ✅ Table(数组、字典、对象)
- ✅ 字符串与模式匹配
进阶部分
- ✅ 模块化与包管理
- ✅ 文件 I/O
- ✅ 错误处理(pcall/xpcall)
- ✅ 面向对象编程(元表)
- ✅ SpringBoot + Lua 整合
- ✅ Redis + Lua 脚本实战
- ✅ OpenResty 入门
你已经具备了在实际 Java 项目中使用 Lua 的能力。去实战吧!💪
由 编程指南 提供
