主要是看《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"
全局变量是容许声明但不使用