摘要 首页显示摘要内容(替换成自己的)
写在前面
嗯,学习GO
,所以有了这篇文章
博文部分内容为《GO语言实战》
读书笔记之一
主要涉及知识
傍晚时分,你坐在屋檐下,看着天慢慢地黑下去,心里寂寞而凄凉,感到自己的生命被剥夺了。当时我是个年轻人,但我害怕这样生活下去,衰老下去。在我看来,这是比死亡更可怕的事。——–王小波
对于这个概念小伙伴一定不陌生,常用的脚本语言中一般都会用,通过函数来整合一些逻辑上耦合性较强的代码块,从而提高代码的可读性,可复用性。
GO语言中的函数语法格式和其他语言略有不同,函数可以先使用后定义,同时go支持闭包,拆包,异常信息
返回等
语法格式
1 2 3 func funcName (param paramType) (returnVal1 returnType, err error) { }
demo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 package mainimport ( "fmt" "time" ) func main () { nowTime := time.Now() fmt.Println(nowTime.Format("2006-01-02 15:04:05" )) i, j := func_demo() func_demos("你好,世界!" ) fmt.Println(i, j) get_func_demo := func (x int ) int { return x } fmt.Println(get_func_demo(3 )) f := get_func_() fmt.Println(f()) } func func_demo () (int , int ) { fmt.Println("你好,世界!" ) return 1 , 2 } func func_demos (mes string ) { fmt.Println(mes) } func get_func_ () (func () int ) { i := 1 return func () (int ) { i+=1 return i } } ============ 2022 -04 -14 10 :01 :03 你好,世界! 你好,世界! 1 2 3 2
其他的脚本语言对比 对于Shell
脚本来说,shell脚本中的函数只能先定义后使用,函数可以看做是一个微小版的shell脚本,功能语法相对简单
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 #!/bin/bash function f (){ echo $(date) } f func_name (){ echo "Hello,Word!" } func_name func_names (){ echo "${0} " echo "${1} " } func_names 5 func_array (){ my_array=("liruilong" 1 2 3) echo ${my_array[*]} } retuens=($(func_array)) echo "${retuens[*]} " func_return (){ return 1 } func_return =========================== 2022年 04月 14日 星期四 10:36:20 CST Hello,Word! fun.sh 5 liruilong 1 2 3
对于Python
来讲,函数也必须先定义后使用。python的函数语法要多一点,支持可变参数,关键字参数,缺省参数,同时支持拆包,闭包等
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 def func_demo (): print ("Hello,Word" ) func_demo() def func_param (a,b,c="," ): print (a,c,b) func_param("Hello" ,"Word" ) func_param(b="Word" ,a="Hello" ) def func_return (): return 2 print (func_return())def func_arges (a,*arges ): for i in arges: print (i) func_arges(1 ,2 ,3 ) func_arges(1 ) def func_aeges_ (**arges ): print (arges) func_aeges_(name='liruilong' , age=18 , id =110 ) def return_num (): return 100 , 200 num1, num2 = return_num() ========================== Hello,Word Hello , Word Hello , Word 2 2 3 {'name' : 'liruilong' , 'age' : 18 , 'id' : 110 }
关于函数介绍到这里,来看看方法,GO语言中方法实际上也是函数,只是在声明时,在关键字func
和方法名
之间增加了一个参数
在面向对象里,方法是类行为和对象行为的体现(类方法和实例方法),GO不是面向对象类型的语言,但是Go方法和java中的方法类似,同样不能单一存在,必须要绑定。
Java中的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class Function { String str; public String getStr () { return str; } public static Function build () { return new Function(); } public static void main (String[] args) { System.out.println("Hello,Word!" ); } }
java中方法必定是和类
或者对象
绑定的,和类绑定的方法被称为静态方法,和对象绑定的为实例方法。GO中的方法必须要和接收者绑定,那GO
中的所谓的接收者又是什么?,即用户定义的类型,听着有些别扭,类比C
中我们常讲的结构体
用户定义的类型(结构体) Go 语言允许用户定义类型(结构体)
。当用户声明一个新类型时,这个声明就给编译器提供了一个框架,告知必要的内存大小
和表示信息
Go 语言里声明用户定义的类型有两种方法。最常用的方法是使用关键字struct
,它可以让用户创建一个结构类型
。
结构里每个字段
都会用一个已知类型声明
。这个已知类型可以是内置类型
,也可以是其他用户定义的类型
。
1 2 3 4 5 type user struct { name string email string }
当声明变量时,这个变量对应的值总是会被初始化
。这个值要么用指定的值初始化
,要么用零值
(即变量类型的默认值
)做初始化
对数值类型来说,零值是 0
;
对字符串来说,零值是空字符串
;
对布尔类型,零值是 false
。
:=
:一个短变量声明操作符在一次操作中完成两件事情:声明一个变量,并初始化
1 2 3 4 lisa := user{ name: "liruilong" , email: "liruilong@qq.com" }
1 2 3 4 user{ name: "liruilong" , email: "liruilong@qq.com" }
不使用字段名,创建结构类型的值,这种形式下,值的顺序很重要,必须要和结构声明中字段的顺序一致。
1 user{"Bill" , "bill@email.com" }
当声明结构类型时,字段的类型并不限制在内置类型,也可以使用其他用户定义的类型
1 2 3 4 5 6 7 8 type admin struct { liruilong user, leve string } fred := admin{ liruilong: user{"Bill" , "bill@email.com" }, level: "super" , }
另一种声明用户定义的类型的方法是,基于一个已有的类型,将其作为新类型的类型说明
。
Duration
是一种描述时间间隔的类型,单位是纳秒(ns)。这个类型使用内置的 int64
类型作为其表示
我们把 int64
类型叫作 Duration
的基础类型,Go 并不认为 Duration 和 int64 是同一种类型。这两个类型是完全不同的有区别的,这里和有些面向对象的语言有很大的区别。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package mainimport ( "fmt" ) type Duration int64 func main () { var dur Duration dur = int64 (1000 ) fmt.Println(dur) } ============ [Running] go run "d:\GolandProjects\code-master\demo\hello.go" # command-line-arguments demo\hello.go :10 :6 : cannot use int64 (1000 ) (type int64 ) as type Duration in assignment [Done] exited with code=2 in 0.705 seconds
OK,了解了GO中的结构体,我们来看看方法
方法 在GO
中,通过方法
能给用户定义的类型
也就是俗称的结构体
添加新的行为。声明时,func
和方法名
之间增加了一个结构体参数
我们把关键字func
和函数名之间的参数被称作接收者
,将函数
与接收者
的类型绑在一起。如果一个函数有接收者,这个函数就被称为方法。
Go 语言里有两种类型的接收者:值接收者
,指针接收者
。
使用值接收者声明一个方法 1 2 3 4 5 6 func (u user) notify () { fmt.Printf("Sending User Email To %s<%s>\n" , u.name, u.email) }
如果使用 值接收者声明方法
,调用时会使用这个值的一个副本来执行
。使用 bill
的值作为接收者进行调用,方法 notify
会接收到 bill
的值的一个副本。
1 2 3 bill := user{"Bill" , "bill@email.com" } bill.notify()
也可以使用指针来调用使用值接收者声明的方法
,使用指向 user
类型值的指针来调用 notify 方法
1 2 3 lisa := &user{"Lisa" , "lisa@email.com" } lisa.notify()
指针被解引用为值,不管是变量调用还是指针调用,notify 操作的都是一个副本。
使用指针接收者声明一个方法 1 2 3 4 func (u *user) changeEmail (email string ) { u.email = email }
使用指针接收者声明。这个接收者的类型是指向 user 类型值的指针
,而不是 user 类型的值
。当调用使用指针接收者声明的方法时,这个方法会共享调用方法时接收者所指向的值
这一点和使用值接收有很大的区别,某种意义上讲,使用值接收
一般用于消费,使用指针接收
,一般用于生产加工
修饰。
1 2 3 4 lisa := &user{"Lisa" , "lisa@email.com" } lisa.changeEmail("lisa@newdomain.com" ) lisa.notify()
值接收者使用值的副本来调用方法,而指针接受者使用实际值来调用方法。
1 2 3 4 bill := user{"Bill" , "bill@email.com" } bill.changeEmail("bill@newdomain.com" ) bill.notify()
Go语言既允许使用值,也允许使用指针来调用方法,不必严格符合接收者的类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 package mainimport ( "fmt" ) type user struct { name string email string } func (u user) notify () { fmt.Printf("Sending User Email To %s<%s>\n" , u.name, u.email) } func (u *user) changeEmail (email string ) { u.email = email } func main () { bill := user{"Bill" , "bill@email.com" } bill.notify() lisa := &user{"Lisa" , "lisa@email.com" } lisa.notify() bill.changeEmail("bill@newdomain.com" ) bill.notify() lisa.changeEmail("lisa@newdomain.com" ) lisa.notify() } ================ [Running] go run "d:\GolandProjects\code-master\chapter5\listing11\tempCodeRunnerFile.go" Sending User Email To Bill<bill@email.com> Sending User Email To Lisa<lisa@email.com> Sending User Email To Bill<bill@newdomain.com> Sending User Email To Lisa<lisa@newdomain.com> [Done] exited with code=0 in 2.288 seconds
bill := user{"Bill", "bill@email.com"}
和 lisa := &user{"Lisa", "lisa@email.com"}
的区别
bill
是值接收者,它创建了一个user类型的值。任何对bill的操作都不会影响实际的user对象。
lisa
是指针接收者,它创建了一个指向user对象的指针*user。任何对lisa的操作实际上都是对底层user对象的操作。
具体来说:
bill是一个用户值,而lisa是一个用户地址。
对bill的修改,只会修改bill本身,不会影响实际的user。
而对lisa的修改,通过它修改的是实际的user对象。
值传递只影响局部变量,不影响原对象
指针传递通过指针可以修改原对象
博文部分内容参考 © 文中涉及参考链接内容版权归原作者所有,如有侵权请告知,这是一个开源项目,如果你认可它,不要吝啬星星哦 :)
《GO语言实战》
© 2018-至今 liruilonger@gmail.com , All rights reserved. 保持署名-非商用-相同方式共享(CC BY-NC-SA 4.0)