go語言學(xué)習(xí)筆記_第1頁
go語言學(xué)習(xí)筆記_第2頁
go語言學(xué)習(xí)筆記_第3頁
go語言學(xué)習(xí)筆記_第4頁
go語言學(xué)習(xí)筆記_第5頁
已閱讀5頁,還剩66頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認(rèn)領(lǐng)

文檔簡介

go語言學(xué)習(xí)筆記(初級)

最近一直在學(xué)習(xí)g。語言,因此打算學(xué)習(xí)的時候能夠記錄

一下筆記。我這個人之前是從來沒有記錄筆記的習(xí)慣,

一直以來都是靠強大的記憶力去把一些要點記住。

讀書的時候因為一直都是有一個很安靜和很專心的環(huán)境,

因此很多事情都能記得很清楚,思考的很透徹。但是隨著

年紀(jì)不斷增加,也算是經(jīng)歷了很多的事情,加上工作有時會讓人

特別煩悶,很難把心好好靜下來去學(xué)習(xí),去思考大自然的終極

奧秘,因此需要記錄一些東西,這些東西一方面可以作為一種自我激勵

的機制,另一方面,也算是自己成長的軌跡吧。

一.順序編程

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)鍵字還有一種是把多個變量的申明放在一起,

var(

vlstring

v2int

)

2.變量初始化

有人說,變量初始化有什么好提的,那么簡單。是的,但是這里面確實

還是有一些值得注意的點。

varaint=10〃完整定義

vara=10〃自動推斷是int型

a:二10〃自動推斷是int型,申明并未該變量賦值

第三種初始化方式無疑是最簡單的。

但是要注意的是,這里面第三種方式是和特別的,比如

varaint

a:=10

等價于

varaint

varaint

a=10

這時候就會報一個重復(fù)定義的錯誤。

3.變量賦值

變量賦值和我們通常的語言基本是一致的,但是多了多重賦值功能。

1,J=J,1

這就直接實現(xiàn)了兩個變量的交換。

4.匿名變量

g。語言的函數(shù)是多返回值的,因此可能有些值并沒有被用到,這時我

們就需要一個占位符去忽略這些返回值。

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”〃自動推斷類型

常量的定義也可以跟一個表達(dá)式,但是這個表達(dá)式應(yīng)該是編譯的時候

就可以求值的.

constmask=1<<3〃正常

constHome=os.GetEnv("HOME")〃錯誤,運行時才能確定

6.預(yù)定義常量

這里要講一個很有意思的東西,叫做iota.

這個東西每一個const出現(xiàn)的位置被置為0,沒出現(xiàn)一個iota出現(xiàn),都自

增1,到寫一個const出現(xiàn)的時候,又置為0.

const(

cl=iota//0

c2=iota//I

c3=iota//2

)

constx=iota//x==0(因為iota又被重設(shè)為0了)

consty=iota//y二二0(同上)

如果兩個const賦值表達(dá)式是一樣的,可以省略后面的賦值表達(dá)式.

const(

cl=iota//0

c2//I

c3//3

)

const(

a=1<<iota//a==1(iota在每個const開頭被重設(shè)為0)

b//b==2

c//c==4

6.枚舉

const(

Sunday=iota

Monday

Tuesday

Wednesday

Thursday

Friday

Saturday

numberOfDays//這個常量沒有導(dǎo)出

)

大寫字母開頭的包外可見,小寫字母開頭的包外不可見.

7.類型

?整型

int8,uint8,int16,uint16,int32,uint32,int64,uint64,int,uint,uintptr

不同類型不能相互比較.

?浮點類型

float32,float64

涉及的一些運算比較簡單,我們不做細(xì)講.

?字符串類型

下面我們展示一個完整的go語言程序,也是以hello

world為主題,畢竟helloworld是一個萬斤油的主題.

packagemain

importz/fmtzz〃引入依賴包

funcmain(){

fmt.Printin(/zhello,world!/z)

}

這基本上是一個最簡單的程序了,但是對于我們的學(xué)習(xí)非常有用,用這個

模板可以寫出非常好的東西出來.

字符串串本身非常簡單,主要就是一些字符串操作,比如取特定位置的

字符等.

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)

)

輸出結(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é)果是亂碼,因為是一個字節(jié)一個字節(jié)輸出的,但是默認(rèn)是

UTF8編碼,一個中文對應(yīng)3個字節(jié).

這里我們要提到一個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

!

事實上,字符類型有兩種,一種就是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ù)組切片的概念比較復(fù)雜,它有點類似于C++中vector的概念,但又不

完全一樣.

我們這里詳細(xì)提幾點.

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ù)組確實是按照值來傳遞.那么如果是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

確實是按照引用來傳遞的.

append函數(shù)可以往切片尾部增加元素.

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)

}

輸出結(jié)果:

[00000]

0xc820012190

[00000123]

0xc820012190

[00000123123]

0xc82005e000

在這里我們看到,slice的地址是所隨著內(nèi)內(nèi)存的改變而變化的,因

此是需要仔細(xì)思考的.我個人不覺得

go語言這種特性有什么好的,反正也是奇葩極了.不過slice還提

供copy,也算是一些彌補吧.

map

go語言中,map使用非常簡單.基本上看代碼就會了.

packagemain

importz/fmt/z〃引入依賴包

〃定義一個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)

〃查找某個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語句不需要在每個case地下寫break,默認(rèn)就是執(zhí)行break.如果

要執(zhí)行多個case,在case最后加入fallthrough.

條件表達(dá)式不限制為常量或者整數(shù).單個case自然可以有多個結(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)比較特別,它用一個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ù)是一個非常重要的概念,也很簡單.go的函數(shù)以func關(guān)鍵字定義,

支持不定參數(shù)和多返回值.函數(shù)名首字符的大小寫是很有講究的:

小寫字母開頭的函數(shù)只在本包內(nèi)可見,大寫字母開頭的函數(shù)才能被其他

包使用。這個規(guī)則也適用于類型和變量的可見性。

packagemain

import〃引入依賴包

//...int是不定參數(shù),實際上就是一個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ù),那么這個sample怎

么傳給其他喊函數(shù)呢?

sample...//...表示是拆成一個個元素傳遞

匿名函數(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)于閉包的概念.這個概念在許式偉的書中被詮釋的非常好:

閉包是可以包含自由(未綁定到特定對象)變量的代碼塊,這些變量不

在這個代碼塊內(nèi)或者任何全局上下文中定義,

而是在定義代碼塊的環(huán)境中定義。

要執(zhí)行的代碼塊(由于自由變量包含在代碼塊中,所以這些自由變量以

及它們引用的對象沒有被釋放)為自由變量提供綁定定的計算環(huán)境(作

用域)。

閉包的實現(xiàn)確保只要閉包還被使用,那么被閉包引用的變量會一直存

在.*

我們來看來兩個閉包的例子.

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

我們從這個結(jié)果中發(fā)現(xiàn),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))

}

輸出結(jié)果:

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

二.面向?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.

這是我們要牢記的.

我們的筆記整體式按照許式偉的書來安排,但是由于許的書提綱性很

強,內(nèi)容上不是很詳細(xì),基本上會重新整理補充一些東西進去.

?結(jié)構(gòu)體

結(jié)構(gòu)體是用struct來申明的,不多廢話,直接上代碼.

packagemain

import(

〃fmt〃

)

〃申明一個結(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確實是值語義.

下面討論一下結(jié)構(gòu)體的組合問題.這點許的書中并沒有過多涉及,但是

還是很有必要的,因為在實際場合中用的會很多.

packagemain

import(

〃fmt〃

〃申明一個結(jié)構(gòu)體

typeHumanstruct{

Namestring

Ageint

Phonestring

}

//再申明一個結(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〃

)

〃申明一個結(jié)構(gòu)體

typeHumanstruct{

Namestring

Ageint

Phonestring

}

//再申明一個結(jié)構(gòu)體

typeEmployeestruct{

Human//匿名字段Human

Specialitystring

//Phonestring//雇員的phone字段

}

funcmain(){

e:=Employee{

Human{"dingding”,11,”6666666〃},

〃〃

〃Phone:"77777777”,

)

fmt.Printin(e.Phone)

}

此時輸出的是6666666,因為相當(dāng)于它把Human的字段Phone繼承下

來了.

如果Employee里也定義Phone字段呢?

packagemain

import(

〃fmt〃

)

〃申明一個結(jié)構(gòu)體

typeHumanstruct{

Namestring

Ageint

Phonestring

}

//再申明一個結(jié)構(gòu)體

typeEmployeestruct{

Human//匿名字段Human

Specialitystring

Phonestring//雇員的phone字段

)

funcmain(){

e:=Employee{

Human{/zdingding/z,11,“6666666〃},

〃〃

“77777777”,

}

fmt.Printin(e.Phone)

}

此時輸出的時77777777,因為相當(dāng)于是覆蓋.那么怎么輸出6666666

呢?

packagemain

import(

〃fmt〃

)

〃申明一個結(jié)構(gòu)體

typeHumanstruct{

Namestring

Ageint

Phonestring

}

//再申明一個結(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)于有繼承的功能.

?為類型添加方法

這個概念和java或者是C++非常不一樣,它的理念是把似乎是把方法

綁定到特定類型上去.

這個概念已經(jīng)不僅僅是對象的概念了,事實上,

我也不知道google這幫人腦子是怎么想的,這種搓劣的復(fù)古風(fēng)格,

也是讓我打開眼界,我個人覺得,g。雖然仗著google的名氣,似乎顯得

很厲害,但是,

比起java和C++,簡直就是個玩具,說的不好聽一點,

比起scala這樣的一出生就是個大胖子,go更像是個缺胳膊少腿的畸形

兒.

好了,不說了,直接上代碼.

packagemain

import(

〃fmt〃

)

//go綁定方法必須是本包內(nèi)的,int不是main包內(nèi)定義的.

〃因此需要type一下,Integer就是本包內(nèi)定義的類型

typeIntegerint

〃為int綁定一個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綁定?個Print方法

func(iInteger)PrintlnO{

fmt.Println(i)

}

funcmain(){

a:=10

a.Printin()

}

輸出結(jié)果:

#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的修改不會反應(yīng)到a上去.

packagemain

import(

〃fmt〃

)

//go綁定方法必須是本包內(nèi)的,int不是main包內(nèi)定義的.

〃因此需要type一下,Integer就是本包內(nèi)定義的類型

typeIntegerint

〃為int綁定一個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綁定?個Print方法

func(this*Integer)Add(){

*this+=2

)

funcmain(){

varaInteger=10

a.Add()

fmt.Println(a)

)

這時輸出12.我們發(fā)現(xiàn),這個this就像是我們C++里面的this指針一樣.

不過也傻13復(fù)古的多.

我們在看一個例子,

packagemain

import(

〃fmt〃

//go綁定方法必須是本包內(nèi)的,int不是main包內(nèi)定義的.

〃因此需要type一下,Integer就是本包內(nèi)定義的類型

typeIntegerint

〃為int綁定一個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綁定一個Print方法

func(this^Integer)Add(){

fmt.Printin(this)

*this+=2

)

該方法用指針調(diào)用和用值去調(diào)用,效果是一樣的.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

}

〃對象方法實現(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()

}

這時就會出現(xiàn)上面我們提到的錯誤.盡管如果我們這么去調(diào)用:

packagemain

import(

〃fmt〃

)

〃定義對象People>Teacher和Student

typePeoplestruct{

Namestring

)

typeTeacherstruct{

People

Departmentstring

)

typeStudentstruct{

People

Schoolstring

)

//對象方法實現(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

}

〃對象方法實現(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()

}

這樣就都沒有任何問題,這就是說什么呢?這說明對于對象的方法,無

論接受者是對象還是對象指針,都沒

任何問題.但是如果是借口,如果接口中存在某個方法,綁定的接收者是

對象指針,那么這個接口

也只能被該對象指針賦值.如此奇葩的設(shè)計,我只能說,g。的設(shè)計者真

是個腦殘.

?繼承

好了,下面正式講繼承的語法,話說那玩意兒的真的算不上繼承,比

C++的繼承真的時不知道low到哪里去了.但是我也不知道為啥這是go

愛好者們最愛標(biāo)榜的東西,

有時我想想,程序員也真是單純的人,一點點的蠱惑,就會讓他們激動

不已,

感覺就要去參加革命了似的.

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有個匿名結(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有個匿名結(jié)構(gòu)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是一個指針類型,因此我們傳入的也必須是個指針,

我們可以看到這種用法.

.接口

這個我不想吐槽什么,前面已經(jīng)吐槽過了,但是這種設(shè)計,確實也是詭

異之極.

g。的借口是非侵入式的,只要實現(xiàn)了接口定義的方法,就等于實現(xiàn)了

該接口.換句話說,接口的實現(xiàn)和定義式可以分離

不相關(guā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ù)聲明

}

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{})

?接口轉(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接收者是個對象指針

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〃)

)

輸出結(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會拋出一個異常,由recover去捕獲.f拋出

異常后,事實上,

main剩下的部分都不會執(zhí)行,但是因為我們defer了,

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〃)

}

此時,在定義defer之前,f已經(jīng)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

調(diào)用方法,會直接拋出一個空指針異常.

那么go會怎么樣呢?

packagemain

funcmain(){

nil.Printin(〃a〃)

}

輸出結(jié)果:

#command-line-arguments

./main,go:4:useofuntypednil

看來還是不行的.

所以調(diào)用前我們還是要進行空的判斷.

三.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)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論