




版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
go語言學習筆記(初級)
最近一直在學習g。語言,因此打算學習的時候能夠記錄
一下筆記。我這個人之前是從來沒有記錄筆記的習慣,
一直以來都是靠強大的記憶力去把一些要點記住。
讀書的時候因為一直都是有一個很安靜和很專心的環境,
因此很多事情都能記得很清楚,思考的很透徹。但是隨著
年紀不斷增加,也算是經歷了很多的事情,加上工作有時會讓人
特別煩悶,很難把心好好靜下來去學習,去思考大自然的終極
奧秘,因此需要記錄一些東西,這些東西一方面可以作為一種自我激勵
的機制,另一方面,也算是自己成長的軌跡吧。
一.順序編程
1.變量
g。語言變量定義的關鍵字是var。類型放在變量名后:
varvlint
varv2string
varv3[10]int〃數組
varv4[]int〃切片
varv5struct{〃結構體
fint
}
varv6*int〃指針
varv7map[string]int//map
varv8func(aint)int〃函數
每一行不需要以分號作為結尾。var
關鍵字還有一種是把多個變量的申明放在一起,
var(
vlstring
v2int
)
2.變量初始化
有人說,變量初始化有什么好提的,那么簡單。是的,但是這里面確實
還是有一些值得注意的點。
varaint=10〃完整定義
vara=10〃自動推斷是int型
a:二10〃自動推斷是int型,申明并未該變量賦值
第三種初始化方式無疑是最簡單的。
但是要注意的是,這里面第三種方式是和特別的,比如
varaint
a:=10
等價于
varaint
varaint
a=10
這時候就會報一個重復定義的錯誤。
3.變量賦值
變量賦值和我們通常的語言基本是一致的,但是多了多重賦值功能。
1,J=J,1
這就直接實現了兩個變量的交換。
4.匿名變量
g。語言的函數是多返回值的,因此可能有些值并沒有被用到,這時我
們就需要一個占位符去忽略這些返回值。
funcGetName()(firstName,lastName,nickNamestring){
return〃May〃,〃Chan〃,〃ChibiMarukoz/
}
nickName:=GetName()
5.定義常量
通過const關鍵字,可以用來定義常量。
constPifloat64=3.1415926
constzero=0.0〃自動推斷類型
const(〃多定義
sizeint64=10
hello=-1
)
constu,vfloat32=0.0,3.0〃多重賦值
consta,b,c=1,2,"hello”〃自動推斷類型
常量的定義也可以跟一個表達式,但是這個表達式應該是編譯的時候
就可以求值的.
constmask=1<<3〃正常
constHome=os.GetEnv("HOME")〃錯誤,運行時才能確定
6.預定義常量
這里要講一個很有意思的東西,叫做iota.
這個東西每一個const出現的位置被置為0,沒出現一個iota出現,都自
增1,到寫一個const出現的時候,又置為0.
const(
cl=iota//0
c2=iota//I
c3=iota//2
)
constx=iota//x==0(因為iota又被重設為0了)
consty=iota//y二二0(同上)
如果兩個const賦值表達式是一樣的,可以省略后面的賦值表達式.
const(
cl=iota//0
c2//I
c3//3
)
const(
a=1<<iota//a==1(iota在每個const開頭被重設為0)
b//b==2
c//c==4
6.枚舉
const(
Sunday=iota
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
numberOfDays//這個常量沒有導出
)
大寫字母開頭的包外可見,小寫字母開頭的包外不可見.
7.類型
?整型
int8,uint8,int16,uint16,int32,uint32,int64,uint64,int,uint,uintptr
不同類型不能相互比較.
?浮點類型
float32,float64
涉及的一些運算比較簡單,我們不做細講.
?字符串類型
下面我們展示一個完整的go語言程序,也是以hello
world為主題,畢竟helloworld是一個萬斤油的主題.
packagemain
importz/fmtzz〃引入依賴包
funcmain(){
fmt.Printin(/zhello,world!/z)
}
這基本上是一個最簡單的程序了,但是對于我們的學習非常有用,用這個
模板可以寫出非常好的東西出來.
字符串串本身非常簡單,主要就是一些字符串操作,比如取特定位置的
字符等.
packagemain
import〃引入依賴包
funcmain(){
varstrstring="hello,world!〃
fmt.Println(str)
ch:=str[0]〃取某個特定位置的字符
fmt.Printf(〃%c\n〃,ch)
length:=len(str)
fmt.Printin(length)//len用來獲取長度
str=〃你好,世界〃
ch=str[0]
fmt.Printf(〃%c\n〃,ch)
length=len(str)
fmt.Printin(length)
)
輸出結果為:
hello,world!
h
12
9
13
這正好說明□和len都不能處理中文.
字符串連接也是用+.
字符串的遍歷:
packagemain
import〃引入依賴包
funcmain(){
varstrstring="hello,world!〃
n:=len(str)
fori:=0;i<n;i++{
ch:=str[i]
fmt.Printf(〃%c\n〃,ch)
}
}
輸出結果:
h
e
1
1
o
w
0
r
1
d
I
對于中文,結果是亂碼,因為是一個字節一個字節輸出的,但是默認是
UTF8編碼,一個中文對應3個字節.
這里我們要提到一個range的關鍵字,它可以把字符串按鍵值對的方式
返回.
packagemain
import〃引入依賴包
funcmain(){
varstrstring=z/hello,world!你好,世界!〃
forch:=rangestr{
fmt.Printf(〃%c\n〃,ch)
}
)
輸出結果為:
h
e
1
1
o
w
o
r
1
d
I
你
好
世
界
!
事實上,字符類型有兩種,一種就是byte(uint8),另一種是rune.第一
種遍歷字符串ch是byte,而第二種是rune.
?數組
數組這種類型是非常常見的
[32]byte
[2*N]struct{x,yint32}
[1000]*float64
[3][5]int
[2][2][2]float64
數組的遍歷和字符串一樣,這里不再重復.
數組是值類型,在賦值時會拷貝一份.
?數組切片
數組切片的概念比較復雜,它有點類似于C++中vector的概念,但又不
完全一樣.
我們這里詳細提幾點.
1.切片的創建
切片有兩種創建方式,一種是基于數組創建,另一種是用make
創建.
packagemain
import〃fmt〃〃引入依賴包
funcmain(){
//從數組創建
varmyArray[10]int=[10]int{1,2,3,4,5,6,7,8,9,10)
varsa[]int=myArray[5:]
fore:=rangesa{
fmt.Println(e)
)
fmt.Printin(len(sa))
fmt.Printin(cap(sa))
〃從make創建
varmySlice2[]int=make([]int,5,10)
fore:=rangemySlice2{
fmt.Println(e)
}
fmt.Printin(len(mySlice2))
fmt.Printin(cap(mySlice2))
〃賦值
varmySlice3[]int=[]int{1,2,3)
fore:=rangemySlice2{
fmt.Println(e)
slice是引用類型.
packagemain
import/zfmt,z〃引入依賴包
functest(a[10]int){
a[0]=10
}
funcprintArray(a[10]int){
fore:=rangea{
fmt.Println(e)
}
)
funcmain(){
varmyArray[10]int=[10]int{1,2,3,4,5,6,7,8,9,10}
printArray(myArray)
test(myArray)
printArray(myArray)
)
輸出結果:
1
2
3
4
5
6
7
8
9
10
1
2
3
4
5
6
7
8
9
10
我們發現數組確實是按照值來傳遞.那么如果是slice呢,會發生
什么?
packagemain
import〃引入依賴包
functest(a[]int){
a[0]=10
}
funcprintArray(a[]int){
fore:=rangea{
fmt.Println(e)
}
)
funcmain(){
varmyArray[]int=[]int{1,2,3,4,5,6,7,8,9,10}
printArray(myArray)
test(myArray)
printArray(myArray)
輸出結果:
1
2
3
4
5
6
7
8
9
10
10
2
3
4
5
6
7
8
9
10
確實是按照引用來傳遞的.
append函數可以往切片尾部增加元素.
mySlice=append(mySlice,1,2,3)
mySlice=append(mySlice,mySlice2...)
…表示把一個slice拆成元素來處理.
packagemain
import〃fnit〃〃引入依賴包
funcmain(){
varslicel[]int=make([]int,5,10)
varslice2[]int=[]int{1,2,3)
fmt.Println(slicel)
fmt.Printf(〃%p\n〃,slicel)
slicel=append(slicel,slice2...)
fmt.Println(slicel)
fmt.Printf(〃%p\n〃,slicel)
slicel=append(slicel,slice2...)
fmt.Printin(slicel)
fmt.Printf(〃%p\n〃,slicel)
}
輸出結果:
[00000]
0xc820012190
[00000123]
0xc820012190
[00000123123]
0xc82005e000
在這里我們看到,slice的地址是所隨著內內存的改變而變化的,因
此是需要仔細思考的.我個人不覺得
go語言這種特性有什么好的,反正也是奇葩極了.不過slice還提
供copy,也算是一些彌補吧.
map
go語言中,map使用非常簡單.基本上看代碼就會了.
packagemain
importz/fmt/z〃引入依賴包
〃定義一個Person的結構體
typePersonstruct{
namestring
ageint
)
funcmain(){
vardiemap[string]Person=make(map[string]Person,100)//初始化
map
dic[〃1234〃]=Person{name:z,lileiz/,age:100)
dic[〃12345〃]=Person{name:zzhanmeimei/z,age:20}
die[〃123456〃]=Person{name:/zdagong/z,age:30}
fmt.Println(dic)
〃刪除dagong
delete(die,“123456")
fmt.Println(dic)
〃查找某個key
value,ok:=dic]〃123456〃]
ifok{
fmt.Printin(value)
}
value,ok=dic]〃1234〃]
ifok{
fmt.Printin(value)
}
fork,v:=rangedie{
fmt.Println(k+〃:〃+v.name)
輸出結果為:
map[12345:{hanmeimei20}123456:{dagong30}1234:{lilei100}]
map[1234:{lilei100}12345:{hanmeimei20}]
{lilei100)
12345:hanmeimei
1234:lilei
map很簡單吧.數據結構我們講完了,接下來可以看看代碼的程序控制
了.
8.程序控制
程序控制本人只提一些關鍵性的東西,不會啰嗦太多.
?switch語句
switch語句不需要在每個case地下寫break,默認就是執行break.如果
要執行多個case,在case最后加入fallthrough.
條件表達式不限制為常量或者整數.單個case自然可以有多個結果可以
選.
packagemain
import〃引入依賴包
functest(aint){
switch{
casea<0:
fmt.Printin("hello")
caseEL——10:
fallthrough
casea>10&&a<100:
fmt.Printin("world")
default:
fmt.Println(/,nimazz)
)
}
funcmain(){
test(-1)
test(10)
test(100)
}
?循環
go語言的循環比較特別,它用一個for就把for和while的活都干了.
packagemain
import"fmt〃〃引入依賴包
funcmain(){
sum:=0
fori:=0;i<=100;i++{
sum+=i
}
fmt.Println(sum)
sum=0
i:=0
for(i<=100){
sum+=i
i++
)
fmt.Println(sum)
}
break還支持break到指定的label處.
forj:=0;j<5;j++{
fori:=0;i<10;i++{
ifi>5{
breakJLoop
}
fmt.Println(i)
}
)
JLoop:
//...
?函數
函數是一個非常重要的概念,也很簡單.go的函數以func關鍵字定義,
支持不定參數和多返回值.函數名首字符的大小寫是很有講究的:
小寫字母開頭的函數只在本包內可見,大寫字母開頭的函數才能被其他
包使用。這個規則也適用于類型和變量的可見性。
packagemain
import〃引入依賴包
//...int是不定參數,實際上就是一個slice,a,b是多返回值
funcSumAndAverage(sample...int)(a,bfloat64){
a,b=0,0
ford:=rangesample{
a+=float64(d)
)
iflen(sample)==0{
b=0
}else{
b=a/float64(len(sample))
}
returna,b
}
funcmain(){
a,b:=SumAndAverage(1,2,3)
fmt.Println(a,b)
}
很簡單吧.注意,如果是函數里面調了其他函數,那么這個sample怎
么傳給其他喊函數呢?
sample...//...表示是拆成一個個元素傳遞
匿名函數的概念也很簡單,只要看代碼就會明白.
packagemain
import/zfmtz/〃引入依賴包
funcmain(){
varmyFuncfunc(...int)(float64,float64)=func(sample...int)(a,b
float64){
a,b=0,0
ford:=rangesample{
a+=float64(d)
}
iflen(sample)二二0{
b=0
}else{
b=a/float64(len(sample))
}
returna,b
}
a,b:=myFunc(1,2,3)
fmt.Println(a,b)
)
下面是關于閉包的概念.這個概念在許式偉的書中被詮釋的非常好:
閉包是可以包含自由(未綁定到特定對象)變量的代碼塊,這些變量不
在這個代碼塊內或者任何全局上下文中定義,
而是在定義代碼塊的環境中定義。
要執行的代碼塊(由于自由變量包含在代碼塊中,所以這些自由變量以
及它們引用的對象沒有被釋放)為自由變量提供綁定定的計算環境(作
用域)。
閉包的實現確保只要閉包還被使用,那么被閉包引用的變量會一直存
在.*
我們來看來兩個閉包的例子.
packagemain
importz/fmtzz〃引入依賴包
functest(iint)func(){
returnfunc(){
fmt.Println(10+i)
fmt.Printf(〃%p\n〃,&i)
)
}
funcmain(){
a:=test(1);
b:=test(2)
a()
b()
)
輸出結果:
11
0xc82000a288
12
0xc82000a2c0
我們從這個結果中發現,i的地址是會變的,因為是作為一個局部變量傳
進去的.
packagemain
import/zfmtz/〃引入依賴包
functest(xint)func(int)int(
returnfunc(yint)int{
fmt.Printf(/z%p\n/z,&x)
returnx+y
funcmain(){
a:=test(1);
fmt.Printin(a(10))
fmt.Printin(a(20))
}
輸出結果:
0xc82000a288
11
0xc82000a288
21
因為X只傳入了一次,因此沒有改變.
packagemain
import(
〃fmt〃
)
funcmain(){
varjint=5
a:=func()(func()){
variint=10
returnfunc(){
fmt.Printf(/zi,j:%d,%d\n〃,i,j)
}
)()
a()
j*=2
a()
此時輸出:
i,j:10,5
i,j:10,10
二.面向對象編程
這里我們先提值語義和引用語義的概念.
b=a
b.Mofify()
如果b改變,a不發生改變,就是值語義.如果b改變,a也發生改變,就
是引用語義.
g。語言大多數類型都是值語義,比如:
基本類型:byte,int,float32,float64,string
復合類型:struct,array,pointer
也有引用語義的,比如:
slice,map,channel,interface.
這是我們要牢記的.
我們的筆記整體式按照許式偉的書來安排,但是由于許的書提綱性很
強,內容上不是很詳細,基本上會重新整理補充一些東西進去.
?結構體
結構體是用struct來申明的,不多廢話,直接上代碼.
packagemain
import(
〃fmt〃
)
〃申明一個結構體
typePersonstruct{
Namestring
Ageint
}
funcmain(){
〃結構體的初始化方式
//I.直接賦值
varpPerson
p.Name="dingding”
p.Age=10
fmt.Println(p)
〃2,順序賦值
pl:=Person{"dingding”,10}
fmt.Println(pl)
//3.keyvalue賦值
p2:=Person{Name:z,dingdingz/,Age:10}
fmt.Println(p2)
〃4.指針賦值
p3:=&Person{Name:z/dingding/z,Age:10}
fmt.Println(p3)
p4:=new(Person)
fmt.Println(p4)
fmt.Println(,z--------------------------〃)
a:=p
a.Name="dongdong”
b:=p3
b.Name="dongdong”
fmt.Println(p)
fmt.Println(p3)
)
輸出結果:
{dingding10)
(dingding10}
{dingding10)
&{dingding10}
&{0}
(dingding10}
&{dongdong10}
這說明,struct確實是值語義.
下面討論一下結構體的組合問題.這點許的書中并沒有過多涉及,但是
還是很有必要的,因為在實際場合中用的會很多.
packagemain
import(
〃fmt〃
〃申明一個結構體
typeHumanstruct{
Namestring
Ageint
Phonestring
}
//再申明一個結構體
typeEmployeestruct{
PersonHuman//匿名字段Human
Specialitystring
Phonestring//雇員的phone字段
)
funcmain(){
e:=Employee{
Person:Human{
Name:〃dingding”,
Age:11,
Phone:〃6666666”,
},
Speciality:/zaaa/z,
Phone:〃77777777〃,
}
fmt.Println(e.Phone)
}
這段代碼看上去非常ok,但是如果我們稍微改一下代碼呢?比如把
Person改成匿名結構,會有些好玩的現象.
packagemain
import(
〃fmt〃
)
〃申明一個結構體
typeHumanstruct{
Namestring
Ageint
Phonestring
}
//再申明一個結構體
typeEmployeestruct{
Human//匿名字段Human
Specialitystring
//Phonestring//雇員的phone字段
}
funcmain(){
e:=Employee{
Human{"dingding”,11,”6666666〃},
〃〃
〃Phone:"77777777”,
)
fmt.Printin(e.Phone)
}
此時輸出的是6666666,因為相當于它把Human的字段Phone繼承下
來了.
如果Employee里也定義Phone字段呢?
packagemain
import(
〃fmt〃
)
〃申明一個結構體
typeHumanstruct{
Namestring
Ageint
Phonestring
}
//再申明一個結構體
typeEmployeestruct{
Human//匿名字段Human
Specialitystring
Phonestring//雇員的phone字段
)
funcmain(){
e:=Employee{
Human{/zdingding/z,11,“6666666〃},
〃〃
“77777777”,
}
fmt.Printin(e.Phone)
}
此時輸出的時77777777,因為相當于是覆蓋.那么怎么輸出6666666
呢?
packagemain
import(
〃fmt〃
)
〃申明一個結構體
typeHumanstruct{
Namestring
Ageint
Phonestring
}
//再申明一個結構體
typeEmployeestruct{
Human//匿名字段Human
Specialitystring
Phonestring//雇員的phone字段
}
funcmain(){
e:=Employee{
Human{"dingding”,11,”6666666〃},
〃〃
“77777777”,
)
fmt.Printin(e.Phone)
fmt.Printin(e.Human.Phone)
)
輸出結果:
77777777
6666666
所以,匿名結構體的組合相當于有繼承的功能.
?為類型添加方法
這個概念和java或者是C++非常不一樣,它的理念是把似乎是把方法
綁定到特定類型上去.
這個概念已經不僅僅是對象的概念了,事實上,
我也不知道google這幫人腦子是怎么想的,這種搓劣的復古風格,
也是讓我打開眼界,我個人覺得,g。雖然仗著google的名氣,似乎顯得
很厲害,但是,
比起java和C++,簡直就是個玩具,說的不好聽一點,
比起scala這樣的一出生就是個大胖子,go更像是個缺胳膊少腿的畸形
兒.
好了,不說了,直接上代碼.
packagemain
import(
〃fmt〃
)
//go綁定方法必須是本包內的,int不是main包內定義的.
〃因此需要type一下,Integer就是本包內定義的類型
typeIntegerint
〃為int綁定一個Print方法
func(iInteger)PrintlnO{
fmt.Println(i)
funcmain(){
varaInteger=10
a.PrintlnO
}
結果輸出10,如果是如下呢?
packagemain
import(
〃fmt〃
)
//go綁定方法必須是本包內的,int不是main包內定義的.
〃因此需要type一下,Integer就是本包內定義的類型
typeIntegerint
〃為int綁定?個Print方法
func(iInteger)PrintlnO{
fmt.Println(i)
}
funcmain(){
a:=10
a.Printin()
}
輸出結果:
#command-line-arguments
./main,go:18:a.Printinundefined(typeinthasnofieldormethod
Printin)
因為a:=10,go會把a推斷為int,但int并沒綁上Printin方法.
注意:
〃為int綁定一個Print方法
func(iInteger)PrintlnO{
fmt.Println(i)
)
這里的i并不是引用類型,因此對i的修改不會反應到a上去.
packagemain
import(
〃fmt〃
)
//go綁定方法必須是本包內的,int不是main包內定義的.
〃因此需要type一下,Integer就是本包內定義的類型
typeIntegerint
〃為int綁定一個Print方法
func(iInteger)Add(){
i+=2
}
funcmain(){
varaInteger=10
a.Add()
fmt.Println(a)
輸出10.
如果我們用引用呢?
packagemain
import(
〃fmt〃
)
//go綁定方法必須是本包內的,int不是main包內定義的.
〃因此需要type一下,Integer就是本包內定義的類型
typeIntegerint
〃為int綁定?個Print方法
func(this*Integer)Add(){
*this+=2
)
funcmain(){
varaInteger=10
a.Add()
fmt.Println(a)
)
這時輸出12.我們發現,這個this就像是我們C++里面的this指針一樣.
不過也傻13復古的多.
我們在看一個例子,
packagemain
import(
〃fmt〃
//go綁定方法必須是本包內的,int不是main包內定義的.
〃因此需要type一下,Integer就是本包內定義的類型
typeIntegerint
〃為int綁定一個Print方法
func(this*Integer)Add(){
fmt.Printin(this)
*this+=2
}
funcmain(){
varbInteger=10
vara*Integer=&b
a.Add()
fmt.Println(a)
)
輸出結果:
0xc82000a288
0xc82000a288
我們發現,
〃為int綁定一個Print方法
func(this^Integer)Add(){
fmt.Printin(this)
*this+=2
)
該方法用指針調用和用值去調用,效果是一樣的.this都是指向原來對
象的指針而已.
下面這個例子來自于許的書中,
packagemain
typeIntegerint
func(aInteger)Less(bInteger)bool{
returna<b
)
func(a^Integer)Add(bInteger){
*a+=b
}
typeLessAdderinterface{
Less(bInteger)bool
Add(bInteger)
)
funcmain(){
varaInteger=1
varbLessAdder=a
}
輸出:
#command-1ine-arguments
./main,go:20:cannotusea(typeInteger)astypeLessAdderinassignment:
IntegerdoesnotimplementLessAdder(Addmethodhaspointerreceiver)
這個例子似乎有點奇怪,為什么呢?
packagemain
typeIntegerint
func(aInteger)Less(bInteger)bool{
returna<b
)
func(a*Integer)Add(bInteger){
*a+=b
}
typeLessAdderinterface{
Less(bInteger)bool
Add(bInteger)
}
typeLessinterface{
Less(bInteger)bool
}
typeAdderinterface{
Add(bInteger)
}
funcmain(){
varaInteger=1
varbAdder=a
b.Add(10)
}
我們可以看得更清楚:
./main,go:28:cannotusea(typeInteger)astypeAdderinassignment:
IntegerdoesnotimplementAdder(Addmethodhaspointerreceiver)
但如果是:
packagemain
typeIntegerint
func(aInteger)Less(bInteger)bool{
returna<b
)
func(a^Integer)Add(bInteger){
*a+=b
}
typeLessAdderinterface{
Less(bInteger)bool
Add(bInteger)
)
typeLessinterface{
Less(bInteger)bool
}
typeAdderinterface{
Add(bInteger)
}
funcmain(){
varaInteger=1
varbInteger=a
b.Add(10)
}
就沒有任何問題.對比起來,就是這兩行代碼:
varbInteger=a
varbAdder=a
我們接下去會娓娓道來其中的奧妙.
packagemain
import(
〃fmt〃
)
〃定義對象People、Teacher和Student
typePeoplestruct{
Namestring
)
typeTeacherstruct{
People
Departmentstring
}
typeStudentstruct{
People
Schoolstring
}
〃對象方法實現
func(p*People)SayHi(){
fmt.Printf(Z/Hi,I'm%s.Nicetomeetyou!\n\p.Name)
}
func(t*Teacher)SayHi(){
fmt.Printf(Z/Hi,mynameis%s.I'mworkingin%s.\n〃,t.Name,
t.Department)
func(s*Student)SayHi(){
fmt.Printf(Z/Hi,mynameis%s.rmstudyingin%s.\n/z,s.Name,s.School)
}
func(s*Student)Study(){
fmt.Printf(/zrmlearningGolangin%s.\n/z,s.School)
}
〃定義接口Speaker和Learner
typeSpeakerinterface{
SayHi()
}
typeLearnerinterface{
SayHi()
Study()
}
funcmain(){
〃初始化對象
people:=People{〃張三〃}
//teacher:=&Teacher{People{〃關B智〃},“ComputerScience”}
//student:=&Student{People{〃李明〃},"YaleUniversity”}
varspeakerSpeaker〃定義Speaker接口類型的變量
speaker=people
speaker.SayHi()
}
這時就會出現上面我們提到的錯誤.盡管如果我們這么去調用:
packagemain
import(
〃fmt〃
)
〃定義對象People>Teacher和Student
typePeoplestruct{
Namestring
)
typeTeacherstruct{
People
Departmentstring
)
typeStudentstruct{
People
Schoolstring
)
//對象方法實現
func(p*People)SayHi(){
fmt.Printf(Z/Hi,I'm%s.Nicetomeetyou!\nz/,p.Name)
}
func(t*Teacher)SayHi(){
fmt.Printf(/zHi,mynameis%s.I'mworkingin%s.\n〃,t.Name,
t.Department)
)
func(s*Student)SayHi(){
fmt.Printf(Z/Hi,mynameis%s.rmstudyingin%s.\n/z,s.Name,s.School)
func(s*Student)Study(){
fmt.Printf(/?rmlearningGolangin%s?\n〃,s.School)
}
〃定義接口Speaker和Learner
typeSpeakerinterface{
SayHi()
)
typeLearnerinterface{
SayHi()
Study()
}
funcmain(){
〃初始化對象
people:=People{〃張三〃}
//teacher:=&Teacher{People{〃關B智〃},“ComputerScience”}
//student:=&Student{People{〃李明〃},“YaleUniversity7/)
//varspeackerSpeaker〃定義Speaker接口類型的變量
//speacker=people
people.SayHi()
)
或者
packagemain
import(
〃fmt〃
〃定義對象People、Teacher和Student
typePeoplestruct{
Namestring
}
typeTeacherstruct{
People
Departmentstring
}
typeStudentstruct{
People
Schoolstring
}
〃對象方法實現
func(p*People)SayHi(){
fmt.Printf(Z/Hi,I'm%s.Nicetomeetyou!\n/z,p.Name)
}
func(t^Teacher)SayHi(){
fmt.Printf(Z/Hi,mynameis%s.I'mworkingin%s.\n〃,t.Name,
t.Department)
}
func(s^Student)SayHi()
fmt.Printf(zzHi,mynameis%s.rmstudyingin%s.\n/z,s.Name,s.School)
func(s*Student)Study(){
fmt.Printf(/?rmlearningGolangin%s?\n〃,s.School)
〃定義接口Speaker和Learner
typeSpeakerinterface{
SayHi()
}
typeLearnerinterface{
SayHi()
Study()
}
funcmain(){
〃初始化對象
people:=People{〃張三〃}
//teacher:=&Teacher{People{〃關B智〃},“ComputerScience”}
//student:=&Student{People{〃李明〃},“YaleUniversity”}
varspeackerSpeaker〃定義Speaker接口類型的變量
speacker=fepeople
speacker.SayHi()
}
這樣就都沒有任何問題,這就是說什么呢?這說明對于對象的方法,無
論接受者是對象還是對象指針,都沒
任何問題.但是如果是借口,如果接口中存在某個方法,綁定的接收者是
對象指針,那么這個接口
也只能被該對象指針賦值.如此奇葩的設計,我只能說,g。的設計者真
是個腦殘.
?繼承
好了,下面正式講繼承的語法,話說那玩意兒的真的算不上繼承,比
C++的繼承真的時不知道low到哪里去了.但是我也不知道為啥這是go
愛好者們最愛標榜的東西,
有時我想想,程序員也真是單純的人,一點點的蠱惑,就會讓他們激動
不已,
感覺就要去參加革命了似的.
g。的繼承非常簡陋,就是一個匿名結構組合的問題.不廢話,直接上代
碼.
packagemain
import(
〃fmt〃
〃基類
typeBasestruct{
Namestring
〃綁定Say方法
func(b*Base)Say()
fmt.Println(b.Name)
〃綁定ok方法
func(b*Base)0k()
fmt.Println(/zokz/)
//Foo有個匿名結構Base
typeFoostruct{
Base
Namestring
)
//重寫Say方法
func(f*Foo)Say(){
f.Base.Say()
fmt.Printin(f.Name)
}
funcmain(){
varf*Foo=&F00{Base{"father"},〃sun〃}
f.0k()
f.Say()
)
輸出結果:
ok
father
sun
ok,下面我們看看多繼承二義性的問題.
packagemain
import(
〃fmt〃
//father
typeFatherstruct{
)
func(f*Father)Say(){
fmt.Printin("father")
}
//mother
typeMotherstruct{
}
func(f"Mother)Say(){
fmt.Printin("mother")
}
//sun
typeSunstruct{
Father
Mother
}
funcmain(){
vars*Sun=new(Sun)
s.Say()
}
輸出:
#command-1ine-arguments
./main,go:32:ambiguousselectors.Say
果然展現了二義性.消歧義方式也是土的掉渣:
packagemain
import(
〃fmt〃
)
//father
typeFatherstruct{
}
func(f*Father)Say(){
fmt.Printin("father")
}
//mother
typeMotherstruct{
)
func(f*Mother)Say(){
fmt.Printin("mother")
}
//sun
typeSunstruct{
Father
Mother
}
funcmain(){
vars*Sun=new(Sun)
s.Father.Say()
s.Mother.Say()
}
我也是醉了.
此外,我們也會看到還有一種用法,
packagemain
import(
)
//基類
typeBasestruct{
Namestring
}
//綁定Say方法
func(b*Base)Say(){
fmt.Printin(b.Name)
}
//綁定ok方法
func(b*Base)0k(){
fmt.Println(〃ok〃)
}
//Foo有個匿名結構Base
typeFoostruct{
*Base〃base是個指針
Namestring
〃重寫Say方法
func(f*Foo)Say(){
f.Base.Say()
fmt.Println(f.Name)
}
funcmain(){
varf*Foo=&F00{&Base{"father"},〃sun〃}
f.Ok()
f.Say()
}
這里Foo的Base是一個指針類型,因此我們傳入的也必須是個指針,
我們可以看到這種用法.
.接口
這個我不想吐槽什么,前面已經吐槽過了,但是這種設計,確實也是詭
異之極.
g。的借口是非侵入式的,只要實現了接口定義的方法,就等于實現了
該接口.換句話說,接口的實現和定義式可以分離
不相關的.
接口的例子還是之前那個:
packagemain
import(
〃fmt〃
typeIntegerint
func(aInteger)Less(bInteger)bool{
returna<b
)
func(a^Integer)Add(bInteger){
*a+=b
}
//定義接口
typeLessAdderinterface{
Less(bInteger)bool〃函數聲明
Add(bInteger)〃函數聲明
}
funcmain(){
varaInteger=10
varbLessAdder二&a〃道理我們前面提到過了,Add接收者是個對象指針
fmt.Printin(b.Less(5))
b.Add(20)
fmt.Println(a)
}
輸出:
false
30
只要兩個接口擁
有相同的方法列表(次序不同不要緊),那么它們就是等同的,可以相
互賦值。
接口賦值并不要求兩個接口必須等價。如果接口A的方法列表是接口B
的方法列表的子集,
那么接口B可以賦值給接口Ao
幾個接口也可以組合出一個接口.
typeReadWriterinterface{
Reader
Writer
}
等價于:
typeReadWriterinterface{
Read(p[]byte)(nint,errerror)
Write(p[]byte)(nint,errerror)
}
?Any類型
由于Go語言中任何對象實例都滿足接口interface。,所以interface。
看起來是任何對象的Any類型
varvlinterface{}=1
varv2interface{}=〃abc〃
varv3interface{}=&v2
varv4interface{}=struct{Xint}{1}
varv5interface{}=fcstruct{Xint}{1}
funcPrintf(fmtstring,args...interface{})
funcPrintin(args...interface{})
?接口轉換和類型查詢
接口轉換
packagemain
import(
〃fmt〃
)
typeIntegerint
func(aInteger)Less(bInteger)bool{
returna<b
}
func(a*Integer)Add(bInteger){
*a+二b
)
〃定義接口
typeLessAdderinterface{
Less(bInteger)bool〃函數聲明
Add(bInteger)//函數聲明
)
〃定義接口
typeAdderinterface{
Add(bInteger)〃函數聲明
}
funcmain(){
varaInteger=10
varbLessAdder=&a〃道理我們前面提到過了,Add接收者是個對象指針
ifc,ok=b.(Adder);ok{
c.Add(10)
fmt.Println(a)
//fmt.Println(c.Less(100))//報錯,c.Lessundefined(typeAdderhas
nofieldormethodLess)
)
)
類型查詢
packagemain
import(
"fmt"
)
funcmain(){
i〃〃
b:=a
varainterface{}=b
switcha.(type){
caseint:
fmt.Printin(z/intz,)
casefloat32:
fmt.Printin(/zfloat32z/)
caseint32:
fmt.Printin(,zint32z/)
casefloat64:
fmt.Printin(zzfloat64/z)
casebool:
fmt.Printin(〃bool〃)
casestring:
fmt.Printin("string")
default:
fmt.Println(/Zok/Z)
?補充
我們補充一些defer-panic-recover的案列.
packagemain
import(
〃fmt〃
)
funcf(){
fmt.Println(〃a〃)
}
funcmain(){
deferfunc(){
iferr:=recover();err!=nil{
fmt.Println(err)
}
)()
f()
fmt.Println(〃b〃)
)
輸出結果:
a
b
如果我們在f中panic呢?這會發生什么呢?
packagemain
import(
〃fmt〃
)
funcf(){
fmt.Println(〃a〃)
panic("error!〃)
}
funcmain(){
deferfunc(){
iferr:=recover();err!=nil{
fmt.Println(err)
)
)()
f()
fmt.Println(/Zb/Z)
}
輸出:
a
error!
我們發現,b沒有輸出.panic會拋出一個異常,由recover去捕獲.f拋出
異常后,事實上,
main剩下的部分都不會執行,但是因為我們defer了,
defer是一定會執行的,因此我們在defer中捕獲了panic拋出的
異常.這就是為什么b沒有輸出.似乎和trycatch很像.如果我們希望
把b也輸出,但也能捕獲異常呢?
packagemain
import(
〃fmt〃
)
funcf(){
fmt.Println(〃a〃)
panic("error!〃)
}
funcmain(){
func(){
deferfunc(){
iferr:=recover();err!=nil{
fmt.Println(err)
}
)()
f()
)()
fmt.Println(〃b〃)
)
輸出結果:
a
error!
b
如果是這樣呢?
packagemain
import(
〃fmt〃
)
funcf(){
fmt.Println(〃a〃)
panic("error!〃)
}
funcmain(){
func(){
f()
deferfunc(){
iferr:=recover();err!=nil{
fmt.Println(err)
)
}()
)()
fmt.Println(〃b〃)
}
此時,在定義defer之前,f已經panic了,沒有recover去捕獲,這個
panic會一直拋出.
直到被go虛擬機捕獲.
輸出:
a
panic:error!
goroutine1[running]:
main,f()
/Users/fengyan/code/go/test/main,go:9+0xlle
main.main,fund()
/Users/fengyan/code/go/test/main,go:14+0x18
main,main()
/Users/fe
go里面有個東西很好玩,nil類似于java的null,那么java如果對null
調用方法,會直接拋出一個空指針異常.
那么go會怎么樣呢?
packagemain
funcmain(){
nil.Printin(〃a〃)
}
輸出結果:
#command-line-arguments
./main,go:4:useofuntypednil
看來還是不行的.
所以調用前我們還是要進行空的判斷.
三.go并發編程
并發不是
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經權益所有人同意不得將文件中的內容挪作商業或盈利用途。
- 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 我身邊的環保故事事件周記11篇
- ××超市成果轉化規定
- 教育行業教職人員資格證明書及授課評價報告(7篇)
- ××中學文化周活動制度
- 2025年銀行從業資格考試金融保險真題試卷
- 2025年保健按摩師職業技能鑒定試卷(按摩保健實踐)
- 2025年俄語ТРКИ八級考試模擬試卷分析
- 2025年護士執業資格考試題庫(外科護理學專項)備考資料大全
- 假期旅游申請表及證明(6篇)
- 2025年征信考試題庫:征信國際合作與交流實務操作試題卷
- 職業行為習慣課件
- 高校智能化教學評價體系變革的技術創新路徑研究
- 高中復讀協議書
- 2025年四川省自貢市中考物理試卷及答案
- 2025年度衛生招聘考試(財務)新版真題卷(附詳細解析)
- 2025-2030中國戊烷發泡劑市場深度解析及前景運行動態研究報告
- 2025年6月14日萍鄉市事業單位面試真題及答案解析
- 2024年深圳市中考生物試卷真題(含答案解析)
- 溝通與演講2023學習通超星課后章節答案期末考試題庫2023年
- DSP課設——正弦波發生器
- 從《國際博物館》看世界博物館發展解析
評論
0/150
提交評論