for循环
我们首先来看for循环。
go
i := 1
for i <= 3 {
fmt.Println(i)
i = i + 1
}
它从1
开始,每次迭代打印i
的当前值,并将i
递增1
。当i
达到4
时循环停止。
虽然在循环外声明i
是可行的,但更常见的做法是在循环初始化时声明它。
go
for j := 0; j < 3; j++ {
fmt.Println(j)
}
这个第二个循环更接近我预期的形式。变量j
在循环内声明,从0
开始,每次迭代后递增,并在j
达到3
时停止。每次迭代打印j
的值。
go
for i := range 3 {
fmt.Println("range", i)
}
这个循环迭代3次,i
依次取值0、1和2。本质上,这与前一个循环相同,只是以更简洁和标准的方式实现。
go
for {
fmt.Println("loop")
break
}
现在事情变得有趣了。这个循环没有停止条件 ,它会无限运行,除非我们在Println
后立即使用break
,在它运行后立即停止。
go
for n := range 6 {
if n%2 == 0 {
continue
}
fmt.Println(n)
}
这个例子很巧妙。我们在这里看到了多个新东西 :取模运算符(%
)、if
和continue
关键字。
这个循环从0
数到5
。它使用n % 2 == 0
检查n
是否为偶数。如果是,continue
跳过当前迭代的剩余部分并移动到下一个数字。否则,它打印n
。
简而言之,它只打印范围内的奇数。
条件语句
条件语句是任何编程语言的基石,允许我们根据输入变量进行分支判断。主要有两种条件块:if/else
语句和switch
语句,让我们来探索它们。
if/else语句
如果语句归结为评估为true
或false
的表达式;如果表达式为true
,则执行某些操作,否则执行其他操作。
go
if 7%2 == 0 {
fmt.Println("7是偶数")
} else {
fmt.Println("7是奇数")
}
我们的老朋友取模运算符又回来了。在这个例子中,我们检查数字7是否为偶数,并打印它是否为偶数。
go
if 8%4 == 0 {
fmt.Println("8能被4整除")
}
这基本上是一个相同的例子,但展示了else
部分不是必需的。
go
if 8%2 == 0 || 7%2 == 0 {
fmt.Println("8或7中有一个是偶数")
}
这里我们有一个或
条件,如果你使用过其他语言,这会很熟悉,允许如果任一条件为真时执行if
语句。
go
if num := 9; num < 0 {
fmt.Println(num, "是负数")
} else if num < 10 {
fmt.Println(num, "有一位数字")
} else {
fmt.Println(num, "有多个数字")
}
最后,我们有一个else if
示例。这允许我们对输入进行多个条件检查并执行第一个为真的条件。
go
num := 9
if num < 0 {
fmt.Println(num, "是负数")
} else if num < 10 {
fmt.Println(num, "有一位数字")
} else {
fmt.Println(num, "有多个数字")
}
Go没有三元运算符,如果你在其他语言中习惯于使用它,这可能会有些遗憾。
switch语句
switch语句虽然类似于if/else块,但有些不同。
让我们来分析这个示例。
go
i := 2
fmt.Print("将", i, "写成")
switch i {
case 1:
fmt.Println("一")
case 2:
fmt.Println("二")
case 3:
fmt.Println("三")
}
第一个示例中,他们已经偷偷加入了一些新东西。fmt
上的Print
方法打印时不换行,我们可以用逗号分隔值将它们连接在一起。
除了这一点,这个示例非常简单,声明i
并检查i
是否为1、2或3,如果是,打印出对应的单词。
go
switch time.Now().Weekday() {
case time.Saturday, time.Sunday:
fmt.Println("今天是周末")
default:
fmt.Println("今天是工作日")
}
在这里,我们看到了对time
包的首次使用。
go
t := time.Now()
switch {
case t.Hour() < 12:
fmt.Println("现在是中午之前")
default:
fmt.Println("现在是中午之后")
}
这个示例与前一个类似,只是我们将time.Now()
调用存储为一个变量,并且没有在switch
中传递表达式。
go
whatAmI := func(i interface{}) {
switch t := i.(type) {
case bool:
fmt.Println("我是一个布尔值")
case int:
fmt.Println("我是一个整数")
default:
fmt.Printf("不知道类型%T\n", t)
}
}
whatAmI(true)
whatAmI(1)
whatAmI("嘿")
首先,我们有一个名为whatAmI
的变量,它接受i
作为interface{}
的func
。
接下来,我们有一个switch表达式,其中我们声明了一个名为t
的新变量,并将i
的类型存储在其中。
我们有两个case
和一个default
回退。前两个case
检查i
是否为bool
,如果不是,则回退打印。Printf
允许格式化输出,其中%T
打印变量的类型。
Docker
运行Go应用程序的一个很好的方法是将它们打包成Docker镜像 ,使它们可以在不同系统上运行。我们将使用多阶段Dockerfile来高效地构建和执行我们的Go程序。
为什么使用多阶段Dockerfile?
多阶段构建有助于保持最终镜像的小巧和高效,通过将构建环境与运行时环境分开。
让我们编写Dockerfile
。
dockerfile
# 构建GO二进制文件
FROM golang:1.24.1-alpine AS build
COPY ./main.go ./main.go
RUN go build -o /bin/output ./main.go
# 执行GO二进制文件
FROM scratch
COPY --from=build /bin/output /bin/output
CMD ["/bin/output"]
构建阶段(build)
我们从官方的golang:1.24.1-alpine镜像开始。我们将main.go
复制到容器中(你可以重命名),并编译它到/bin/output
。
运行时阶段
运行时阶段使用FROM scratch
,这意味着它没有基础操作系统。scratch
是一个空镜像,其他任何东西都不存在。这减小了镜像的大小。我们使用命令COPY --from=build
从构建阶段高效地复制编译后的二进制文件,并设置CMD
为["/bin/output"]
作为入口命令。
构建和运行Docker镜像
要构建和运行镜像,请使用以下命令:
perl
# 构建镜像(点表示当前目录作为上下文)
docker build -t my-go-app:latest .
# 运行容器
docker run my-go-app:latest