版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
go語言學(xué)習(xí)筆記(初級)
最近一直在學(xué)習(xí)g。語言,因此打算學(xué)習(xí)的時(shí)候能夠記錄
一下筆記。我這個(gè)人之前是從來沒有記錄筆記的習(xí)慣,
一直以來都是靠強(qiáng)大的記憶力去把一些要點(diǎn)記住。
讀書的時(shí)候因?yàn)橐恢倍际怯幸粋€(gè)很安靜和很專心的環(huán)境,
因此很多事情都能記得很清楚,思考的很透徹。但是隨著
年紀(jì)不斷增加,也算是經(jīng)歷了很多的事情,加上工作有時(shí)會讓人
特別煩悶,很難把心好好靜下來去學(xué)習(xí),去思考大自然的終極
奧秘,因此需要記錄一些東西,這些東西一方面可以作為一種自我激勵(lì)
的機(jī)制,另一方面,也算是自己成長的軌跡吧。
一.順序編程
1.變量
g。語言變量定義的關(guān)鍵字是var。類型放在變量名后:
varvlint
varv2string
varv3[10]int〃數(shù)組
varv4[]int〃切片
varv5struct{〃結(jié)構(gòu)體
fint
}
varv6*int〃指針
varv7map[string]int//map
varv8func(aint)int〃函數(shù)
每一行不需要以分號作為結(jié)尾。var
關(guān)鍵字還有一種是把多個(gè)變量的申明放在一起,
var(
vlstring
v2int
)
2.變量初始化
有人說,變量初始化有什么好提的,那么簡單。是的,但是這里面確實(shí)
還是有一些值得注意的點(diǎn)。
varaint=10〃完整定義
vara=10〃自動推斷是int型
a:二10〃自動推斷是int型,申明并未該變量賦值
第三種初始化方式無疑是最簡單的。
但是要注意的是,這里面第三種方式是和特別的,比如
varaint
a:=10
等價(jià)于
varaint
varaint
a=10
這時(shí)候就會報(bào)一個(gè)重復(fù)定義的錯(cuò)誤。
3.變量賦值
變量賦值和我們通常的語言基本是一致的,但是多了多重賦值功能。
1,J=J,1
這就直接實(shí)現(xiàn)了兩個(gè)變量的交換。
4.匿名變量
g。語言的函數(shù)是多返回值的,因此可能有些值并沒有被用到,這時(shí)我
們就需要一個(gè)占位符去忽略這些返回值。
funcGetName()(firstName,lastName,nickNamestring){
return〃May〃,〃Chan〃,〃ChibiMarukoz/
}
nickName:=GetName()
5.定義常量
通過const關(guān)鍵字,可以用來定義常量。
constPifloat64=3.1415926
constzero=0.0〃自動推斷類型
const(〃多定義
sizeint64=10
hello=-1
)
constu,vfloat32=0.0,3.0〃多重賦值
consta,b,c=1,2,"hello”〃自動推斷類型
常量的定義也可以跟一個(gè)表達(dá)式,但是這個(gè)表達(dá)式應(yīng)該是編譯的時(shí)候
就可以求值的.
constmask=1<<3〃正常
constHome=os.GetEnv("HOME")〃錯(cuò)誤,運(yùn)行時(shí)才能確定
6.預(yù)定義常量
這里要講一個(gè)很有意思的東西,叫做iota.
這個(gè)東西每一個(gè)const出現(xiàn)的位置被置為0,沒出現(xiàn)一個(gè)iota出現(xiàn),都自
增1,到寫一個(gè)const出現(xiàn)的時(shí)候,又置為0.
const(
cl=iota//0
c2=iota//I
c3=iota//2
)
constx=iota//x==0(因?yàn)閕ota又被重設(shè)為0了)
consty=iota//y二二0(同上)
如果兩個(gè)const賦值表達(dá)式是一樣的,可以省略后面的賦值表達(dá)式.
const(
cl=iota//0
c2//I
c3//3
)
const(
a=1<<iota//a==1(iota在每個(gè)const開頭被重設(shè)為0)
b//b==2
c//c==4
6.枚舉
const(
Sunday=iota
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
numberOfDays//這個(gè)常量沒有導(dǎo)出
)
大寫字母開頭的包外可見,小寫字母開頭的包外不可見.
7.類型
?整型
int8,uint8,int16,uint16,int32,uint32,int64,uint64,int,uint,uintptr
不同類型不能相互比較.
?浮點(diǎn)類型
float32,float64
涉及的一些運(yùn)算比較簡單,我們不做細(xì)講.
?字符串類型
下面我們展示一個(gè)完整的go語言程序,也是以hello
world為主題,畢竟helloworld是一個(gè)萬斤油的主題.
packagemain
importz/fmtzz〃引入依賴包
funcmain(){
fmt.Printin(/zhello,world!/z)
}
這基本上是一個(gè)最簡單的程序了,但是對于我們的學(xué)習(xí)非常有用,用這個(gè)
模板可以寫出非常好的東西出來.
字符串串本身非常簡單,主要就是一些字符串操作,比如取特定位置的
字符等.
packagemain
import〃引入依賴包
funcmain(){
varstrstring="hello,world!〃
fmt.Println(str)
ch:=str[0]〃取某個(gè)特定位置的字符
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)
)
輸出結(jié)果為:
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)
}
}
輸出結(jié)果:
h
e
1
1
o
w
0
r
1
d
I
對于中文,結(jié)果是亂碼,因?yàn)槭且粋€(gè)字節(jié)一個(gè)字節(jié)輸出的,但是默認(rèn)是
UTF8編碼,一個(gè)中文對應(yīng)3個(gè)字節(jié).
這里我們要提到一個(gè)range的關(guān)鍵字,它可以把字符串按鍵值對的方式
返回.
packagemain
import〃引入依賴包
funcmain(){
varstrstring=z/hello,world!你好,世界!〃
forch:=rangestr{
fmt.Printf(〃%c\n〃,ch)
}
)
輸出結(jié)果為:
h
e
1
1
o
w
o
r
1
d
I
你
好
世
界
!
事實(shí)上,字符類型有兩種,一種就是byte(uint8),另一種是rune.第一
種遍歷字符串ch是byte,而第二種是rune.
?數(shù)組
數(shù)組這種類型是非常常見的
[32]byte
[2*N]struct{x,yint32}
[1000]*float64
[3][5]int
[2][2][2]float64
數(shù)組的遍歷和字符串一樣,這里不再重復(fù).
數(shù)組是值類型,在賦值時(shí)會拷貝一份.
?數(shù)組切片
數(shù)組切片的概念比較復(fù)雜,它有點(diǎn)類似于C++中vector的概念,但又不
完全一樣.
我們這里詳細(xì)提幾點(diǎn).
1.切片的創(chuàng)建
切片有兩種創(chuàng)建方式,一種是基于數(shù)組創(chuàng)建,另一種是用make
創(chuàng)建.
packagemain
import〃fmt〃〃引入依賴包
funcmain(){
//從數(shù)組創(chuàng)建
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創(chuàng)建
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)
)
輸出結(jié)果:
1
2
3
4
5
6
7
8
9
10
1
2
3
4
5
6
7
8
9
10
我們發(fā)現(xiàn)數(shù)組確實(shí)是按照值來傳遞.那么如果是slice呢,會發(fā)生
什么?
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)
輸出結(jié)果:
1
2
3
4
5
6
7
8
9
10
10
2
3
4
5
6
7
8
9
10
確實(shí)是按照引用來傳遞的.
append函數(shù)可以往切片尾部增加元素.
mySlice=append(mySlice,1,2,3)
mySlice=append(mySlice,mySlice2...)
…表示把一個(gè)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)
}
輸出結(jié)果:
[00000]
0xc820012190
[00000123]
0xc820012190
[00000123123]
0xc82005e000
在這里我們看到,slice的地址是所隨著內(nèi)內(nèi)存的改變而變化的,因
此是需要仔細(xì)思考的.我個(gè)人不覺得
go語言這種特性有什么好的,反正也是奇葩極了.不過slice還提
供copy,也算是一些彌補(bǔ)吧.
map
go語言中,map使用非常簡單.基本上看代碼就會了.
packagemain
importz/fmt/z〃引入依賴包
〃定義一個(gè)Person的結(jié)構(gòu)體
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)
〃查找某個(gè)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)
輸出結(jié)果為:
map[12345:{hanmeimei20}123456:{dagong30}1234:{lilei100}]
map[1234:{lilei100}12345:{hanmeimei20}]
{lilei100)
12345:hanmeimei
1234:lilei
map很簡單吧.數(shù)據(jù)結(jié)構(gòu)我們講完了,接下來可以看看代碼的程序控制
了.
8.程序控制
程序控制本人只提一些關(guān)鍵性的東西,不會啰嗦太多.
?switch語句
switch語句不需要在每個(gè)case地下寫break,默認(rèn)就是執(zhí)行break.如果
要執(zhí)行多個(gè)case,在case最后加入fallthrough.
條件表達(dá)式不限制為常量或者整數(shù).單個(gè)case自然可以有多個(gè)結(jié)果可以
選.
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)
}
?循環(huán)
go語言的循環(huán)比較特別,它用一個(gè)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:
//...
?函數(shù)
函數(shù)是一個(gè)非常重要的概念,也很簡單.go的函數(shù)以func關(guān)鍵字定義,
支持不定參數(shù)和多返回值.函數(shù)名首字符的大小寫是很有講究的:
小寫字母開頭的函數(shù)只在本包內(nèi)可見,大寫字母開頭的函數(shù)才能被其他
包使用。這個(gè)規(guī)則也適用于類型和變量的可見性。
packagemain
import〃引入依賴包
//...int是不定參數(shù),實(shí)際上就是一個(gè)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)
}
很簡單吧.注意,如果是函數(shù)里面調(diào)了其他函數(shù),那么這個(gè)sample怎
么傳給其他喊函數(shù)呢?
sample...//...表示是拆成一個(gè)個(gè)元素傳遞
匿名函數(shù)的概念也很簡單,只要看代碼就會明白.
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)
)
下面是關(guān)于閉包的概念.這個(gè)概念在許式偉的書中被詮釋的非常好:
閉包是可以包含自由(未綁定到特定對象)變量的代碼塊,這些變量不
在這個(gè)代碼塊內(nèi)或者任何全局上下文中定義,
而是在定義代碼塊的環(huán)境中定義。
要執(zhí)行的代碼塊(由于自由變量包含在代碼塊中,所以這些自由變量以
及它們引用的對象沒有被釋放)為自由變量提供綁定定的計(jì)算環(huán)境(作
用域)。
閉包的實(shí)現(xiàn)確保只要閉包還被使用,那么被閉包引用的變量會一直存
在.*
我們來看來兩個(gè)閉包的例子.
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()
)
輸出結(jié)果:
11
0xc82000a288
12
0xc82000a2c0
我們從這個(gè)結(jié)果中發(fā)現(xiàn),i的地址是會變的,因?yàn)槭亲鳛橐粋€(gè)局部變量傳
進(jìn)去的.
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))
}
輸出結(jié)果:
0xc82000a288
11
0xc82000a288
21
因?yàn)閄只傳入了一次,因此沒有改變.
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()
此時(shí)輸出:
i,j:10,5
i,j:10,10
二.面向?qū)ο缶幊?/p>
這里我們先提值語義和引用語義的概念.
b=a
b.Mofify()
如果b改變,a不發(fā)生改變,就是值語義.如果b改變,a也發(fā)生改變,就
是引用語義.
g。語言大多數(shù)類型都是值語義,比如:
基本類型:byte,int,float32,float64,string
復(fù)合類型:struct,array,pointer
也有引用語義的,比如:
slice,map,channel,interface.
這是我們要牢記的.
我們的筆記整體式按照許式偉的書來安排,但是由于許的書提綱性很
強(qiáng),內(nèi)容上不是很詳細(xì),基本上會重新整理補(bǔ)充一些東西進(jìn)去.
?結(jié)構(gòu)體
結(jié)構(gòu)體是用struct來申明的,不多廢話,直接上代碼.
packagemain
import(
〃fmt〃
)
〃申明一個(gè)結(jié)構(gòu)體
typePersonstruct{
Namestring
Ageint
}
funcmain(){
〃結(jié)構(gòu)體的初始化方式
//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)
)
輸出結(jié)果:
{dingding10)
(dingding10}
{dingding10)
&{dingding10}
&{0}
(dingding10}
&{dongdong10}
這說明,struct確實(shí)是值語義.
下面討論一下結(jié)構(gòu)體的組合問題.這點(diǎn)許的書中并沒有過多涉及,但是
還是很有必要的,因?yàn)樵趯?shí)際場合中用的會很多.
packagemain
import(
〃fmt〃
〃申明一個(gè)結(jié)構(gòu)體
typeHumanstruct{
Namestring
Ageint
Phonestring
}
//再申明一個(gè)結(jié)構(gòu)體
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改成匿名結(jié)構(gòu),會有些好玩的現(xiàn)象.
packagemain
import(
〃fmt〃
)
〃申明一個(gè)結(jié)構(gòu)體
typeHumanstruct{
Namestring
Ageint
Phonestring
}
//再申明一個(gè)結(jié)構(gòu)體
typeEmployeestruct{
Human//匿名字段Human
Specialitystring
//Phonestring//雇員的phone字段
}
funcmain(){
e:=Employee{
Human{"dingding”,11,”6666666〃},
〃〃
〃Phone:"77777777”,
)
fmt.Printin(e.Phone)
}
此時(shí)輸出的是6666666,因?yàn)橄喈?dāng)于它把Human的字段Phone繼承下
來了.
如果Employee里也定義Phone字段呢?
packagemain
import(
〃fmt〃
)
〃申明一個(gè)結(jié)構(gòu)體
typeHumanstruct{
Namestring
Ageint
Phonestring
}
//再申明一個(gè)結(jié)構(gòu)體
typeEmployeestruct{
Human//匿名字段Human
Specialitystring
Phonestring//雇員的phone字段
)
funcmain(){
e:=Employee{
Human{/zdingding/z,11,“6666666〃},
〃〃
“77777777”,
}
fmt.Printin(e.Phone)
}
此時(shí)輸出的時(shí)77777777,因?yàn)橄喈?dāng)于是覆蓋.那么怎么輸出6666666
呢?
packagemain
import(
〃fmt〃
)
〃申明一個(gè)結(jié)構(gòu)體
typeHumanstruct{
Namestring
Ageint
Phonestring
}
//再申明一個(gè)結(jié)構(gòu)體
typeEmployeestruct{
Human//匿名字段Human
Specialitystring
Phonestring//雇員的phone字段
}
funcmain(){
e:=Employee{
Human{"dingding”,11,”6666666〃},
〃〃
“77777777”,
)
fmt.Printin(e.Phone)
fmt.Printin(e.Human.Phone)
)
輸出結(jié)果:
77777777
6666666
所以,匿名結(jié)構(gòu)體的組合相當(dāng)于有繼承的功能.
?為類型添加方法
這個(gè)概念和java或者是C++非常不一樣,它的理念是把似乎是把方法
綁定到特定類型上去.
這個(gè)概念已經(jīng)不僅僅是對象的概念了,事實(shí)上,
我也不知道google這幫人腦子是怎么想的,這種搓劣的復(fù)古風(fēng)格,
也是讓我打開眼界,我個(gè)人覺得,g。雖然仗著google的名氣,似乎顯得
很厲害,但是,
比起java和C++,簡直就是個(gè)玩具,說的不好聽一點(diǎn),
比起scala這樣的一出生就是個(gè)大胖子,go更像是個(gè)缺胳膊少腿的畸形
兒.
好了,不說了,直接上代碼.
packagemain
import(
〃fmt〃
)
//go綁定方法必須是本包內(nèi)的,int不是main包內(nèi)定義的.
〃因此需要type一下,Integer就是本包內(nèi)定義的類型
typeIntegerint
〃為int綁定一個(gè)Print方法
func(iInteger)PrintlnO{
fmt.Println(i)
funcmain(){
varaInteger=10
a.PrintlnO
}
結(jié)果輸出10,如果是如下呢?
packagemain
import(
〃fmt〃
)
//go綁定方法必須是本包內(nèi)的,int不是main包內(nèi)定義的.
〃因此需要type一下,Integer就是本包內(nèi)定義的類型
typeIntegerint
〃為int綁定?個(gè)Print方法
func(iInteger)PrintlnO{
fmt.Println(i)
}
funcmain(){
a:=10
a.Printin()
}
輸出結(jié)果:
#command-line-arguments
./main,go:18:a.Printinundefined(typeinthasnofieldormethod
Printin)
因?yàn)閍:=10,go會把a(bǔ)推斷為int,但int并沒綁上Printin方法.
注意:
〃為int綁定一個(gè)Print方法
func(iInteger)PrintlnO{
fmt.Println(i)
)
這里的i并不是引用類型,因此對i的修改不會反應(yīng)到a上去.
packagemain
import(
〃fmt〃
)
//go綁定方法必須是本包內(nèi)的,int不是main包內(nèi)定義的.
〃因此需要type一下,Integer就是本包內(nèi)定義的類型
typeIntegerint
〃為int綁定一個(gè)Print方法
func(iInteger)Add(){
i+=2
}
funcmain(){
varaInteger=10
a.Add()
fmt.Println(a)
輸出10.
如果我們用引用呢?
packagemain
import(
〃fmt〃
)
//go綁定方法必須是本包內(nèi)的,int不是main包內(nèi)定義的.
〃因此需要type一下,Integer就是本包內(nèi)定義的類型
typeIntegerint
〃為int綁定?個(gè)Print方法
func(this*Integer)Add(){
*this+=2
)
funcmain(){
varaInteger=10
a.Add()
fmt.Println(a)
)
這時(shí)輸出12.我們發(fā)現(xiàn),這個(gè)this就像是我們C++里面的this指針一樣.
不過也傻13復(fù)古的多.
我們在看一個(gè)例子,
packagemain
import(
〃fmt〃
//go綁定方法必須是本包內(nèi)的,int不是main包內(nèi)定義的.
〃因此需要type一下,Integer就是本包內(nèi)定義的類型
typeIntegerint
〃為int綁定一個(gè)Print方法
func(this*Integer)Add(){
fmt.Printin(this)
*this+=2
}
funcmain(){
varbInteger=10
vara*Integer=&b
a.Add()
fmt.Println(a)
)
輸出結(jié)果:
0xc82000a288
0xc82000a288
我們發(fā)現(xiàn),
〃為int綁定一個(gè)Print方法
func(this^Integer)Add(){
fmt.Printin(this)
*this+=2
)
該方法用指針調(diào)用和用值去調(diào)用,效果是一樣的.this都是指向原來對
象的指針而已.
下面這個(gè)例子來自于許的書中,
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)
這個(gè)例子似乎有點(diǎn)奇怪,為什么呢?
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
}
〃對象方法實(shí)現(xiàn)
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{〃關(guān)B智〃},“ComputerScience”}
//student:=&Student{People{〃李明〃},"YaleUniversity”}
varspeakerSpeaker〃定義Speaker接口類型的變量
speaker=people
speaker.SayHi()
}
這時(shí)就會出現(xiàn)上面我們提到的錯(cuò)誤.盡管如果我們這么去調(diào)用:
packagemain
import(
〃fmt〃
)
〃定義對象People>Teacher和Student
typePeoplestruct{
Namestring
)
typeTeacherstruct{
People
Departmentstring
)
typeStudentstruct{
People
Schoolstring
)
//對象方法實(shí)現(xiàn)
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{〃關(guān)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
}
〃對象方法實(shí)現(xiàn)
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{〃關(guān)B智〃},“ComputerScience”}
//student:=&Student{People{〃李明〃},“YaleUniversity”}
varspeackerSpeaker〃定義Speaker接口類型的變量
speacker=fepeople
speacker.SayHi()
}
這樣就都沒有任何問題,這就是說什么呢?這說明對于對象的方法,無
論接受者是對象還是對象指針,都沒
任何問題.但是如果是借口,如果接口中存在某個(gè)方法,綁定的接收者是
對象指針,那么這個(gè)接口
也只能被該對象指針賦值.如此奇葩的設(shè)計(jì),我只能說,g。的設(shè)計(jì)者真
是個(gè)腦殘.
?繼承
好了,下面正式講繼承的語法,話說那玩意兒的真的算不上繼承,比
C++的繼承真的時(shí)不知道low到哪里去了.但是我也不知道為啥這是go
愛好者們最愛標(biāo)榜的東西,
有時(shí)我想想,程序員也真是單純的人,一點(diǎn)點(diǎn)的蠱惑,就會讓他們激動
不已,
感覺就要去參加革命了似的.
g。的繼承非常簡陋,就是一個(gè)匿名結(jié)構(gòu)組合的問題.不廢話,直接上代
碼.
packagemain
import(
〃fmt〃
〃基類
typeBasestruct{
Namestring
〃綁定Say方法
func(b*Base)Say()
fmt.Println(b.Name)
〃綁定ok方法
func(b*Base)0k()
fmt.Println(/zokz/)
//Foo有個(gè)匿名結(jié)構(gòu)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()
)
輸出結(jié)果:
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
果然展現(xiàn)了二義性.消歧義方式也是土的掉渣:
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有個(gè)匿名結(jié)構(gòu)Base
typeFoostruct{
*Base〃base是個(gè)指針
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è)指針類型,因此我們傳入的也必須是個(gè)指針,
我們可以看到這種用法.
.接口
這個(gè)我不想吐槽什么,前面已經(jīng)吐槽過了,但是這種設(shè)計(jì),確實(shí)也是詭
異之極.
g。的借口是非侵入式的,只要實(shí)現(xiàn)了接口定義的方法,就等于實(shí)現(xiàn)了
該接口.換句話說,接口的實(shí)現(xiàn)和定義式可以分離
不相關(guān)的.
接口的例子還是之前那個(gè):
packagemain
import(
〃fmt〃
typeIntegerint
func(aInteger)Less(bInteger)bool{
returna<b
)
func(a^Integer)Add(bInteger){
*a+=b
}
//定義接口
typeLessAdderinterface{
Less(bInteger)bool〃函數(shù)聲明
Add(bInteger)〃函數(shù)聲明
}
funcmain(){
varaInteger=10
varbLessAdder二&a〃道理我們前面提到過了,Add接收者是個(gè)對象指針
fmt.Printin(b.Less(5))
b.Add(20)
fmt.Println(a)
}
輸出:
false
30
只要兩個(gè)接口擁
有相同的方法列表(次序不同不要緊),那么它們就是等同的,可以相
互賦值。
接口賦值并不要求兩個(gè)接口必須等價(jià)。如果接口A的方法列表是接口B
的方法列表的子集,
那么接口B可以賦值給接口Ao
幾個(gè)接口也可以組合出一個(gè)接口.
typeReadWriterinterface{
Reader
Writer
}
等價(jià)于:
typeReadWriterinterface{
Read(p[]byte)(nint,errerror)
Write(p[]byte)(nint,errerror)
}
?Any類型
由于Go語言中任何對象實(shí)例都滿足接口interface。,所以interface。
看起來是任何對象的Any類型
varvlinterface{}=1
varv2interface{}=〃abc〃
varv3interface{}=&v2
varv4interface{}=struct{Xint}{1}
varv5interface{}=fcstruct{Xint}{1}
funcPrintf(fmtstring,args...interface{})
funcPrintin(args...interface{})
?接口轉(zhuǎn)換和類型查詢
接口轉(zhuǎn)換
packagemain
import(
〃fmt〃
)
typeIntegerint
func(aInteger)Less(bInteger)bool{
returna<b
}
func(a*Integer)Add(bInteger){
*a+二b
)
〃定義接口
typeLessAdderinterface{
Less(bInteger)bool〃函數(shù)聲明
Add(bInteger)//函數(shù)聲明
)
〃定義接口
typeAdderinterface{
Add(bInteger)〃函數(shù)聲明
}
funcmain(){
varaInteger=10
varbLessAdder=&a〃道理我們前面提到過了,Add接收者是個(gè)對象指針
ifc,ok=b.(Adder);ok{
c.Add(10)
fmt.Println(a)
//fmt.Println(c.Less(100))//報(bào)錯(cuò),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)
?補(bǔ)充
我們補(bǔ)充一些defer-panic-recover的案列.
packagemain
import(
〃fmt〃
)
funcf(){
fmt.Println(〃a〃)
}
funcmain(){
deferfunc(){
iferr:=recover();err!=nil{
fmt.Println(err)
}
)()
f()
fmt.Println(〃b〃)
)
輸出結(jié)果:
a
b
如果我們在f中panic呢?這會發(fā)生什么呢?
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!
我們發(fā)現(xiàn),b沒有輸出.panic會拋出一個(gè)異常,由recover去捕獲.f拋出
異常后,事實(shí)上,
main剩下的部分都不會執(zhí)行,但是因?yàn)槲覀僤efer了,
defer是一定會執(zhí)行的,因此我們在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〃)
)
輸出結(jié)果:
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〃)
}
此時(shí),在定義defer之前,f已經(jīng)panic了,沒有recover去捕獲,這個(gè)
panic會一直拋出.
直到被go虛擬機(jī)捕獲.
輸出:
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里面有個(gè)東西很好玩,nil類似于java的null,那么java如果對null
調(diào)用方法,會直接拋出一個(gè)空指針異常.
那么go會怎么樣呢?
packagemain
funcmain(){
nil.Printin(〃a〃)
}
輸出結(jié)果:
#command-line-arguments
./main,go:4:useofuntypednil
看來還是不行的.
所以調(diào)用前我們還是要進(jìn)行空的判斷.
三.go并發(fā)編程
并發(fā)不是
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 二零二五年度企業(yè)安全生產(chǎn)管理與培訓(xùn)合同-@-1
- 2025年度住宅拆除及土地平整合同范本4篇
- 二零二五版公司員工授權(quán)委托書范本3篇
- 個(gè)體工商戶用工也需簽勞動合同
- 2025年中國弧焊機(jī)行業(yè)發(fā)展運(yùn)行現(xiàn)狀及發(fā)展趨勢預(yù)測報(bào)告
- 2025版城市綠化項(xiàng)目水土保持技術(shù)服務(wù)合同3篇
- 二零二四年份文化旅游推廣與策劃合同
- 2025年科技園區(qū)項(xiàng)目股權(quán)轉(zhuǎn)讓與高新技術(shù)產(chǎn)業(yè)合作合同
- 2025-2031年中國AR購物行業(yè)市場需求預(yù)測及投資戰(zhàn)略規(guī)劃報(bào)告
- 2025年針織毯子項(xiàng)目可行性研究報(bào)告
- 垃圾處理廠工程施工組織設(shè)計(jì)
- 天皰瘡患者護(hù)理
- 2025年蛇年新年金蛇賀歲金蛇狂舞春添彩玉樹臨風(fēng)福滿門模板
- 四川省成都市青羊區(qū)石室聯(lián)中學(xué)2024年八年級下冊物理期末學(xué)業(yè)水平測試試題含解析
- 門診導(dǎo)醫(yī)年終工作總結(jié)
- 新生物醫(yī)藥產(chǎn)業(yè)中的人工智能藥物設(shè)計(jì)研究與應(yīng)用
- 損失補(bǔ)償申請書范文
- 壓力與浮力的原理解析
- 鐵路損傷圖譜PDF
- 裝修家庭風(fēng)水學(xué)入門基礎(chǔ)
- 移動商務(wù)內(nèi)容運(yùn)營(吳洪貴)任務(wù)二 社群的種類與維護(hù)
評論
0/150
提交評論