go基本语法
一、main函数与package声明
1、main 函数概览
main函数要点:无参数、无返回值;main 方法必须要在 main 包里面;`go run main.go` 就可以执行;如果文件不叫 `main.go`,则需要`go build` 之后再 `go run`。
2、package 声明
语法形式:package xxxx,package使用字母和下划线的组合,package名称可以和文件夹不同,但是同一个文件夹下的声明必须一致。
引入包语法形式:import [alias] xxx,如果一个包引入了但是没有使用,会报错,如果需要匿名引入:前面多一个下划线。如下图所示,如果不使用fmt包,但是引入就会报错,如果是使用下划线匿名引用,则不会报错。这里可能会有疑问,不用为什么要引用,这是因为有的时候需要使用包的init方法,因此需要匿名引用。
二、string 和 基础类型
1、String
String 声明:双引号引起来,则内部双引号需要使用\转义,` 引号引起来,则内部`需要\转义
String 长度:string 的长度很特殊:用 len(str)获取的是字节长度:和编码无关;字符数量:和编码有关,用编码库来计算。
func main() { println("hello world") println(len("你好")) // 输出6 println(utf8.RuneCountInString("你好")) // 输出2 }
String 包:string 的拼接直接使用 + 号就可以。注意的是,某些语言支持 string 和别的类型拼接,但是golang 不可以
String的工具包是strings, 主要方法(你所需要的全部都可以找到):查找和替换、大小写转换、子字符串相关、相等
2、rune 类型
rune,直观理解,就是字符,rune 不是 byte,其本质是 int32,一个 rune 四个字节。
rune 在很多语言里面是没有的,与之对应的是,golang 没有 char 类型。rune 不是数字,也不是 char,也不是 byte,实际中不太常用
3、bool, int, uint, float 家族
bool: true, false
int8, int16, int32, int64, int
uint8, uint16, uint32, uint64, uint
float32, float64
4、byte 类型
byte,字节,本质是 uint8,对应的操作包在 bytes 上
总结:
golang 的数字类型明确标注了长度、有无符号,golang 不会帮你做类型转换,类型不同无法通过编译。也因此,string 只能和string 拼接,golang 有一个很特殊的 rune 类型,接近一般语言的 char 或者 character 的概念,非面试情况下,可以理解为 “rune = 字符”。
string 遇事不决找 strings 包。
三、变量声明 & 方法声明与调用
1、变量声明
var 声明变量:
语法:var name type = value
局部变量、包变量、块声明、驼峰命名、首字符是否大写控制了访问性,如下面的例子,如果是大写,在其他包也能访问,小写的话,不能在其他包访问,其子包也不能访问;在定义变量时,可以写变量类型,也可以不定义,golang 支持类型推断,但是如果其推断的和我们想定义的不一致,就必须明确,例如实例中的变量c
func main() { println("hello world") println(len("你好")) // 输出6 println(utf8.RuneCountInString("你好")) // 输出2 var a int = 16 println(a) var b = 16 println(b) var c uint = 16 println(c) } var Global = "全局变量" var local = "包变量" var ( First string = "abc" second int32 = 16 )
:= 声明变量:
只能用于局部变量,即方法内部。golang 使用类型推断来推断类型。数字会被理解为 int 或者 float64。(所以要其它类型的数字,就得用 var 来声明)
d := 17 println(d)
变量声明易错点:变量声明了没有使用、类型不匹配、同作用域下,变量只能声明一次,特别注意的是变量声明了没有使用的情况,也会编译不通过
const 声明常量:首字符是否大写控制了访问性、驼峰命名、支持类型推断
const internal = "包内可访问" const External = "包外可访问"
2、方法声明
方法声明包含四个部分:关键字 func、方法名字(首字母是否大写决定了作用域)、参数列表:[<name type>]、返回列表: [<name type>] ,如果是多返回值,可以直接使用return或者是return后带上具体返回项。
func main() { println(Func01("zhangsan")) println(Func002) println(Func003("lisi", 20)) } // 一个参数一个返回值 func Func01(name string) string { return "返回值" + name } func Func002() { println(" no param no return !") } func Func003(name string, age int) (returnName string, returnAge int) { returnName = "param:" + name returnAge = age + 10 return //return returnName, returnAge }
具体怎么编码,怎么规范,看个人爱好和公司规范
3、方法调用
就正常调用,没什么可说的,但是可以使用 _ 忽略返回值
k := Func01("zhangsan") println(k) name1, age := Func003("wangyu", 30) println(name1) println(age) name, _ := Func003("lisi", 20) println(name)
4、方法声明与调用总结
golang 支持多返回值,这是一个很大的不同点
golang 方法的作用域和变量作用域一样,通过大小写控制
golang 的返回值是可以有名字的,可以通过给予名字让调用方清楚知道你返回的是什么
四、最简单的Web服务器
官方文档:
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)) }
最简单的 web 服务器 —— 增加几个路由
func main() { http.HandleFunc("/", handler) http.HandleFunc("/home", home) http.HandleFunc("/user", user) http.HandleFunc("/order", order) log.Fatal(http.ListenAndServe(":8080", nil)) } func home(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "首页") } func user(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "用户") } func order(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "订单") }
fmt 格式化输出,由于golang不允许不同类型的变量拼劲,因此需要使用 fmt 的格式化 拼接
name := "zhangsan" age := 20 // 格式化输出 str := fmt.Sprintf("hello world %s hello world %d hahaha\n", name, age) println(str)
fmt 包有完整的说明:
掌握常用的:%s, %d, %v, %+v, %#v,不仅仅是 `fmt`的调用,所有格式化字符串的 API 都可以用;因为golang字符串拼接只能在 string 之间,所以这个包非常常用
例如 %s 是字符串占位符,%d 是数字占位符,%v 是返回体占位符等,一般相关的描述都在goSDK对应包中的doc.go文档中
五、数组与切片
1、数组,语法是:[cap]type
数组需要指定初始化长度(或者叫做容量),在定义时可以直接设置初始值,如果不设置,初始值为默认值,这里有个坑需要注意一下,虽然没有初始化内容,但是默认已经被初始化,长度和容量都是定义的长度。
数组支持使用arr[i]下标的形式访问元素,len 和 cap 操作用于获取数组长度(数组的len 和 cap 结果是一样的,就是数组的长度)
func main() { a1 := [3]int{1,2,3} print(a1) var a2 [3]int print(a2) } func print(a1 [3]int) { fmt.Printf("a1: %v,len: %d, cap: %d ", a1, len(a1), cap(a1)) }
2、切片,语法:[]type
切片和数组的区别就是,数组需要直接指定长度,切片不需要指定。
可以直接初始化,也可以使用make初始化:make([]type, length, capacity);可以使用arr[i] 下标的形式访问元素;使用append 追加元素、len 获取元素数量、cap 获取切片容容量;
推荐写法:s1 := make([]type, 0, capacity) ,否则的话,使用append会直接在已有的数据后面添加。
func main() { a3 := []int{1,2,3,4} print2(a3) a4 := make([]int, 3, 4) print2(a4) a5 := make([]int, 4) print2(a5) a4 = append(a4, 5) print2(a4) a4 = append(a4, 6) print2(a4) fmt.Printf("%d", a4[4]) } func print2(a1 []int) { fmt.Printf("a1: %v,len: %d, cap: %d \n", a1, len(a1), cap(a1)) }
3、数组与切片对比
通过上面对比可以看到,切片比数组功能更强大,因此遇事不决用切片,基本不会出错
4、子切片:
数组和切片都可以通过[start:end] 的形式来获取子切片,遵循 左闭右开原则:1. arr[start:end],获得[start, end)之间的元素;2. arr[:end],获得[0, end) 之间的元素;3. arr[start:],获得[start, len(arr))之间的元素
s1 := a4[1:3] print3(s1) s2 := a4[1:] print3(s2) s3 := a4[:2] print3(s3)
对于使用子切片有个需要注意的地方,在没有扩容时,切片和自切片是共享底层的,但是如果切片或者自切片发生了扩容,就不再是共享底层了,因此使用子切片一般用于只读场景,不要用于写场景
五、控制语句
1、for
for 和别的语言差不多,有三种形式:1. for {} ,类似 while 的无限循环;2. fori,一般的按照下标循环;3. for range 最为特殊的 range 遍历;
func main() { arr := []int {1,2,3,4,5} forLoop(arr) forI(arr) forRange(arr) } func forLoop(arr []int) { index := 0 for { if index == 3 { break } fmt.Printf(" index = %d,number = %d \n", index, arr[index]) index++ } } func forI(arr []int) { for i := 0; i < len(arr); i++ { fmt.Printf(" index = %d,number = %d \n", i, arr[i]) } } func forRange(arr []int){ for index, value := range arr{ fmt.Printf(" index = %d,number = %d \n", index, value) } }
break 和 continue 和别的语言一样
2、if else
if-else 和别的语言也差不多。这里有个比较特殊的用法,带局部变量声明的 if- else,例如下面例子的第二个if中的sub变量,只能在 if 块,或者后边所有的 else 块里面使用,脱离了 if - else 块,则不能再使用
func ifelse(age int) { if age<18{ fmt.Printf("age < 18") }else { fmt.Printf("age >= 18") } if sub := age -10; sub > 10 { fmt.Printf("age-10=%d", sub) } }
3、switch
switch 和别的语言差不多,switch 后面可以是基础类型和字符串,或者满足特定条件的结构体最大的差别:终于不用加 break 了!
大多数时候,switch 后面只会用基础类型或者字符串
func chooiceFood(food string) { switch food { case "apple": fmt.Printf("apple") case "banana": fmt.Printf("banana") default: fmt.Printf("other") } }
-----------------------------------------------------------
---------------------------------------------
朦胧的夜 留笔~~
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律