Go编程语言:支持并发、垃圾回收的编译型系统级编程语言!本文主要是按照无闻的《Go 编程基础》开源视频学习并记录笔记。
一、函数function
1、基本概念
Go 语言最少有个 main() 函数函数声明告诉了编译器函数的名称,返回类型,和参数
2、函数定义
函数定义格式如下:
func function_name( [parameter list] ) [return_types] {
函数体
}
函数定义解析:
funcfunction_nameparameter list参数就像一个占位符参数类型、顺序、及参数个数可选return_types函数体
示例:
/* 函数返回两个数的最大值 */
func max(num1, num2 int) int {
/* 声明局部变量 */
var result int
if (num1 > num2) {
result = num1
} else {
result = num2
}
return result
}
3、函数特性
不支持支持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)
4、匿名函数
func main() {
// 将一个函数赋值一个变量,该变量是函数类型
a := func(){
fmt.Println("匿名函数")
}
// 调用
a()
}
5、闭包
/**
* 闭包函数
*
* 该闭包函数接收一个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
6、defer
析构函数相反顺序严重错误资源清理、文件关闭、解锁以及记录时间修改panic/recoverPanicrecover
简单的测试:
func main() {
fmt.Println("a")
defer fmt.Println("b")
defer fmt.Println("c")
}
上边的执行会打印什么结果呢? 会打印:a b c 吗?
让我们实际执行一下:
myfirstgo go run func.go
a
c
b
实际打印的结果为:a c b
析构函数相反顺序
使用闭包
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
二、结构Struct
1.基本概念
在结构体中我们可以为不同项定义不同的数据类型由一系列具有相同类型或不同类型的数据构成的数据集合
2.结构体定义
typestruct
type struct_variable_type struct {
member definition;
member definition;
...
member definition;
}
一旦定义了结构体类型,它就能用于变量的声明,语法格式如下:
variable_name := structure_variable_type {value1, value2...valuen}
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
3.struct特性
type 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如何继承呢
组合
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}
三、方法method
1.基本概念
Go不像其它面相对象语言一样可以写个class,然后在class里面写一堆方法,但是它也很巧妙的实现了这种效果,我们只需要在普通函数前面加个接受者(receiver,写在函数名前面的括号里面),这样编译器就知道这个函数(方法)属于哪个struct了。
methodrecevier
2.method定义
func (r ReceiverType) funcName(parameters) (results)
ReceiverTypefuncNamefuncNameReceiverType
示例:
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 里面可以访问接受者的字段,比如 r1.area() 就可以访问 r1 里面的 width 和 height。
虽然 method 的名字是一样的,但是不同的 receiver 不一样,那么 method 就不一样。这一点很重要哦
还有一点,method不仅能作用再struct上,也可以定义再任何自定义的类型、内置类型等各种类型上面。
method 中的 receiver 可以是值传递,也可以是指针。指针的话,就可以直接修改 receiver 中的内容。
3.method特性
不存在方法重载方法是函数的语法糖
举例:
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是以大小写来区分是公有还是私有,但都是针对包级别的,所以在包内所有的都能访问,而方法绑定本身只能绑定包内的类型,所以方法可以访问接收者所有成员。如果是包外调用某类型的方法,则需要看方法名是大写还是小写,大写能被包外访问,小写只能被包内访问。