go语言基础语法|青训营笔记

79 阅读6分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 1 天

一、本堂课重点内容:

  • go的优势
  • 开发配置
  • 基础语法

二、详细知识点介绍:

go的优势

  1. 学习曲线容易
  2. 开发效率和运行效率高
  3. 自由高效
  4. 拥有强大的标准库、
  5. 部署方便
  6. 高度支持并发性
  7. 稳定性高

开发配置

基础语法

变量

声明变量可以用var

var a int

常用变量类型有

  • 整数类型:(u)int,(u)int8,(u)int16,(u)int32,(u)int64,uintptr(指针),byte,rune
  • 字符(串)类型:byte,rune,string
  • 浮点型:float32,float64
  • 布尔型:bool

条件控制语句

if{}

当条件表达式为True时,就会执行括号中的代码块。

if age > 15 { 
    fmt.Println("你的年龄大于15岁") 
}

if else{}

当条件表达式为True时,否则执行代码块2。

if age > 15 { 
    fmt.Println("你的年龄大于15岁") 
} else { 
    fmt.Println("你的年龄小于等于15岁") 
}

if{} else if

  • 先判断条件表达式1是否成立,如果为True,就执行代码块1
  • 如果条件表达式1位False,就去判断条件表达式2是否成立,如果条件表达式2成立,就执行代码块2
  • 依次类推,如果所有的条件表达式不成立,就执行else语句块
  • else不是必须的
  • 注意:多分支语句只能有一个执行入口
if age > 15 { 
    fmt.Println("你的年龄大于15岁") 
} else if age < 10 { 
    fmt.Println("你的年龄小于10岁") 
} else {
    fmt.Println("你的年龄小于等于15岁大于等于10岁") 
}

switch

  • switch语句用于基于不同条件执行不同动作,每个case分支都是唯一的,从上往下逐一测试,直到匹配为止。注意,匹配项后面不需要再加break。
  • switch的执行流程是先执行表达式,得到值,然后和case的表达式进行比较,如果相等,就匹配到;接着执行对应case的语句块,然后退出switch控制。
  • 如果switch表达式的值没有和任何case的表达式匹配成功,则执行default的语句块,执行后退出switch的控制。
  • golang中的case后的表达式可以有多个,使用逗号间隔。
  • golang中的case语句块不需要写break,因为默认会有。即在默认情况下,当程序执行完case语句块后,就直接退出该switch控制结构。
switch key {
		case '1':
			fmt.Println("今天星期一")
		case '2':
			fmt.Println("今天星期二")
		case '3':
			fmt.Println("今天星期三")
		case '4':
			fmt.Println("今天星期四")
		case '5':
			fmt.Println("今天星期五")
		case '6':
			fmt.Println("今天星期六")
		case '7':
			fmt.Println("今天星期天")
		default:
			fmt.Println("输入有误....")
	}

for循环

for i

str := "Hello, go" 
for i := 0; i < len(str); i++ { 
    ch := str[i] 
    fmt.Println(i, ch, , string(ch)) 
}

for range

str := "Hello, go" 
for i, ch := range str { 
fmt.Println(i, ch, string(ch)) 
}

函数

基本概念

go语言函数的表达式 func 函数名(形参名1 类型1,形参名2 类型2,…)(返回类型1,返回类型2,…) { 函数体 } 注意:go语言的函数支持多个返回值。函数名大写表示该函数为Public可以被其它package调用,小写为private,不可以被其它包调用。

加粗部分为函数签名

func test(a int, b int) (int, int) {
    return a+b, a-b
}
​
//_表示忽视该返回值
_, b := test(1,2)

细节

  1. go函数不支持重载

  2. 函数也是一种数据类型,可以赋值给变量(参考匿名函数的第二种定义方法)

    //函数类型为func<int, int> int
    func test(a int, b int) int {
        return a+b
    }
    ​
    a := tset
    res = a(1,2)
    
  3. 函数可以作为型参类型

    func myFun(fun func(int, int) int, a int, b int) int {
        return fun(a, b)
    }
    ​
    res := myFun(test, 1, 2)
    
  4. type自定义数据类型(和类型别名不同)

    //自定义数据类型
    type newFunc func(int, int) int//类型别名
    type myFunc = func(int, int) intfunc main() {
        var a newFunc
        var b myFunc
    ​
        fmt.Printf("type of a:%T\n", a) //type of a:main.newFunc,表示main包下定义的newFunc类型。这是一个新的数据类型。
        fmt.Printf("type of b:%T\n", b) //type of b:func<int, int> int,myFunc类型别名只会在代码中存在,编译完成时并不会有myFunc类型。
    }
    
  5. 对函数返回值命名

    func fun(a int, b int) (res int) {
        res = a + b
         //return res
        return
    }
    
  6. 使用_标识符(作为一个占位符)忽略返回值

    func test(a int, b int) (int, int) {
        return a+b, a-b
    }
    ​
    //_表示忽视该返回值
    _, b := test(1,2)
    
  7. 支持可变参数(可以传0个或多个参数)

    //args本质是切片(slice,一种可变数组),通过args[index]访问参数
    func fun(a int, args... int) (sum int) {
        sum += a
        for i := 0; i < len(args); i++ {
            sum += args[i]
        }
        return
    }
    ​
    res = fun(1, 2, 3)
    

init函数

基本概念

每一个.go文件都可以有一个init函数,该函数会在main函数调用前被go运行框架调用,可用于初始化工作

func init() {
    a = 1
}

细节

  1. 执行流程:导入包的内容->全局变量定义->init函数->main函数

匿名函数

可以用于函数内定义函数

定义后直接调用(仅使用一次)

res := func (a int, b int) int {
    return a + b
}(1, 2)

赋给一个变量,然后调用变量来使用

//a的数据类型就是函数类型,类似于函数指针
a := func (a int, b int) int {
    return a + b
}
​
res := a(1,2)

全局匿名函数

将一个匿名函数定义给一个全局变量

// 这种因式分解关键字的写法一般用于声明全局变量
var {
    //全局匿名函数,如果是小写的fun则为私有
    Fun = func (a int, b int) int {
        return a + b
    }
}

闭包

基本概念

一个函数其他相关引用环境组合的一个整体(实体)(函数内部引用函数外的变量)

//这段代码实现了一个累加器,不像for循环那样限制了使用的位置
func AddUpper() func (int) int {
    n := 10
    return func(x int) int {
        n += x
        return n
    }
}
​
func main() {
    f1 := AddUpper
    fmt.Println(f1(1))//11
    fmt.Println(f1(2))//13
    fmt.Println(f1(3))//16
    
    f2 := AddUpper()
    fmt.Println(f2(1))//11
​
    fmt.Println(f1(4))//20
}

其中AddUpper函数内的代码就是闭包

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

其返回的是一个匿名函数,但是该匿名函数引用到了外部的变量n,因此与其形成一个整体,构成闭包(类似一个类里的字段和操作)

注:string(36) -> $(36十进制对应的ascii码字符)

结构体

定义与声明结构体

结构体定义需要使用 type 和 struct 语句。struct 语句定义一个新的数据类型,结构体中有一个或多个成员。type 语句设定了结构体的名称。结构体的格式如下:

type struct_variable_type struct {
   member definition
   member definition
   ...
   member definition
}

一旦定义了结构体类型,它就能用于变量的声明,语法格式如下:

variable_name := structure_variable_type {value1, value2...valuen}
或
variable_name := structure_variable_type { key1: value1, key2: value2..., keyn: valuen}

访问结构体成员

如果要访问结构体成员,需要使用点号 . 操作符,格式为:

结构体.成员名"

结构体指针

你可以定义指向结构体的指针类似于其他指针变量,格式如下:

var struct_pointer *Books

以上定义的指针变量可以存储结构体变量的地址。查看结构体变量地址,可以将 & 符号放置于结构体变量前:

struct_pointer = &Book1

使用结构体指针访问结构体成员,使用 "." 操作符:

struct_pointer.title

注意

struct作类

struct 类似于 java 中的类,可以在 struct 中定义成员变量。

要访问成员变量,可以有两种方式:

  • 1.通过 struct 变量.成员 变量来访问。
  • 2.通过 struct 指针.成员 变量来访问。

不需要通过 getter, setter 来设置访问权限。

type Rect struct{   //定义矩形类
    x,y float64       //类型只包含属性,并没有方法
    width,height float64
}
func (r *Rect) Area() float64{    //为Rect类型绑定Area的方法,*Rect为指针引用可以修改传入参数的值
    return r.width*r.height         //方法归属于类型,不归属于具体的对象,声明该类型的对象即可调用该类型的方法
}

结构体中属性的首字母大小写问题

  • 首字母大写相当于 public。
  • 首字母小写相当于 private。

注意: 这个 public 和 private 是相对于包(go 文件首行的 package 后面跟的包名)来说的。

敲黑板,划重点

当要将结构体对象转换为 JSON 时,对象中的属性首字母必须是大写,才能正常转换为 JSON。

示例一:

type Person struct {
   Name string      //Name字段首字母大写
   age int               //age字段首字母小写
}
​
func main() {
  person:=Person{"小明",18}
  if result,err:=json.Marshal(&person);err==nil{  //json.Marshal 将对象转换为json字符串
    fmt.Println(string(result))
  }
}
​
//输出
{"Name":"小明"}    //只有Name,没有age

示例二:

type Person  struct{
     Name  string      //都是大写
     Age    int               
}
​
//输出
{"Name":"小明","Age":18}   //两个字段都有

那这样 JSON 字符串以后就只能是大写了么? 当然不是,可以使用 tag 标记要返回的字段名。

示例三:

type Person  struct{
     Name  string   `json:"name"`   //标记json名字为name   
     Age    int     `json:"age"`
     Time int64    `json:"-"`        // 标记忽略该字段
​
}
​
func main(){
  person:=Person{"小明",18, time.Now().Unix()}
  if result,err:=json.Marshal(&person);err==nil{
   fmt.Println(string(result))
  }
}
​
//输出
{"name":"小明","age":18}
```go
 if name == "config.ini" {
        return nil
    } else {
        return errors.New("读取文件错误")
    }
}

func test() {
    err := readConf("config2.ini")
    if err != nil {
        //输出错误并终止程序
        panic(err)
    }
    
    fmt.Println("test()")
}

func main() {
    test()
    fmt.Println("main()")
}

//panic: 读取文件错误

三、引用参考: