GoLang基础语法 + 面向对象


Golang 的优势

极简单的部署方式:可直接编译成机器码、不依赖其他库、直接运行即可部署。

静态类型语言,编译的时候可以检查出大多数问题。

语言层面的并发:天生的基因支持、充分的利用多核

func goFunc(i int) {
	fmt.Println("goroutine ", i, " ...")
}

func main() {
	for i := 0; i < 1000; i++ {
		go goFunc(i) 
	}
	time.Sleep(time.Second)
}

强大的标准库:runtime 系统调度机制、高效的 CG 垃圾回收、丰富的标准库

“大厂” 领军:Google、facebook、Tencent、Baidu、七牛、字节 …

不同语言的斐波那契数列算法 编译 + 运行 时间对比:

字符串操作的 4 个包:bytes、strings、strconv、unicode

  • bytes 包操作 []byte。因为字符串是只读的,因此逐步构创建字符串会导致很多分配和复制,使用 bytes.Buffer 类型会更高。
  • strings 包提供 切割、索引、前缀、查找、替换 等功能。
  • strconv 包提供 布尔型、整型数、浮点数 和对应字符串的相互转换,还提供了双引号转义相关的转换。
  • unicode 包提供了 IsDigit、IsLetter、IsUpper、IsLower 等类似功能,用于给字符分类。

如果 string 中包含汉字,要注意:

  • UTF-8 编码中,一个汉字需要 3 个字节,通过 len() 获取的是字符串占据的字节数
str1 := "hello 世界"
fmt.Println(len(str1)) 
  • 如果想要得到字符串本身的长度,可以将 string 转为 rune 数组再计算:
str2 := "hello 世界"
fmt.Println(len([]rune(str2))) 

遍历

byte 是 uint8 的别名

rune 是 int32 的别名,相当于 Go 里面的 char

如果包含汉字,以下遍历方式会出现乱码:

str := "你好世界!"

for i := 0; i < len(str); i++ {
  fmt.Printf("%c", str[i])
}

  • 解决方案 1:转成 rune 切片再遍历
str := "你好世界!"
newStr := []rune(str)
for i := 0; i < len(newStr); i++ {
  fmt.Printf("%c", newStr[i])
}

  • 解决方案 2:使用 range 来遍历

range 按照字符遍历,前面的 for 按照字节遍历

str := "你好世界123"
for index, value := range str {
  fmt.Printf("index = %d value = %c\n", index, value)
}
index = 0 value = 你
index = 3 value = 好
index = 6 value = 世
index = 9 value = 界
index = 12 value = 1
index = 13 value = 2
index = 14 value = 3

unicode 包

/src/unicode/letter.go

func IsUpper(r rune) bool


func IsLower(r rune) bool





func IsTitle(r rune) bool


func ToUpper(r rune) rune


func ToLower(r rune) rune




func ToTitle(r rune) rune



func To(_case int, r rune) rune

/src/unicode/digit.go

func IsDigit(r rune) bool

/src/unicode/graphic.go

func IsNumber(r rune) bool



func IsLetter(r rune) bool





func IsSpace(r rune) bool




func IsControl(r rune) bool





func IsGraphic(r rune) bool






func IsPrint(r rune) bool


func IsPunct(r rune) bool


func IsSymbol(r rune) bool


func IsMark(r rune) bool


func IsOneOf(set []*RangeTable, r rune) bool

循环语句

go 语言中的 for 循环有 3 种形式:

for init; condition; post { }
for condition { }
for { }
func main() {
	numbers := [6]int{1, 2, 3, 5}

	for i := 0; i < len(numbers); i++ {
		fmt.Println(numbers[i])
	}

	i := 0
	for i < len(numbers) {
		fmt.Println(numbers[i])
		i++
	}

	for i, x := range numbers {
		fmt.Printf("index: %d, value: %d\n", i, x)
	}

  
	for {
		fmt.Println("endless...")
	}
}

slice

Golang 默认都是采用值传递,有些值天生就是指针:slice、map、channel。

注意:定长数组是值传递,slice 是指针传递

数组

声明数组的方式:(固定长度的数组)

var array1 [10]int
array2 := [10]int{1,2,3,4}
array3 := [4]int{1,2,3,4}

数组的长度是固定的,并且在传参的时候,严格匹配数组类型

func printArray(myArray [4]int) {
	fmt.Println(myArray) 
	myArray[0] = 666     
}

func main() {
	myArray := [4]int{1, 2, 3, 4}
	printArray(myArray)
	fmt.Println(myArray) 
}

myArray := [...]int{1, 2, 3, 4} 是自动计算数组长度,但并不是引用传递。

声明动态数组和声明数组一样,只是不用写长度。

func printArray(myArray []int) {
	fmt.Println(myArray) 
	myArray[0] = 10      
}

func main() {
	myArray := []int{1, 2, 3, 4}
	printArray(myArray)
	fmt.Println(myArray) 
}

struct

type Book struct {
	title string
	price string
}

func changeBook(book Book) {
	
	book.price = "666"
}

func changeBook2(book *Book) {
	
	book.price = "777"
}

func main() {
	var book Book
	book.title = "Golang"
	book.price = "111"
	fmt.Printf("%v\n", book) 

	changeBook(book)
	fmt.Printf("%v\n", book) 

	changeBook2(&book)
	fmt.Printf("%v\n", book) 
}

一道 struct 与指针面试题:

type student struct {
	name string
	age  int
}

func main() {
	m := make(map[string]*student)
	stus := []student{
		{name: "aaa", age: 18},
		{name: "bbb", age: 23},
		{name: "ccc", age: 28},
	}
	for _, stu := range stus {
		m[stu.name] = &stu
	}
	for k, v := range m {
		fmt.Println(k, "=>", v.name)
	}
}
aaa => ccc
bbb => ccc
ccc => ccc

解决方法 1:

for _, stu := range stus {
  
  temp := stu
  m[stu.name] = &temp
}

解决方法 2:

for i, stu := range stus {
  
  m[stu.name] = &stus[i]
}

如何理解 Golang 中的接口? - 波罗学的回答 - 知乎

Golang 中多态的基本要素:

  • 有一个父类(有接口)
type AnimalIF interface {
	Sleep()
	GetColor() string 
	GetType() string  
}
  • 有子类(实现了父类的全部接口)
type Cat struct {
	color string 
}

func (c *Cat) Sleep() {
	fmt.Println("Cat is Sleep")
}

func (c *Cat) GetColor() string {
	return c.color
}

func (c *Cat) GetType() string {
	return "Cat"
}
  • 父类类型的变量(指针)指向(引用)子类的具体数据变量
var animal AnimalIF
animal = &Cat{"Green"}
animal.Sleep() 

不同接收者实现接口

type Mover interface {
	move()
}

type dog struct {
	name string
}

值类型接收者实现接口:可以同时接收 值类型 和 指针类型。

Go 语言中有对指针类型变量求值的语法糖,dog 指针 dog2 内部会自动求值 *dog2

func (d dog) move() {
	fmt.Println(d.name, "is moving")
}

func main() {
	var m Mover

	var dog1 = dog{"dog1"}
	m = dog1 
	m.move()

	var dog2 = &dog{"dog2"}
	m = dog2 
	m.move()
}

指针类型接收者实现接口:只能接收指针类型。

func (d *dog) move() {
	fmt.Println(d.name, "is moving")
}

func main() {
	var m Mover

	
	
	
	

	var dog2 = &dog{"dog2"}
	m = dog2
	m.move()
}

一道面试题:以下代码能否通过编译?

type People interface {
	Speak(string) string
}

type Student struct{}

func (stu *Student) Speak(think string) (talk string) {
	if think == "sb" {
		talk = "你是个大帅比"
	} else {
		talk = "您好"
	}
	return
}

func main() {
	var peo People = Student{}
	think := "bitch"
	fmt.Println(peo.Speak(think))
}

不能。修改 var peo People = Student{} 为 var peo People = &Student{} 即可。

通用万能类型

interface{} 表示空接口,可以用它引用任意类型的数据类型。

func myFunc(arg interface{}) {
	fmt.Println(arg)
}

type Book struct {
	auth string
}

func main() {
	book := Book{"Golang"}

	myFunc(book)
	myFunc(100)
	myFunc("abc")
	myFunc(3.14)
}

Golang 给 interface{} 提供类型断言机制,用来区分此时引用的类型:

注意断言这个操作会有两个返回值

func myFunc(arg interface{}) {
  
  value, ok := arg.(string)
  if !ok {
    fmt.Println("arg is not string type")
  } else {
    fmt.Println("arg is string type, value = ", value)
    fmt.Printf("value type is %T\n", value)
  }
}

一个接口的值(简称接口值)是由一个 具体类型 和 具体类型的值 两部分组成的。

这两部分分别称为接口的动态类型和动态值。

var w io.Writer
w = os.Stdout
w = new(bytes.Buffer)
w = nil

switch 判断多个断言:

func justifyType(x interface{}) {
    switch v := x.(type) {
    case string:
        fmt.Printf("x is a string,value is %v\n", v)
    case int:
        fmt.Printf("x is a int is %v\n", v)
    case bool:
        fmt.Printf("x is a bool is %v\n", v)
    default:
        fmt.Println("unsupport type!")
    }
}