編譯運行
在安裝了go環境的機器上,通過go run 源代碼文件名可以直接運行,如
go run main.go
同時還可以通過go build命令生成二進制文件
go build main.go
.\main.exe
則在沒有安裝go環境的機器上可以直接運行二進制文件
go的基本數據類型
- 布爾型
bool 布爾型的值只可以是常量 true 或者 false,默認值爲 false。 - 字符串類型
string 編碼統一爲 UTF-8 編碼標識 Unicode 文本,默認值爲空字符串。 - 整型(默認值爲 0)
uint8: 無符號 8 位整型(0 ~ 255);
uint16:無符號 16 位整型(0 ~ 65535);
uint32:無符號 32 位整型(0 ~ 4294967295);
uint64:無符號 64 位整型(0 ~ 18446744073709551615);
int8:有符號 8 位整型(-128 ~ 127);
int16:有符號 16 位整型(-32768 ~ 32767);
int32:有符號 32 位整型(-2147483648 ~ 2147483647);
int64:有符號 64 位整型(-9223372036854775808 ~ 9223372036854775807) - 浮點型(默認值爲 0)
float32:IEEE-754 32 位浮點數;
float64:IEEE-754 64 位浮點數;
complex64:32 位實數和虛數;
complex128:64 位實數和虛數; - 其他數值類型
byte:類似 uint8;
rune:類似 int32;
uint:32 或 64 位;
int:與 uint 一樣大小;
uintptr:無符號整型,用於存放一個指針;
包管理
golang中語句結尾可以不加分號。
定義包名在程序代碼第一行使用
package 包名
指定包名,使用
import (
alias "p1"
)
方式導入包,其中alias參數是包的別名,不指定別名時,可以省略括號,使用’.'代表以當前路徑作爲別名,所以使用包中成員時不需要加包前綴。當導入包的源文件包含init函數時,會在主函數運行前執行其init函數,然後再執行當前源程序的init函數,最終再執行當前程序的主函數。
自動補齊分號
golang在一些情況下,會在代碼中每行末尾自動補齊分號:
1.當輸入被斷開爲標記時,如果行末標記爲:
- 一個標識符
- 一個整數、浮點數、虛數、字符或字符串文字
- 關鍵字break、continue、fallthrough或return中的一個
- 運算符和分隔符++、–、)、]或}中的一個
則分號將被自動插入到標記流中非空白行的末尾。
2.如果讓複合語句佔用單獨一行,則在“)”或“"}"”後面的分號可以被省略
類型別名
golang支持使用type關鍵字爲類型起別名,如
type duration int
則爲int起了個別名duration,接下來duration就可以用來指代int
變量定義
golang具備編譯時的類型推斷機制,可以使用var關鍵字定義變量,變量類型會根據初始值自動推斷類型,此種方式必須在定義時指定變量初始值。比如如下定義一個整型變量
var a = 5
此種定義方式還可以簡寫爲
a := 5
如果不想指定初始值或者指定變量類型,可以通過
var variable typename
使用指定類型定義變量,或者同時初始化
var variable typename = value
比如
var a int
var b int = 9
使用fmt輸出
package main
import (
"fmt"
)
func main() {
var a = 10 //自動推導爲int
var b int = 5 //主動定義爲int
c := 9 //相當於 var c = 9
fmt.Printf("Hello word %d ,%d ,%d", a, b, c)
}
golang還支持同時初始化多個變量,使用逗號分隔變量和初始值,形式如
var A,B = 100,200
則A的值爲100,B的值爲200
函數定義
golang中的函數定義如下
func name() (typename...){}
golang中的函數可以一次返回多個數據,這與他可以同時初始化多個變量值的特性相關,當返回數據就一個時,可以省略返回值的括號,如
func re2val() (int,string){
return 100,"aaa"
}
var num,str = re2val()
當函數沒有返回值時,可以不寫返回類型
所以完整的主類如下,fmt是輸入輸出包
package main
import "fmt"
func main() {
fmt.Printf("Hello word")
}
與其他語言不同的是,go中函數傳參數組名,與數組賦值,執行的是數組複製而不是引用賦值
匿名變量
當我們需要使用一個複用一個表達式的結果,而不想爲其開闢內存空間時,會面臨類似c++的右值引用問題,golang中提供了匿名變量,通過下劃線,表示定義一個匿名變量,形式如下
_, b := 100,200
此處代表,b接收200,匿名變量指向100的臨時變量空間
字符串
golang提供類型string代表字符串類型,字符串中,ascii字符會佔據一個字節,其他字符根據需要自動計算爲2~4給字節。
字符串的字符可以直接使用索引獲取,如
var s string = "abc"
println(s[0])//字符a
使用len內置函數獲取字符串長度
len(s)
字符串可以直接使用+拼接,返回一個新字符串
s1,s2 := "a","bc"
s3 := s1+s2
struct
golang中支持使用struct定義複合類型,如下定義一個包含一個整型成員和一個字符串成員的複合類型,並起名爲Stu
package main
import (
"fmt"
)
type Stu struct{
num int
name string
}
func main() {
var stu1 = Stu {
num : 1,
name : "yyt", //如果不加逗號,則右括號必須在最後一行成員末尾,不能換行
}
var stu2 Stu //使用自動初始化
var test = struct {
txt string
}{
txt: "aaa",
} //定義匿名結構體,並進行初始化
fmt.Printf("Hello word %d ,%d,%s", stu1.num, stu2.num, test.txt) // 1,0,aaa
}
指針類型
通過在類型前面加上 *代表是改類型的指針類型,比如
var a int = 1
var p * int = &a
與c++不同,golang中的結構體指針和普通結構體變量,都是通過.運算符獲得成員,所以指針變量使用起來與普通變量差別看起來不是很大,只是因爲其代表的是指針的變量,不一定是棧中變量。比如
type Stu struct{
name string
}
var stu1 Stu;
var stu2 * Stu = &stu1
println(stu1.name,stu2.name)
println是golang默認提供的輸出函數,不需要導入包,通過逗號運算符自動拼接多個輸入
結構體函數
golang支持爲結構體定義未實現的函數成員,並且可以通過賦值提供實現,這也意味着,我們可能實例化一個結構體對象,而沒有提供其方法成員的實現,這時如果我們調用該成員方法,則會拋出invalid memory address or nil pointer dereference的錯誤,比如
type Test struct{
test func()
}
func main(){
t := Test{}
t.test = func(){}
t.test()
}
golang支持在結構體外部爲結構體提供函數成員,形式如下
func (variable typename) funName(){}
variable代表指定調用當前函數的結構體變量對象的別名,則函數中可以通過這個別名訪問改變量,typeName代表對應的結構體類型,比如
type Stu struct{
name string
}
func (stu Stu) call() string {
return stu.name
}
func (stu *Stu) pCall() string {
return "*" + stu.name
}
如上定義,則 Stu類型和* Stu類型變量都具備了call和pCall方法,這是因爲golang會完成自動的取地址運算和指針解引用運算,在使用Stu類型變量調用Stu指針類型的方法時,golang會自動將代碼執行爲(&variable).方法名;對應的Stu指針類型變量調用Stu類型方法時,會被執行爲(*variable).方法名
函數變量
go語言真的沒有oop,其方法就是函數,而非行爲,只是通過調用結構體方法時,會傳遞該結構體對象自身,所以還可以通過函數變量,來接受結構體函數,比如
type Stu struct{
name string
}
func (stu Stu) call(age int) string {
return stu.name
}
var stu Stu
f := stu.call
f(1) //=> stu.call(1)
const與iota
go中存在const關鍵字,用於創建常變量,比如
const a = 5
iota表示一個計數器,在一個const表達式中,多個變量定義的iota會逐行遞增,遞增變量爲1,默認iota初始值爲0,如
const (
A = iota
B = iota
)
最終結果,A變量值爲0,B變量值爲1
在const表達式中,不指定變量初始值,會自動沿用上一變量的初始值表達式,所以上述代碼可以簡寫爲
const (
A = iota
B
)
再利用匿名變量,如
const (
A = iota
_
B
)
則此時B的值會是2
使用iota執行計算,如下
const (
A,B = iota+1 , iota+2
C = iota+4
D
)
則結果A的值爲1,B的值爲2,C的值爲5,D的值爲6(沿用iota+4)
接口
golang支持定義接口類型,作爲限制類型規範,要求變量的值類型必須是實現對應方法的結構體類型或對應結構體指針類型,比如
type Reader interface {
read(b []byte) ()
}
type File struct{
}
func(file File) read(b []byte){}
var fileReader Reader = File{}
接口類型不自動轉化指針類型與普通類型,也就是說,如果只爲struct的指針類型實現了接口方法,則只能傳遞指針變量,雖然他的普通變量也可以調用該方法,但那是因爲他會被執行取地址後再執行該方法,不算實現了該接口,也就是說,如
type Man struct{
name string
}
func (man * Man ) call() string {
return man .name
}
type Human interface{
call()
}
//var human Human = Man{} 無法編譯,因爲沒有實現call方法
var human Human = &Man{}
嵌入類型與方法重寫
golang支持在結構體中組合其他的結構體,其中如下爲普通的成員組合,沒有嵌入的情況
type Man struct{
}
func (man Man ) call() {
}
type Postman struct{
man Man
}
var postman Postman = Postman {
man : Man{},
}
postman .man.call()
而一種獨特的形式是嵌入類型,嵌入類型使用類型名作爲字段名,然後則該嵌入類型的所有成員及方法的作用域會擴展到外部類型(實際調用時自動解析爲裏面的嵌入類型),如
type Man struct{
}
func (man Man ) call() {
}
type Postman struct{
Man
}
var postman Postman = Postman {
Man: Man{},
}
postman .man.call()
postman .call() //自動解析爲postman .Man.call()
因爲嵌入類型使得可以通過外部類型訪問內部類型的成員。所以內部類型實現的接口,相當於外部類型也間接實現了,此時可以通過外部類型變量傳遞給內部類型實現的接口類型變量,比如
type Caller interface {
call()
}
type Man struct {
}
func (man Man) call() {
println("call ...")
}
type Postman struct {
Man
}
var caller Caller = Postman {
Man: Man{},
}
caller .call() //call...
同時,此時因爲外部類型和嵌入類型是不同struct,則如果爲外部類型創建於嵌入類型相同的方法,則在通過外部類型調用時,優先匹配外部類型方法,形成類似繼承對方法的重寫,比如剛剛的例子,在PostMan中重寫call方法
func (postman Postman) call() {
println("postman call ...")
}
最終輸出就成爲了重寫方法的結果,類似的接口也存在嵌套效果,其表現行爲與結構體一樣,嵌套以後,外部接口則具備了內部接口的所有成員
type Reader interface {
read(b []byte) ()
}
type MyReader interface{
Reader
}
類型轉化
通過 typeName(variable)進行安全類型轉化,如
var a int = 5
var b float32 = float32(a)
通過variable.(typeName)進行非安全類型轉化
使用該方法的對象必須是interface{}類型,而這是個空接口,意味着我們只需要通過一次變量賦值,即可得到(空接口類型可以存儲任意值)
其第一個返回值爲,將該對象轉化爲指定類型後的值,第二個返回值爲是否滿足該類型定義。
package main;
type A interface{
a()
}
type B struct{}
func main(){
var var1 interface{} = B{}
//不能使用val1 := B{},因爲這樣類型是B{}
val,ok := var1.(A)
println(ok) //false
println(val == nil) //true
var a interface{} = 5
var b int = a.(int)//ok
var d interface{} = 5
e,_ := d.(B)
e.a = 9 //ok
}
- 轉化類型是基本類型,如果對象不滿足類型定義,則拋出panic
- 轉化類型是結構體類型,則即便該對象不是該結構體類型,也會使用默認構造方法構造轉化對象
- 轉化類型是接口類型,如果對象不滿足類型定義,則返回nil
同時,因爲golang的switch的case表達式可以是任意類型(包括類型名稱),所以通過關鍵字type結合switch可以進行類型判斷,如
switch t := areaIntf.(type) {
case *Square:
fmt.Printf("Type Square %T with value %v\n", t, t)
case *Circle:
fmt.Printf("Type Circle %T with value %v\n", t, t)
case nil:
fmt.Printf("nil value: nothing to check?\n")
default:
fmt.Printf("Unexpected type %T\n", t)
}
更多文章,請搜索公衆號歪歪梯Club