GO111MODULE: 控制是否启用gomod,默认不用管 GOROOT: 安装路径,一般不需要配置 GOPATH: 关键,设置为自已的golang的项目放置路径,比如"GOPATH=D:\workspace\go" GOPROXY:推荐使用"https:/goproxy.cn" GOPRIVATE:指向自己的私有库,比如说自己公司的私有库 其余选项暂时不用管,几乎不改
HelloWorld
package main
func main() {
println("hello world")
}
main函数要点
无参数/无返回值
main方法必须放在main包里
go run main.go就可以执行
如果不叫main.go, 则需要go build得到可运行的文件, 直接运行就可以
包声明
语法形式: package xxxx
字母和下划线的组合
可以和文件夹不同名字
同一个文件夹下的声明一致
引入包语法形式: import [alias] xxx
如果一个包引入了但是没有使用, 会报错
匿名引入: 前面多一个下划线. (引入但可以不显式使用, 使用包的初始化方法)
string和基础类型
string 声明
双引号引起来, 内部的双引号就需要使用\转义
引起来, 内部的就需要使用\转义, 可以换行, 长字符串
strings包
strings的拼接直接使用+号就可以. 注意的是, 某些语言支持string和别的类型拼接, 但是go不可以 strings主要用法
- 查找替换
- 大小写转换
- 字符串相关
- 相等
println(len("你好")) // 6, 字节数
println(utf8.RuneCountInString("你好")) // 2, 字符数
rune类型
rune 直观理解, 就是字符 rune 不是byte rune 本质是int32, 一个rune四个字节 rune 在很多语言里没有, 与之对应的是, golang没有char. rune 不是数字, 也不是char, 也不是byte 实际不太常用
bool, int, uint, float
bool: true, false int8, int16, int32, int64, int uint8, uint16, uint32, uint64, uint float32, float64
byte
字节, 本质上就是uint8 对应的操作包在bytes上
总结
golang的数字类型明确标注了长度、有无符号 golang不会帮你做类型转换,类型不同无法通过编译。也因此,string只能和string拼接 golang有一个很特殊的rune类型,接近一般语言的char或者character的概念,非面试情况下,可以理解为“rune=字符” string遇事不决找strings包
变量声明
变量声明 var
var, 语法 var name type = value
- 局部变量
- 包变量
- 块声明 驼峰命名 首字符是否大写控制了访问性: 大写包外可访问; golang 支持类型推断
变量声明 :=
只能用于局部变量, 即方法内部 golang使用类型推断来推断类型. 数字会被理解成为int或者float64 (所有其他类型的数字, 就得用var来声明)
变量声明易错点
变量声明了没有使用 类型不匹配 同作用域下, 变量只能声明一次
常量声明 const
首字符是否大写控制了访问性: 大写包外可访问; 驼峰命名 支持类型推断 无法修改值
方法声明与调用
四个部分
- 关键字 func
- 方法名 首字符是否大写决定了作用域
- 参数列
[<name type>] - 返回列表
[<name type>]支持多返回值
方法声明(推荐写法)
参数列表含有参数名 返回值不具有返回值名
// Fun1 多个参数 多个返回值 参数有名字 返回值没有
func Fun1(a string, b int) (int, string) {
return 0, "Hello"
}
// Fun2 的返回值具有名字, 可以直接在内部直接赋值, 然后返回
func Fun2(a string, b int) (age int, name string) {
age = 0
name = "hello"
// return 0, "hello" // 也可以忽略, 直接返回
return
}
// Fun3 多个参数具有相同类型放在一起, 可以只写一次类型
func Fun3(a, b, c string, abc int, p string) (d, e int, g string) {
d = 15
e = 16
g = "你好"
return
}
// Fun4 不定参数 不定参数要放在最后面
func Fun4(a string, b int, names ...string) {
for _, name := range names {
fmt.Printf("不定参数 %s\n", name)
}
}
方法调用
使用 _ 忽略返回值
方法声明与调用总结
- 支持多返回值
- 方法的作用域和变量的作用域一样, 通过大小控制
- 返回值可以有名字, 可以通过给予名字, 让调用方清楚知道你返回的是什么
最简单的WebServer
package main
import (
"fmt"
"log"
"net/http"
)
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hi there, I love %s!", r.URL.Path[1:])
}
func main() {
http.HandleFunc("/", handler)
log.Fatal(http.ListenAndServe(":8080", nil))
}
fmt格式化输出
fmt包有完整的说明
- 常用的
%s, %d, %v, %+v, %#vstr := fmt.Sprintf("hello i am %s, I am %d years old", name, age) - 不仅仅是
fmt的调用, 所有格式化字符串的API都可以用 - golang字符串拼接只能在string之间, 因此该包非常常用
数组和切片
- 数组
数组和别的语言数组差不多, 语法是
[cap]type
- 初始化要指定长度(或者叫做容量)
- 直接初始化
arr[i]的形式访问元素- len和cap操作用于获取数组长度
a1 := [3]int{9, 8, 7}
fmt.Printf("a1: %v, len: %d, cap: %d\n", a1, len(a1), cap(a1))
var a2 [3]int // 默认都是0
fmt.Printf("a2: %v, len: %d, cap: %d\n", a2, len(a2), cap(a2))
// a1 = append(a2, 12) // 数组不支持append操作
fmt.Printf("a1[1]: %d\n", a1[1])
- 切片
语法
[]type
- 直接初始化
- make初始化:
make([]type, length, capacity) arr[i]的形式访问元素append追加元素len获取元素数量cap获取切片容量- 推荐写法
s1 := make([]type, 0, cap)![[Pasted image 20230418212001.png]]
s1 := []int{9, 8, 7} // 直接初始化3个元素的切片
fmt.Printf("s1: %v, len: %d, cap: %d\n", s1, len(s1), cap(s1)) // len是已经放了多少, cap是一共可以放多少
s2 := make([]int, 3, 4) // 创建一个包含三个元素, 容量为4的切片
fmt.Printf("s2: %v, len: %d, cap: %d\n", s2, len(s2), cap(s2))
s1 = append(s1, 12) // 追加一个元素, 但没有超出容量限制
fmt.Printf("s1: %v, len: %d, cap: %d\n", s1, len(s1), cap(s1))
s1 = append(s1, 10) // 超出容量限制, 进行扩容(两倍)
fmt.Printf("s1: %v, len: %d, cap: %d\n", s1, len(s1), cap(s1))
s3 := make([]int, 4) // len = 4, cap = 4
fmt.Printf("s3: %v, len: %d, cap: %d\n", s3, len(s3), cap(s3))
- 子切片
数组和切片都可以通过
[start, end]的形式来获取子切片
arr[start:end]获得[start, end)之间的元素arr[:end]获得[0, end)之间的元素arr[start:]获得[start, len(arr))之间的元素
最直观的对比:ArrayList,.即基于数组的List的实现,切片的底层也是数组 跟ArrayList的区别:
- 切片操作是有限的,不支持随机增删(即没有add,delete方法,需要自己写代码)
- 只有append操作
- 切片支持子切片操作,和原本切片是共享底层数组
基础语法 - for
for和别的语言差不多 有三种形式
for {}, 类似while的无限循环(golang 没有 while)fori, 一般的按照下标循环- for range最为特殊的range遍历
- break和continue和别的语言一样
arr := []int{1, 2, 3, 4}
// for{}
index := 0
for {
if index == 3 {
break
}
fmt.Printf("%d ", arr[index])
index++
}
fmt.Println()
// fori
for i := 0; i < len(arr); i++ {
fmt.Printf("%d ", arr[i])
}
fmt.Println()
// for range
for index, value := range arr {
fmt.Printf("arr[%d] = %d\n", index, value)
}
for _, value := range arr {
fmt.Printf("%d\n", value)
}
if-else 语法
// 和其他语言差不多
if age < 18 {
// ...
} else {
// ...
}
// 作用域
if dis := end - start; dis > 100 {
// ...
} else {
// ...
}
// 出了if else 不能访问dis
switch语法
switch之后可以使基础类型和字符串, 或者满足特定条件的结构体 不用再加break了