Golang從09年發(fā)布,中間經(jīng)歷了多個(gè)版本的演進(jìn)召廷,已經(jīng)漸漸趨于成熟,并且出現(xiàn)了很多優(yōu)秀的開(kāi)源項(xiàng)目账胧,比如我們熟知的docker竞慢,etcd,kubernetes等治泥,其媲美于C的性能筹煮、Python的開(kāi)發(fā)效率,又被稱為21世紀(jì)的C語(yǔ)言居夹,尤其適合開(kāi)發(fā)后臺(tái)服務(wù)败潦。這篇文章主要是介紹Golang的一些主要特性本冲,和Java做一個(gè)對(duì)比,以便更好的理解Golang這門(mén)語(yǔ)言变屁。
關(guān)于Golang環(huán)境的搭建就不講了眼俊,可以參考官方文檔或者Google一下,配置下SDK和PATH即可粟关,非常簡(jiǎn)單疮胖,我們就從Go版本的Hello World開(kāi)始
Hello World
每種語(yǔ)言都有自己的Hello World,Go也不例外闷板,Go版本的如下:
package main
import "fmt"
func main() {
fmt.Println("Hello, 世界")
}
我們使用go run運(yùn)行后澎灸,會(huì)在控制臺(tái)終端看到Hello, 世界
的輸出。我們來(lái)看下這段代碼:
- package 是一個(gè)關(guān)鍵字遮晚,定義一個(gè)包性昭,和Java里的package一樣,也是模塊化的關(guān)鍵县遣。
- main包是一個(gè)特殊的包名糜颠,它表示當(dāng)前是一個(gè)可執(zhí)行程序,而不是一個(gè)庫(kù)萧求。
- import 也是一個(gè)關(guān)鍵字其兴,表示要引入的包,和Java的import關(guān)鍵字一樣夸政,引入后才可以使用它元旬。
- fmt是一個(gè)包名,這里表示要引入fmt這個(gè)包守问,這樣我們就可以使用它的函數(shù)了匀归。
- main函數(shù)是主函數(shù),表示程序執(zhí)行的入口耗帕,Java也有同名函數(shù)穆端,但是多了一個(gè)String[]類型的參數(shù)。
- Println是fmt包里的函數(shù)仿便,和Java里的system.out.println作用類似徙赢,這里輸出一段文字。
整段代碼非常簡(jiǎn)潔探越,關(guān)鍵字狡赐、函數(shù)、包等和Java非常相似钦幔,不過(guò)注意枕屉,go是不需要以;(分號(hào))結(jié)尾的。
變量
go語(yǔ)言變量的聲明和java的略有不同鲤氢,以聲明一個(gè)int類型搀擂,變量名為age為例西潘,go語(yǔ)言變量生成如下:
var age int =10
同樣的變量,在java中的聲明是:
int age = 10;
可以看到go的變量聲明哨颂,修飾變量的類型在變量的后面喷市,而且是以var關(guān)鍵字開(kāi)頭。
var 變量名 類型 = 表達(dá)式
最后面的賦值可以在聲明的時(shí)候忽略威恼,這樣變量就有一個(gè)默認(rèn)的值品姓,稱之為零值
。零值
是一個(gè)統(tǒng)稱箫措,以類型而定腹备,比如int類型的零值為0,string類型的零值是”“空字符串斤蔓。
在go中除了以var聲明變量之外植酥,還有一種簡(jiǎn)短的變量聲明方式:=,比如上面例子,可以如下簡(jiǎn)單聲明:
age := 10
這種方式和上面的例子等價(jià)弦牡,但是少了var和變量類型友驮,所以簡(jiǎn)短方便,用的多驾锰。使用這種方式卸留,變量的類型由go根據(jù)值推導(dǎo)出來(lái),比如這里默認(rèn)是int稻据。
常量
有了變量艾猜,就少不了常量买喧,和var關(guān)鍵字不一樣捻悯,go的常量使用const聲明,這個(gè)和C里的常量一樣淤毛。
const age = 10
這樣就聲明了一個(gè)常量age今缚,其值是10,因?yàn)槲覀冞@里沒(méi)有指定常量的類型低淡,所以常量的類型是根據(jù)值推導(dǎo)出來(lái)的姓言。所以等價(jià)的我們也可以指定常量類型,如下:
const age int = 10
相比來(lái)說(shuō)蔗蹋,java下的常量定義就要復(fù)雜一些何荚,要有static final修飾符,才是常量:
private static final int AGE = 10;
這個(gè)和go的實(shí)現(xiàn)等價(jià)猪杭,但是它的定義修飾符比go多多了餐塘,而且常量類型不能省略。
大小寫(xiě)標(biāo)記訪問(wèn)權(quán)限
我們上面的go例子中我特意用了小些的變量名age皂吮,甚至常量我也沒(méi)有寫(xiě)成AGE戒傻,但是在java中税手,對(duì)于常量我們的習(xí)慣是全部大些。
在go中不能隨便使用大小寫(xiě)的問(wèn)題需纳,是因?yàn)榇笮?xiě)具有特殊意義芦倒,在go中,大些字母開(kāi)頭的變量或者函數(shù)等是public的不翩,可以被其他包訪問(wèn)兵扬;小些的則是private的,不能被其他包訪問(wèn)到慌盯。這樣就省去了public和private聲明的煩惱周霉,使代碼變的更簡(jiǎn)潔。
特別說(shuō)明亚皂,這些導(dǎo)出規(guī)則只適用于包級(jí)別名字定義俱箱,不能使函數(shù)內(nèi)部的定義。
包
包的規(guī)則和java很像灭必,每個(gè)包都有自己獨(dú)立的空間狞谱,所以可以用來(lái)做模塊化,封裝禁漓,組織代碼等跟衅。
和java不同的是,go的包里可以有函數(shù)播歼,比如我們常用的fmt.Println(),但是在在java中沒(méi)有這種用法伶跷,java的方法必須是屬于一個(gè)類或者類的實(shí)例的。
要使用一個(gè)包秘狞,就需要先導(dǎo)入叭莫,使用import關(guān)鍵字,和java也一樣烁试,可以參見(jiàn)前面的helloworld示例雇初。
如果我們需要導(dǎo)入多個(gè)包的時(shí)候,可以像java一樣减响,一行行導(dǎo)入靖诗,也可以使用快捷方式一次導(dǎo)入,這個(gè)是java所沒(méi)有的支示。
import (
"io"
"log"
"net"
"strconv"
)
類型轉(zhuǎn)換
go對(duì)于變量的類型有嚴(yán)格的限制刊橘,不同類型之間的變量不能進(jìn)行賦值、表達(dá)式等操作颂鸿,必須要要轉(zhuǎn)換成同一類型才可以促绵,比如int32和int64兩種int類型的變量不能直接相加,要轉(zhuǎn)換成一樣才可以。
var a int32 = 13
var b int64 = 20
c := int64(a) + b
這種限制主要是防止我們誤操作绞愚,導(dǎo)致一些莫名其妙的問(wèn)題叙甸。在java中因?yàn)橛凶詣?dòng)轉(zhuǎn)型的概念,所以可以不同類型的可以進(jìn)行操作位衩,比如int可以和double相加裆蒸,int類型可以通過(guò)+
和字符串拼接起來(lái),這些在go中都是不可行的糖驴。
map
map類型僚祷,Java里是Map接口,go里叫做字典贮缕,因?yàn)槠涑S谜廾眨趃o中,被優(yōu)化為一個(gè)語(yǔ)言上支持的結(jié)構(gòu)感昼,原生支持装哆,就像一個(gè)關(guān)鍵字一樣,而不是java里的要使用內(nèi)置的sdk集合庫(kù)定嗓,比如HashMap等蜕琴。
ages := make(map[string]int)
ages["linday"] = 20
ages["michael"] = 30
fmt.Print(ages["michael"])
go里要?jiǎng)?chuàng)建一個(gè)map對(duì)應(yīng),需要使用關(guān)鍵字make宵溅,然后就可以對(duì)這個(gè)map進(jìn)行操作凌简。
map的結(jié)構(gòu)也非常簡(jiǎn)單,符合KV模型恃逻,定義為map[key]value, 方括號(hào)里是key的類型雏搂,方括號(hào)外緊跟著對(duì)應(yīng)的value的類型,這些明顯和Java的Map接口不同寇损。如果在go中我們要?jiǎng)h除map中的一個(gè)元素怎么辦凸郑?使用內(nèi)置的delete函數(shù)就可以,如下代碼刪除ages這個(gè)map中,key為michael的元素润绵。
delete(ages,"michael")
如果我們想遍歷map中的KV值怎么辦线椰?答案是使用range風(fēng)格的for循環(huán)胞谈,可比Java Map的遍歷簡(jiǎn)潔多了尘盼。
for name,age := range ages {
fmt.Println("name:",name,",age:",age)
}
range一個(gè)map,會(huì)返回兩個(gè)值烦绳,第一個(gè)是key卿捎,第二個(gè)是value,這個(gè)也是go多值返回的優(yōu)勢(shì)径密,下面會(huì)講午阵。
函數(shù)方法
在go中,函數(shù)和方法是不一樣的,我們一般稱包級(jí)別的(直接可以通過(guò)包調(diào)用的)稱之為函數(shù)底桂,比如fmt.Println()植袍;把和一個(gè)類型關(guān)聯(lián)起來(lái)的函數(shù)稱之為方法,如下示例:
package lib
import "time"
type Person struct {
age int
name string
}
func (p Person) GetName() string {
return p.name
}
func GetTime() time.Time{
return time.Now()
}
其中GetTime()可以通過(guò)lib.GetTime()直接調(diào)用籽懦,稱之為函數(shù)于个;而GetName()則屬于Person這個(gè)結(jié)構(gòu)體的函數(shù),只能聲明了Person類型的實(shí)例后才可以調(diào)用暮顺,稱之為方法厅篓。
不管是函數(shù)還是方法,定義是一摸一樣的捶码。而在這里羽氮,最可以講的就是多值返回,也就是可以同時(shí)返回多個(gè)值惫恼,這就大大為我們帶來(lái)了方便档押,比如上個(gè)遍歷map的例子,直接可以獲取KV祈纯,如果只能返回一個(gè)值汇荐,我們就需要調(diào)用兩次方法才可以。
func GetTime() (time.Time,error){
return time.Now(),nil
}
多值返回也很簡(jiǎn)單盆繁,返回的值使用逗號(hào)隔開(kāi)即可掀淘。如果要接受多值的返回,也需要以逗號(hào)分隔的變量油昂,有幾個(gè)返回值革娄,就需要幾個(gè)變量,比如這里:
now,err:=GetTime()
如果有個(gè)返回值冕碟,我們用不到拦惋,不想浪費(fèi)一個(gè)變量接收怎么辦?這時(shí)候可以使用空標(biāo)志符_
,這是java沒(méi)有的安寺。
now,_:=GetTime()
指針
Go的指針和C中的聲明定義是一樣的厕妖,其作用類似于Java引用變量效果。
var age int = 10
var p *int = &age
*p = 11
fmt.Println(age)
其中指針p指向變量age的內(nèi)存地址挑庶,如果修改*p的值言秸,那么變量age的值也同時(shí)會(huì)被修改,例子中打印出來(lái)的值為11迎捺,而不是10.
相對(duì)應(yīng)java引用類型的變量举畸,可以理解為一個(gè)HashMap類型的變量,這個(gè)變量傳遞給一個(gè)方法凳枝,在該方法里對(duì)HashMap修改抄沮,刪除,就會(huì)影響原來(lái)的HashMap。引用變量集合類最容易理解叛买,自己的類也可以砂代,不過(guò)基本類型不行,基本類型不是引用類型的率挣,他們?cè)诜椒▊鲄⒌臅r(shí)候泊藕,是拷貝的值。
結(jié)構(gòu)體替代類
Go中沒(méi)有類型的概念难礼,只有結(jié)構(gòu)體娃圆,這個(gè)和C是一樣的。
type Person struct {
age int
name string
}
Go中的結(jié)構(gòu)體是不能定義方法的蛾茉,只能是變量讼呢,這點(diǎn)和Java不一樣的,如果要訪問(wèn)結(jié)構(gòu)體內(nèi)的成員變量,通過(guò).
操作符即可谦炬。
func (p Person) GetName() string {
return p.name
}
這就是通過(guò).
操作符訪問(wèn)變量的方式悦屏,同時(shí)它也是一個(gè)為結(jié)構(gòu)體定義方法的例子,和函數(shù)不一樣的是键思,在func
關(guān)鍵字后要執(zhí)行該方法的接收者础爬,這個(gè)方法就是屬于這個(gè)接收者,例子中是Person這個(gè)結(jié)構(gòu)體吼鳞。
在Go中如果想像Java一樣看蚜,讓一個(gè)結(jié)構(gòu)體繼承另外一個(gè)結(jié)構(gòu)體怎么辦?也有辦法赔桌,不過(guò)在Go中稱之為組合或者嵌入供炎。
type Person struct {
age int
name string
Address
}
type Address struct {
city string
}
結(jié)構(gòu)體Address被嵌入了Person中,這樣Person就擁有了Address的變量和方法疾党,就想自己的一樣音诫,這就是組合的威力。通過(guò)這種方式雪位,我們可以把簡(jiǎn)單的對(duì)象組合成復(fù)雜的對(duì)象竭钝,并且他們之間沒(méi)有強(qiáng)約束關(guān)系,Go倡導(dǎo)的是組合雹洗,而不是繼承香罐、多態(tài)。
接口
Go的接口和Java類型队伟,不過(guò)它不需要強(qiáng)制實(shí)現(xiàn)穴吹,在Go中幽勒,如果你這個(gè)類型(基本類型嗜侮,結(jié)構(gòu)體等都可以)擁有了接口的所有方法,那么就默認(rèn)為這個(gè)類型實(shí)現(xiàn)了這個(gè)接口,是隱式的锈颗,不需要和java一樣顷霹,強(qiáng)制使用implement
強(qiáng)制實(shí)現(xiàn)。
type Stringer interface {
String() string
}
func (p Person) String() string {
return "name is "+p.name+",age is "+strconv.Itoa(p.age)
}
以上實(shí)例中可以看到击吱,Person這個(gè)結(jié)構(gòu)體擁有了fmt.Stringer接口的方法淋淀,那么就說(shuō)明Person實(shí)現(xiàn)了fmt.Stringer接口。
接口也可以像結(jié)構(gòu)體一樣組合嵌套覆醇,這里不再贅述朵纷。
并發(fā)
Go并發(fā)主要靠go goroutine支持,也稱之為go協(xié)程或者go程永脓,他是語(yǔ)言層面支持的袍辞,非常輕量級(jí)的多任務(wù)支持,也可以把他簡(jiǎn)單的理解為java語(yǔ)言的線程常摧,不過(guò)是不一樣的搅吁。
go run()
這就啟動(dòng)一個(gè)goroutine來(lái)執(zhí)行run函數(shù),代碼非常簡(jiǎn)潔落午,如果在java中谎懦,需要先New一個(gè)Thread,然后在重寫(xiě)他的run方法溃斋,然后在start才可以開(kāi)始界拦。
兩個(gè)goroutine可以通過(guò)channel來(lái)通信,channel是一個(gè)特殊的類型梗劫,也是go語(yǔ)言級(jí)別上的支持寞奸,他類似于一個(gè)管道,可以存儲(chǔ)信息在跳,也可以從中讀取信息枪萄。
package main
import "fmt"
func main() {
result:=make(chan int)
go func() {
sum:=0
for i:=0;i<10;i++{
sum=sum+i
}
result<-sum
}()
fmt.Print(<-result)
}
以上示例使用一個(gè)單獨(dú)的goroutine求和,當(dāng)?shù)玫浇Y(jié)果時(shí)猫妙,存放在result這個(gè)chan里瓷翻,然后供main goroutine讀取出來(lái)。當(dāng)result沒(méi)有被存儲(chǔ)值的時(shí)候割坠,讀取result是阻塞的齐帚,所以會(huì)等到結(jié)果返回,協(xié)同工作彼哼,通過(guò)chan通信对妄。
對(duì)于并發(fā),go還提供了一套同步機(jī)制敢朱,都在sync包里剪菱,有鎖摩瞎,有一些常用的工具函數(shù)等,和java的concurrent框架差不多孝常。
異常機(jī)制
相比java的Exception來(lái)說(shuō)旗们,go有兩種機(jī)制,不過(guò)最常用的還是error錯(cuò)誤類型构灸,panic只用于嚴(yán)重的錯(cuò)誤上渴。
type error interface {
Error() string
}
go內(nèi)置的error類型非常簡(jiǎn)潔,只用實(shí)現(xiàn)Error方法即可喜颁,可以打印一些詳細(xì)的錯(cuò)誤信息稠氮,比如常見(jiàn)的函數(shù)多值返回,最后一個(gè)返回值經(jīng)常是error半开,用于傳遞一些錯(cuò)誤問(wèn)題括袒,這種方式要比java throw Exception的方法更優(yōu)雅。
Defer代替finally
go中沒(méi)有java的finally了稿茉,那么如果我們要關(guān)閉一些一些連接锹锰,文件流等怎么辦呢,為此go為我們提供了defer關(guān)鍵字漓库,這樣就可以保證永遠(yuǎn)被執(zhí)行到恃慧,也就不怕關(guān)閉不了連接了。
f,err:=os.Open(filename)
defer f.Close()
readAll(f)
統(tǒng)一編碼風(fēng)格
在編碼中渺蒿,我們有時(shí)為了是否空行控硼,大括號(hào)是否獨(dú)占一行等編碼風(fēng)格問(wèn)題爭(zhēng)論不休搀捷,到了Go這里就終止了缤灵,因?yàn)間o是強(qiáng)制的虐唠,比如花括號(hào)不能獨(dú)占一行,比如定義的變量必須使用少态,否則就不能編譯通過(guò)城侧。
第二種就是go fmt這個(gè)工具提供的非強(qiáng)制性規(guī)范,雖然不是強(qiáng)制的彼妻,不過(guò)也建議使用嫌佑,這樣整個(gè)團(tuán)隊(duì)的代碼看著就像一個(gè)人寫(xiě)的。很多go代碼編輯器都提供保存時(shí)自動(dòng)gofmt格式的話侨歉,所以效率也非常高屋摇。
便捷的部署
go最終生成的是一個(gè)可執(zhí)行文件,不管你的程序依賴多少庫(kù)幽邓,都會(huì)被打包進(jìn)行炮温,生成一個(gè)可執(zhí)行文件,所以相比java龐大的jar庫(kù)來(lái)說(shuō)牵舵,他的部署非常方便柒啤,執(zhí)行運(yùn)行這個(gè)可執(zhí)行文件就好了倦挂。
對(duì)于Web開(kāi)發(fā),更方便白修,不用安裝jdk妒峦,tomcat容器等等這些環(huán)境重斑,直接一個(gè)可執(zhí)行文件兵睛,就啟動(dòng)了。對(duì)于go這種便捷的部署方式窥浪,我覺(jué)得他更能推進(jìn)docker的服務(wù)化祖很,因?yàn)閐ocker就是倡導(dǎo)一個(gè)實(shí)例一個(gè)服務(wù),而且不用各種依賴漾脂,layer層級(jí)又沒(méi)那么多假颇,docker image也會(huì)小很多。
最后骨稿,go目前已經(jīng)在TIOBE語(yǔ)言排行榜上名列13名了笨鸡,上升速度還是非常快的坦冠,而且隨著服務(wù)化形耗,容器化,他的優(yōu)勢(shì)會(huì)越來(lái)越多的顯現(xiàn)出來(lái)辙浑,得到更廣泛的應(yīng)用激涤。
如果你感興趣,那么開(kāi)始吧判呕,提前準(zhǔn)備倦踢,機(jī)會(huì)來(lái)的時(shí)候,就不會(huì)錯(cuò)過(guò)了侠草。
尊重原創(chuàng)文章轉(zhuǎn)載自 飛雪無(wú)情
http://blog.csdn.net/michael__li/article/details/53941388