go的基础语法

163 阅读17分钟

以下内容参考了Go语言圣经 gopl-zh.github.io/index.html

安装

下载地址:studygolang.com/dl

error code 2503

win下安装出现

image.png

用管理员身份运行即可

$ go version
go version go1.22.0 windows/amd64

配置

镜像源

goproxy.cn/

$ go env -w GO111MODULE=on
$ go env -w GOPROXY=https://goproxy.cn,direct

模块

更新或初始化 Go Module

go mod init <module-name>

这将创建一个 go.mod 文件,标志着项目现在是一个 Go 模块。

src目录下先创建一个文件夹,在文件夹下执行init

$ >go mod init yhq
go: creating new go.mod: module yhq
go: to add module requirements and sums:
        go mod tidy

goimports

$ >go install golang.org/x/tools/cmd/goimports@latest

程序结构

命名

Go语言中的函数名变量名常量名类型名语句标号包名等所有的命名,都遵循一个简单的命名规则:一个名字必须以一个字母(Unicode字母)或下划线开头,不能以数字开头,后面可以跟任意数量的字母、数字或下划线。大写字母和小写字母是不同的:yhqYhq是两个不同的名字。

Go语言中类似ifswitch的关键字有25个;关键字不能用于自定义名字,只能在特定语法结构中使用。

break      default       func     interface   select
case       defer         go       map         struct
chan       else          goto     package     switch
const      fallthrough   if       range       type
continue   for           import   return      var

此外,还有大约30多个预定义的名字,比如inttrue等,主要对应内建的常量、类型和函数。

内建常量: true false iota nil

内建类型: int int8 int16 int32 int64
          uint uint8 uint16 uint32 uint64 uintptr
          float32 float64 complex128 complex64
          bool byte rune string error

内建函数: make len cap new append copy close delete
          complex real imag
          panic recover

这些内部预先定义的名字并不是关键字,可以在定义中重新使用它们

package main

import (
	"fmt"
)

func main() {
	true := 666
	fmt.Print(true)  // 666

}

在一些特殊的场景中重新定义它们也是有意义的,但是也要注意避免过度而引起语义混乱。

如果一个名字是在函数内部定义,那么它就只在函数内部有效。如果是在函数外部定义,那么将在当前包的所有文件中都可以访问。

名字的开头字母的大小写决定了名字在包外的可见性。如果一个名字是大写字母开头的(必须是在函数外定义的包级名字,包级函数名本身也是包级名字),那么它将是公开的,也就是public,如果是小写字母开头的,则是private

变量

package main

import "fmt"

// 全局变量

var gb = 666

var (
	gb1 = 777
	gb2 = 888
)

func main() {

	// 单个变量声明

	// 如果没有如果初始化表达式被省略,那么将用零值初始化该变量。 ,
	// `数值类型`变量对应的零值是`0`,`布尔类型`变量对应的零值是`false`,`字符串类型`对应的零值是`空字符串`
	// `接口或引用类型`(包括slice、指针、map、chan和函数)变量对应的零值是`nil`
	var a int
	//类型推导
	var b = 1
	// 只能在函数中使用
	c := 2

	// 多变量声明
	var d, e, f int
	var g, h, i = 3, "s", 5
	j, k, l := 6, "q", 8

	fmt.Println(a, b, c, d, e, f, g, h, i, j, k, l, gb, gb1, gb2)

}

注意事项

敲黑板::=是一个变量声明语句,=是一个变量赋值操作。

i, j = j, i // 交换 i 和 j 的值

敲黑板:简短变量声明左边的变量可能并不是全部刚刚声明的。如果有一些已经在相同的词法域声明过了,那么简短变量声明语句对这些已经声明过的变量就只有赋值行为了。

in, err := os.Open(infile)  //对 in err 声明
out, err := os.Create(outfile) // 对 out 声明  对 err 赋值

简短变量声明语句中必须至少要声明一个新的变量

f, err := os.Open(infile)
f, err := os.Create(outfile) // compile error: no new variables

//这段代码无法编译通过

解决的方法是第二个简短变量声明语句改用普通的多重赋值语句。

指针

一个变量对应一个保存了变量对应类型值的内存空间。普通变量在声明语句创建时被绑定到一个变量名,比如叫yhq的变量,但是还有很多变量始终以表达式方式引入,例如yhq[i]yhq.y变量。所有这些表达式一般都是读取一个变量的值,如果它们出现在赋值语句的左边,这种时候是给对应变量赋予一个新的值。

一个指针对应变量在内存中的存储位置。并不是每一个值都会有一个内存地址,但是对于每一个变量必然有对应的内存地址。通过指针可以直接读或更新对应变量的值,而不需要知道该变量的名字。

如果用var x int声明一个x变量,那么&x表达式(取x变量的内存地址)将产生一个指向该整数变量的指针,指针对应的数据类型是*int,指针被称之为指向int类型的指针。如果指针名字为p,那么可以说p指针指向变量x,或者说p指针保存了x变量的内存地址。同时*p表达式对应p指针指向的变量的值。一般*p表达式读取指针指向的变量的值,这里为int类型的值,同时因为*p对应一个变量,所以该表达式也可以出现在赋值语句的左边,表示更新指针所指向的变量的值。

package main

import "fmt"

func main() {

	x := 1          //声明一个变量
	p := &x         // &x代表指针  这时候p也是指针
	fmt.Println(*p) // 1  *p的方式找到对应内存的值
	fmt.Println(p)  // 0xc000100038  直接输出是内存的地址
	*p = 2          // 将内存地址的值改变
	fmt.Println(x)  // "2"

}

敲黑板:任何类型的指针的零值都是nil。

var x *int
fmt.Print(x == nil) // true

指针之间也是可以进行相等测试的,只有当它们指向同一个变量全部是nil时才相等。

var x, y int
fmt.Println(&x == &x, &x == &y, &x == nil) // "true false false"

类型

变量或表达式的类型定义了对应存储值的属性特征,例如数值在内存的存储大小(或者是元素的bit个数),它们在内部是如何表达的,是否支持一些操作符,以及它们自己关联的方法集等。

在任何程序中都会存在一些变量有着相同的内部结构,但是却表示完全不同的概念。例如,一个int类型的变量可以用来表示一个循环的迭代索引、或者一个时间戳、或者一个文件描述符、或者一个月份;一个float64类型的变量可以用来表示每秒移动几米的速度、或者是不同温度单位下的温度;一个字符串可以用来表示一个密码或者一个颜色的名称。

一个类型声明语句创建了一个新的类型名称,和现有类型具有相同的底层结构。新命名的类型提供了一个方法,用来分隔不同概念的类型,这样即使它们底层类型相同也是不兼容的。

type 类型名字 底层类型

类型声明语句一般出现在包一级,因此如果新创建的类型名字的首字符大写,则在包外部也可以使用。

// Package tempconv performs Celsius and Fahrenheit temperature computations.
package tempconv

import "fmt"

type Celsius float64    // 摄氏温度
type Fahrenheit float64 // 华氏温度

const (
    AbsoluteZeroC Celsius = -273.15 // 绝对零度
    FreezingC     Celsius = 0       // 结冰点温度
    BoilingC      Celsius = 100     // 沸水温度
)

func CToF(c Celsius) Fahrenheit { return Fahrenheit(c*9/5 + 32) }

func FToC(f Fahrenheit) Celsius { return Celsius((f - 32) * 5 / 9) }

在这个包声明了两种类型:CelsiusFahrenheit分别对应不同的温度单位。它们虽然有着相同的底层类型float64,但是它们是不同的数据类型,因此它们不可以被相互比较混在一个表达式运算。刻意区分类型,可以避免一些像无意中使用不同单位的温度混合计算导致的错误。因此需要一个类似Celsius(t)Fahrenheit(t)形式的显式转型操作才能将float64转为对应的类型。Celsius(t)Fahrenheit(t)是类型转换操作,它们并不是函数调用。类型转换不会改变值本身,但是会使它们的语义发生变化。另一方面,CToFFToC两个函数则是对不同温度单位下的温度进行换算,它们会返回不同的值。

// Package tempconv performs Celsius and Fahrenheit temperature computations.
package main

import "fmt"

type Celsius float64    // 摄氏温度
type Fahrenheit float64 // 华氏温度

const (
	AbsoluteZeroC Celsius = -273.15 // 绝对零度
	FreezingC     Celsius = 0       // 结冰点温度
	BoilingC      Celsius = 100     // 沸水温度
)

func CToF(c Celsius) Fahrenheit { return Fahrenheit(c*9/5 + 32) }

func FToC(f Fahrenheit) Celsius { return Celsius((f - 32) * 5 / 9) }

func main() {

	var c Celsius
	var f Fahrenheit
	fmt.Println(c == 0) // "true"
	fmt.Println(f >= 0) // "true"
	//fmt.Println(c == f)          // compile error: type mismatch
	fmt.Println(c == Celsius(f)) // "true"
}

包和文件

Go语言中的包和其他语言的库或模块的概念类似,目的都是为了支持模块化、封装、单独编译和代码重用。一个包的源代码保存在一个或多个以.go为文件后缀名的源文件中,通常一个包所在目录路径的后缀是包的导入路径;例如包gopl.io/ch1/helloworld对应的目录路径是$GOPATH/src/gopl.io/ch1/helloworld。

每个包都对应一个独立的名字空间。例如,在image包中的Decode函数和在unicode/utf16包中的 Decode函数是不同的。要在外部引用该函数,必须显式使用image.Decodeutf16.Decode形式访问。

test/change/tempconv.go

package change

type Celsius float64
type Fahrenheit float64

const (
	AbsoluteZeroC Celsius = -273.15
	FreezingC     Celsius = 0
	BoilingC      Celsius = 100
)

test/change/conv.go

package change

// CToF converts a Celsius temperature to Fahrenheit.
func CToF(c Celsius) Fahrenheit { return Fahrenheit(c*9/5 + 32) }

// FToC converts a Fahrenheit temperature to Celsius.
func FToC(f Fahrenheit) Celsius { return Celsius((f - 32) * 5 / 9) }

test/main.go

package main

import (
	"fmt"
	"test/change"
)

func main() {
	fmt.Println(change.CToF(change.AbsoluteZeroC))
	fmt.Println(change.CToF(change.BoilingC))
}

运算符

package main

import "fmt"

func main() {

	// 除法需要注意

	var c float32 = 10 / 4
	fmt.Print(c) // 2 只会保留整数部分,不会四舍五入

	var a float32 = 10.0
	c = a / 4
	fmt.Print(c) //  2.5

}

基础数据类型

整型

类型描述
uint8无符号 8 位整型 (0 到 255)
uint16无符号 16 位整型 (0 到 65535)
uint32无符号 32 位整型 (0 到 4294967295)
uint64无符号 64 位整型 (0 到 18446744073709551615)
int8有符号 8 位整型 (-128 到 127)
int16有符号 16 位整型 (-32768 到 32767)
int32有符号 32 位整型 (-2147483648 到 2147483647)
int64有符号 64 位整型 (-9223372036854775808 到 9223372036854775807)
package main

import (
	"fmt"
	"unsafe"
)

func main() {

	// 查看数据类型和字节数
	var s = 100                                          // 整型默认是int
	fmt.Printf("s的类型是 %T  字节数是 %d", s, unsafe.Sizeof(s)) //s的类型是 int  字节数是 8

}

浮点型

类型描述
float32单精度IEEE-754 32位浮点型数
float64双精度IEEE-754 64位浮点型数
package main

import (
	"fmt"
)

func main() {

	// 浮点类型默认是 float64

	var a float32 = 666.777
	var b float32 = -123.321
	// 666.777 -123.321
	fmt.Print(a, b)

	var c float32 = 123.0000999
	var d float64 = 123.0000999
	// 123.0001 123.0000999
	fmt.Print(c, d)

}

布尔型

布尔型的值只可以是常量 true 或者 false。,默认为 false

布尔类型不会隐式的转为01

package main

import "fmt"

func main() {

	var a bool
	var b bool = true
	fmt.Println(a)
	// false
	fmt.Println(b)
	// true

}

字符串

字符串就是一串固定长度的字符连接起来的字符序列。默认为""(空字符串)。字符串之间可以使用+拼接。

package main

import "fmt"

func main() {

	a := "abcdefg"

	fmt.Print(a[0])  // 97
	fmt.Print(a[2:]) // cdefg

	//字符串拼接
	var s = "你好" + "啊"
	s += "!"
	fmt.Print(s)

	//长文本
	var long = `
	啊
	是
	的
	
	
	`
	fmt.Print(long)

	// 多个拼接 最后要是 +
	var sss = "啊啊" + "啊啊" + "啊啊" +
		"啊啊" + "啊啊" +
		"啊啊"
	fmt.Print(sss)

}

类型转换

Go在不同类型的变量之间赋值时需要显示转换

package main

import "fmt"

func main() {

	var s float32 = 12.12
	var ss float64 = float64(s)
	fmt.Printf("%T", ss) // float64

	var a int64 = 5
	var aa int8
	var aaa int8

	aa = int8(a) + 127  // 能编译通过,但是会溢出
	aaa = int8(a) + 128 //无法通过编译
	fmt.Print(aa, aaa)

}
package main

import "fmt"

func main() {

	// 基本类型转字符串
	var s int = 6
	var ss float32 = 12.12
	var b bool = true
	var str string

	str = fmt.Sprintf("%d", s)
	fmt.Printf("%T", str)
	str = fmt.Sprintf("%f", ss)
	fmt.Printf("%T", str)
	str = fmt.Sprintf("%t", b)
	fmt.Printf("%T", str)

}

常量

常量是一个简单值的标识符,在程序运行时,不会被修改的量。

常量中的数据类型只可以是布尔型、数字型(整数型、浮点型和复数)和字符串型。

常量的定义格式:

const identifier [type] = value

可以省略类型说明符 [type],因为编译器可以根据变量的值来推断其类型。

  • 显式类型定义: const b string = "abc"
  • 隐式类型定义: const b = "abc"

多个相同类型的声明可以简写为:

const c_name1, c_name2 = value1, value2
package main

import "fmt"

func main() {

	const LENGTH = 10
	const WIDTH = 5

	var area int
	// 多重赋值
	const a, b, c = 1, false, "str"

	area = LENGTH * WIDTH
	fmt.Printf("面积为: %d", area)
	// 面积为: 50
	fmt.Println(a, b, c)
	// 1 false str

	// 枚举

	const (
		A = 100
		B = 90
		C = 80
	)

	fmt.Println(A, B, C)
	// 100 90 80

}

iota

iota,特殊常量,可以认为是一个可以被编译器修改的常量。

iota const关键字出现时将被重置为 0(const 内部的第一行之前)const 中每新增一行常量声明将使 iota 计数一次(iota 可理解为 const 语句块中的行索引)。

iota 可以被用作枚举值:

package main

import "fmt"

func main() {

	const (
		a = iota
		b = iota
		c = iota
	)

	fmt.Print(a, b, c)
	// 0 1 2

	// 简写

	const (
		A = iota
		B
		C
	)
	fmt.Println(A, B, C)
	// 0 1 2

	const (
		AA = iota  // 0
		BB         // 1
		CC         // 2
		DD = "yhq" // yhq
		EE         // yhq  没有指定值,继承上一个值
		FF = 100   // 100
		GG         // 100  没有指定值,继承上一个值
		HH = iota  // 7    恢复计数,const中每新增一行常量声明将使 iota 计数一次
		II         // 8
	)
	fmt.Println(AA, BB, CC, DD, EE, FF, GG, HH, II)

}
package main

import "fmt"

func main() {

	const (
		i = 1 << iota //等同于   i = 1<<0
		j = 3 << iota //等同于   j = 3<<1
		k             //等同于   k = 3<<2
		l             //等同于   l = 3<<3
	)
	fmt.Println(i, j, k, l)
	// 1 6 12 24

}

数组

在 Go 语言中,数组的大小是类型的一部分,因此不同大小的数组是不兼容的,也就是说 [5]int[10]int 是不同的类型。

package main

import "fmt"

func main() {
	//没有明确值的,不能使用 = 赋值
	var arr1 [5]int //不能使用  var arr1 =[5]int
	var arr2 = [3]int{1, 3, 5}
	var arr3 = [...]int{1, 2, 3, 4, 5}
	var more [4][5]int

	fmt.Println(arr1, arr2, arr3, more)
	// [0 0 0 0 0] [1 3 5] [1 2 3 4 5] [[0 0 0 0 0] [0 0 0 0 0] [0 0 0 0 0] [0 0 0 0 0]]
	fmt.Println(len(more)) // 4

	for i := 0; i < len(more); i++ {
		for j := 0; j < len(more[i]); j++ {
			fmt.Print(more[i][j])
		}
		fmt.Print("\n")

	}
	/*
		00000
		00000
		00000
		00000
	*/

	for i := 0; i < len(arr2); i++ {
		fmt.Println(arr2[i]) // 1  3   5
	}

	for i, v := range arr2 {
		fmt.Println(i)       // 0  1   2
		fmt.Println(v)       // 1  3   5
		fmt.Println(arr2[i]) // 1  3   5
	}

}

Slice

Slice本身没有数据,是对底层array的一个view

package main

import "fmt"

func main() {

	// 创建Slice

	var slice []int
	fmt.Print(slice == nil) //true

	for i := 0; i < 5; i++ {
		slice = append(slice, 2*i+1)
	}
	fmt.Println(slice) // [1 3 5 7 9]

	var new_slice = []int{1, 2, 3}
	fmt.Println(new_slice) // [1 2 3]

	var make_slice = make([]int, 16)
	fmt.Println(make_slice) // [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]

	var make_slice_cap = make([]int, 16, 32)
	fmt.Println(make_slice_cap) // [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]

	// 通过数组得到

	var arr = [10]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
	fmt.Print(arr[2:5]) // [2 3 4]
	fmt.Print(arr[:5])  // [0 1 2 3 4]
	fmt.Print(arr[1:])  // [1 2 3 4 5 6 7 8 9]
	fmt.Print(arr[:])   // [0 1 2 3 4 5 6 7 8 9]

	//修改

	arr = [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}

	s1 := arr[2:5]
	fmt.Println(s1) // [2 3 4]
	editSlice(s1)
	fmt.Println(s1) // [100 3 4]
	s2 := arr[3:]
	fmt.Println(s2) // [3 4 5 6 7 8 9]
	editSlice(s2)
	fmt.Println(s2)  // [100 4 5 6 7 8 9]
	fmt.Println(arr) // [0 1 100 100 4 5 6 7 8 9]

}
func editSlice(s []int) {
	s[0] = 100
}

扩展

Slice可以向后扩展,不能向前扩展。 s[i] 不可以超过 len(s),向后扩展不能超过底层数组 cap(s)

image.png

image.png

package main

import "fmt"

func main() {

	arr := [...]int{0, 1, 2, 3, 4, 5, 6, 7}

	s1 := arr[2:6]
	fmt.Print(s1) // [2 3 4 5]
	s2 := s1[3:5]
	fmt.Print(s2) // [5 6]
	s3 := s2[1:]
	s4 := s2[1:3]
	fmt.Print(s3)                                                   // [6]
	fmt.Print(s4)                                                   // [6 7]
	fmt.Printf("s3=%v,len(s3)=%v,cap(s3)=%v", s3, len(s3), cap(s3)) // s3=[6],len(s3)=1,cap(s3)=2

}

append()

  • 添加元素时如果超过cap,系统会分配更大的底层数组
  • 由于值传递的关系,必须接收append的返回值
package main

import "fmt"

func main() {

	var arr = []int{0, 1, 2, 3, 4, 5, 6, 7}

	s2 := s1[3:5]
	fmt.Print(s2)              // [5 6]
	s3 := append(s2, 10)       // [5 6 10]
	s4 := append(s3, 11)       // [5 6 10 11]
	s5 := append(s4, 12)       // [5 6 10 11 12]
	fmt.Print(arr) // [0 1 2 3 4 5 6 10]

}

copy

package main

import "fmt"

func main() {

	s1 := []int{1, 2, 3, 4}
	s2 := make([]int, 5)
	copy(s2, s1)
	fmt.Print(s2) // [1 2 3 4 0]
	s3 := []int{11, 12, 13, 14, 15, 16}
	// copy时超过
	copy(s2, s3)
	fmt.Print(s1, s2) // [11 12 13 14 15]
}

取值

package main

import "fmt"

func main() {

	s1 := []int{1, 2, 3, 4, 5}
	s2 := append(s1[:2], s1[3:]...)
	fmt.Print(s2) // [1 2 4 5]  注意此时s2的 len=4 cap=5

	//取首尾
	s3 := s1[0]
	s4 := s1[len(s1)-1] // 1
	fmt.Print(s3, s4)   // 5

}

Map

它是一个无序的key/value对的集合,其中所有的key都是不同的。

	var ma map[int]int
	ma[1] = 1
	fmt.Print(ma)
	// 上面方式会报错 panic: assignment to entry in nil map 赋值之前要先make
	ma = make(map[int]int, 10)
package main

import "fmt"

func main() {

	m0 := map[string]int{
		"A": 1,
		"B": 2,
		"C": 3,
	}

	m1 := make(map[string]int) // empty map

	var m2 map[string]string // nil

	fmt.Print(m0, m1, m2)
	// map[1:A 2:B 3:C] map[] map[]
    
    
    
    var more = map[string]map[string]string{
		"k1": {
			"a": "A",
			"b": "B",
		},
	}
	more["k1"] = val1
	fmt.Print(more)
    
    

	for k, v := range m0 {
		fmt.Print(k, v)
	}
	// A1B2C3

	one := m0["A"]
	fmt.Print(one) // 1

	// 查找不存在的key
	one_err := m0["AAA"]
	fmt.Print(one_err) // 0

	//删除key  删除一个不存在的key,不会报错
	delete(m0, "A")
	fmt.Print(m0) // map[B:2 C:3]

	//判断key是否存在
	is_exists, ok := m0["B"]
	fmt.Print(is_exists, ok) // 2 true
	is_exists0, ok0 := m0["A"]
	fmt.Print(is_exists0, ok0) // 0 false

}

结构体

结构体是一种聚合的数据类型,是由零个或多个任意类型的值聚合成的实体。每个值称为结构体的成员。用结构体的经典案例是处理公司的员工信息,每个员工信息包含一个唯一的员工编号、员工的名字、家庭住址、出生日期、工作岗位、薪资、上级领导等等。所有的这些信息都需要绑定到一个实体中,可以作为一个整体单元被复制,作为函数的参数或返回值,或者是被存储到数组中,等等。

package main

import "fmt"

func main() {

	// 相当于定义了一个书class
	type book struct {
		height, width int    // 这本书的宽度和高度
		page          int    // 这个本书有多少页
		name          string // 书名
	}

	var dpcq book //实例化一本书 斗破苍穹
	dpcq.height = 100
	dpcq.width = 200
	dpcq.page = 10000
	dpcq.name = "斗破苍穹"
	fmt.Println(dpcq) // {100 200 10000 斗破苍穹}

	// 另一种赋值方式
	var dzz = book{200, 300, 20000, "大主宰"} // 实例化一本书 大主宰
	fmt.Println(dzz)                       // {200 300 20000 大主宰}

	// // 指针访问
	n_dzz_name := &dzz.name
	*n_dzz_name = *n_dzz_name + " 番外"
	fmt.Println(dzz) // {200 300 20000 大主宰 番外

	dpcq.name = dpcq.name + " 番外"
	fmt.Println(dpcq) // {100 200 10000 斗破苍穹 番外}

	var dzz2 *book = &dzz // 指针赋值
	dzz2.name = dzz.name + "2"
	fmt.Println(dzz2) // &{200 300 20000 大主宰 番外2}
	fmt.Println(dzz)  // {200 300 20000 大主宰 番外2}

}

方法

相当于在类中定义一个方法

package main

import "fmt"

type cat struct {
	Name string
	Age  int
}

// 这是一个cat的方法
func (c cat) eat() {
	fmt.Print(c.Name + "在吃饭") //小猫在吃饭
}

func (cc cat) look() {
	cc.Name = "小小毛"
	fmt.Print(cc.Name + "在吃饭") //小小毛在吃饭
}

// 定义一个统计方法 count方法能传入一个参数 返回一个数值
func (cc cat) count(age int) int {

	return cc.Age + age

}

func main() {

	var ccc cat
	ccc.Name = "小猫"
	ccc.eat()
	ccc.look()
	fmt.Print(ccc.Name) // 小猫  look方法中的Name不会影响
	ccc.Age = 111
	add_age := ccc.count(666)
	fmt.Print(add_age) // 777

}

封装

test/change/tempconv.go

package change

// 这里有一个 dog类,但是是private的,实现外部调用

type dog struct {
	Name string
	age  int //此处的属性也是private的
}

func NewDog(name string, age int) *dog {
	return &dog{
		Name: name,
		age:  age,
	}
}

func (d dog) GetAge() int {
	return d.age
}

func (d *dog) SetAge(age int) {
	d.age = age
}

test/main.go

package main

import (
	"fmt"
	"test/change"
)

func main() {

	//dog.Name = "灰灰"
	//会找不到
	var dog = change.NewDog("灰灰", 666)

	fmt.Print(dog.Name)     // 灰灰
	fmt.Print(dog.GetAge()) // 666
	dog.SetAge(777)
	fmt.Print(dog.GetAge()) // 777

}

继承

  • 结构体可以使用嵌套结构体的所有熟悉和方法,不区分大小写
  • 当结构体和匿名结构体有相同字段或方法时,采用就近原则,如果希望访问结构体的属性或方法,可以通过指定的方式
package main

import (
	"fmt"
)

type info struct {
	class   string
	count   int
	address string
}

// 添加信息
func (info *info) add(class string, count int, address string) {
	info.class = class
	info.count = count
	info.address = address
}

// 查询信息
func (info info) search() {
	fmt.Print("开始查询信息")
	fmt.Print("\n")
	fmt.Printf("名称=%v  人数=%v  地址=%v", info.class, info.count, info.address)
	fmt.Print("\n")
}

func (info info) test() {
	fmt.Print("我是基类的")
}

type school struct {
	info          //匿名结构体
	slogan string //自定义熟悉
}

func (school *school) add(class string, count int, address string, slogan string) {
	school.class = class
	school.count = count
	school.address = address
	school.slogan = slogan
}

// 查询信息
func (school school) search() {
	fmt.Print("开始查询信息")
	fmt.Print("\n")
	fmt.Printf("名称=%v  人数=%v  地址=%v  口号=%v", school.class, school.count, school.address, school.slogan)
	fmt.Print("\n")
}

// 学校特有方法
func (sc school) run() {
	fmt.Printf("晨跑的口号是%v", sc.slogan)
	fmt.Print("\n")
}

func (school school) test() {
	fmt.Print("我是学校的")
}

type company struct {
	info //匿名结构体
}

// 公司特有方法
func (sc school) check() {
	fmt.Print("打卡")
	fmt.Print("\n")
}

func main() {

	var info info
	info.add("一年级1班", 10, "1001")
	info.search()

	var sc school
	sc.add("哈工大", 100, "东北", "还没想好")
	sc.slogan = "还没想好"
	sc.search()
	sc.info.test()

}
package main

type A struct {
	Name string
	age  int
}

type B struct {
	Name   string
	source string
}

type C struct {
	A
	B
}

type D struct {
	da A
}

func main() {
	// var c C
	// c.A.Name  相同熟悉时,指定匿名结构体名,除非 C本身有Name

	// var d D
	// d.da.Name  取名后应该这样访问

}

接口

package main

import "fmt"

type Usb interface {
	Start()
	Stop()
}

type phone struct {
}

func (p phone) Start() {
	fmt.Print("手机开始工作...")
}

func (p phone) Stop() {
	fmt.Print("手机停止工作...")
}

type camera struct {
}

func (c camera) Start() {
	fmt.Print("相机开始工作...")
}

func (c camera) Stop() {
	fmt.Print("相机停止工作...")
}

type computer struct {
}

func (cp computer) Work(usb Usb) {
	usb.Start()
	usb.Stop()
}

func main() {
	computer := computer{}
	phone := phone{}
	// camera := camera{}
	computer.Work(phone)
	// computer.Work(camera)

}

流程控制

if...else

package main

import "fmt"

func main() {

	var source = 99
	if source <= 60 {
		fmt.Println("不及格")
	} else if source <= 90 {
		fmt.Println("良好")
	} else if source <= 100 {
		fmt.Println("真棒")
	}
	// 真棒

}

package main

import "fmt"

func main() {
    
    // err变量的作用域只存在于 if
	if err := ErrCode(22); err != 1 {
		fmt.Println(err)
		// 23
	}

}

func ErrCode(info int) int {
	return info + 1

}

switch

switch 语句执行的过程从上至下,直到找到匹配项。switch 默认情况下 case 到匹配项后,自带 break 语句(所以不需要显式地写 break),匹配成功后就不会执行其他 case,如果我们需要执行后面的 case,可以使用fallthrough

  • case后面是一个表达式:变量、常量、有返回值的函数都可以

  • case表达式的类型必须和switch的类型一样

  • case后面可以跟多个表达式,用,隔开

package main

import "fmt"

func main() {

	var source int = 100

	switch {
	case source >= 90:
		fmt.Println(90)
	case source >= 95:
		fmt.Println(95)
	case source >= 100:
		fmt.Println(100)
	}
	// 90

	// case多个
	switch {
	case source > 1, source < 50:
		fmt.Println("这样也可以哦")
	case source >= 100:
		fmt.Println(100)
	}

	switch {
	case source >= 90:
		fmt.Println(90)
		fallthrough
	case source >= 95:
		fmt.Println(95)
		fallthrough
	case source >= 100:
		fmt.Println(100)
	}
	// 90
	// 95
	// 100

}

循环语句

for

package main

import "fmt"

func main() {

	var sum = 1

	for i := 1; i <= 100; i++ {
		sum += i
	}

	fmt.Println(sum)
	// 5050

	var count = 1

	for count <= 100 {
		count++
	}
	fmt.Println(count)
	// 101

	// 死循环 一般配合break
	var s int = 1
	for {
		if s <= 100 {
			fmt.Println("今天就到这吧")
			break
		}
		s++
	}

}

package main

import "fmt"

func main() {

	count := 0
	sum := 0

	for i := 1; i <= 100; i++ {
		if (i % 9) == 0 {
			count++
			sum += i

		}
	}

	fmt.Print(count, sum)
	fmt.Print("\n")
	number := 6
	for i := 0; i <= number; i++ {
		fmt.Printf("%d + %d = %d\n", i, number-i, number)
	}

	// 金字塔
	count = 7
	for i := 1; i <= count; i++ {
		for j := 1; j <= count-i; j++ {
			fmt.Print(" ")
		}
		for x := 1; x <= i*2-1; x++ {
			fmt.Print("*")
		}
		fmt.Print("\n")

	}

	fmt.Print("\n")

	// 空心金字塔
	count = 7
	for i := 1; i <= count; i++ {
		for j := 1; j <= count-i; j++ {
			fmt.Print(" ")
		}
		for x := 1; x <= i*2-1; x++ {
			if x == 1 || x == i*2-1 || i == count {
				fmt.Print("*")
			} else {
				fmt.Print(" ")
			}

		}
		fmt.Print("\n")

	}

	fmt.Print("\n")

	//九九乘法表
	for i := 1; i <= 9; i++ {

		for j := 1; j <= i; j++ {
			fmt.Printf("%d*%d=%d\t", j, i, i*j)
		}
		fmt.Print("\n")
	}

}

break

package main

func main() {

	for i := 0; i < 5; i++ {
		for j := 0; j < 5; j++ {
			if j == 2 {
				break
			}

		}

	}

	//通过标签跳出 和上面相同
	for i := 0; i < 5; i++ {
	tag1:
		for j := 0; j < 5; j++ {
			if j == 2 {
				break tag1
			}

		}

	}

tag11:
	for i := 0; i < 5; i++ {
		for j := 0; j < 5; j++ {
			if j == 2 {
				break tag11
			}

		}

	}

}

continue

跳出本次循环

package main

func main() {

	for i := 0; i < 13; i++ {
		if i == 10 {
			continue
		}

	}

}

函数

Go 语言函数定义格式如下:

func function_name( [parameter list] ) [return_types] {
   函数体
}
package main

import (
	"fmt"
)

func number(a, b int, option string) int {

	switch option {
	case "+":
		return a + b
	case "-":
		return a - b
	case "*":
		return a * b
	case "/":
		return a / b
	default:
		return 0

	}
}

func more(a, b int) (k, y int) {
	return a / b, a % b
}

func main() {
	fmt.Println(number(1, 2, "+")) // 3
	fmt.Println(more(5, 2))        //2 1
	w, _ := more(5, 2)
	_, z := more(5, 2)
	fmt.Println(w) //2
	fmt.Println(z) //1
}
func add(x int, y int) int   {return x + y}
func sub(x, y int) (z int)   { z = x - y; return}
func first(x int, _ int) int { return x }
func zero(int, int) int      { return 0 }

闭包

package main

import "fmt"

func main() {
    x := 10
    increment := func() int {
        x++
        return x
    }

    fmt.Println(increment()) // 输出: 11
    fmt.Println(increment()) // 输出: 12
    fmt.Println(x)           // 输出: 10

    // 即使 increment 函数被调用,x 的值仍然保持在 main 函数的作用域内。
}

字符串函数

package main

import (
	"fmt"
	"strconv"
	"strings"
)

func main() {

	str := "abc"
	fmt.Print(len(str)) // 3
	str = "abc啊"
	fmt.Print(len(str)) // 6

	//处理中文
	str_zh := []rune(str)
	fmt.Print(len(str_zh)) //4

	// 字符串转int
	str_number := "123"
	str2int, str2int_err := strconv.Atoi(str_number)
	fmt.Print(str2int, str2int_err) // 123 <nil>

	// int转字符串
	int_number := 123
	fmt.Print(strconv.Itoa(int_number)) // 123

	//查找一个字符串是否存在于另一个字符串
	exists_str := strings.Contains("abcd", "e")
	fmt.Print(exists_str) // false

	//查找一个字符串在另一个字符串的出现的数量
	count_str := strings.Count("abcdabcda", "a")
	fmt.Print(count_str) // 3

	//比较两个字符串是否相同 不区分大小写
	diff_str := strings.EqualFold("abc", "ABC")
	fmt.Print(diff_str) // true

	// 查找一个字符串在另一个字符串出现的位置,如果没有则是 -1
	index_str := strings.Index("abcabc", "b")
	fmt.Print(index_str) // 1
	index_str = strings.Index("abcabc", "bc")
	fmt.Print(index_str) // 1

	// 查找一个字符串在另一个字符串最后出现的位置
	last_index_str := strings.LastIndex("abcabc", "b")
	fmt.Print(last_index_str) // 4
	last_index_str = strings.LastIndex("abcabc", "bc")
	fmt.Print(last_index_str) // 4

	// 替换字符串 replace的第三个参数表示替换次数
	replace_str := strings.Replace("abcdabcd", "a", "A", -1)
	fmt.Print(replace_str) // AbcdAbcd
	replace_str = strings.Replace("abcdabcd", "a", "A", 1)
	fmt.Print(replace_str) // Abcdabcd

	// 字符串转数组
	arr_str := strings.Split("ab,cd", ",")
	fmt.Print(arr_str) // [ab cd]

	// 字符串转大小写
	to_str := strings.ToLower("ABC")
	fmt.Print(to_str) // abc
	to_str = strings.ToUpper(to_str)
	fmt.Print(to_str) // ABC

	//去除两边空格
	space_str := strings.TrimSpace("  abc  ")
	fmt.Print(space_str) // abc

	//去除左右边指定字符
	left_str := strings.TrimLeft("!abc!", "!")
	fmt.Print(left_str) // abc!
	right_str := strings.TrimRight("!abc!", "!")
	fmt.Print(right_str) // !abc

	//判断字符串是否以另一个字符串开头
	start_str := strings.HasPrefix("https://juejin.cn/user/460064855429152", "https:")
	fmt.Print(start_str) // true

	//判断字符串是否以另一个字符串结束
	stop_str := strings.HasSuffix("https://juejin.cn/user/460064855429152", "9152")
	fmt.Print(stop_str) // true

}

时间或日期函数

需要导入time

package main

import (
	"fmt"
	"time"
)

func main() {

	// 获取当前时间
	now := time.Now()
	//fmt.Print(now) // 2024-05-17 20:00:04.3221252 +0800 CST m=+0.017045301
	fmt.Print(now.Year())       // 2024
	fmt.Print(int(now.Month())) // 5
	fmt.Print(now.Day())        // 17
	fmt.Print(now.Hour())       // 20
	fmt.Print(now.Minute())     // 3
	fmt.Print(now.Second())     // 15

	// 格式化日期
	curr_time := now.Format("2006/01/02 15:04:05")
	fmt.Print(curr_time)
	// curr_time = now.Format("2006-01-02 15:04:05")
	curr_year := now.Format("2006")
	fmt.Print(curr_year)

	fmt.Print("\n")

	start_time := time.Now().Unix()

	fmt.Print(start_time)

}

错误处理机制

package main

import "fmt"

func TestErr() int {

	defer func() {
		err := recover()
		if err != nil {
			fmt.Print("原来出错了")
		}

	}()

	a := 10
	b := 0

	return a / b
}

func main() {

	TestErr()
	fmt.Print("能不能执行啊")

}

goroutine

package main

import (
	"fmt"
	"time"
)

func together() {

	for i := 1; i <= 10; i++ {
		fmt.Printf("slave 的循环 %v \n", i)
		time.Sleep(time.Millisecond)
	}

}

func main() {

	go together()
	for i := 1; i <= 10; i++ {
		fmt.Printf("master 的循环 %v \n", i)
		time.Sleep(time.Millisecond)
	}

}

channel

package main

import (
	"fmt"
)

func main() {

	//声明一个管道
	var intChan chan int
	intChan = make(chan int, 5)

	//向管道写数据
	w := 10
	intChan <- w
	intChan <- 66

	fmt.Printf("管道的长度= %v  容量= %v", len(intChan), cap(intChan)) // 管道的长度= 2  容量= 5

	//向管道读数据
	ww := <-intChan
	fmt.Print(ww)                                               // 10
	fmt.Printf("管道的长度= %v  容量= %v", len(intChan), cap(intChan)) // 管道的长度= 1  容量= 5

}