主要是看《the way to go》時的一些筆記,比較凌亂,內容也不全,之後慢慢補充。git
標籤(空格分隔): go 監控github
break
default
func
interface
select
case
defer
go
map
struct
chan
else
goto
package
switch
const
fallthrough
if
range
type
continue
for
import
return
var
數據庫
append
bool
byte
cap
close
complex
complex64
complex128
uint16
copy
false
float32
float64
imag
int
int8
int16
uint32
int32
int64
iota
len
make
new
nil
panic
uint64
print
println
real
recover
string
true
uint
uint8
uintptr
express
首字母大寫至關於public,小寫至關於pivatejson
package main import fm "fmt" // alias3 func main() { fm.Println("hello, world") }
declare after package import數組
func Sum(a, b int) int { return a + b } func functionName(parameter_list) (return_value_list) { … } //???????????? func (t T) Method1() { //... }
parameter_list 的形式爲 (param1 type1, param2 type2, …)
return_value_list 的形式爲 (ret1 type1, ret2 type2, …)安全
能夠是布爾型、數字型(整數型、浮點型和複數)和字符串型。常量的定義格式const identifier [type] = value
網絡
顯式類型定義: const b string = "abc"
數據結構
隱式類型定義: const b = "abc"
閉包
常量的值必須是可以在編譯時就可以肯定的,數字型的常量是沒有大小和符號的,而且可使用任何精度而不會致使溢出
var a, b *int var a int var b bool var str string var ( a int b bool str string )
變量被聲明以後,系統自動賦予它該類型的零值:int 爲 0,float 爲 0.0,bool 爲 false,string 爲空字符串,指針爲 nil。
編譯時便可推斷類型
var identifier [type] = value var a int = 15 var i = 5 var b bool = false var str string = "Go says hello to the world!" //編譯時推斷類型 var a = 15 var b = false var str = "Go says hello to the world!" var ( a = 15 b = false str = "Go says hello to the world!" numShips = 50 city string ) //運行時自動推斷 var ( HOME = os.Getenv("HOME") USER = os.Getenv("USER") GOROOT = os.Getenv("GOROOT") )
函數內部變量首字母大寫全局or局部:函數內部變量均爲局部
整數
int8(-128 -> 127)
int16(-32768 -> 32767)
int32(-2,147,483,648 -> 2,147,483,647)
int64(-9,223,372,036,854,775,808 -> 9,223,372,036,854,775,807)
無符號整數
uint8(0 -> 255)
uint16(0 -> 65,535)
uint32(0 -> 4,294,967,295)
uint64(0 -> 18,446,744,073,709,551,615)
浮點型(IEEE-754 標準)
float32(+- 1e-45 -> +- 3.4 * 1e38)
float64(+- 5 1e-324 -> 107 1e308)
float32 精確到小數點後 7 位,float64 精確到小數點後 15 位。經過增長前綴 0 來表示 8 進制數(如:077),增長前綴 0x 來表示 16 進制數(如:0xFF),以及使用 e 來表示 10 的連乘(如: 1e3 = 1000,或者 6.022e23 = 6.022 x 1e23)。
package main import "fmt" func main() { var n int16 = 34 var m int32 // compiler error: cannot use n (type int16) as type int32 in assignment //m = n m = int32(n) fmt.Printf("32 bit int is: %d\n", m) fmt.Printf("16 bit int is: %d\n", n) }
格式化輸出%d
用於格式化整數(%x
和 %X
用於格式化 16 進製表示的數字),%g
用於格式化浮點型(%f
輸出浮點數,%e
輸出科學計數表示法),%0d
用於規定輸出定長的整數,其中開頭的數字 0 是必須的。%n.mg
用於表示數字 n 並精確到小數點後 m 位,除了使用 g 以外,還可使用 e 或者 f,例如:使用格式化字符串 %5.2e
來輸出 3.4 的結果爲 3.40e+00
。
類型轉換
func IntFromFloat64(x float64) int { if math.MinInt32 <= x && x <= math.MaxInt32 { // x lies in the integer range whole, fraction := math.Modf(x) if fraction >= 0.5 { whole++ } return int(whole) } panic(fmt.Sprintf("%g is out of the int32 range", x)) }
運算符優先級
優先級 運算符 7 ^ ! 6 * / % << >> & &^ 5 + - | ^ 4 == != < <= >= > 3 <- 2 && 1 ||
解釋字符串
該類字符串使用雙引號括起來,其中的相關的轉義字符將被替換,這些轉義字符包括:
\n:換行符 \r:回車符 \t:tab 鍵 \u 或 \U:Unicode 字符 \\:反斜槓自身
非解釋字符串
該類字符串使用反引號括起來,支持換行,例如:This is a raw string \n
中的 \n
會被原樣輸出。
在循環中使用加號+拼接字符串並非最高效的作法,更好的辦法是使用函數 strings.Join(),有沒有更好地辦法了?有!使用字節緩衝(bytes.Buffer)拼接更加給力!
字符串操做strings和strconv包
//前綴和後綴 strings.HasPrefix(s, prefix string) bool strings.HasSuffix(s, suffix string) bool //包含關係 strings.Contains(s, substr string) bool strings.ContainsAny(s, chars string) bool strings.ContainsRune(s string, r rune) bool //索引 strings.Index(s, str string) int strings.LastIndex(s, str string) int strings.IndexRune(s string, r rune) int //替換 strings.Replace(str, old, new, n) string //次數統計 strings.Count(s, str string) int //重複次數 strings.Repeat(s, count int) string //大小寫 strings.ToLower(s) string strings.ToLower(s) string //修剪字符串 func Trim(s string, cutset string) string func TrimLeft(s string, cutset string) string func TrimRight(s string, cutset string) string func TrimSpace(s string) string //分割字符串 strings.Fields(s) []string strings.Split(s, sep string) []string //拼接字符串 strings.Join(sl []string, sep string) string
日期和時間
func timeTest(){ t := time.Now() fm.Println(t) fm.Println(t.Day(), t.Hour(), t.Minute()) t = time.Now().UTC() fm.Println(t) fm.Println(t.Format(time.RFC822)) fm.Println(t.Format(time.ANSIC)) fm.Println(t.Format("02 Jan 2006 15:04")) }
指針
//聲明方式 var intP *int //使用方式 intP = &i1
不容許指針運算pointer+2
、c=*p++
,空指針的反向引用是不合法。
控制結構
if-esle語句
switch語句
不須要break,執行完分支自動退出switch,若是須要繼續執行則使用fallthrough
switch num1 { case 98, 99: fmt.Println("It's equal to 98") case 100: fmt.Println("It's equal to 100") default: fmt.Println("It's not equal to 98 or 100") } //多條件同行,fallthrough,return跳出 func switchStruct() int { a := 4 switch a { case 1,2,3: fmt.Println(a) case 4: fallthrough case 5: fm.Println("break") return 1 default: fmt.Println("ALL") } return 0 } //bool用法 switch { case num1 < 0: fmt.Println("Number is negative") case num1 > 0 && num1 < 10: fmt.Println("Number is between 0 and 10") default: fmt.Println("Number is 10 or greater") } //初始化用法 switch a, b := x[i], y[j]; { case a < b: t = -1 case a == b: t = 0 case a > b: t = 1 }
for語句
func forStatement() { for i := 1; i < 15; i++{ fm.Println(i) } j := 1 GO: fm.Println(j) j++ if j < 15{ goto GO } for k := 0; k < 5; k++ { for m := 0; m < k; m++ { fm.Printf("G") } fm.Println() } str := "GGGGGGGGGGGGGGGGGGGGGGGG" for i := 0; i < 5; i++ { fm.Println(str[0:i+1]) } str1 := "G" for i := 0; i < 5; i++ { fm.Println(str1) str1 += "G" } }
Go 裏面有三種類型的函數:
普通的帶有名字的函數
匿名函數或者lambda函數
方法(Methods)
defer關鍵字
在函數return以後執行相關語句,多個defer逆序執行
func a() { i := 0 defer fmt.Println(i) i++ return } //輸出0,輸出語句以前的值
關閉文件流:
// open a file defer file.Close() (詳見第 12.2 節)
解鎖一個加鎖的資源
mu.Lock() defer mu.Unlock() (詳見第 9.3 節)
打印最終報告
printHeader() defer printFooter()
關閉數據庫連接
// open a database connection defer disconnectFromDB()
func testDefer() { fmt.Printf("In function1 at the top\n") defer func2() fmt.Printf("In function1 at the bottom!\n") i := 0 i++ defer fmt.Println(i) i++ for i := 0; i < 5; i++ { defer fmt.Printf("%d", i) } defer deferC(deferA(), deferB()) fm.Println("Begin:\n") return } func deferA() (rtn string) { fm.Println("in A") rtn = "AA" tmp := "tmp1" defer func() { fm.Println(tmp) }() defer fm.Println(rtn) tmp = "tmp2" return "AAAA" } func deferB() string { fm.Println("in B") return "B" } func deferC(str1, str2 string) { fm.Println("in C") } /*輸出 In function1 at the top In function1 at the bottom! in A AA tmp2 in B Begin: in C 432101 function2: Deferred until the end of the calling function! */
defer
函數中的函數(defer deferC(deferA(), deferB())
中的A
和B
)會先於return執行,defer func()
能夠rentun
語句執行完變量的最新的值,無func()
時則函數參數取對應defer
語句執行完變量的最新值。
內置函數
遞歸函數
函數做參數
func main() { callback(1, Add) } func Add(a, b int) { fmt.Printf("The sum of %d and %d is: %d\n", a, b, a+b) } func callback(y int, f func(int, int)) { f(y, 2) // this becomes Add(1, 2) }
閉包
func main() { var f = Adder() fmt.Print(f(1), " - ") fmt.Print(f(20), " - ") fmt.Print(f(300)) } func Adder() func(int) int { var x int return func(delta int) int { x += delta return x } } // 1,21,321
數組
Go 語言中的數組是一種值類型(不像 C/C++ 中是指向首元素的指針),因此能夠經過 new() 來建立: var arr1 = new([5]int)。arr1 的類型是 *[5]int,而 arr2的類型是 [5]int。結果就是當把一個數組賦值給另外一個時,須要在作一次數組內存的拷貝操做。例如:
arr2 := arr1 arr2[2] = 100
兩個數組就有了不一樣的值,在賦值後修改 arr2 不會對 arr1 生效。
func f(a [3]int) { fmt.Println(a) } func fp(a *[3]int) { fmt.Println(a) } func main() { var ar [3]int f(ar) // passes a copy of ar fp(&ar) // passes a pointer to ar }
當數組賦值時,發生了數組內存拷貝。
切片
切片是引用,因此它們不須要使用額外的內存而且比使用數組更有效率。
var identifier []type var slice1 []type = arr1[start:end] var slice1 []type = arr1[:] //所有 slice1 = &arr1 //所有
一個由數字 一、二、3 組成的切片能夠這麼生成:
s := [3]int{1,2,3}
甚至更簡單的s := []int{1,2,3}
。數組區別:數組是切片的特例
buffer 串聯字符串
建立一個 buffer,經過 buffer.WriteString(s)
方法將字符串 s 追加到後面,最後再經過 buffer.String()
方法轉換爲 string,這種實現方式比使用 +=
要更節省內存和 CPU。
var buffer bytes.Buffer for { if s, ok := getNextString(); ok { //method getNextString() not shown here buffer.WriteString(s) } else { break } } fmt.Print(buffer.String(), "\n")
func AppendByte(slice []byte, data ...byte) []byte { m := len(slice) n := m + len(data) if n > cap(slice) { // if necessary, reallocate // allocate double what's needed, for future growth. newSlice := make([]byte, (n+1)*2) copy(newSlice, slice) slice = newSlice } slice = slice[0:n] copy(slice[m:n], data) return slice }
for-range
seasons := []string{"Spring", "Summer", "Autumn", "Winter"} //index&val for ix, season := range seasons { fmt.Printf("Season %d is: %s\n", ix, season) } //just val var season string for _, season = range seasons { fmt.Printf("%s\n", season) } //just index for ix := range seasons { fmt.Printf("%d", ix) }
複製與追加
func main() { // count number of characters: str1 := "asSASA ddd dsjkdsjs dk" fmt.Printf("The number of bytes in string str1 is %d\n",len(str1)) fmt.Printf("The number of characters in string str1 is %d\n",utf8.RuneCountInString(str1)) str2 := "asSASA ddd dsjkdsjsこん dk" fmt.Printf("The number of bytes in string str2 is %d\n",len(str2)) fmt.Printf("The number of characters in string str2 is %d",utf8.RuneCountInString(str2)) } /* Output: The number of bytes in string str1 is 22 The number of characters in string str1 is 22 The number of bytes in string str2 is 28 The number of characters in string str2 is 24 */ //將一個字符串追加到某一個字符數組的尾部 var b []byte var s string b = append(b, s...)
Go 語言中的字符串是不可變的,必須先將字符串轉換成字節數組,而後再經過修改數組中的元素值來達到修改字符串的目的,最後將字節數組轉換回字符串格式。
s := "hello" c := []byte(s) c[0] = ’c’ s2 := string(c) // s2 == "cello"
append操做
將切片 b 的元素追加到切片 a 以後:a = append(a, b...)
複製切片 a 的元素到新的切片 b 上:
b = make([]T, len(a)) copy(b, a)
刪除位於索引 i 的元素:a = append(a[:i], a[i+1:]...)
切除切片 a 中從索引 i 至 j 位置的元素:a = append(a[:i], a[j:]...)
爲切片 a 擴展 j 個元素長度:a = append(a, make([]T, j)...)
在索引 i 的位置插入元素 x:a = append(a[:i], append([]T{x}, a[i:]...)...)
在索引 i 的位置插入長度爲 j 的新切片:a = append(a[:i], append(make([]T, j), a[i:]...)...)
在索引 i 的位置插入切片 b 的全部元素:a = append(a[:i], append(b, a[i:]...)...)
取出位於切片 a 最末尾的元素 x:x, a = a[len(a)-1], a[:len(a)-1]
將元素 x 追加到切片 a:a = append(a, x)
垃圾回收
var digitRegexp = regexp.MustCompile("[0-9]+") func FindDigits(filename string) []byte { b, _ := ioutil.ReadFile(filename) return digitRegexp.Find(b) } // 經過拷貝避免小切片佔用大內存(整個文件) func FindDigits(filename string) []byte { b, _ := ioutil.ReadFile(filename) b = digitRegexp.Find(b) c := make([]byte, len(b)) copy(c, b) return c }
error: rune is not a Type
:代碼中又於rune重名的函數
var map1 map[keytype]valuetype
不要使用 new,永遠用 make 來構造 map
map類型是非線程安全的,並行訪問map數據會出錯。而且爲了性能map沒有鎖機制
// map排序 var ( barVal = map[string]int{"alpha": 34, "bravo": 56, "charlie": 23,"delta": 87, "echo": 56, "foxtrot": 12,"golf": 34, "hotel": 16, "indio": 87,"juliet": 65, "kili": 43, "lima": 98} ) func main() { fmt.Println("unsorted:") for k, v := range barVal { fmt.Printf("Key: %v, Value: %v / ", k, v) } keys := make([]string, len(barVal)) i := 0 for k, _ := range barVal { keys[i] = k i++ } sort.Strings(keys) fmt.Println() fmt.Println("sorted:") for _, k := range keys { fmt.Printf("Key: %v, Value: %v / ", k, barVal[k]) } }
regexp包
鎖和sync包
big包
可使用 make() 的三種類型:slices / maps / channels(見第 14 章)
在一個結構體中對於每一種數據類型只能有一個匿名字段。
內嵌結構體
package main import "fmt" type A struct { ax, ay int } type B struct { A bx, by float32 } func main() { b := B{A{1, 2}, 3.0, 4.0} fmt.Println(b.ax, b.ay, b.bx, b.by) fmt.Println(b.A) }
定義方法的通常格式:func (recv receiver_type) methodName(parameter_list) (return_value_list) { ... }
結構體方法
type TwoInts struct { a int b int } func main() { two1 := new(TwoInts) two1.a = 12 two1.b = 10 fmt.Printf("The sum is: %d\n", two1.AddThem()) fmt.Printf("Add them to the param: %d\n", two1.AddToParam(20)) two2 := TwoInts{3, 4} fmt.Printf("The sum is: %d\n", two2.AddThem()) } func (tn *TwoInts) AddThem() int { return tn.a + tn.b } func (tn *TwoInts) AddToParam(param int) int { return tn.a + tn.b + param }
非結構體類型方法
type IntVector []int func (v IntVector) Sum() (s int) { for _, x := range v { s += x } return } func main() { fmt.Println(IntVector{1, 2, 3}.Sum()) // 輸出是6 }
類型和做用在它上面定義的方法能夠不一樣文件,可是必須在同一個包裏定義,除非定義別名:
type myTime struct { time.Time //anonymous field } func (t myTime) first3Chars() string { return t.Time.String()[0:3] } func main() { m := myTime{time.Now()} // 調用匿名Time上的String方法 fmt.Println("Full time now:", m.String()) // 調用myTime.first3Chars fmt.Println("First 3 chars:", m.first3Chars()) } /* Output: Full time now: Mon Oct 24 15:34:54 Romance Daylight Time 2011 First 3 chars: Mon */
指針方法和值方法均可以在指針或非指針上被調用
type List []int func (l List) Len() int { return len(l) } func (l *List) Append(val int) { *l = append(*l, val) } func main() { // 值 var lst List lst.Append(1) fmt.Printf("%v (len: %d)", lst, lst.Len()) // [1] (len: 1) // 指針 plst := new(List) plst.Append(2) fmt.Printf("%v (len: %d)", plst, plst.Len()) // &[2] (len: 1) }
併發訪問對象
import 「sync」 type Info struct { mu sync.Mutex // ... other fields, e.g.: Str string } func Update(info *Info) { info.mu.Lock() // critical section: info.Str = // new value // end critical section info.mu.Unlock() }
內嵌方法
//內嵌方法測試 func methodTest() { point := &NamedPoint{Point{3, 4}, "name"} fm.Println(point.Abs(), point.Point.Abs()) } type Point struct { x, y float64 } func (p *Point)Abs() float64 { return math.Sqrt(p.x*p.x + p.y*p.y) } type NamedPoint struct { Point name string } func (p *NamedPoint)Abs() float64 { return p.Point.Abs() * p.Point.Abs() }
多重繼承
type Camera struct{} func (c *Camera) TakeAPicture() string { return "Click" } type Phone struct{} func (p *Phone) Call() string { return "Ring Ring" } type CameraPhone struct { Camera Phone } func main() { cp := new(CameraPhone) fmt.Println("Our new CameraPhone exhibits multiple behaviors...") fmt.Println("It exhibits behavior of a Camera: ", cp.TakeAPicture()) fmt.Println("It works like a Phone too: ", cp.Call()) }
垃圾回收和SetFinalizer
內存狀態
// fmt.Printf("%d\n", runtime.MemStats.Alloc/1024) // 此處代碼在 Go 1.5.1下再也不有效,更正爲 var m runtime.MemStats runtime.ReadMemStats(&m) fmt.Printf("%d Kb\n", m.Alloc / 1024)
回收對象時,對象移除前操做:
runtime.SetFinalizer(obj, func(obj *typeObj))
不一樣的結構實現interface中定義的接口,經過接口變量自動識別對應結構來實現多態,全部類型必須徹底實現interface中的全部函數
type Shaper interface { Area() float32 } type Square struct { side float32 } func (sq *Square) Area() float32 { return sq.side * sq.side } type Rectangle struct { length, width float32 } func (r Rectangle) Area() float32 { return r.length * r.width } func main() { r := Rectangle{5, 3} // Area() of Rectangle needs a value q := &Square{5} // Area() of Square needs a pointer // shapes := []Shaper{Shaper(r), Shaper(q)} // or shorter shapes := []Shaper{r, q} fmt.Println("Looping through shapes for area ...") for n, _ := range shapes { fmt.Println("Shape details: ", shapes[n]) fmt.Println("Area of this shape is: ", shapes[n].Area()) } }
接口的實現經過指針則變量必須傳入指針;接口能夠嵌套。
類型判斷
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) } //簡潔版 switch areaIntf.(type) { case *Square: // TODO case *Circle: // TODO ... default: // TODO }
也可用於測試變量是否實現某接口
type Stringer interface { String() string } if sv, ok := v.(Stringer); ok { fmt.Printf("v implements String(): %s\n", sv.String()) // note: sv, not v }
接口調用
指針方法能夠經過指針調用
值方法能夠經過值調用
接收者是值的方法能夠經過指針調用,由於指針會首先被解引用
接收者是指針的方法不能夠經過值調用,由於存儲在接口中的值沒有地址
類型 T 的可調用方法集包含接受者爲 T 或 T 的全部方法集
類型 T 的可調用方法集包含接受者爲 T 的全部方法
類型 T 的可調用方法集不包含接受者爲 *T 的方法
自定義類型排序接口
package sort // 排序接口 type Sorter interface { Len() int Less(i, j int) bool Swap(i, j int) } func Sort(data Sorter) { len := data.Len() for i := 1; i < len; i++{ for j := 0; j < len - i; j++ { if data.Less(j+1, j) { data.Swap(j+1, j) } } } }
func main(){ Sunday := day{0, "SUN", "Sunday"} Monday := day{1, "MON", "Monday"} Tuesday := day{2, "TUE", "Tuesday"} Wednesday := day{3, "WED", "Wednesday"} Thursday := day{4, "THU", "Thursday"} Friday := day{5, "FRI", "Friday"} Saturday := day{6, "SAT", "Saturday"} data2 := []day{Tuesday, Thursday, Wednesday, Sunday, Monday, Friday, Saturday, Thursday} array2 := dayArr(data2) sort.Sort(array2) for _, d := range data2 { fmt.Printf("%v ", d) } fmt.Printf("\n") fm.Println(array2) fmt.Printf("\n") } // 本身想到的實現方式 type day struct { num int shortName string longName string } type dayArr []day func (p dayArr)Len() int { return len(p) } func (p dayArr)Less(i, j int) bool { return p[i].num < p[j].num } func (p dayArr)Swap(i, j int) { p[i], p[j] = p[j], p[i] }
func main(){ Sunday := day{0, "SUN", "Sunday"} Monday := day{1, "MON", "Monday"} Tuesday := day{2, "TUE", "Tuesday"} Wednesday := day{3, "WED", "Wednesday"} Thursday := day{4, "THU", "Thursday"} Friday := day{5, "FRI", "Friday"} Saturday := day{6, "SAT", "Saturday"} data3 := []*day{&Tuesday, &Thursday, &Wednesday, &Sunday, &Monday, &Friday, &Saturday} // 結構體初始化 array3 := dayArray{data3} sort.Sort(&array3) for _, d := range data3 { fmt.Printf("%v ", d) } fmt.Printf("\n") fm.Println(array3) fmt.Printf("\n") } // 做者實現方式 type dayArray struct { data []*day } func (p *dayArray)Len() int { return len(p.data) } func (p *dayArray)Less(i, j int) bool { return p.data[i].num < p.data[j].num } func (p *dayArray)Swap(i, j int) { p.data[i], p.data[j]= p.data[j], p.data[i] }
兩種方式對比:
第一種方式使用struct數組(切片更恰當)和變量,都是經過值拷貝;第二種經過struct指針數組和數組的指針,經過引用。故內存佔用和調用消耗有差異。
暫時沒想到,之後補充
構建通用類型或包含不一樣類型變量的數組
對於返回值未知的接口,能夠經過返回空接口。
package min type Miner interface { Len() int ElemIx(ix int) interface{} Less(i, j int) bool } func Min(data Miner) interface{} { min := data.ElemIx(0) for i:=1; i < data.Len(); i++ { if data.Less(i, i-1) { min = data.ElemIx(i) } } return min } type IntArray []int func (p IntArray) Len() int { return len(p) } func (p IntArray) ElemIx(ix int) interface{} { return p[ix] } func (p IntArray) Less(i, j int) bool { return p[i] < p[j] } type StringArray []string func (p StringArray) Len() int { return len(p) } func (p StringArray) ElemIx(ix int) interface{} { return p[ix] } func (p StringArray) Less(i, j int) bool { return p[i] < p[j] }
複製數據切片至空接口切片
內存佈局不同,須要一個個賦值
var dataSlice []myType = FuncReturnSlice() var interfaceSlice []interface{} = make([]interface{}, len(dataSlice)) for ix, d := range dataSlice { interfaceSlice[ix] = d }
通用節點數據結構
type Node struct { le *Node data interface{} ri *Node } func NewNode(left, right *Node) *Node { return &Node{left, nil, right} } func (n *Node) SetData(data interface{}) { n.data = data }
接口和動態類型
函數重載
經過函數參數...T
實現,或空接口fmt.Printf(format string, a ...interface{}) (n int, errno error)
接口繼承
當一個類型包含(內嵌)另外一個類型(實現了一個或多個接口)的指針時,這個類型就可使用(另外一個類型)全部的接口方法。
type Task struct { Command string *log.Logger } func NewTask(command string, logger *log.Logger) *Task { return &Task{command, logger} } task.Log() //多重繼承 type ReaderWriter struct { *io.Reader *io.Writer }
類型轉換問題
對空接口interface{}進行強制類型轉換報錯
Conversions are expressions of the form T(x) where T is a type and x is an expression that can be converted to type T.
A non-constant value x can be converted to type T in any of these cases:
x is assignable to T.
x's type and T have identical underlying types.
x's type and T are unnamed pointer types and their pointer base types have identical underlying types.
x's type and T are both integer or floating point types.
x's type and T are both complex types.
x is an integer or a slice of bytes or runes and T is a string type.
x is a string and T is a slice of bytes or runes.
轉換是形如
T(x)
的表達式,其中T
是一種類型,而x
是能轉換爲類型T
的表達式
下面任何一種狀況中很是量x都能轉換爲類型T
x可賦值給T
x的類型和T的底層類型一致
x的類型和T是匿名指針類型且它們的指針基類型的底層類型一致
x的類型和T都是整型或浮點型
x的類型和T都是複數類型
x是整數、bytes切片或runes,且T是string
x是string且T是bytes切片或runes
OO 語言最重要的三個方面分別是:封裝,繼承和多態,在 Go 中它們是怎樣表現的呢?
封裝(數據隱藏):Go 把它從4層簡化爲了2層:
包範圍內的:經過標識符首字母小寫,對象只在它所在的包內可見
可導出的:經過標識符首字母大寫,對象 對所在包之外也可見
繼承:用組合實現:內嵌一個(或多個)包含想要的行爲(字段和方法)的類型;多重繼承能夠經過內嵌多個類型實現
多態:用接口實現:某個類型的實例能夠賦給它所實現的任意接口類型的變量。類型和接口是鬆耦合的,而且多重繼承能夠經過實現多個接口實現。
輸入輸出
文件讀寫
inputFile, inputError := os.Open("input.dat") if inputError != nil { fmt.Printf("An error occurred on opening the inputfile\n" + "Does the file exist?\n" + "Have you got acces to it?\n") return // exit the function on error } defer inputFile.Close() inputReader := bufio.NewReader(inputFile) for { inputString, readerError := inputReader.ReadString('\n') if readerError == io.EOF { return } fmt.Printf("The input was: %s", inputString) }
讀到字符串
func main() { inputFile := "products.txt" outputFile := "products_copy.txt" buf, err := ioutil.ReadFile(inputFile) if err != nil { fmt.Fprintf(os.Stderr, "File Error: %s\n", err) // panic(err.Error()) } fmt.Printf("%s\n", string(buf)) err = ioutil.WriteFile(outputFile, buf, 0644) // oct, not hex if err != nil { panic(err. Error()) } }
緩衝讀取
buf := make([]byte, 1024) ... n, err := inputReader.Read(buf) if (n == 0) { break}
按列讀取
func main() { file, err := os.Open("products2.txt") if err != nil { panic(err) } defer file.Close() var col1, col2, col3 []string for { var v1, v2, v3 string _, err := fmt.Fscanln(file, &v1, &v2, &v3) // scans until newline if err != nil { break } col1 = append(col1, v1) col2 = append(col2, v2) col3 = append(col3, v3) } fmt.Println(col1) fmt.Println(col2) fmt.Println(col3) }
寫文件
func main () { outputFile, outputError := os.OpenFile("output.dat", os.O_WRONLY|os.O_CREATE, 0666) if outputError != nil { fmt.Printf("An error occurred with file opening or creation\n") return } defer outputFile.Close() outputWriter := bufio.NewWriter(outputFile) outputString := "hello world!\n" for i:=0; i<10; i++ { outputWriter.WriteString(outputString) } outputWriter.Flush() }
os.O_RDONLY:只讀
os.O_WRONLY:只寫
os.O_CREATE:建立:若是指定文件不存在,就建立該文件。
os.O_TRUNC:截斷:若是指定文件已存在,就將該文件的長度截爲0。
// 非緩衝寫入 func main() { os.Stdout.WriteString("hello, world\n") f, _ := os.OpenFile("test", os.O_CREATE|os.O_WRONLY, 0) defer f.Close() f.WriteString("hello, world in a file\n") }
文件拷貝
func CopyFile(dstName, srcName string) (written int64, err error) { src, err := os.Open(srcName) if err != nil { return } defer src.Close() dst, err := os.OpenFile(dstName, os.O_WRONLY|os.O_CREATE, 0644) if err != nil { return } defer dst.Close() return io.Copy(dst, src) }
讀取參數
func main() { who := "Alice " if len(os.Args) > 1 { who += strings.Join(os.Args[1:], " ") } fmt.Println("Good Morning", who) }
JSON
JSON 與 Go 類型對應以下
bool 對應 JSON 的 booleans
float64 對應 JSON 的 numbers
string 對應 JSON 的 strings
nil 對應 JSON 的 null
不是全部的數據均可以編碼爲JSON類型:只有驗證經過的數據結構才能被編碼:
JSON 對象只支持字符串類型的 key;要編碼一個 Go map 類型,map 必須是 map[string]T(T是json包中支持的任何類型)
Channel,複雜類型和函數類型不能被編碼
不支持循環數據結構;它將引發序列化進入一個無限循環指針能夠被編碼,其實是對指針指向的值進行編碼(或者指針是 nil)
不要經過共享內存來通訊,而經過通訊來共享內存。
func main() { runtime.GOMAXPROCS(2) ch1 := make(chan int) ch2 := make(chan int) go pump1(ch1) go pump2(ch2) go suck(ch1, ch2) time.Sleep(1e9) } func pump1(ch chan int) { for i:=0; ; i++ { ch <- i*2 } } func pump2(ch chan int) { for i:=0; ; i++ { ch <- i+5 } } func suck(ch1,ch2 chan int) { for i := 0; ; i++ { select { case v := <- ch1: fmt.Printf("%d - Received on channel 1: %d\n", i, v) case v := <- ch2: fmt.Printf("%d - Received on channel 2: %d\n", i, v) } } }
協程間通訊
通道聲明:
var identifier chan type id := make(chan type) // 緩衝通道 iden := make(chan type, len)
流向通道(發送)ch <- int1
表示:用通道 ch 發送變量int1(雙目運算符,中綴 = 發送)
從通道流出(接收),三種方式:
int2 = <- ch
表示:變量 int2 從通道 ch(一元運算的前綴操做符,前綴 = 接收)接收數據(獲取新值)
假設 int2 已經聲明過了,若是沒有的話能夠寫成:int2 := <- ch
<- ch 能夠單獨調用獲取通道的(下一個)值,當前值會被丟棄,可是能夠用來驗證,如下代碼是合法的:
if <- ch != 1000{ ... }
數據傳輸
func TestChan() { ch := make(chan string) go sendData(ch) go recvData(ch) time.Sleep(1e9) } func sendData(ch chan string) { ch <- "hu" ch <- "yuan" ch <- "sky" } func recvData(ch chan string) { recv := "" for { recv = <-ch fmt.Println(recv) } }
阻塞
// 發送阻塞 func main() { ch1 := make(chan int) go pump(ch1) // pump hangs time.Sleep(2*1e9) fmt.Println("Recv", <-ch1) // prints only 0 time.Sleep(1e9) } func pump(ch chan int) { for i := 1; ; i++ { ch <- i fmt.Println("Send ",i) } } //發送阻塞 func main() { c := make(chan int) go func() { time.Sleep(15 * 1e9) x := <-c fmt.Println("received", x) }() fmt.Println("sending", 10) c <- 10 fmt.Println("sent", 10) }
信號量模式
循環並行執行
// 測試同步執行 type Empty interface {} const N = 100000 func Compare() { ch_buf := make(chan Empty, N) data := make([]float64, N) for i:=0; i < N; i++{ data[i] = float64(i) } s := time.Now() TestChannal(data, ch_buf) fmt.Println(time.Now().Sub(s)) //fmt.Println(data) for i:=0; i < N; i++{ data[i] = float64(i) } s = time.Now() TestNormal(data) fmt.Println(time.Now().Sub(s)) //fmt.Println(data) } func TestChannal(data []float64, ch_buf chan Empty) { var em Empty for ix, val :=range data { go func(ix int, val float64) { for j := 0; j < N; j++ { data[ix] += val } ch_buf <- em }(ix, val) } for i := 0; i < N; i++ { <-ch_buf} } func TestNormal(data []float64){ for ix, val := range data { for j := 0; j < N; j++ { data[ix] += val } } }
通道的方向
var send_only chan<- int // channel can only send data var recv_only <-chan int // channel can onley recv data
只接收的通道(
<-chan T
)沒法關閉,由於關閉通道是發送者用來表示再也不給通道發送值了,因此對只接收通道是沒有意義的。
全部的包名使用小寫
若是對一個包進行更改或從新編譯,全部引用了這個包的客戶端程序都必須所有從新編譯。
must define main function in main package else you will get error 'undefined'. main function has not params and return value
init function execute before main
production server must use function in package "fmt"
全局變量是容許聲明但不使用