Lua快速入门教程

文章目录

  • 1、Linux安装Lua
  • 2、语法练习
    • 2.1、变量
    • 2.2、循环
    • 2.3、函数
    • 2.4、数组
    • 2.5、迭代器
    • 2.6、Table操作
    • 2.7、Lua 模块与包
    • 2.8、加载机制
    • 2.9、Lua 元表(Metatable)
  • 3、Lua 协同程序(coroutine)
  • 4、文件IO操作
    • 4.1、简单模式
    • 4.2、完全模式
  • 5、错误处理

内容来源菜鸟教程,主要列举一些重要部分和c语言有歧义的部分
https://www.runoob.com/lua/lua-file-io.html

1、Linux安装Lua

1、下载
wget http://www.lua.org/ftp/lua-5.3.0.tar.gz2、解压
tar zxf lua-5.3.0.tar.gz3、进入目录
cd lua-5.3.04、编译
make linux test5、拷贝到系统位置
make install

2、测试使用
vi test.lua

print(“hello world”)

保存内容

运行lua脚本
lua test.lua

3、首行加入,直接可以运行
#!/usr/local/bin/lua

2、语法练习

1、… 拼接字符串,可以利用nil来删除表中的值

tal = {ke1 = "val1",ke2 = "val2","val3"}
for k,v in pairs(tal) doprint(k .. "-" .. v)
endtal.ke1 = nilfor k,v in pairs(tal) doprint(k,v)
end

2、lua中数字字符串会默认转换成数字进行操作符运算

tal = {}tal["key"] = "val"
key = 10
tal[key]  = 10
tal[key] = tal[key] + 22
for k,y in pairs(tal) doprint(k,y)
end

3、 lua中表中数组下标是从1开始

-- lua中表中数组下标是从1开始
tal = {"key1","key2","key3"}
for key,val in pairs(tal) doprint("key",key)
end

4、在 Lua 中,函数是被看作是"第一类值(First-Class Value)",函数可以存在变量里

function fun(n)if n == 0 thenreturn 1elsereturn n*fun(n-1)end
end
print(fun(5))
fun2 = fun
print(fun2(5))

5、function 可以以匿名函数(anonymous function)的方式通过参数传递:

function fun2(tal,fun)for key,val in pairs(tal) doprint(fun(key,val))end
endtal = {key1 = "val1",key2 = "val2",key3 = "val3"}
fun2(tal,
function(key,val)return key.."="..val
end
)

2.1、变量

1、Lua 变量有三种类型:全局变量、局部变量、表中的域。

a = 5       -- 全局变量
local b = 5 -- 局部变量function joke()c = 5   -- 全局变量local d = 6 -- 局部变量
endjoke()
print(c,d)  --> 5 nildo local a = 6 -- 局部变量b = 6   -- 对局部变量重新赋值print(a,b); --> 6 6
endprint(a,b)  --> 5 6

2、赋值语句

a,b = "hello",111
print(a,b)-- 可以利用这种方式对变量进行交换操作
a,b = 111,222
print(a,b)a,b = b,a
print(a,b)

3、当变量个数和值的个数不一致时,Lua会一直以变量个数为基础采取以下策略:
a. 变量个数 > 值的个数 按变量个数补足nil
b. 变量个数 < 值的个数 多余的值会被忽略

a, b, c = 0, 1
print(a,b,c)    --> 0   1   nila, b = a+1, b+1, b+2    -- value of b+2 is ignored
print(a,b)          --> 1   2a, b, c = 0
print(a,b,c)        --> 0   nil   nil

2.2、循环

这里只是列举出与c语言不一样的地方

重复执行循环,直到 指定的条件为真时为止

--[ 变量定义 --]
a = 10
--[ 执行循环 --]
repeatprint("a的值为:", a)a = a + 1
until( a > 15 )

Lua 语言中的 goto 语句允许将控制流程无条件地转到被标记的语句处。

local i = 0
::s1:: doprint(i)i = i+1
endif i == 1 thengoto s1
end

2.3、函数

可变参数

function add(...)local s = 0  for i, v in ipairs{...} do   --> {...} 表示一个由所有变长参数构成的数组  s = s + vendreturn s
end
print(add(1,2,3,4,5))

Lua 中我们可以将函数作为参数传递给函数

myprint = function (param)print("##",param,"##")
endfunction add(x,y,myp)myp(x+y)
endadd(10,20,myprint)

将可变参数赋值给一个变量
我们也可以通过 select(“#”,…) 来获取可变参数的数量

function average(...)local arg = {...} -- arg为一个表,可变参local result = 0for key,val in ipairs(arg) doresult = result + valendprint("总长度为:",#arg)return result/#arg
endprint(average(1,2,3,4,56))

2.4、数组

多维数组
多维数组即数组中包含数组或一维数组的索引键对应一个数组。
以下是一个三行三列的阵列多维数组:

-- 初始化数组
array = {}
for i=1,3 doarray[i] = {}for j=1,3 doarray[i][j] = i*jend
end-- 访问数组
for i=1,3 dofor j=1,3 doprint(array[i][j])end
end

不同索引键的三行三列阵列多维数组:

-- 初始化数组
array = {}
maxRows = 3
maxColumns = 3
for row=1,maxRows dofor col=1,maxColumns doarray[row*maxColumns +col] = row*colend
end-- 访问数组
for row=1,maxRows dofor col=1,maxColumns doprint(array[row*maxColumns +col])end
end

2.5、迭代器

多状态的迭代器

local tab = {"c","c++"}function element(collection)local index = 0local count = #collection-- 闭包函数return function ()index = index +1if(index<=count) thenreturn collection[index]endend
endfor e in element(tab) doprint(e)
end

2.6、Table操作

Table 连接

fruits = {"banana","orange","apple"}
-- 返回 table 连接后的字符串
print("连接后的字符串 ",table.concat(fruits))-- 指定连接字符
print("连接后的字符串 ",table.concat(fruits,", "))-- 指定索引来连接 table
print("连接后的字符串 ",table.concat(fruits,", ", 2,3))

连接后的字符串 bananaorangeapple
连接后的字符串 banana, orange, apple
连接后的字符串 orange, apple

插入和移除

fruits = {"banana","orange","apple"}-- 在末尾插入
table.insert(fruits,"mango")
print("索引为 4 的元素为 ",fruits[4])-- 在索引为 2 的键处插入
table.insert(fruits,2,"grapes")
print("索引为 2 的元素为 ",fruits[2])print("最后一个元素为 ",fruits[5])
table.remove(fruits)
print("移除后最后一个元素为 ",fruits[5])

索引为 4 的元素为 mango
索引为 2 的元素为 grapes
最后一个元素为 mango
移除后最后一个元素为 nil

Table 排序

fruits = {"banana","orange","apple","grapes"}
print("排序前")
for k,v in ipairs(fruits) doprint(k,v)
endtable.sort(fruits)
print("排序后")
for k,v in ipairs(fruits) doprint(k,v)
end

排序前
1 banana
2 orange
3 apple
4 grapes
排序后
1 apple
2 banana
3 grapes
4 orange

2.7、Lua 模块与包

模块类似一个封装库,从5.1开始,lua中加入了标准的模块管理机制,可以将一个公共的代码,放入到一个文件中,以API的形式提供,lua使用,变量,函数组成的table表,创建一个模块很简单,就是创建一个table表,将需要导入的数据放入到table表中,然后将其返回

module = {}module.constant = "这是一个常量"-- 定义一个函数function module.func1()io.write("这是一个函数")
endlocal function func2()print("这是一个私有函数")
endfunction module.func3()func2()
endreturn module

使用模块
require 函数
Lua提供了一个名为require的函数用来加载模块。要加载一个模块,只需要简单地调用就可以了。例如:
require(“<模块名>”)或者require “<模块名>”

-- 调用模块
-- 执行 require 后会返回一个由模块常量或函数组成的 table,并且还会定义一个包含该 table 的全局变量。
require("module")print(module.constant)
module.func3()

或者给加载的模块定义一个别名变量,方便调用:

-- test_module2.lua 文件
-- module 模块为上文提到到 module.lua
-- 别名变量 m
local m = require("module")print(m.constant)m.func3()

2.8、加载机制

对于自定义的模块,模块文件不是放在哪个文件目录都行,函数 require 有它自己的文件路径加载策略,它会尝试从 Lua 文件或 C 程序库中加载模块。
require 用于搜索 Lua 文件的路径是存放在全局变量 package.path 中,当 Lua 启动后,会以环境变量 LUA_PATH 的值来初始这个环境变量。如果没有找到该环境变量,则使用一个编译时定义的默认路径来初始化。
当然,如果没有 LUA_PATH 这个环境变量,也可以自定义设置,在当前用户根目录下打开 .profile 文件(没有则创建,打开 .bashrc 文件也可以),例如把 “~/lua/” 路径加入 LUA_PATH 环境变量里:

#LUA_PATH
export LUA_PATH="~/lua/?.lua;;"

文件路径以 “;” 号分隔,最后的 2 个 “;;” 表示新加的路径后面加上原来的默认路径。

接着,更新环境变量参数,使之立即生效。

source ~/.profile

这时假设 package.path 的值是:

/Users/dengjoe/lua/?.lua;./?.lua;/usr/local/share/lua/5.1/?.lua;/usr/local/share/lua/5.1/?/init.lua;/usr/local/lib/lua/5.1/?.lua;/usr/local/lib/lua/5.1/?/init.lua

那么调用 require(“module”) 时就会尝试打开以下文件目录去搜索目标。

/Users/dengjoe/lua/module.lua;
./module.lua
/usr/local/share/lua/5.1/module.lua
/usr/local/share/lua/5.1/module/init.lua
/usr/local/lib/lua/5.1/module.lua
/usr/local/lib/lua/5.1/module/init.lua

如果找过目标文件,则会调用 package.loadfile 来加载模块。否则,就会去找 C 程序库。

搜索的文件路径是从全局变量 package.cpath 获取,而这个变量则是通过环境变量 LUA_CPATH 来初始。

搜索的策略跟上面的一样,只不过现在换成搜索的是 so 或 dll 类型的文件。如果找得到,那么 require 就会通过 package.loadlib 来加载它。

2.9、Lua 元表(Metatable)

3、Lua 协同程序(coroutine)

协同程序与线程非常类似,拥有独立的堆栈,和独立的局部变量,独立的指令和指针,又共享全局变量,协同程序可以理解为一种特殊的线程,可以暂停和恢复其执行,从而允许非抢占式的多任务处理。

同程序由 coroutine 模块提供支持。
使用协同程序,你可以在函数中使用 coroutine.create 创建一个新的协同程序对象,并使用 coroutine.resume 启动它的执行。协同程序可以通过调用 coroutine.yield 来主动暂停自己的执行,并将控制权交还给调用者。

function foo()print("协同程序foo开始执行")local value = coroutine.yield("让出foo的执行")print("协同程序 foo 恢复执行,传入的值为: " .. tostring(value))print("协同程序 foo 结束执行")return 1
end-- 创建协同程序
local co = coroutine.create(foo)-- 启动协同程序
local status,result = coroutine.resume(co)
print(result)-- 恢复协同程序的执行,并传入一个值
status, result = coroutine.resume(co, 42)
print(result) -- 输出: 协同程序 foo 恢复执行,传入的值为: 42

以上实例中,我们定义了一个名为 foo 的函数作为协同程序。在函数中,我们使用 coroutine.yield 暂停了协同程序的执行,并返回了一个值
。在主程序中,我们使用 coroutine.create 创建了一个协同程序对象,并使用 coroutine.resume 启动了它的执行。
在第一次调用 coroutine.resume 后,协同程序执行到 coroutine.yield 处暂停,并将值返回给主程序。然后,我们再次调用 coroutine.resume,并传入一个值作为协同程序恢复执行时的参数。执行以上代码输出结果为:

在这里插入图片描述

同程序的状态可以通过 coroutine.status 函数获取,通过检查状态可以确定协同程序的执行情况(如运行中、已挂起、已结束等)。

-- 创建了一个新的协同程序对象 co,其中协同程序函数打印传入的参数 i
co = coroutine.create(function(i)print(i);end
)
-- 使用 coroutine.resume 启动协同程序 co 的执行,并传入参数 1。协同程序开始执行,打印输出为 1
coroutine.resume(co, 1) -- 1-- 通过 coroutine.status 检查协同程序 co 的状态,输出为 dead,表示协同程序已经执行完毕
print(coroutine.status(co)) -- dead
print("----------")-- 使用 coroutine.wrap 创建了一个协同程序包装器,将协同程序函数转换为一个可直接调用的函数对象
co = coroutine.wrap(function(i)print(i);end
)co(1)print("----------")
-- 创建了另一个协同程序对象 co2,其中的协同程序函数通过循环打印数字 1 到 10,在循环到 3 的时候输出当前协同程序的状态和正在运行的线程
co2 = coroutine.create(function()for i=1,10 doprint(i)if i == 3 thenprint(coroutine.status(co2))    --runningprint(coroutine.running())      --thread:XXXXXXendcoroutine.yield()endend
)-- 连续调用 coroutine.resume 启动协同程序 co2 的执行
coroutine.resume(co2) --1
coroutine.resume(co2) --2
coroutine.resume(co2) --3-- 通过 coroutine.status 检查协同程序 co2 的状态,输出为 suspended,表示协同程序暂停执行
print(coroutine.status(co2))    -- suspended
print(coroutine.running())
print("----------")

在这里插入图片描述
更详细的实例:

function foo (a)print("foo 函数输出", a)return coroutine.yield(2 * a) -- 返回  2*a 的值
endco = coroutine.create(function (a , b)
print("第一次协同程序执行输出", a, b) -- co-body 1 10
local r = foo(a + 1)
print("第二次协同程序执行输出", r)
local r, s = coroutine.yield(a + b, a - b) -- a,b的值为第一次调用协同程序时传入
print("第三次协同程序执行输出", r, s)
return b, "结束协同程序" -- b的值为第二次调用协同程序时传入
end)print("main", coroutine.resume(co, 1, 10)) -- true, 4
print("--分割线----")
print("main", coroutine.resume(co, "r")) -- true 11 -9
print("---分割线---")
print("main", coroutine.resume(co, "x", "y")) -- true 10 end
print("---分割线---")
print("main", coroutine.resume(co, "x", "y")) -- cannot resume dead coroutine
print("---分割线---")

生产者消费者模型

-- 生产者消费者模型
-- 1、创建携程
-- 2、调用receive函数,激活协程
-- 2、协程生成一个数据,然后让出协程,将这个数据抛出去
-- 3、receive激活携程这里收到抛出的数据,然后打印出来local newProductorfunction productor()local i = 0while true doi = i + 1send(i)     -- 将生产的物品发送给消费者end
endfunction consumer()while true dolocal i = receive()     -- 从生产者那里得到物品print(i)end
endfunction receive()local status, value = coroutine.resume(newProductor)return value
endfunction send(x)coroutine.yield(x)     -- x表示需要发送的值,值返回以后,就挂起该协同程序
end-- 启动程序
newProductor = coroutine.create(productor)
consumer()

线程和协同程序区别

1、线程与协同程序的主要区别在于,一个具有多个线程的程序可以同时运行几个线程,而协同程序却需要彼此协作的运行。
2、在任一指定时刻只有一个协同程序在运行,并且这个正在运行的协同程序只有在明确的被要求挂起的时候才会被挂起。

4、文件IO操作

4.1、简单模式

简单模式使用标准的 I/O 或使用一个当前输入文件和一个当前输出文件。

-- 以只读方式打开文件
file = io.open("test.lua", "r")-- 设置默认输入文件为 test.lua
io.input(file)-- 输出文件第一行
print(io.read())-- 关闭打开的文件
io.close(file)-- 以附加的方式打开只写文件
file = io.open("test.lua", "a")-- 设置默认输出文件为 test.lua
io.output(file)-- 在文件最后一行添加 Lua 注释
io.write("--  test.lua 文件末尾注释")-- 关闭打开的文件
io.close(file)

在这里插入图片描述

4.2、完全模式

通常我们需要在同一时间处理多个文件。我们需要使用 file:function_name 来代替 io.function_name 方法。以下实例演示了如何同时处理同一个文件

-- 以只读方式打开文件
file = io.open("test.lua", "r")-- 输出文件第一行
print(file:read())-- 关闭打开的文件
file:close()-- 以附加的方式打开只写文件
file = io.open("test.lua", "a")-- 在文件最后一行添加 Lua 注释
file:write("--test")-- 关闭打开的文件
file:close()

5、错误处理

我们可以使用两个函数:assert 和 error 来处理错误。实例如下:

local function add(a,b)assert(type(a) == "number", "a 不是一个数字")assert(type(b) == "number", "b 不是一个数字")return a+b
end
add(10)

error函数

error (message [, level])功能:终止正在执行的函数,并返回message的内容作为错误信息(error函数永远都不会返回)
通常情况下,error会附加一些错误位置的信息到message头部。Level参数指示获得错误的位置:
Level=1[默认]:为调用error位置(文件+行号)
Level=2:指出哪个调用error的函数的函数
Level=0:不添加错误位置信息

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/111168.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【LeetCode-数组】-- 寻找数组的中心索引

寻找数组的中心索引 class Solution {public int pivotIndex(int[] nums) {int n nums.length,sum 0;for(int i 0;i<n;i){ //计算所有元素之和sumnums[i];}int sum_l 0; //统计左边元素之和for(int i 0;i<n;i){sum - nums[i];if(sum_l sum){return i;}sum_l nums…

Linux文件管理与用户管理

一、查看文件内容 1、回顾之前的命令 cat命令、tac命令、head命令、tail命令、扩展&#xff1a;tail -f动态查看一个文件的内容 2、more分屏显示文件内容&#xff08;了解&#xff09; 基本语法&#xff1a; # more 文件名称 特别注意&#xff1a;more命令在加载文件时并不…

【三:Mock服务的使用】

目录 1、工具包2、mock的demo1、get请求2、post请求3、带cookies的请求4、带请求头的请求5、请求重定向 1、工具包 1、&#xff1a;服务包的下载 moco-runner-0.11.0-standalone.jar 下载 2、&#xff1a;运行命令java -jar ./moco-runner-0.11.0-standalone.jar http -p 888…

边写代码边学习之mlflow

1. 简介 MLflow 是一个多功能、可扩展的开源平台&#xff0c;用于管理整个机器学习生命周期的工作流程和工件。 它与许多流行的 ML 库内置集成&#xff0c;但可以与任何库、算法或部署工具一起使用。 它被设计为可扩展的&#xff0c;因此您可以编写插件来支持新的工作流程、库和…

【算法学习】归并算法Merge Sort总结

归并排序思路简单&#xff0c;速度仅次于快速排序&#xff0c;为稳定排序算法&#xff0c;一般用于对总体无序&#xff0c;但是各子项相对有序的数列。 1. 基本思想 归并排序使用分治思想&#xff0c;分治模式下每一层递归有三个步骤&#xff1a; 分解&#xff08;divide)&a…

Git命令在线练习网址--非常友好的提示及动画展示

Git命令在线练习 https://learngitbranching.js.org/ 举个栗子: 在练习时会给你相应提示,你可以按照相应步骤进行操作,并且每一步都有动画演示

直播带货前途渺茫了

我是卢松松&#xff0c;点点上面的头像&#xff0c;欢迎关注我哦&#xff01; 种种迹象表明电商行业和直播带货将受到冲击。直播带货前途渺茫了&#xff0c;相信很快就有政策出来了&#xff0c;针对电商这块的&#xff0c;支持实体、支持取消直播带货。 (1)目前&#xff0c;…

协同创新、奔赴未来——“华为云杯”2023人工智能创新应用大赛华丽谢幕

9月27日&#xff0c;在苏州工业园区管理委员会、华为云计算技术有限公司的指导下&#xff0c;由SISPARK&#xff08;苏州国际科技园&#xff09;、华为&#xff08;苏州&#xff09;人工智能创新中心联合主办&#xff0c;东北大学工业智能与系统优化国家级前沿科学中心、浙江大…

驱动:驱动相关概念,内核模块编程,内核消息打印printk函数的使用

一、驱动相关概念 1.操作系统的功能 向下管理硬件&#xff0c;向上提供接口 操作系统向上提供的接口类型&#xff1a; 内存管理&#xff1a;内存申请&#xff08;malloc&#xff09; 内存释放&#xff08;free&#xff09;等 文件管理&#xff1a; 通过文件系统格式对文件ext2…

E054-web安全应用-Brute force暴力破解进阶

课程名称&#xff1a; E054-web安全应用-Brute force暴力破解进阶 课程分类&#xff1a; web安全应用 实验等级: 中级 任务场景: 【任务场景】 小王接到磐石公司的邀请&#xff0c;对该公司旗下的网站进行安全检测&#xff0c;经过一番检查发现该网站可能存在弱口令漏洞…

AAOS CarMediaService 服务框架

文章目录 前言MediaSessionCarMediaService作用是什么&#xff1f;提供了哪些接口&#xff1f;如何使用&#xff1f;CarMediaService的实现总结 前言 CarMediaService 是AAOS中统一管理媒体播放控制、信息显示和用户交互等功能的服务。这一服务依赖于android MediaSession框架…

搭建NGINX服务 如何统计网页访问量

一、搭建Nginx服务 搭建之前需要查看一下我们的内核数制 我们进行简单的内核优化 命令&#xff1a;ulimit -n 65535 安装Nginx服务 第一步 关闭防火墙和安全机制 systemctl stop firewalld #关闭防火墙 systemct disable firewalld #开机自动关闭防火墙 setenfor…

美创科技信创数据安全「利基者」!

近日&#xff0c;第一新声研究部正式发布《2023年中国信创网络安全产品竞争力象限》&#xff08;下称“象限报告“&#xff09;。 ◼︎ 象限报告综合考虑企业占有率、在技术/应用上的成熟度、在客户方面的交付完成度及口碑、产品在市场/营销/商业模式/行业拓展等战略上的领先性…

无人机新手防炸飞行技巧

不要在室内飞行,容易撞墙。起飞前设置好避障和返航模式。使用模拟器熟练掌握操控。选择开阔环境目视起飞。使用低速档平稳飞行。合理使用避障功能,不要盲目依赖。使用九宫格避障法。留意电量,及时返航。极低电量时放弃强行返航。飞行后及时为电池充电保养。

文件管理系统的基本认识

1.文件的属性 文件的定义:一组有意义的信息的集合。 1.文件名: 由创建文件的用户决定文件名&#xff0c;主要是为了方便用户找到文件&#xff0c;同一目录下不允许有重名文件。 2.标识符: 一个系统内的各文件标识符唯一&#xff0c;对用户来说毫无可读性&#xff0c; 因此…

NetCore/Net8下使用Redis的分布式锁实现秒杀功能

目的 本文主要是使用NetCore/Net8加上Redis来实现一个简单的秒杀功能&#xff0c;学习Redis的分布式锁功能。 准备工作 1.Visual Studio 2022开发工具 2.Redis集群&#xff08;6个Redis实例&#xff0c;3主3从&#xff09;或者单个Redis实例也可以。 实现思路 1.秒杀开始…

mysql按照日期分组统计数据(date_formatstr_to_date)

学习链接 mysql按照日期分组统计数据 博主-山茶花开时的 【Mysql专栏学习】 mysql按照日期分组统计数据 Mysql的date_format函数想必大家都使用过吧&#xff0c;一般用于日期时间转化&#xff0c;如下所示 # 可以得出 2023-01-01 08:30:50 select DATE_FORMAT(2023-01-01…

【合宙Air700E/780E短信转发】短信转发移动联通 不要钉钉不要微信,转发自建服务器-傻瓜式搭建

官方提供的教程介绍了通过钉钉、微信等工具接收短信验证码的方法&#xff0c;但最终实现的目的是获取验证码&#xff0c;而不是通过工具间接获得。 因此&#xff0c;我们可以直接调用API接口来获取验证码&#xff0c;从而达到更快、更便捷地获得验证码的目的。 所以做了一个服…

STM32内部flash闪存的总结

最近在做无人船和机巢远程在线升级的项目&#xff0c;牵扯到flash的操作&#xff0c;特此记录&#xff0c;便于以后查找。IMU也用到过&#xff0c;当时没记录 具体细节看 E:\Documets\AY\a-project\IMU\IMU16500\S0IMU v3.3 study\User\Driver\source eeprom.c E:\Documets\A…

【Java基础面试三十四】、接口中可以有构造函数吗?

文章底部有个人公众号&#xff1a;热爱技术的小郑。主要分享开发知识、学习资料、毕业设计指导等。有兴趣的可以关注一下。为何分享&#xff1f; 踩过的坑没必要让别人在再踩&#xff0c;自己复盘也能加深记忆。利己利人、所谓双赢。 面试官&#xff1a;接口中可以有构造函数吗…