Go编程语言:支持并发、垃圾回收的编译型系统级编程语言!本文主要是按照无闻的《Go 编程基础》开源视频学习并记录笔记。编程
函数是基本的代码块,用于执行一个任务。Go 语言最少有个 main() 函数
。函数声明告诉了编译器函数的名称,返回类型,和参数
。
Go 语言标准库提供了多种可动用的内置的函数。例如,len() 函数能够接受不一样类型参数并返回该类型的长度。若是咱们传入的是字符串则返回字符串的长度,若是传入的是数组,则返回数组中包含的函数个数。数组
函数定义格式以下:闭包
func function_name( [parameter list] ) [return_types] { 函数体 }
函数定义解析:并发
func
:函数由 func 开始声明function_name
:函数名称,函数名和参数列表一块儿构成了函数签名。parameter list
:参数列表,参数就像一个占位符
,当函数被调用时,你能够将值传递给参数,这个值被称为实际参数。参数列表指定的是参数类型、顺序、及参数个数
。参数是可选
的,也就是说函数也能够不包含参数。return_types
:返回类型,函数返回一列值。return_types 是该列值的数据类型。有些功能不须要返回值,这种状况下 return_types 不是必须的。函数体
:函数定义的代码集合。示例:编程语言
/* 函数返回两个数的最大值 */ func max(num1, num2 int) int { /* 声明局部变量 */ var result int if (num1 > num2) { result = num1 } else { result = num2 } return result }
不支持
嵌套、重载和默认参数 支持
无需声明原型、不定长度变参、多返回值、命名返回值参数、匿名函数、闭包func
,且左大括号不能另起一行函数也能够做为一种类型使用
ackage main import "fmt" func main() { A(1, 2, 3, 4 ,5) } // ... 不定长变参 func A(a ...int) { fmt.Println(a) }
输出:函数
➜ myfirstgo go run func.go [1 2 3 4 5]
不定长变参特性:
一、不能够在不定长变参后边添加其余参数 func b(a ...int, b string)
, 这种写法是错误
的
二、不定长参数能够放在其余参数后边 func b(b string, a ...int)
学习
func main() { // 将一个函数赋值一个变量,该变量是函数类型 a := func(){ fmt.Println("匿名函数") } // 调用 a() }
/** * 闭包函数 * * 该闭包函数接收一个int型参数,其返回值是函数类型 * */ func closure(x int) func(int) int { fmt.Println("%p\n", &x) return func (y int) int { fmt.Println("%p\n", &x) fmt.Println(x) fmt.Println(y) return x + y } } func main() { f := closure(10); fmt.Println(f(1)) fmt.Println(f(2)) }
打印结果:测试
➜ myfirstgo go run func.go %p 0xc42000e228 %p 0xc42000e228 10 1 11 %p 0xc42000e228 10 2 12 ➜ myfirstgo
析构函数
,在函数体执行结束后按照调用顺序的相反顺序
逐个执行严重错误
也会执行资源清理、文件关闭、解锁以及记录时间
等操做修改
函数计算结果panic/recover
模式来处理错误Panic
能够在任何地方引起,但 recover
只有在 defer 调用的函数中有效简单的测试:指针
func main() { fmt.Println("a") defer fmt.Println("b") defer fmt.Println("c") }
上边的执行会打印什么结果呢? 会打印:a b c 吗?
让咱们实际执行一下:code
myfirstgo go run func.go a c b
实际打印的结果为:a c b
defer 的执行方式相似其余语言中的
析构函数
,在函数体执行结束后按照调用顺序的相反顺序
逐个执行
使用闭包
func main() { for i := 0; i < 3; i++ { // defer 普通调用 // defer fmt.Println(i) // 打印 2 1 0 // 使用闭包,引用局部变量 defer func () { fmt.Println(i) }() } }
打印结果:
➜ myfirstgo go run func.go 3 3 3
panic 使用示例:
func main() { A() B() C() } func A() { fmt.Println("FUNC A") } func B() { // 匿名函数,若是没有参数,则末尾须要使用括号 defer func() { if err := recover(); err != nil { fmt.Println("Recover is B") } }() panic("B panic") } func C() { fmt.Println("FUNC C") }
打印结果:
➜ myfirstgo go run func.go FUNC A Recover is B FUNC C ➜ myfirstgo
Go 语言中数组能够存储同一类型的数据,但在结构体中咱们能够为不一样项定义不一样的数据类型
。
结构体是由一系列具备相同类型或不一样类型的数据构成的数据集合
。
结构体表示一项记录,好比保存图书馆的书籍记录,每本书有如下属性:
Title :标题
Author : 做者
Subject:学科
ID:书籍ID
结构体定义须要使用 type
和 struct
语句。struct 语句定义一个新的数据类型,结构体中有一个或多个成员。type 语句设定告终构体的名称。结构体的格式以下:
type struct_variable_type struct { member definition; member definition; ... member definition; }
一旦定义告终构体类型,它就能用于变量的声明,语法格式以下:
variable_name := structure_variable_type {value1, value2...valuen}
type 是定义名称,struct 是结构体类型,如同 int
类型同样。
结构体能够包含多种数据类型,数组只能是单一类型的数据集合。
访问结构体成员
若是要访问结构体成员,须要使用点号 (.) 操做符,格式为:"结构体.成员名"
。
结构体类型变量使用struct
关键字定义,实例以下:
package main import "fmt" type Books struct { title string author string subject string book_id int } func main() { var Book1 Books /* 声明 Book1 为 Books 类型 */ var Book2 Books /* 声明 Book2 为 Books 类型 */ /* book 1 描述 */ Book1.title = "Go 语言" Book1.author = "www.runoob.com" Book1.subject = "Go 语言教程" Book1.book_id = 6495407 /* book 2 描述 */ Book2.title = "Python 教程" Book2.author = "www.runoob.com" Book2.subject = "Python 语言教程" Book2.book_id = 6495700 /* 打印 Book1 信息 */ fmt.Printf( "Book 1 title : %s\n", Book1.title) fmt.Printf( "Book 1 author : %s\n", Book1.author) fmt.Printf( "Book 1 subject : %s\n", Book1.subject) fmt.Printf( "Book 1 book_id : %d\n", Book1.book_id) /* 打印 Book2 信息 */ fmt.Printf( "Book 2 title : %s\n", Book2.title) fmt.Printf( "Book 2 author : %s\n", Book2.author) fmt.Printf( "Book 2 subject : %s\n", Book2.subject) fmt.Printf( "Book 2 book_id : %d\n", Book2.book_id) }
以上实例执行运行结果为:
Book 1 title : Go 语言 Book 1 author : www.runoob.com Book 1 subject : Go 语言教程 Book 1 book_id : 6495407 Book 2 title : Python 教程 Book 2 author : www.runoob.com Book 2 subject : Python 语言教程 Book 2 book_id : 6495700
type <Name> struct{}
定义结构,名称遵循可见性规则匿名
结构,可用做成员或定义成员变量示例:
package main import "fmt" type person struct{ Name string Age int } func main() { a := person{ Name:"Jam", Age:19, } // a.Age = 20 fmt.Println(a) }
打印:
➜ myfirstgo go run struct.go {Jam 19}
传递指针变量:
package main import "fmt" type person struct{ Name string Age int } func main() { a := person{ Name:"Jam", Age:19, } // a.Age = 20 fmt.Println(a) A(a) // 值拷贝,若是须要原来的改变,则须要添加指针 B(&a) fmt.Println(a) } // per 为变量名,person表示为结构体类型 func A(per person) { per.Age = 25 fmt.Println("A", per) } // per 为变量名,person表示为结构体类型 func B(per *person) { per.Age = 18 fmt.Println("B", per) }
打印:
➜ myfirstgo go run struct.go {Jam 19} A {Jam 25} B &{Jam 18} {Jam 18}
或者在初始化结构体时,获取到变量地址并赋值变量,这样作的好处是在传递参数时,不须要传递地址符号了,只需在函数定义时,给参数加星号便可。
package main import "fmt" type person struct{ Name string Age int } func main() { // 在结构初识化时,咱们习惯取地址符号,这样a就为指向某个结构的指针 a := &person{ Name:"Jam", Age:19, } a.Name = "Corwien" // a.Age = 20 fmt.Println(a) A(a) // 值拷贝,若是须要原来的改变,则须要添加指针 B(a) fmt.Println(a) } // per 为变量名,person表示为结构体类型 func A(per *person) { per.Age = 25 fmt.Println("A", per) } // per 为变量名,person表示为结构体类型 func B(per *person) { per.Age = 18 fmt.Println("B", per) }
打印:
➜ myfirstgo go run struct.go &{Corwien 19} A &{Corwien 25} B &{Corwien 18} &{Corwien 18}
匿名结构:
匿名结构,没有名称的结构体
func main() { // 匿名结构,没有名称的结构体 a := struct { Name string Age int }{ Name:"Corwien", Age: 20, } fmt.Println(a) }
打印:
➜ myfirstgo go run struct.go {Corwien 20}
匿名结构嵌套:
type person struct{ Name string Age int Contact struct { Phone, City string Code int // 门牌号 } } func main() { a := person{Name:"Corwien", Age:15} a.Contact.Phone = "10086" a.Contact.City = "Guangzhou" a.Contact.Code = 2007 fmt.Println(a) }
打印:
➜ myfirstgo go run struct.go {Corwien 15 {10086 Guangzhou 2007}}
匿名字段:
匿名字段:结构体没有命名结构体属性的字段,只有类型,匿名字段必须严格遵照字段类型声明的顺序。
type person struct{ string int } func main() { // 匿名字段必须严格遵照字段类型声明的顺序 a := person{"Corwien", 12} fmt.Println(a) }
打印:
➜ myfirstgo go run struct.go {Corwien 12}
结构类型比较
type person struct{ Name string Age int } func main() { // 匿名字段必须严格遵照字段类型声明的顺序 a := person{Name:"Corwien", Age:12} b := person{Name:"Corwien", Age:12} fmt.Println(a == b) }
打印:
➜ myfirstgo go run struct.go true
咱们知道其余语言有继承,好比相同的属性,咱们没必要重复去写,只需继承父类的公共属性便可。遗憾的是Go没有继承,但Go有组合
.
package main import "fmt" // 嵌入结构做为匿名字段 type human struct { Sex int } type teacher struct { human // Go会将嵌入字段默认做为属性名,因此在赋值时须要这样写:human: human{Sex: 1} Name string Age int } type student struct { human Name string Age int } func main() { a := teacher{Name:"Corwien", Age:25, human: human{Sex: 1}} b := student{Name:"mark", Age:12, human: human{Sex: 1}} a.Name = "Jack" a.Age = 10 // a.human.Sex = 0 a.Sex = 0 fmt.Println(a, b) }
打印:
➜ myfirstgo go run struct.go {{0} Jack 10} {{1} mark 12}
Go不像其它面相对象语言同样能够写个class,而后在class里面写一堆方法,可是它也很巧妙的实现了这种效果,咱们只须要在普通函数前面加个接受者(receiver,写在函数名前面的括号里面),这样编译器就知道这个函数(方法)属于哪一个struct了。
method
是附属在一个给定的类型上,语法和函数的声明语法几乎同样,只是再func后面增长了一个recevier
(也就是method所依从的主体)
func (r ReceiverType) funcName(parameters) (results)
形象一点说,就是 ReceiverType
类型的全部字段,方法 funcName
都是可使用的,能够认为 funcName
属于 ReceiverType
。
示例:
package main import ( "fmt" "math" ) type Rectangle struct { width, height float64 } type Circle struct { radius float64 } func (r Rectangle) area() float64 { return r.width * r.height } func (c Circle) area() float64 { return c.radius * c.radius * math.Pi } func main() { r1 := Rectangle{12, 2} r2 := Rectangle{9, 4} c1 := Circle{10} c2 := Circle{25} fmt.Println("Area of r1 is: ", r1.area()) fmt.Println("Area of r2 is: ", r2.area()) fmt.Println("Area of c1 is: ", c1.area()) fmt.Println("Area of c2 is: ", c2.area()) }
输出:
Area of r1 is: 24 Area of r2 is: 36 Area of c1 is: 314.1592653589793 Area of c2 is: 1963.4954084936207
method 是经过 .
来访问,就像访问struct里面字段同样。
method 里面能够访问接受者的字段,好比 r1.area() 就能够访问 r1 里面的 width 和 height。
虽然 method 的名字是同样的,可是不一样的 receiver 不同,那么 method 就不同。这一点很重要哦
。
还有一点,method不只能做用再struct上,也能够定义再任何自定义的类型、内置类型等各类类型上面。
method 中的 receiver 能够是值传递,也能够是指针。指针的话,就能够直接修改 receiver 中的内容。
不存在方法重载
方法是函数的语法糖
,由于receiver其实就是方法所接收的第1个参数)举例:
package main import "fmt" type A struct { Name string } type B struct { Name string } func main() { a := A{} a.Print() fmt.Println(a.Name) b := B{} b.Print() fmt.Println(b.Name) } // 指针传递 func (a *A) Print() { a.Name = "AA" fmt.Println("A") } func (b B) Print() { b.Name = "BB" fmt.Println("B") }
打印:
➜ myfirstgo go run method.go A AA B ➜ myfirstgo
type TZ int func main() { var a TZ a.Print() fmt.Println(a) } func (a *TZ) Print() { fmt.Println("TZ") }
打印:
➜ myfirstgo go run method.go TZ 0
最后说下访问权限,由于Go是以大小写来区分是公有仍是私有,但都是针对包级别的,因此在包内全部的都能访问,而方法绑定自己只能绑定包内的类型,因此方法能够访问接收者全部成员。若是是包外调用某类型的方法,则须要看方法名是大写仍是小写,大写能被包外访问,小写只能被包内访问。