Go基础语法| 青训营笔记

167 阅读6分钟

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, %#v str := fmt.Sprintf("hello i am %s, I am %d years old", name, age)
  • 不仅仅是fmt的调用, 所有格式化字符串的API都可以用
  • golang字符串拼接只能在string之间, 因此该包非常常用

数组和切片

  • 数组 数组和别的语言数组差不多, 语法是 [cap]type
  1. 初始化要指定长度(或者叫做容量)
  2. 直接初始化
  3. arr[i]的形式访问元素
  4. 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
  1. 直接初始化
  2. make初始化: make([]type, length, capacity)
  3. arr[i]的形式访问元素
  4. append追加元素
  5. len获取元素数量
  6. cap获取切片容量
  7. 推荐写法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]的形式来获取子切片
  1. arr[start:end] 获得[start, end)之间的元素
  2. arr[:end]获得[0, end)之间的元素
  3. arr[start:]获得[start, len(arr))之间的元素

最直观的对比:ArrayList,.即基于数组的List的实现,切片的底层也是数组 跟ArrayList的区别:

  1. 切片操作是有限的,不支持随机增删(即没有add,delete方法,需要自己写代码)
  2. 只有append操作
  3. 切片支持子切片操作,和原本切片是共享底层数组

基础语法 - for

for和别的语言差不多 有三种形式

  1. for {}, 类似while的无限循环(golang 没有 while)
  2. fori, 一般的按照下标循环
  3. for range最为特殊的range遍历
  4. 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了