package main
import "fmt"
func main() {
fmt.Println("Hello,World!")
}
package main
import (
"fmt"
"log"
"net/http"
)
func handler(w http.ResponseWriter,r *http.Request) {
s := "你好,世界!"
fmt.Fprintf(w,"%s",s)
log.Printf("%s",s)
}
func main() {
fmt.Println("server start.")
http.HandleFunc("/",handler)
if err := http.ListenAndServe("localhost:1234",nil); err != nil {
log.Fatal("ListenAndServe:",err)
}
}
- Go语言是编译型语言,将这几行代码保存在
hello.go
文件中,然后在文件当前目录下,命令行执行go run hello.go
即可执行该,在命令行会输出Hello,World!
,然而实际处理流程仍会先将其编译为二进制机器指令,然后链接相关资源再运行,最后输出结果- 实际上可以将编译和执行分为两步,先使用命令
go build
指令进行编译,然后找到编译生成的二进制文件直接执行该文件即可
实际执行结果如下所示
PS D:\book\ch01\1.1\helloworld> go run hello.go
Hello,World!
PS D:\book\ch01\1.1\helloworld> go build hello.go
PS D:\book\ch01\1.1\helloworld> ls
目录: D:\book\ch01\1.1\helloworld
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 2022/12/27 17:08 2108024 hello
-a---- 2022/12/28 16:44 1937408 hello.exe
-a---- 2022/12/27 17:08 80 hello.go
PS D:\book\ch01\1.1\helloworld> .\hello.exe
Hello,World!
PS D:\book\ch01\1.1\helloworld>
第二段代码的执行
PS D:\book\ch01\1.1\helloserver> go run .\main.go
server start.
2022/12/28 17:16:55 你好,世界!
2022/12/28 17:16:55 你好,世界!
同时打开浏览器,访问地址localhost:1234
,也会显示你好,世界!
package main
:包的声明,也是没个项目的入口,main包是独立的可执行程序,包是Go语言项目结构的核心组成部分,如果其他Go程序要导入已有的程序,要通过import将对应的包引入;每一个Go语言程序的第一行都是包的声明,每个包可以有一个或者多个以.go为扩展名的文件
import
:导入了Go语言提供的基础包fmt
,Go语言提供了大量的标准包,此外用户自己开发的包也可以通过这种方式导入,在import关键字后使用小括号可以写入用到的多个包;这里需要注意的是与其他语言不同的,使用import导入的包必须在本程序中用到,如果导入了但没有用到,编译是无法通过的,同样的声明的变量也必须用到,否则也无法编译通过
func
:是定义函数和方法的关键字,func关键后跟函数名,小括号内是参数列表,函数体用大括号括起来
fmt.Println("Hello,World!")
:使用fmt的Println函数打印一个字符串,函数名的第一个字母大写表示包外可见,此外语句后面不需要加分号,只有当把多行代码写在一行的时候才会使用分号隔离,但这是非常差的写法,与Java不同与Python类似,但又不同于Python,Go语言在编译的时候会把换行符解析为分号,因此在实际写代码的时候注意换行即可,且函数体的大括号({
)如果单独放在一行编译也是无法通过的
在第二段代码中,使用Go语言构建了一个Web服务,Go语言构建Web服务非常简单,直接使用标准的net/http
包即可
代码中定义了函数handler
,其参数是固定的,因为要满足接口的要求,其中第二个参数前面带个*
号,表示此处参数的传递是传指针,http.Request
代表一次请求,是一个结构体
:=
表示声明和赋值另个动作一并完成,如果仅仅是声明而不赋值,则需要使用关键字var
来声明,并且:=
必须是未声明的变量,如果声明过则无法使用此方法来赋值
http.HandleFunc("/",handler)
讲路径和处理函数绑定,表明请求该路径的时候,交给绑定的函数进行处理
if err := http.ListenAndServe("localhost:1234",nil); err != nil {
log.Fatal("ListenAndServe:",err)
}
该段代码用于启动Web服务,并监听相应端口
Go语言的变量声明使用var关键字,常量声明使用const关键字,语法如下:
var name[类型]=[表达式]
在使用过程中,类型和表达式两者可省略其一,但不能同时省略,如果省略类型,Go会根据表达式推测得出类型,如果省略了表达式,Go会为变量赋予一个默认值
数字类型默认值为0,布尔类型默认值为false,字符串类型默认值为“”,其他诸如接口、slice、指针、通道、函数的默认值为nil;如果是复合类型,例如结构体、其内部所有元素被赋予对应的默认值
package main
import (
"fmt"
"math/rand"
)
func main() {
//多个变量一起通过类型声明
var i, j, k int
fmt.Printf("i:%d,j:%d,k:%d", i, j, k)
fmt.Println()
//多个变量一起通过表达式声明
var a, b, c = 1, "s", true
fmt.Printf("a:%d,b:%s,c:%t", a, b, c)
fmt.Println()
//声明赋值的缩写
f := rand.Float64() * 100
fmt.Printf("f:%g", f)
fmt.Println()
m, n := 2, 1
fmt.Println(m, n)
}
执行结果
PS D:\book\ch01\1.2\var> go run .\main.go
i:0,j:0,k:0
a:1,b:s,c:true
f:60.466028797961954
2 1
Go语言定义常量使用的是const关键字,Go语言中的常量一般会用作全局常量,常量的值在程序编译的时候就确定了,之后不可再变
const LENGTH=100
const s1 = "test"
const s2 string = "test"
同样的没有指明类型的声明,Go会根据等号右侧表达式的值推测出其类型,需要指出的是,这种用法成为无类型用法,无类型也是一种类型,它是一种比基本类型精度更高的类型,至少可达256位,比机器硬件精度更高,无类型在Go语言中有六种,无类型布尔、无类型整数、无类型文字符号、无类型浮点数、无类型复数和无类型字符串,无类型可以用来处理基本类型处理不了的数据,例如精度特别大的浮点数
const(
SYS="Debian"
TYPE="pro"
)
声明常量可以使用常量生成器iota,它可以通过枚举创建一系列相关的值,而且不需要明确定义类型,iota每次从0开始取值,逐次加1
package main
import "fmt"
type data int
const (
Zero data = iota
One
Two
Three
Four
)
const (
p2_0 = 1 << iota
p2_1
p2_2
p2_3
)
func main() {
fmt.Println(Zero)
fmt.Println(One)
fmt.Println(Two)
fmt.Println(Three)
fmt.Println(Four)
fmt.Println(p2_0)
fmt.Println(p2_1)
fmt.Println(p2_2)
fmt.Println(p2_3)
}
执行结果
PS D:\book\ch01\1.2\iota> go run .\main.go
0
1
2
3
4
1
2
4
8
假设有一个int型变量x,&x表示取x的地址,将此值赋给p,那么p就是指针,取得指针指向的值,使用*p
var int x //x默认初始值为0
p:=&x //p为整形指针,指向x
fmt.Println(*p) //此时打印输出0
*p=1 //类似于x=1
fmt.Println(x) //此时打印输出1
Go官方文档中提到的Zero Value,在很多场景下被称为“零值”,在C语言中称为默认初始值
可以使用指针来代替变量名,如果指针改变了变量值,会影响到变量名,本质上他们就是同一个内存空间,实际上复合类型也可以通过指针进行操作,复合类型内的具体元素同样有地址,并且可以通过指针进行操作,对于上边代码中的p
我们称为指针类型,其默认值是nil
,可以用过p==nil
来判断是否取得地址,nil
表示未成功取得
package main
import "fmt"
func main(){
m:=1
selfPlusPointer(&m)
fmt.Println(m) //2
// selfPlus返回得是指针类型,所以要加上*号才能取到指针指向的变量值,否则只是个地址
fmt.Println(*selfPlus(1)) //2
}
// 定义了一个自增函数,接受的参数是一个int指针,函数体通过指针执行了自增,且函数没有返回值,因为通过指针直接修改了变量值
func selfPlusPointer(n *int){
*n++
}
// 该函数接收一个整型参数,用一个临时变量暂时存储n的自增1的结果,然后再返回临时变量的指针类型
func selfPlus(n int) *int{
t:=n+1
return &t
}
在Go语言中除非显示的使用指针,否则所有的值传递都是具体值得赋值,包括符合结构
Go语言还提供了new
函数,用于创建一个不需要名称的变量,并可以直接赋值给一个指针
p:=new(int) //p为*int类型
fmt.Println(*p) //0