轉自:http://blog.csdn.net/yue7603835/article/details/44264925
1、
不支持繼承、重載 ,比如C++、Java的接口,接口的修改會影響整個實現改接口的類行為的修改,Go 設計者認為這一特點或許根本沒用.
2、任何函數定義必須花括號跟在函數聲明后面而不能換行 如
func funca(a int){ }
在Go語言中 函數也是一種類型 可以被推導 使用支持匿名函數 和閉包。
函數的返回值支持多重返回類似Python , 如果不賦值,整數類型默認 0 浮點數類型默認0.0 error類型 默認是nil
3、
不用的包一定不要引入,這是Go的原則,就如Python的嚴格制表符對其一樣 ,和 unused 變量都會被 編譯器所報錯
4、package做為引入包用。
5、import "a" 調用包內部的函數 a.xx()
6、CGo是Go語言的一個特性,可以方便快速的在Go中調用C,相比Java JNI簡單容易實現
7、go通過goroutine進行協程優化 提高並發性能。動態線程調整。
8、6g和6l是64位版本的Go編譯器和鏈接器,對應的32位版本工具為8g和8l。Go還有另外一個 GCC版本的編譯器,名為 gccgo。
9、注意多個go文件可以用同一個包名字,如果要為 Go生成可執行文件 那么必須 進行 package main
main函數的聲明
func main() { args := os.Args if args == nil || len(args) < 2 { return }
}
10、Linux 為了能夠構建這個工程, 需要先把這個工程的根目錄加入到環境變量GOPATH中。 假設calcproj 目錄位於~/goyard下,則應編輯
~/.bashrc文件,並添加下面這行代碼:
export GOPATH=~/goyard/calcproj
然后執行以下命令應用該設置:
$ source ~/.bashrc,GOPATH和PATH環境變量一樣,也可以接受多個路徑,並且路徑和路徑之間用冒號分割。
設置完GOPATH后,現在我們開始構建工程。假設我們希望把生成的可執行文件放到 calcproj/bin目錄.
11、GoRoot go安裝路徑 ....上面的時候工程路徑
12、GDB調試
不用設置什么編譯選項, Go語言編譯的二進制程序直接支持GDB調試, 比如之前用go build calc編譯出來的可執行文件calc,就可以直接用以下命令以調試模式運行: $ gdb calc
因為GDB的標准用法與Go沒有特別關聯,這里就不詳細展開了,有興趣的讀者可以自行查
看對應的文檔。需要注意的是,Go編譯器生成的調試信息格式為DWARFv3,只要版本高於7.1
的GDB應該都支持它。
13、Go語言會被稱為“更好的C語言”
14、
每一行不需要加分號自動添加
15、添加了
map[string] int
16、添加了類型推導
var a int =1 等價 a:=1 var v1 int = 10 // 正確的使用方式1 var v2 = 10 // 正確的使用方式2,編譯器可以自動推導出v2的類型 v3 := 10 // 正確的使用方式3,編譯器可以自動推導出v3的類型
出現在
:=左側的變量不應該是已經被聲明過的,否則會導致編譯錯誤,比如下面這個
寫法:
var i int
i := 2
會導致類似如下的編譯錯誤:
no new variables on left side of :=
17、支持多重賦值,i, j = j, i 兩個值可以如此簡單的進行交換 而不許引入外部變量
18、我們在使用傳統的強類型語言編程時,經常會出現這種情況,即在調用函數時為了獲取一個
值,卻因為該函數返回多個值而不得不定義一堆沒用的變量。在Go中這種情況可以通過結合使
用多重返回和匿名變量來避免這種丑陋的寫法,讓代碼看起來更加優雅。
假設GetName()函數的定義如下,它返回3個值,分別為firstName、lastName和nickName:
func GetName() (firstName, lastName, nickName string) { return "May", "Chan", "Chibi Maruko" }
若只想獲得nickName,則函數調用語句可以用如下方式編寫:
_, _, nickName := GetName()
這種用法可以讓代碼非常清晰,基本上屏蔽掉了可能混淆代碼閱讀者視線的內容,從而大幅
降低溝通的復雜度和代碼維護的難度
19、在Go語言中,常量是指編譯期間就已知且不可改變的值。常量可以是數值類型(包括整型、浮點型和復數類型) 、布爾類型、字符串類型等。所謂字面常量(literal) ,是指程序中硬編碼的常量,如:
-12
3.14159265358979323846 // 浮點類型的常量
3.2+12i // 復數類型的常量
true // 布爾類型的常量
"foo" // 字符串常量
在其他語言中,常量通常有特定的類型,比如12在C語言中會認為是一個int類型的常量。
如果要指定一個值為12的long類型常量,需要寫成-12l,這有點違反人們的直觀感覺。Go語言
的字面常量更接近我們自然語言中的常量概念,它是無類型的。只要這個常量在相應類型的值域
范圍內,就可以作為該類型的常量,比如上面的常量-12,它可以賦值給int、uint、int32、
int64、float32、float64、complex64、complex128等類型的變量。
20、常量的定義
通過const關鍵字,你可以給字面常量指定一個友好的名字:
const Pi float64 = 3.14159265358979323846 const zero = 0.0 // 無類型浮點常量 const ( size int64 = 1024 eof = -1 // 無類型整型常量 ) const u, v float32 = 0, 3 // u = 0.0, v = 3.0,常量的多重賦值 const a, b, c = 3, 4, "foo" // a = 3, b = 4, c = "foo", 無類型整型和字符串常量
Go的常量定義可以限定常量類型,但不是必需的。如果定義常量時沒有指定類型,那么它
與字面常量一樣,是無類型常量。
常量定義的右值也可以是一個在編譯期運算的常量表達式,比如
const mask = 1 << 3
由於常量的賦值是一個編譯期行為, 所以右值不能出現任何需要運行期才能得出結果的表達
式,比如試圖以如下方式定義常量就會導致錯誤
const Home = os.GetEnv("HOME")
原因很簡單,os.GetEnv()只有在運行期才能知道返回結果,在編譯期並不能確定,所以
無法作為常量定義的右值。
21、Go語言預定義了這些常量:true、false和iota。
iota比較特殊,可以被認為是一個可被編譯器修改的常量,在每一個const關鍵字出現時被
重置為0,然后在下一個const出現之前,每出現一次iota,其所代表的數字會自動增1。
重置為0,然后在下一個const出現之前,每出現一次iota,其所代表的數字會自動增1。
從以下的例子可以基本理解iota的用法:
const ( // iota被重設為0 c0 = iota // c0 == 0 c1 = iota // c1 == 1 c2 = iota // c2 == 2 ) const ( a = 1 << iota // a == 1 (iota在每個const開頭被重設為0) b = 1 << iota // b == 2 c = 1 << iota // c == 4 ) const ( u = iota * 42 // u == 0 v float64 = iota * 42 // v == 42.0 w = iota * 42 // w == 84 ) const x = iota // x == 0 (因為iota又被重設為0了) const y = iota // y == 0 (同上)
如果兩個const的賦值語句的表達式是一樣的,那么可以省略后一個賦值表達式。因此,上
面的前兩個const語句可簡寫為:
const ( // iota被重設為0 c0 = iota // c0 == 0 c1 // c1 == 1 c2 // c2 == 2 ) const ( a = 1 <<iota // a == 1 (iota在每個const開頭被重設為0) b // b == 2 c // c == 4 )
22、 關於枚舉 所有符號 以大寫開頭在包外是可見的 小寫只能在包內部使用
枚舉指一系列相關的常量,比如下面關於一個星期中每天的定義。通過上一節的例子,我們
看到可以用在const后跟一對圓括號的方式定義一組常量, 這種定義法在Go語言中通常用於定義
枚舉值。Go語言並不支持眾多其他語言明確支持的enum關鍵字。
下面是一個常規的枚舉表示法,其中定義了一系列整型常量:
const ( Sunday = iota Monday Tuesday Wednesday Thursday Friday Saturday numberOfDays // 這個常量沒有導出 )
同Go語言的其他符號(symbol)一樣,以大寫字母開頭的常量在包外可見。
以上例子中numberOfDays為包內私有,其他符號則可被其他包訪問。
23、go中的布爾類型 只能用 false true != ==不能進行強制轉換
Go語言中的布爾類型與其他語言基本一致,關鍵字也為bool,可賦值為預定義的true和
false示例代碼如下:
var v1 bool v1 = true v2 := (1 == 2) // v2也會被推導為bool類型
布爾類型不能接受其他類型的賦值,不支持自動或強制的類型轉換。以下的示例是一些錯誤
的用法,會導致編譯錯誤:
var b bool b = 1 // 編譯錯誤 b = bool(1) // 編譯錯誤
以下的用法才是正確的:
var b bool b = (1!=0) // 編譯正確 fmt.Println("Result:", b) // 打印結果為Result: true
24、Go語言支持以下的幾種比較運算符:>、<、==、>=、<=和!=。這一點與大多數其他語言相 同,與C語言完全一致。
if i!=j { } //必須帶大括號 i,j:=1,2 if i==j{ fmt.Println("i==j"); }else { fmt.Println("i!=j"); }
25、兩個不同類型的值不能比較 比如 int8 int16,只能強制轉換 然后再做比較
var a int8 var b int16 a,b=1,2 if int16(a)==b{ fmt.Printf("a==b") }
26、雖然兩個 int8 int16不能直接比較 但是 任何整數類型都能和字面常量整數進行比較 ,但是不能和字符串字面常量進行比較
var a int16 if a==1{ fmt.Printf("a!=\"a\""); }
27、Go語言中的位運算 注意 C語言中的~ 取反。 而Go中變成了^
Go語言支持表2-2所示的位運算符。
表 2-2 運 算 含 義 樣 例 x << y 左移 124 << 2 // 結果為496 x >> y 右移 124 >> 2 // 結果為31 x ^ y 異或 124 ^ 2 // 結果為126 x & y 與 124 & 2 // 結果為0 x | y 或 124 | 2 // 結果為126 ^x 取反 ^2 // 結果為-3 =
Go語言的大多數位運算符與C語言都比較類似,除了取反在C語言中是~x,而在Go語言中 是^x
28、關於浮點數的操作 浮點數 自動推導 是float64即C語言中的double 不能直接和fload32轉換 要進行強制轉換
因為浮點數的比較精度
浮點型用於表示包含小數點的數據,比如1.234就是一個浮點型數據。Go語言中的浮點類型
采用IEEE-754標准的表達方式。
1. 浮點數表示
Go語言定義了兩個類型float32和float64,其中float32等價於C語言的float類型,
float64等價於C語言的double類型。
在Go語言里,定義一個浮點數變量的代碼如下:
而不管賦給它的數字是否是用32位長度表示的。因此,對於以上的例子,下面的賦值將導致編譯
錯誤:
fvalue1 = fvalue2
采用IEEE-754標准的表達方式。
1. 浮點數表示
Go語言定義了兩個類型float32和float64,其中float32等價於C語言的float類型,
float64等價於C語言的double類型。
在Go語言里,定義一個浮點數變量的代碼如下:
var fvalue1 float32 fvalue1 = 12 fvalue2 := 12.0 // 如果不加小數點,fvalue2會被推導為整型而不是浮點型
對於以上例子中類型被自動推導的fvalue2,需要注意的是其類型將被自動設為float64,
而不管賦給它的數字是否是用32位長度表示的。因此,對於以上的例子,下面的賦值將導致編譯
錯誤:
fvalue1 = fvalue2
對於以上例子中類型被自動推導的fvalue2,需要注意的是其類型將被自動設為float64,
而不管賦給它的數字是否是用32位長度表示的。因此,對於以上的例子,下面的賦值將導致編譯
錯誤:
fvalue1 = fvalue2
而必須使用這樣的強制類型轉換:
fvalue1 = float32(fvalue2)
29、自定義精准的 浮點數比較 ,由於浮點數不是一種精確的表達方式 所以比較精度 可能不准確
因為浮點數不是一種精確的表達方式, 所以像整型那樣直接用==來判斷兩個浮點數是否相等
是不可行的,這可能會導致不穩定的結果。
下面是一種推薦的替代方案:
import "math" // p為用戶自定義的比較精度,比如0.00001 func IsEqual(f1, f2, p float64) bool { return math.Fdim(f1, f2) < p }
30、 Go語言中的復數類型 , real 取出 實部 imag取出虛部 虛部為0 的復數 為純虛數。 某一類數字可以表示成這種復數類型
var v1 complex64 v1 = 2.5+15i v2 := 2.5+15i v3 :=complex(2.5,15) fmt.Println(v1) fmt.Println(v2) fmt.Println(v3) fmt.Println("real:",real(v1)) fmt.Println("real:",imag(v1))
復數實際上由兩個實數(在計算機中用浮點數表示)構成,一個表示實部(real),一個表示虛部(imag)。
對於什么是復數可以參考:http://baike.baidu.com/view/10078.htm
復數實際上由兩個實數(在計算機中用浮點數表示)構成,一個表示實部(real) ,一個表示
虛部(imag) 。如果了解了數學上的復數是怎么回事,那么Go語言的復數就非常容易理解了。
1. 復數表示
復數表示的示例如下:
var value1 complex64 // 由2個float32構成的復數類型 value1 = 3.2 + 12i value2 := 3.2 + 12i // value2是complex128類型 value3 := complex(3.2, 12) // value3結果同 value2
2. 實部與虛部
對於一個復數z = complex(x, y),就可以通過Go語言內置函數real(z)獲得該復數的實
部,也就是x,通過imag(z)獲得該復數的虛部,也就是y。
更多關於復數的函數,請查閱math/cmplx標准庫的文檔。
31、Golang中的字符串操作
go中的字符串聲明之后只能獲取 字符不能修改字符 但是可以修改整個字符串
var str string str = "abc" str += "d" str="1111" fmt.Println(str)
我們可以獲取單個字符但是不能修改單個字符
var str string str = "abc" str += "d" ch:=str[0] fmt.Printf("A:%c",ch) //println不能格式化
32、關於Go的編碼處理 只支持 Unicode UTF-8格式
import strings 這個包中包含了處理string類型的所有工具函數函數 Go編譯器支持UTF-8的源代碼文件格式。這意味着源代碼中的字符串可以包含非ANSI的字符,比如“Hello world. 你好,世界! ”可以出現在Go代碼中。但需要注意的是,如果你的Go代碼需要包含非ANSI字符,保存源文件時請注意編碼格式必須選擇UTF-8。特別是在Windows下一般編輯器都默認存為本地編碼,比如中國地區可能是GBK編碼而不是UTF-8,如果沒注意這點在編譯和運行時就會出現一些意料之外的情況。 字符串的編碼轉換是處理文本文檔(比如TXT、XML、HTML等)非常常見的需求,不過可惜的是Go語言僅支持UTF-8和Unicode編碼。對於其他編碼,Go語言標准庫並沒有內置的編碼轉換支持。不過,所幸的是我們可以很容易基於iconv庫用Cgo包裝一個。這里有一個開源項目:
33、在Go中字符串的單個字符就是 byte類型也就是 uint8
一個字符串的長度len返回的默認是int類型也就是平台相關類型,我們在做相應的操作的收 要么自動推導 要么進行強制轉換
var str string str = "abcdefghijklmn" var length int8=int8(len(str)) for i:=0 ;i<int(length) ;i++{ fmt.Printf("%c",str[i]) }
34、關於遍歷Unicode字符,每個 unicode的字符類型是 rune
每個中文字符在UTF-8中占3個字節,而不是1個字節。
另一種是以Unicode字符遍歷:
str := "Hello,世界" for i, ch := range str { fmt.Println(i, ch)//ch的類型為rune }
輸出結果為:
0 72
1 101
2 108
3 108
4 111
5 44
6 32
7 19990
10 30028
以Unicode字符方式遍歷時,每個字符的類型是rune(早期的Go語言用int類型表示Unicode
字符) ,而不是byte。
測試代碼
package main import "fmt" func main() { var strUnicode string = "hello,世界" for i,ch := range strUnicode{ fmt.Println(i,ch) } }
35、關於 Go語言中支持的兩種字符類型 一種是 byte 實際上是uint8的別名 ,另一種是unicode類型的字符 關鍵字為 rune
在Go語言中支持兩個字符類型,一個是byte(實際上是uint8的別名) ,代表UTF-8字符串的單個字節的值;另一個是rune,代表單個Unicode字符。
關於rune相關的操作,可查閱Go標准庫的unicode包。另外unicode/utf8包也提供了 UTF8和Unicode之間的轉換。
出於簡化語言的考慮,Go語言的多數API都假設字符串為UTF-8編碼。盡管Unicode字符在標 准庫中有支持,但實際上較少使用。
36、遍歷unicode字符的另一種是 可以用變量占位符去掉不想要的數據
package main import "fmt" func main() { var strUnicode string = "hello,世界" for _,ch := range strUnicode{ fmt.Printf("%c\n",ch) } }
37、關於Go語言的指針操作
package main import "fmt" func main() { var inta int8=3 ; var pinta*int8=&inta ; fmt.Printf("%d",*pinta); }
38、
關於數組的遍歷 range 遍歷可以選擇忽略 索引
package main import "fmt" func main() { byteArr:=[5]byte{1,2,3,4,5} for _,val:=range byteArr { fmt.Println(val) } } //////////////////////////////////各種數組的聲明///////////////////////////////// [32]byte // 長度為32的數組,每個元素為一個字節 [2*N] struct { x, y int32 } // 復雜類型數組 [1000]*float64 // 指針數組 [3][5]int // 二維數組 [2][2][2]float64 // 等同於[2]([2]([2]float64)) /////關於二維數組的初始化以及聲明.................................. package main import "fmt" func main() { td:=[2][5]int{{1,2,3,4,5},{5,4,3,2,1}} for _,val:=range td{ for _,vall:=range val{ fmt.Println(vall) } } }
39、關於Go的數組 是一個值類型,在做為參數傳遞 或者 做為函數返回的時候 都是 數組的副本,所以不能通過傳遞 數組參數在函數內部 進行修改 。、
package main import "fmt" func modify(arr[5]int){ arr[1]=1 fmt.Printf("arr[1]=%d\n",arr[1]) } func main() { td:=[5]int{1,2,3,4,5} modify(td) for _,val:=range td{ fmt.Println(val) } } //////Go Web
40、Go語言中的數組切片 可以從一個已存在的數組創建 也可以直接手動創建一個數組切片
一個指向原生數組的指針;
數組切片中的元素個數;
數組切片已分配的存儲空間。
從底層實現的角度來看,數組切片實際上仍然使用數組來管理元素,因此它們之間的關系讓
C++程序員們很容易聯想起STL中std::vector和數組的關系。基於數組,數組切片添加了一系
列管理功能,可以隨時動態擴充存放空間,並且可以被隨意傳遞而不會導致所管理的元素被重復
基於數組創建切片
package main import "fmt" func modify(arr[]int){ arr[1]=1 fmt.Printf("arr[1]=%d\n",arr[1]) } func main() { td:=[]int{1,2,3,4,5} //基於數組創建切片 slice:=td[:3] // td[:] td[begin:end] 都可以創建數組切片 還可以創建一個比數組還大的切片 for _,val:=range slice{ fmt.Println(val) } }
41、主動創建數組切片 操作數組 所有的方法 都是適用於數組切片 ,合理利用切片能極大提高內存操作的速度
數組切片支持內建的cap()函數和len()函數
從數組切片創建數組切片的時候只要不超過模板切片的大小那么創建是沒問題的,否則會報出數組切片越界的錯誤。
td:=[]int{1,2,3,4,5} //這樣創建出來的實際上是數組切片
數組切片做為參數傳遞給函數是可以被修改值的 ,解決了Go中數組屬於值類型 結果函數傳遞參數的時候值被復制
package main import "fmt" func modify(arr[]int){ arr[1]=1 fmt.Printf("arr[1]=%d\n",arr[1]) } func main() { td:=[]int{1,2,3,4,5} //基於數組創建切片 slice:=td[:3] //從切片創建切片 都可以 modify(slice) for _,val:=range slice{ fmt.Println(val) } }
從輸出結果我們發現了 數組切片可以作為參數傳遞到函數中並且被函數所修改
並非一定要事先准備一個數組才能創建數組切片。Go語言提供的內置函數make()可以用於
靈活地創建數組切片。下面的例子示范了直接創建數組切片的各種方法。
創建一個初始元素個數為5的數組切片,元素初始值為0:
mySlice1 := make([]int, 5)
創建一個初始元素個數為5的數組切片,元素初始值為0,並預留10個元素的存儲空間:
mySlice2 := make([]int, 5, 10)
直接創建並初始化包含5個元素的數組切片:
mySlice3 := []int{1, 2, 3, 4, 5}
當然,事實上還會有一個匿名數組被創建出來,只是不需要我們來操心而已。
數組切片支持Go語言內置的cap()函數和len()函數,代碼清單2-2簡單示范了這兩個內置
函數的用法。可以看出,cap()函數返回的是數組切片分配的空間大小,而len()函數返回的是
數組切片中當前所存儲的元素個數
動態創建 一個 初始化五個0 並且 內存儲空間初始化20的 數組切片
td1:=make([]int,5,20)
fmt.Println(cap(td1))
fmt.Println(len(td1))
//為數組切片動態增加元素
append(td1,1,2,3,4,56,6,7)
//為數組元素添加數組切片
append(td1,td2...) //一定要加... 附加數組切片的時候
需要注意的是,我們在第二個參數mySlice2后面加了三個點,即一個省略號,如果沒有這個省
略號的話,會有編譯錯誤,因為按append()的語義,從第二個參數起的所有參數都是待附加的
元素。因為mySlice中的元素類型為int,所以直接傳遞mySlice2是行不通的。加上省略號相
當於把mySlice2包含的所有元素打散后傳入
42. 數組切片支持內容復制
數組切片支持Go語言的另一個內置函數copy(),用於將內容從一個數組切片復制到另一個
數組切片。如果加入的兩個數組切片不一樣大,就會按其中較小的那個數組切片的元素個數進行
復制。下面的示例展示了copy()函數的行為:
slice1 := []int{1, 2, 3, 4, 5} slice2 := []int{5, 4, 3} copy(slice2, slice1) // 只會復制slice1的前3個元素到slice2中 copy(slice1, slice2) // 只會復制slice2的3個元素到slice1的前3個位置 old:=[]int{1,2,3,4,5,6} newSlice:=[]int{1,3,3} copy(old,newSlice) fmt.Println(old)
43、在map中使用復雜數據類型
我們可以使用Go語言內置的函數make()來創建一個新map。下面的這個例子創建了一個鍵
類型為string、值類型為PersonInfo的map:
myMap = make(map[string] PersonInfo)
也可以選擇是否在創建時指定該map的初始存儲能力,下面的例子創建了一個初始存儲能力
為100的map:
myMap = make(map[string] PersonInfo, 100)
關於存儲能力的說明,可以參見2.3.6節中的內容。
創建並初始化map的代碼如下:
myMap = map[string] PersonInfo{ "1234": PersonInfo{"1", "Jack", "Room 101,..."}, } package main import "fmt" type Info struct{ name string age int8 } func main() { var infoMap map[string] Info infoMap=make(map[string] Info) infoMap["s1"]= Info{"ydw",11} infoMap["s2"]=Info{"xxx",22} fmt.Println(infoMap) /////如果sone 沒有查找到那么返回值應該是nil 實際上我們只需要判斷 ok是否是 true or false 即可判斷元素是否查找到 sone,ok:=infoMap["s1"] if ok { fmt.Println("s1 student info exists!",sone.name,":",sone.age) }else{ fmt.Println("s1 student info not exists!") } } /////刪除一個map用 delete(map,"key")
Go語言提供了一個內置函數delete(),用於刪除容器內的元素。下面我們簡單介紹一下如
何用delete()函數刪除map內的元素:
delete(myMap, "1234")
上面的代碼將從myMap中刪除鍵為“1234”的鍵值對。如果“1234”這個鍵不存在,那么這個調
用將什么都不發生,也不會有什么副作用。但是如果傳入的map變量的值是nil,該調用將導致
程序拋出異常(panic)
44、對於函數的返回值的限制
func returnFunc(num int) int{ if num > 0 { return 100 //錯誤 返回值不能寫在if...else之中結構之中 } return 100 }
45、switch case default用法
switch i { case 0: fmt.Printf("0") case 1: fmt.Printf("1") case 2: fallthrough case 3: fmt.Printf("3") case 4, 5, 6: fmt.Printf("4, 5, 6") default: fmt.Printf("Default") }
46、Go語言的goto 和break Label更加的靈活處理 循環和跳轉
47、自定義復雜數據類型
type Info struct{ name string age int8 }
48、關於函數返回 一個值和函數返回多個值
package main import "fmt" func ret1() int{ return 1 } func ret2()(a int,b int){ a,b=1,2 return } func main() { a,b:=ret2() fmt.Println(ret1(),a,b) }
48、Go的大小寫規則
Go牢記這樣的規則:
小寫字母開頭的函數只在本包內可見,大寫字母開頭的函數才能被其他包使用。
這個規則也適用於類型和變量的可見性。
49、函數的不定參數 實際上是一種"語法糖"
package main import "fmt" func show(args ...int){ for _,val:= range args{ fmt.Println(val) } } func main() { show(1,2,3) }
50、不定參數的傳遞 不定參數還可以傳遞給其他不定參數的 函數 並且可以打亂傳遞 ...
unc myfunc(args ...int) { // 按原樣傳遞 myfunc3(args...) // 傳遞片段,實際上任意的int slice都可以傳進去 myfunc3(args[1:]...) }
51、// ... 傳遞任意類型的參數
package main import "fmt" func Printfx(args ...interface{}) { for _,val:= range args{ fmt.Println(val) } } func main() { Printfx(1,2,3,"adsd","sdaddf") }
52、Go的不定參數語法糖
Go的不定參數語法糖會把 參數構成一個數組切片 當然你直接傳遞 切片數組是不可以的,因為他的參數就是1,2,3,4,5,66,7, 但是我們可以通過...的方式打亂 數組 或者數組切片
...只可以打亂數組切片,常規數組是個值類型你是無法操作的
package main import "fmt" func show(args ...int){ for _,val:= range args{ fmt.Println(val) } } // ... func Printfx(args ...interface{}) { for _,val:= range args{ fmt.Println(val) } } func main() { Printfx(1,2,3,"adsd","sdaddf") slice:=[]int{5,4,3,2,6,7,8} show(slice...) }
53、通過傳遞不定參數獲取 任意類型不定參數的類型
package main import "fmt" func checkType(args...interface{}){ for _,val:=range args{ switch val.(type){ case int : fmt.Println("Type is int!") case string: fmt.Println("Type is string!") default: fmt.Println("Type is unknow!") } } } func main() { checkType(1,2,3,"aaaa",int64(22)) }
54、使用匿名函數和閉包 Go的匿名函數實際上就是閉包
///定義匿名函數 並且調用 funAdd:=func(a,b int)int{ return a+b } r:=funAdd(11,22) fmt.Println("a+b=",r) ////定義 +調用匿名函數 一起 r=func(a,b int)int{ return a-b }(11,2) fmt.Println("a+b=",r) ////Go閉包 通過函數創建匿名函數 並且返回函數 package main import "fmt" func createFunc()(func(aa,bb,cc int) int){ return func(aa,bb,cc int)int{ return aa+bb+cc } } func main() { add:=createFunc() addNum:=add(1,2,3) fmt.Println("addNum:",addNum) } package main import ( "fmt" ) ///////函數的閉包定義 直接調用 .......閉包內部使用的代碼塊外部的變量 只要代碼塊沒有釋放那么變量不會被釋放的 func main() { var j int = 5 a := func()(func()) { var i int = 10 return func() { fmt.Printf("i, j: %d, %d\n", i, j) } }() a() j *= 2 a() }
55、函數多返回值
func ret()(int,int){ return 1,2 } a,b:=ret() fmt.Println("a,b=",a,b)
56、對於結構類型空的值是nil
57、定義結構體一定要加 type,type和C/C++的 typedef 類似也可以 起別名
package main import "fmt" type Data struct{ name string age int } func main() { data:=Data{"a",1} fmt.Println(data) } ///////////////////給結構體起個別名 package main import "fmt" type Data struct{ name string age int } type DData Data func main() { data:=DData{"a",1} fmt.Println(data) }
58、Go的defer和資源釋放 相關問題 defer語句是按照 先進后出的原則,也就是說最后一個defer將會被先執行。
defer字面的意思是延遲執行,也就是說會在不需要的時候自動執行
而Go語言使用defer
關鍵字簡簡單單地解決了資源何時釋放的問題,比如以下的例子:
func CopyFile(dst, src string) (w int64, err error) { srcFile, err := os.Open(src) if err != nil { return } defer srcFile.Close() dstFile, err := os.Create(dstName) if err != nil { return } defer dstFile.Close() return io.Copy(dstFile, srcFile) }
即使其中的Copy()函數拋出異常,Go仍然會保證dstFile和srcFile會被正常關閉。
如果覺得一句話干不完清理的工作,也可以使用在defer后加一個匿名函數的做法:
defer func() { // 做你復雜的清理工作 } ()
另外,一個函數中可以存在多個defer語句,因此需要注意的是,defer語句的調用是遵照
先進后出的原則,即最后一個defer語句將最先被執行。只不過,當你需要為defer語句到底哪
個先執行這種細節而煩惱的時候,說明你的代碼架構可能需要調整一下了
59、panic()和recover()
////panic場景1 package main import "fmt" func main() { defer func(){ fmt.Println("hello,defer go") }() panic(11111) } ////recover場景2 package main import "fmt" func main() { defer func(){ fmt.Println("hello,defer go") }() panic(11111) }
Go語言引入了兩個內置函數panic()和recover()以報告和處理運行時錯誤和程序中的錯誤場景:
func panic(interface{})
func recover() interface{}
當在一個函數執行過程中調用panic()函數時,正常的函數執行流程將立即終止,但函數中之前使用defer關鍵字延遲執行的語句將正常展開執行,之后該函數將返回到調用函數,並導致逐層向上執行panic流程,直至所屬的goroutine中所有正在執行的函數被終止。錯誤信息將被報告,包括在調用panic()函數時傳入的參數,這個過程稱為錯誤處理流程。
從panic()的參數類型interface{}我們可以得知,該函數接收任意類型的數據,比如整型、字符串、對象等。調用方法很簡單,下面為幾個例子:
panic(404) panic("network broken") panic(Error("file not exists"))
recover()函數用於終止錯誤處理流程。一般情況下,recover()應該在一個使用defer
關鍵字的函數中執行以有效截取錯誤處理流程。如果沒有在發生異常的goroutine中明確調用恢復過程(使用recover關鍵字) ,會導致該goroutine所屬的進程打印異常信息后直接退出。
以下為一個常見的場景。
我們對於foo()函數的執行要么心里沒底感覺可能會觸發錯誤處理,或者自己在其中明確加入了按特定條件觸發錯誤處理的語句,那么可以用如下方式在調用代碼中截取recover():
defer func() { if r := recover(); r != nil { log.Printf("Runtime error caught: %v", r) } }() foo()
無論foo()中是否觸發了錯誤處理流程,該匿名defer函數都將在函數退出時得到執行。假如foo()中觸發了錯誤處理流程,recover()函數執行將使得該錯誤處理過程終止。如果錯誤處理流程被觸發時,程序傳給panic函數的參數不為nil,則該函數還會打印詳細的錯誤信息。
60、panic()函數和recover()函數的調用具體區別在哪里
在panic()開始錯誤處理流程,傳入的類型是interface{}任意類型 , 如果我們在defer 函數中調用
recover()函數那么會打斷錯誤處理流程。 panic的調用會終止正常的程序流程
recover()函數用於終止錯誤處理流程。一般情況下,recover()應該在一個使用defer
關鍵字的函數中執行以有效截取錯誤處理流程。如果沒有在發生異常的goroutine中明確調用恢復
過程(使用recover關鍵字) ,會導致該goroutine所屬的進程打印異常信息后直接退出。
package main import "fmt" func main() { //通過閉包定義 defer匿名函數 並且直接調用 defer func(){ //recover 結束當前錯誤處理過程 並且返回 panic的參數,並不結束其他goroutine的執行....... if r:=recover() ;r!=nil{ fmt.Println("recover() called!") } fmt.Println("hello,defer go") }() panic("hello,go") }
61、關於const 和iota的使用
package main import "fmt" var str string="aaa" const( A=iota B C ) func main() { fmt.Println(B) defer func(){ if r:=recover() ;r!=nil{ fmt.Println("recover() called!") fmt.Println(r) } fmt.Println("hello,defer go") }() panic("hello,go") }
62、快速排序算法與數組切片的使用
package main import "fmt" import "math/rand" /////冒泡排序Go實現 時間復雜富 O(n)=n~n^2 func bubbledSort(values []int){ var flags bool =true for i:=0 ;i<len(values);i++{ flags=true for j:=0;j<len(values)-i-1;j++{ if values[j]>values[j+1] { values[j],values[j+1]=values[j+1],values[j] flags=false } } if flags { break } } } ///////快速排序Go實現 func quickSort(values []int,left,right int){ temp := values[left] p := left i, j := left, right for i <= j { for j >= p && values[j] >= temp { j-- } if j >= p { values[p] = values[j] p = j } if values[i] <= temp && i <= p { i++ } if i <= p { values[p] = values[i] p = i } } values[p] = temp if p - left > 1 { quickSort(values, left, p - 1) } if right - p > 1 { quickSort(values, p + 1, right) } } func main() { //創建初始化0個元素 容量1000的 切片 如果用索引直接訪問切片會越界的 容量必須大於等於 初始化元素個數 //val[1]=11 val:=make([]int,0,1000) for i:=0;i<1000;i++{ val=append(val,rand.Intn(1000)) } fmt.Println("冒泡排序前:",val) bubbledSort(val) fmt.Println("冒泡排序后:",val) }
63、Go中的import和package 等等的關系
//import只是引入的文件夾而已,,,可以使用相對路徑或者絕對路徑
//他會把文件夾中的所有.go文件引入 ,,,,,,
///package xx 實際上是 在外不用調用用的 比如xx.A() 並不是給import使用 package內部的小寫全部是私有 大寫全部是公有
//外部包不能和主模塊放到一起會編譯不過的
import "./bubble"