go学习笔记 go学习笔记

转自 https://www.cnblogs.com/xhen/p/11106776.html


 

go学习笔记

 

0、值类型:变量直接存储值,内存通常在栈中分配:int、float、bool、string以及数组和struct

   引用类型:变量存储的是一个地址,这个地址存储最终的值。内存通常在堆上分配。通过gc回收:指针、slice、map、chan等都是引用类型

使用&结构体{}就相当与使用new实例化了一次结构体

1、变量(或常量)包含数据,这些数据可以有不同的数据类型,简称类型。使用 var 声明的变量的值会自动初始化为该类型的零值。类型定义了某个变量的值的集合与可对其进行操作的集合。

 类型可以是基本类型,如:int、float、bool、string;结构化的(复合的),如:struct、array、slice、map、channel;只描述类型的行为的,如:interface。

 结构化的类型没有真正的值,它使用 nil 作为默认值(在 Objective-C 中是 nil,在 Java 中是 null,在 C 和 C++ 中是NULL或 0)。值得注意的是,Go 语言中不存在类型继承。

 比如:定义变量的两种方式 var i int=15 或 i :=15

    定义数组:arr1 :=[] int { }  数组名称  数组长度  数组里面的类型  数组值

2、如果设定的全局变量希望被外部的包使用,其首字母应该大写。

3、字符串:Strings.HasPrefix(string,prefix) 字符串是否以xx开头   Strings.HasSuffix(string,suffix) 字符串是否以xx结尾 eg:str="this is a example"   String.HasPrefix(str,"th");]

4、时间和日期:获取当前时间:time.Now(),获取当前年份:time.Now().Year() 获取当前月份:time.Now().Month() 获取当前天:time.Now().Day()

5、代码块: a ,err :=strconv.Itoa(an)  注意这里的error是为了防止转换失败  if !err {  fmt.Printf("输出成功:%d”,a)}

6、定义函数 func 函数名称 参数类型  返回参数值  eg:

1
2
3
4
5
6
func  func1(s string) (n int, err error) {
     defer  func () {
         log.Printf( "func1(%q) = %d, %v" , s, n, err)
     }()
     return  7, io.EOF
}

7、利用闭包来调试函数:

1
2
3
4
5
6
7
8
9
where :=  func () {
     _, file, line, _ := runtime.Caller(1)
     log.Printf( "%s:%d" , file, line)
}
where()
// some code
where()
// some more code
where()

8、记算代码执行时间:

1
2
3
4
5
start := time.Now()
longCalculation()
end := time.Now()
delta := end.Sub(start)
fmt.Printf( "longCalculation took this amount of time: %s\n" , delta)

9、使用new创建得数组和普通创建数组得不同

1
2
3
4
5
6
7
8
9
10
11
12
        var  arr1=new([5]int)
     for  i:=0;i<len(arr1);i++{
         arr1[i]=1+i
     }
     var  arr2= *arr1
     arr2[0]=7
     for  i,_ := range  arr1{
         fmt.Printf( "this value is %d" ,arr1[i])
     }
//注意使用new([5]int创建得数组是带有地址的   而普通创建的没有
//例如:使用new创建的类型是 *[5]int,而 使用var arr1=[5]int创建的类型是 [5]int。
//var arr2=*arr1   如果不带指针符号则修改arr2的值会使得arr1的值也改变

10、将一个大数组传递给函数会消耗很多内存,有两种方法可以避免:①传递数组的指针  ②使用数组的切片

11、切片(slice)是对数组一个连续片段的引用(该数组我们称之为相关数组,通常是匿名的),所以切片是一个引用类型(因此千万不能指针指向切片,因为它自己就是一个指针)

  声明切片的格式是: var identifier []type(不需要说明长度)

  切片的初始化格式是:var slice1 []type = arr1[start:end]

  还可以使用make创建:var slice1 []int = make([]int, 10)

  var slice1 []type = arr1[:] 那么 slice1 就等于完整的 arr1 数组(所以这种表示方式是 arr1[0:len(arr1)] 的一种缩写)。另外一种表述方式是:slice1 = &arr1

  arr1[2:] 和 arr1[2:len(arr1)] 相同,都包含了数组从第三个到最后的所有元素。

  arr1[:3] 和 arr1[0:3] 相同,包含了从第一个到第三个元素(不包括第四个)。

  如果你想去掉 slice1 的最后一个元素,只要 slice1 = slice1[:len(slice1)-1]

  一个由数字 1、2、3 组成的切片可以这么生成:s := [3]int{1,2,3}[:](注: 应先用s := [3]int{1, 2, 3}生成数组, 再使用s[:]转成切片) 甚至更简单的 s := []int{1,2,3}

  s2 := s[:] 是用切片组成的切片,拥有相同的元素,但是仍然指向相同的相关数组。

  一个切片 s 可以这样扩展到它的大小上限:s = s[:cap(s)],如果再扩大的话就会导致运行时错误

12、数组与切片的应用:可以直接通过 c := []byte(s) 来获取一个字节的切片 c。另外,您还可以通过 copy 函数来达到相同的目的:copy(dst []byte, src string)。可以使用 c := []int32(s) 语法,这样切片中的每个 int 都会包含对应的 Unicode 代码,因为字符串中的每次字符都会对应一个整数。len([]int32(s)) 来获得字符串中字符的数量,但使用 utf8.RuneCountInString(s) 效率会更高一点。

13、将一个字符串添加到一个字节切片尾部:

var b []byte
var s string
b = append(b, s...)

14、获取字符串的某一部分:substr := str[start:end] 可以从字符串 str 获取到从索引 start 开始到 end-1 位置的子字符串。go中字符串是不可变的,srt[index]这种事不可以放在等号左侧的,如果想要修改字符串,就必须先把字符串转化为字符串切片,然后在进行变换,代码如下:

1
2
3
4
s :=  "hello"
c := []byte(s)
c[0] =  'c'
s2 := string(c)  // s2 == "cello"

15、对数组或者切片进行排序:可以使用sort包中的函数,sort.Ints(arr)来对int类型的数组或切片进行排序。为了检查某个数组是否已经被排序,可以通过函数 IntsAreSorted(a []int) bool 来检查,如果返回 true 则表示已经被排序。类似的可以使用sort.Float64s来对Float类型的数组或切片进行排序,使用sort.Strings对字符串类型的数组或切片进行排序。 

16、对数组或切片进行搜索:该数组或切片必须先被排序(因为标准库的搜索算法使用的是二分法)。然后,可以使用函数 sort.SearchInts(a []int, n int),类似的还可以使用sort.SearchFloat64s(a []int, n int),sort.SearchStrings(a []int, n int)来对不同类型的数组或切片进行排序。

17、map类型:map是引用类型,可以使用make来声明(尽量使用make来声明),不能用new来声明否则的到的是一个空引用的指针。处于性能的考虑对于大容量的map和容易快速扩张的数组,也尽量规定它的容量。如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
map2 := make( map [string]float32, 100)<br> //定义值为整形,键为字符型的map<br>tmp := make(map[string]int)<br>
 
//下面代码证明了map的值可以是任意类型的
package  main
import  "fmt"
 
func  main() {
     mf :=  map [int] func () int{
         1:  func () int {  return  10 },
         2:  func () int {  return  20 },
         5:  func () int {  return  50 },
     }
     fmt.Println(mf)
}

 18、在map中如果key不存在当 value1 := tmp[key]时,如果key不存在会返回空,如果它本来设置得就是空就无法区分是否存在该key,所以可以使用value,ispresent :=tmp[key] 如果存在则ispresent=true,如果不存在则ispresent=false。可以直接从map使用delete(map,key)删除某个key。map 默认是无序的,不管是按照 key 还是按照 value 默认都不排序,所以遍历map时你会发现它得输出时随机的。

19、map类型的切片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package  main
import  "fmt"
 
func  main(){
     //map类型的切片
     //第一种定义值得方式Version A
     items :=make([] map [int]int,5)
     for  i,_ := range  items{
         items[i] = make( map [int]int,1) //定义items的第一个元素是一个切片
         items[i][1]=2
     }
     fmt.Printf( "version B:value of items:%v\n" ,items)
 
     //第二种定义值得方式Version B
     items2 := make([] map [int]int,5)
     for  _,items := range  items2{
         items =make( map [int]int)
         items[1]=2
     }
 
     fmt.Println(items2)
}
 
//应当像 A 版本那样通过索引使用切片的 map 元素。
//在 B 版本中获得的项只是 map 值的一个拷贝而已,
//所以真正的 map 元素没有得到初始化。

 20、对map类型进行排序:因为map 默认是无序的,不管是按照 key 还是按照 value 默认都不排序。为了达到这个效果需要将key保存到一个切片,再对切片进行排序。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package  main
import (
     "fmt"
     "sort"
)
 
var (
     sortMap =  map [string]int{ "cherry" :4, "apple" :3, "lemon" :9, "banana" :5, "durain" :1}
)
func  main(){
     fmt.Println( "this is unsort map" )
     for  key,value :=  range  sortMap{
         fmt.Printf( "this key is:%v,this value is :%v \n" ,key,value)
     }
     fmt.Println( "this is sort map" )
     keys :=make([]string,len(sortMap))
     i :=0
     for  key := range  sortMap{
         keys[i]=key
         i++
     }
     sort.Strings(keys)
     for  _,value := range  keys{
         fmt.Printf( "this key is:%v,this value is:%v \n" ,value,sortMap[value])
     }
}

 21、strconv包的使用:

   ①字符串转整型,整型转字符串:这是最常见的两种,Atoi(string to int)和 Itoa(int to string)。eg:strconv.Atoi("-42")    strconv.Itoa(-42)

   ②ParseBool,ParseFloat,ParseInt 和 ParseUint 将字符串转换为值 eg: b, err := strconv.ParseBool("true")f, err := strconv.ParseFloat("3.1415", 64)i, err := strconv.ParseInt("-42", 10, 64)u, err := strconv.ParseUint("42", 10, 64)

   ③FormatBool,FormatFloat,FormatInt 和 FormatUint将值转换为字符串 eg:  s := strconv.FormatBool(true)s := strconv.FormatFloat(3.1415, 'E', -1, 64)s := strconv.FormatInt(-42, 16)s := strconv.FormatUint(42, 16)

 22、结构体:类似于面向对象编程中无法方法的类一样,因为go中没有类这个概念,所以结构体在go语言中显得尤为重要。以下是一个定义简单结构体的例子 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package  main
import  "fmt"
type  struct1  struct {
      a int
      b float32
      c string
}
 
function main(){
     s1 := new(sruct1)
     s1.a=1
     s1.b=3.1
     s1.c = "hello world"
     fmt.Printf( "s1.a=$d" ,s1.a)
     fmt.Printf( "s1.b=$f" ,s1.b)
     fmt.Printf( "s1.c=$s" ,s1.c)<br>    fmt.Println(s1)    
} <br> //以下是输出结果<br>s1.a=1   s1.b=3.1 s1.c=hello world   &{1 3.1,hello world}

  可以通过更简短和惯用的方式来比如:

1
2
3
4
var  s1 = &struct1{1,3.1, "hello world" }
var  s1 struct1
s1 :=struct1{1,3.1, "hello world" }

23、结构体工厂:在 Go 语言中常常像下面这样在工厂方法里使用初始化来简便的实现构造函数  

1
2
3
4
5
6
7
8
9
10
11
12
type  File  struct {
     type   int
     name  string
}
 
fun NewFile(fid int name string){
     if  fid<0 {
         return  nil
     }
     
     return  &File{fid,name}
}<br><br>f :=NewFile(2, "1.text" )

24、结构体中的继承:Go 语言中的继承是通过内嵌或组合来实现的,结构体可以包含一个或多个 匿名(或内嵌)字段,即这些字段没有显式的名字,只有字段的类型是必须的,此时类型就是字段的名字。匿名字段本身可以是一个结构体类型,即 结构体可以包含内嵌结构体。(这里得注意:在一个结构体中对于每一种数据类型只能有一个匿名字段)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package  main
 
import  "fmt"
 
type  father  struct {
     firstname string
     homename string
}
type  son  struct {
     lastname string
     int   //匿名字段
     string  //匿名字段
     father     //内嵌结构体,就相当于继承
}
 
func  main(){
     xiaoming :=new(son)
     xiaoming.firstname= "xiao"
     xiaoming.lastname= "ming"
     xiaoming.homename= "jiangxi"
     xiaoming.int=13
     xiaoming.string= "xm"
     fmt.Printf( "xiaoming.fastname=%s\n" ,xiaoming.firstname)
     fmt.Printf( "xiaoming.lastname=%s\n" ,xiaoming.lastname)
     fmt.Printf( "xiaoming.homename=%s\n" ,xiaoming.homename)
     fmt.Printf( "xiaoming.int=%d\n" ,xiaoming.int)
     fmt.Printf( "xiaoming.string=%s\n" ,xiaoming.string)
     fmt.Println(xiaoming)
} 

25、结构体中的方法:Go 方法是作用在接收者(receiver)上的一个函数,接收者是某种类型的变量。因此方法是一种特殊类型的函数。

          接收者几乎可以是任何类型,不单单是结构体类型,任何类型都可以有方法,但是除了接口类型,接收者还不能是个指针类型。

          Go中类型的代码和绑定在它类型中的方法代码不能放置在一起,可以存在不同的源文件中,但是唯一的要求是必须在一个包里面。定义方法的一般格式:func (recv receiver_type) methodName(parameter_list) (return_value_list) { ... }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package  main
import  "fmt"
 
type  instance  struct { //定义结构体
     t1  int
     t2  int
}
  
func  main(){
     test := new(instance)
     test.t1 = 1
     test.t2 = 2
     fmt.Printf( "the sum is %d" ,test.addSum())
     fmt.printf( "the sum add user input param is %d" ,test.addParam(10))
     
}
 
func (t *instance) addSum() int { //结构体中的参数相加
     return  t.t1+t.t2
}
 
func (t *instance) addParam(param int) int{ //增加用户添加的参数
     return  t.t1+t.t2+param
}

26、类型的String()方法:如果类型定义了 String() 方法,它会被用在 fmt.Printf() 中生成默认的输出:等同于使用格式化描述符 %v 产生的输出。还有 fmt.Print() 和 fmt.Println() 也会自动使用 String() 方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package  main
 
import  (
     "fmt"
     "strconv"
)
 
type  TwoInts  struct  {
     a int
     b int
}
 
func  main() {
     two1 := new(TwoInts)
     two1.a = 12
     two1.b = 10
     fmt.Printf( "two1 is: %v\n" , two1)
     fmt.Println( "two1 is:" , two1)
     fmt.Printf( "two1 is: %T\n" , two1)
     fmt.Printf( "two1 is: %#v\n" , two1)
}
 
func  (tn *TwoInts) String() string {
     return  "("  + strconv.Itoa(tn.a) +  "/"  + strconv.Itoa(tn.b) +  ")"
}
 
//运行结果
two1 is: (12/10)
two1 is: (12/10)
two1 is: *main.TwoInts
two1 is: &main.TwoInts{a:12, b:10}

27、Go中的垃圾回收:在 Go 运行时中有一个独立的进程,即垃圾收集器(GC),会处理这些事情,它搜索不再使用的变量然后释放它们的内存。可以通过 runtime 包访问 GC 进程。调用 runtime.GC() 函数可以显式的触发 GC,但这只在某些罕见的场景下才有用,比如当内存资源不足时调用 runtime.GC(),它会在此函数执行的点上立即释放一大片内存,此时程序可能会有短时的性能下降(因为 GC 进程在执行)。

1
2
3
4
5
6
7
//获取当前内存状态
var  m runtime.MemStats
runtime.ReadMemStats(&m)
fmt.Printf( "%d Kb\n" , m.Alloc / 1024)
 
//在一个对象 obj 被从内存移除前执行一些特殊操作
runtime.SetFinalizer(obj,  func (obj *typeObj))

28、Go中的接口:接口定义了一组方法(方法集),但是这些方法不包含(实现)代码:它们没有被实现(它们是抽象的)。接口里也不能包含变量。

  (按照约定,只包含一个方法的)接口的名字由方法名加 [e]r 后缀组成,例如 PrinterReaderWriterLoggerConverter 等等。还有一些不常用的方式(当后缀 er 不合适时),比如 Recoverable,此时接口名以 able 结尾,或者以 I 开头(像 .NET 或 Java 中那样)。

   Go 语言中的接口都很简短,通常它们会包含 0 个、最多 3 个方法。

   类型不需要显式声明它实现了某个接口:接口被隐式地实现。多个类型可以实现同一个接口。

     实现某个接口的类型(除了实现接口方法外)可以有其他的方法。

     一个类型可以实现多个接口。

     接口类型可以包含一个实例的引用, 该实例的类型实现了此接口(接口是动态类型)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package  main
import  "fmt"
 
type  Driver  interface //定义接口
     Driver() int
}
 
type  bike  struct {
     speed  int
     name string
}
type  train  struct {
     speed int
     name string
}
func (b bike) Driver() int{  //实现接口方法
     return  b.speed*2
}
func (t *train) Driver() int{  //实现接口方法
     return  t.speed*5
}
 
func  main(){
     // bikes :=&bike{10,"bike"}
     // trains :=&train{40,"train"}
     // cars :=[]Driver{bikes,trains}   //声明是实现了方法
     // for i,_ :=range cars{
     //  fmt.Printf("the speed is %d \n",cars[i].Driver())
     // }
 
     var  o Driver =bike{10, "bike" }   //这是另一种简约声明方式
     fmt.Printf( "the speed is %d \n" ,o.Driver())
     o =&train{40, "train" }
     fmt.Printf( "the speed is %d \n" ,o.Driver())
}

29、检测和转换接口变量的类型:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package  main
 
import  (
     "fmt"
     "math"
)
 
type  Square  struct  {
     side float32
}
 
type  Circle  struct  {
     radius float32
}
 
type  Shaper  interface  {
     Area() float32
}
 
func  main() {
     var  areaIntf Shaper
     sq1 := new(Square)
     sq1.side = 5
 
     areaIntf = sq1
     // Is Square the type of areaIntf?
     if  t, ok := areaIntf.(*Square); ok {
         fmt.Printf( "The type of areaIntf is: %T\n" , t)
     }
     if  u, ok := areaIntf.(*Circle); ok {
         fmt.Printf( "The type of areaIntf is: %T\n" , u)
     else  {
         fmt.Println( "areaIntf does not contain a variable of type Circle" )
     }
}
 
func  (sq *Square) Area() float32 {
     return  sq.side * sq.side
}
 
func  (ci *Circle) Area() float32 {
     return  ci.radius * ci.radius * math.Pi
}

30、测定某个值是否实现了接口:v 是一个值,然后我们想测试它是否实现了 Stringer 接口

1
2
3
4
5
6
7
type  Stringer  interface  {
     String() string
}
 
if  sv, ok := v.(Stringer); ok {
     fmt.Printf( "v implements String(): %s\n" , sv.String())  // note: sv, not v
}

31、Go中的排序:对于一般性的排序,sort 包定义了一个接口

1
2
3
4
5
type  Interface  interface  {
     Len() int
     Less(i, j int) bool
     Swap(i, j int)
} 

 其中的两个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package  sort
 
type  Sorter  interface  {
     Len() int
     Less(i, j int) bool
     Swap(i, j int)
}
 
func  Sort(data Sorter) {
     for  pass := 1; pass < data.Len(); pass++ {
         for  i := 0; i < data.Len()-pass; i++ {
             if  data.Less(i+1, i) {
                 data.Swap(i, i+1)
             }
         }
     }
}
 
func  IsSorted(data Sorter) bool {
     n := data.Len()
     for  i := n - 1; i > 0; i-- {
         if  data.Less(i, i-1) {
             return  false
         }
     }
     return  true
}
 
// Convenience types for common cases
type  IntArray []int
 
func  (p IntArray) Len() int           {  return  len(p) }
func  (p IntArray) Less(i, j int) bool {  return  p[i] < p[j] }
func  (p IntArray) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
 
type  StringArray []string
 
func  (p StringArray) Len() int           {  return  len(p) }
func  (p StringArray) Less(i, j int) bool {  return  p[i] < p[j] }
func  (p StringArray) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
 
// Convenience wrappers for common cases
func  SortInts(a []int)       { Sort(IntArray(a)) }
func  SortStrings(a []string) { Sort(StringArray(a)) }
 
func  IntsAreSorted(a []int) bool       {  return  IsSorted(IntArray(a)) }
func  StringsAreSorted(a []string) bool {  return  IsSorted(StringArray(a)) }

排序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
package  main
 
import  (
     "./sort"
     "fmt"
)
 
func  ints() {
     data := []int{74, 59, 238, -784, 9845, 959, 905, 0, 0, 42, 7586, -5467984, 7586}
     a := sort.IntArray(data)  //conversion to type IntArray
     sort.Sort(a)
     if  !sort.IsSorted(a) {
         panic( "fails" )
     }
     fmt.Printf( "The sorted array is: %v\n" , a)
}
 
func  strings() {
     data := []string{ "monday" "friday" "tuesday" "wednesday" "sunday" "thursday" "" "saturday" }
     a := sort.StringArray(data)
     sort.Sort(a)
     if  !sort.IsSorted(a) {
         panic( "fail" )
     }
     fmt.Printf( "The sorted array is: %v\n" , a)
}
 
type  day  struct  {
     num       int
     shortName string
     longName  string
}
 
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] }
 
func  days() {
     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" }
     data := []*day{&Tuesday, &Thursday, &Wednesday, &Sunday, &Monday, &Friday, &Saturday}
     a := dayArray{data}
     sort.Sort(&a)
     if  !sort.IsSorted(&a) {
         panic( "fail" )
     }
     for  _, d :=  range  data {
         fmt.Printf( "%s " , d.longName)
     }
     fmt.Printf( "\n" )
}
 
func  main() {
     ints()
     strings()
     days()
}

32、Go中的读和写:io包中提供了读和写的接口

1
2
3
4
5
6
7
type  Reader  interface  {
     Read(p []byte) (n int, err error)
}
 
type  Writer  interface  {
     Write(p []byte) (n int, err error)
}

33、空接口:空接口或则最小接口不包含任何方法,它对实现没有任何要求。可以给一个空接口类型的变量 var val interface {} 赋任何类型的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package  main
import  "fmt"
 
var  i = 5
var  str =  "ABC"
 
type  Person  struct  {
     name string
     age  int
}
 
type  Any  interface {}
 
func  main() {
     var  val Any
     val = 5
     fmt.Printf( "val has the value: %v\n" , val)
     val = str
     fmt.Printf( "val has the value: %v\n" , val)
     pers1 := new(Person)
     pers1.name =  "Rob Pike"
     pers1.age = 55
     val = pers1
     fmt.Printf( "val has the value: %v\n" , val)
     switch  t := val.( type ) {
     case  int:
         fmt.Printf( "Type int %T\n" , t)
     case  string:
         fmt.Printf( "Type string %T\n" , t)
     case  bool:
         fmt.Printf( "Type boolean %T\n" , t)
     case  *Person:
         fmt.Printf( "Type pointer to Person %T\n" , t)
     default :
         fmt.Printf( "Unexpected type %T" , t)
     }
}
 
//输出
val has the value: 5
val has the value: ABC
val has the value: &{Rob Pike 55}
Type pointer to Person *main.Person

34、Go中对变量类型的判断以及获取变量的值:引入reflect 包,使用reflect.TypeOf(变量) 判断变量的类型 使用reflect.ValueOf(变量) 获取变量的值。(了解更多:https://github.com/unknwon/the-way-to-go_ZH_CN/blob/master/eBook/11.10.md

35、遍历出结构体中的字段值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package  main
 
import  (
     "fmt"
     "reflect"
)
 
type  NotknownType  struct  {
     s1, s2, s3 string
}
 
func  (n NotknownType) String() string {
     return  n.s1 +  " - "  + n.s2 +  " - "  + n.s3
}
 
// variable to investigate:
var  secret  interface {} = NotknownType{ "Ada" "Go" "Oberon" }
 
func  main() {
     value := reflect.ValueOf(secret)  // <main.NotknownType Value>
     typ := reflect.TypeOf(secret)     // main.NotknownType
     // alternative:
     //typ := value.Type()  // main.NotknownType
     fmt.Println(typ)
     knd := value.Kind()  // struct
     fmt.Println(knd)
 
     // iterate through the fields of the struct:
     for  i := 0; i < value.NumField(); i++ {
         fmt.Printf( "Field %d: %v\n" , i, value.Field(i))
         // error: panic: reflect.Value.SetString using value obtained using unexported field
         //value.Field(i).SetString("C#")
     }
 
     // call the first method, which is String():
     results := value.Method(0).Call(nil)
     fmt.Println(results)  // [Ada - Go - Oberon]
}
 
//结果
main.NotknownType
struct
Field 0: Ada
Field 1: Go
Field 2: Oberon
[Ada - Go - Oberon]

36、接口的继承:当一个类型包含(内嵌)另一个类型(实现了一个或多个接口)的指针时,这个类型就可以使用(另一个类型)所有的接口方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
type  Task  struct  {
     Command string
     *log.Logger
}
//这个类型的工厂方法像这样
func  NewTask(command string, logger *log.Logger) *Task {
     return  &Task{command, logger}
}
//当 log.Logger 实现了 Log() 方法后,Task 的实例 task 就可以调用该方法
 
task.Log()
 
//类型可以通过继承多个接口来提供像 多重继承 一样的特性
type  ReaderWriter  struct  {
     *io.Reader
     *io.Writer
}

37、Go中的封装、继承、多态:

  • 封装(数据隐藏):和别的 OO 语言有 4 个或更多的访问层次相比,Go 把它简化为了 2 层

    1)包范围内的:通过标识符首字母小写,对象 只在它所在的包内可见

    2)可导出的:通过标识符首字母大写,对象 对所在包以外也可见类型只拥有自己所在包中定义的方法。

  • 继承:用组合实现:内嵌一个(或多个)包含想要的行为(字段和方法)的类型;多重继承可以通过内嵌多个类型实现
  • 多态:用接口实现:某个类型的实例可以赋给它所实现的任意接口类型的变量。类型和接口是松耦合的,并且多重继承可以通过实现多个接口实现。Go 接口不是 Java 和 C# 接口的变体,而且接口间是不相关的,并且是大规模编程和可适应的演进型设计的关键。

38、Sscanf()函数的使用:从一大长串的字符串截取我们需要的数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package  main
 
import  "fmt"
 
var (
     s string
     i int
     f float32
     input =  "56/39.45/Go"
     format = "%d/%f/%s"  //规定想要数据的格式
)
 
func  main(){
     fmt.Sscanf(input,format,&i,&f,&s) //把对应数据的信息赋值给所对应变量
     fmt.Printf( "i=%d,f=%f,s=%s" ,i,f,s)
}

39、 使用bufio 包提供的缓冲读取(buffered reader)来读取数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package  main
import  (
     "fmt"
     "bufio"
     "os"
)
var  readerInput *bufio.Reader
var  input string
var  err error
func  main(){
     readerInput = bufio.NewReader(os.Stdin)  //创建一个读取器并将它与标准输入绑定
     fmt.Println( "please enter you input:" )
     input,err = readerInput.ReadString( '\n' ) //这是读取器中的一个方法,该方法从输入中读取内容,直到碰到 delim 指定的字符,然后将读取到的内容连同 delim 字符一起放到缓冲区。
     if  err==nil{
         fmt.Printf( "the user input is:%s\n" ,input)  //输出用户读取的内容
     }
} 

 40、读文件:文件使用指向 os.File 类型的指针来表示的,也叫做文件句柄。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package  main
import (
     "fmt"
     "os"
     "bufio"
     "io"
)
func  main(){
     content,err :=os.Open( "1.html" ) //打开文件
     if  err!=nil {
         fmt.Printf( "maybe your file not exist or you dont't have the pormission which open the file" )
         return
     }
     defer  content.Close() //在文件末尾关闭文件
     fileContent:=bufio.NewReader(content)  //创建一个读取器并将它与文件读取内容绑定
     for {
         fileString,err :=fileContent.ReadString( '\n' ) //遍历读取内容时按照遇见换行符停止
         fmt.Printf( "the file content is :%s" ,fileString) //输出文件内容
         if  err==io.EOF{
             return
         }
     }
 
}

41、写文件:OpenFile 函数有三个参数:文件名、一个或多个标志(使用逻辑运算符“|”连接),使用的文件权限。

  • os.O_RDONLY:只读
  • os.O_WRONLY:只写
  • os.O_CREATE:创建:如果指定文件不存在,就创建该文件。
  • os.O_TRUNC:截断:如果指定文件已存在,就将该文件的长度截为0。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    package  main
     
    import  (
         "os"
         "bufio"
         "fmt"
    )
     
    func  main () {
         // var outputWriter *bufio.Writer
         // var outputFile *os.File
         // var outputError os.Error
         // var outputString string
         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()
    }  

42、文件拷贝。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package  main
 
import  (
     "fmt"
     "io"
     "os"
)
 
func  main() {
     CopyFile( "target.txt" "source.txt" )
     fmt.Println( "Copy done!" )
}
 
func  CopyFile(dstName, srcName string) (written int64, err error) {
     src, err := os.Open(srcName)
     if  err != nil {
         return
     }
     defer  src.Close()
 
     dst, err := os.Create(dstName)
     if  err != nil {
         return
     }
     defer  dst.Close()
 
     return  io.Copy(dst, src)
}

 

0、值类型:变量直接存储值,内存通常在栈中分配:int、float、bool、string以及数组和struct

   引用类型:变量存储的是一个地址,这个地址存储最终的值。内存通常在堆上分配。通过gc回收:指针、slice、map、chan等都是引用类型

使用&结构体{}就相当与使用new实例化了一次结构体

1、变量(或常量)包含数据,这些数据可以有不同的数据类型,简称类型。使用 var 声明的变量的值会自动初始化为该类型的零值。类型定义了某个变量的值的集合与可对其进行操作的集合。

 类型可以是基本类型,如:int、float、bool、string;结构化的(复合的),如:struct、array、slice、map、channel;只描述类型的行为的,如:interface。

 结构化的类型没有真正的值,它使用 nil 作为默认值(在 Objective-C 中是 nil,在 Java 中是 null,在 C 和 C++ 中是NULL或 0)。值得注意的是,Go 语言中不存在类型继承。

 比如:定义变量的两种方式 var i int=15 或 i :=15

    定义数组:arr1 :=[] int { }  数组名称  数组长度  数组里面的类型  数组值

2、如果设定的全局变量希望被外部的包使用,其首字母应该大写。

3、字符串:Strings.HasPrefix(string,prefix) 字符串是否以xx开头   Strings.HasSuffix(string,suffix) 字符串是否以xx结尾 eg:str="this is a example"   String.HasPrefix(str,"th");]

4、时间和日期:获取当前时间:time.Now(),获取当前年份:time.Now().Year() 获取当前月份:time.Now().Month() 获取当前天:time.Now().Day()

5、代码块: a ,err :=strconv.Itoa(an)  注意这里的error是为了防止转换失败  if !err {  fmt.Printf("输出成功:%d”,a)}

6、定义函数 func 函数名称 参数类型  返回参数值  eg:

1
2
3
4
5
6
func  func1(s string) (n int, err error) {
     defer  func () {
         log.Printf( "func1(%q) = %d, %v" , s, n, err)
     }()
     return  7, io.EOF
}

7、利用闭包来调试函数:

1
2
3
4
5
6
7
8
9
where :=  func () {
     _, file, line, _ := runtime.Caller(1)
     log.Printf( "%s:%d" , file, line)
}
where()
// some code
where()
// some more code
where()

8、记算代码执行时间:

1
2
3
4
5
start := time.Now()
longCalculation()
end := time.Now()
delta := end.Sub(start)
fmt.Printf( "longCalculation took this amount of time: %s\n" , delta)

9、使用new创建得数组和普通创建数组得不同

1
2
3
4
5
6
7
8
9
10
11
12
        var  arr1=new([5]int)
     for  i:=0;i<len(arr1);i++{
         arr1[i]=1+i
     }
     var  arr2= *arr1
     arr2[0]=7
     for  i,_ := range  arr1{
         fmt.Printf( "this value is %d" ,arr1[i])
     }
//注意使用new([5]int创建得数组是带有地址的   而普通创建的没有
//例如:使用new创建的类型是 *[5]int,而 使用var arr1=[5]int创建的类型是 [5]int。
//var arr2=*arr1   如果不带指针符号则修改arr2的值会使得arr1的值也改变

10、将一个大数组传递给函数会消耗很多内存,有两种方法可以避免:①传递数组的指针  ②使用数组的切片

11、切片(slice)是对数组一个连续片段的引用(该数组我们称之为相关数组,通常是匿名的),所以切片是一个引用类型(因此千万不能指针指向切片,因为它自己就是一个指针)

  声明切片的格式是: var identifier []type(不需要说明长度)

  切片的初始化格式是:var slice1 []type = arr1[start:end]

  还可以使用make创建:var slice1 []int = make([]int, 10)

  var slice1 []type = arr1[:] 那么 slice1 就等于完整的 arr1 数组(所以这种表示方式是 arr1[0:len(arr1)] 的一种缩写)。另外一种表述方式是:slice1 = &arr1

  arr1[2:] 和 arr1[2:len(arr1)] 相同,都包含了数组从第三个到最后的所有元素。

  arr1[:3] 和 arr1[0:3] 相同,包含了从第一个到第三个元素(不包括第四个)。

  如果你想去掉 slice1 的最后一个元素,只要 slice1 = slice1[:len(slice1)-1]

  一个由数字 1、2、3 组成的切片可以这么生成:s := [3]int{1,2,3}[:](注: 应先用s := [3]int{1, 2, 3}生成数组, 再使用s[:]转成切片) 甚至更简单的 s := []int{1,2,3}

  s2 := s[:] 是用切片组成的切片,拥有相同的元素,但是仍然指向相同的相关数组。

  一个切片 s 可以这样扩展到它的大小上限:s = s[:cap(s)],如果再扩大的话就会导致运行时错误

12、数组与切片的应用:可以直接通过 c := []byte(s) 来获取一个字节的切片 c。另外,您还可以通过 copy 函数来达到相同的目的:copy(dst []byte, src string)。可以使用 c := []int32(s) 语法,这样切片中的每个 int 都会包含对应的 Unicode 代码,因为字符串中的每次字符都会对应一个整数。len([]int32(s)) 来获得字符串中字符的数量,但使用 utf8.RuneCountInString(s) 效率会更高一点。

13、将一个字符串添加到一个字节切片尾部:

var b []byte
var s string
b = append(b, s...)

14、获取字符串的某一部分:substr := str[start:end] 可以从字符串 str 获取到从索引 start 开始到 end-1 位置的子字符串。go中字符串是不可变的,srt[index]这种事不可以放在等号左侧的,如果想要修改字符串,就必须先把字符串转化为字符串切片,然后在进行变换,代码如下:

1
2
3
4
s :=  "hello"
c := []byte(s)
c[0] =  'c'
s2 := string(c)  // s2 == "cello"

15、对数组或者切片进行排序:可以使用sort包中的函数,sort.Ints(arr)来对int类型的数组或切片进行排序。为了检查某个数组是否已经被排序,可以通过函数 IntsAreSorted(a []int) bool 来检查,如果返回 true 则表示已经被排序。类似的可以使用sort.Float64s来对Float类型的数组或切片进行排序,使用sort.Strings对字符串类型的数组或切片进行排序。 

16、对数组或切片进行搜索:该数组或切片必须先被排序(因为标准库的搜索算法使用的是二分法)。然后,可以使用函数 sort.SearchInts(a []int, n int),类似的还可以使用sort.SearchFloat64s(a []int, n int),sort.SearchStrings(a []int, n int)来对不同类型的数组或切片进行排序。

17、map类型:map是引用类型,可以使用make来声明(尽量使用make来声明),不能用new来声明否则的到的是一个空引用的指针。处于性能的考虑对于大容量的map和容易快速扩张的数组,也尽量规定它的容量。如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
map2 := make( map [string]float32, 100)<br> //定义值为整形,键为字符型的map<br>tmp := make(map[string]int)<br>
 
//下面代码证明了map的值可以是任意类型的
package  main
import  "fmt"
 
func  main() {
     mf :=  map [int] func () int{
         1:  func () int {  return  10 },
         2:  func () int {  return  20 },
         5:  func () int {  return  50 },
     }
     fmt.Println(mf)
}

 18、在map中如果key不存在当 value1 := tmp[key]时,如果key不存在会返回空,如果它本来设置得就是空就无法区分是否存在该key,所以可以使用value,ispresent :=tmp[key] 如果存在则ispresent=true,如果不存在则ispresent=false。可以直接从map使用delete(map,key)删除某个key。map 默认是无序的,不管是按照 key 还是按照 value 默认都不排序,所以遍历map时你会发现它得输出时随机的。

19、map类型的切片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package  main
import  "fmt"
 
func  main(){
     //map类型的切片
     //第一种定义值得方式Version A
     items :=make([] map [int]int,5)
     for  i,_ := range  items{
         items[i] = make( map [int]int,1) //定义items的第一个元素是一个切片
         items[i][1]=2
     }
     fmt.Printf( "version B:value of items:%v\n" ,items)
 
     //第二种定义值得方式Version B
     items2 := make([] map [int]int,5)
     for  _,items := range  items2{
         items =make( map [int]int)
         items[1]=2
     }
 
     fmt.Println(items2)
}
 
//应当像 A 版本那样通过索引使用切片的 map 元素。
//在 B 版本中获得的项只是 map 值的一个拷贝而已,
//所以真正的 map 元素没有得到初始化。

 20、对map类型进行排序:因为map 默认是无序的,不管是按照 key 还是按照 value 默认都不排序。为了达到这个效果需要将key保存到一个切片,再对切片进行排序。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package  main
import (
     "fmt"
     "sort"
)
 
var (
     sortMap =  map [string]int{ "cherry" :4, "apple" :3, "lemon" :9, "banana" :5, "durain" :1}
)
func  main(){
     fmt.Println( "this is unsort map" )
     for  key,value :=  range  sortMap{
         fmt.Printf( "this key is:%v,this value is :%v \n" ,key,value)
     }
     fmt.Println( "this is sort map" )
     keys :=make([]string,len(sortMap))
     i :=0
     for  key := range  sortMap{
         keys[i]=key
         i++
     }
     sort.Strings(keys)
     for  _,value := range  keys{
         fmt.Printf( "this key is:%v,this value is:%v \n" ,value,sortMap[value])
     }
}

 21、strconv包的使用:

   ①字符串转整型,整型转字符串:这是最常见的两种,Atoi(string to int)和 Itoa(int to string)。eg:strconv.Atoi("-42")    strconv.Itoa(-42)

   ②ParseBool,ParseFloat,ParseInt 和 ParseUint 将字符串转换为值 eg: b, err := strconv.ParseBool("true")f, err := strconv.ParseFloat("3.1415", 64)i, err := strconv.ParseInt("-42", 10, 64)u, err := strconv.ParseUint("42", 10, 64)

   ③FormatBool,FormatFloat,FormatInt 和 FormatUint将值转换为字符串 eg:  s := strconv.FormatBool(true)s := strconv.FormatFloat(3.1415, 'E', -1, 64)s := strconv.FormatInt(-42, 16)s := strconv.FormatUint(42, 16)

 22、结构体:类似于面向对象编程中无法方法的类一样,因为go中没有类这个概念,所以结构体在go语言中显得尤为重要。以下是一个定义简单结构体的例子 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package  main
import  "fmt"
type  struct1  struct {
      a int
      b float32
      c string
}
 
function main(){
     s1 := new(sruct1)
     s1.a=1
     s1.b=3.1
     s1.c = "hello world"
     fmt.Printf( "s1.a=$d" ,s1.a)
     fmt.Printf( "s1.b=$f" ,s1.b)
     fmt.Printf( "s1.c=$s" ,s1.c)<br>    fmt.Println(s1)    
} <br> //以下是输出结果<br>s1.a=1   s1.b=3.1 s1.c=hello world   &{1 3.1,hello world}

  可以通过更简短和惯用的方式来比如:

1
2
3
4
var  s1 = &struct1{1,3.1, "hello world" }
var  s1 struct1
s1 :=struct1{1,3.1, "hello world" }

23、结构体工厂:在 Go 语言中常常像下面这样在工厂方法里使用初始化来简便的实现构造函数  

1
2
3
4
5
6
7
8
9
10
11
12
type  File  struct {
     type   int
     name  string
}
 
fun NewFile(fid int name string){
     if  fid<0 {
         return  nil
     }
     
     return  &File{fid,name}
}<br><br>f :=NewFile(2, "1.text" )

24、结构体中的继承:Go 语言中的继承是通过内嵌或组合来实现的,结构体可以包含一个或多个 匿名(或内嵌)字段,即这些字段没有显式的名字,只有字段的类型是必须的,此时类型就是字段的名字。匿名字段本身可以是一个结构体类型,即 结构体可以包含内嵌结构体。(这里得注意:在一个结构体中对于每一种数据类型只能有一个匿名字段)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package  main
 
import  "fmt"
 
type  father  struct {
     firstname string
     homename string
}
type  son  struct {
     lastname string
     int   //匿名字段
     string  //匿名字段
     father     //内嵌结构体,就相当于继承
}
 
func  main(){
     xiaoming :=new(son)
     xiaoming.firstname= "xiao"
     xiaoming.lastname= "ming"
     xiaoming.homename= "jiangxi"
     xiaoming.int=13
     xiaoming.string= "xm"
     fmt.Printf( "xiaoming.fastname=%s\n" ,xiaoming.firstname)
     fmt.Printf( "xiaoming.lastname=%s\n" ,xiaoming.lastname)
     fmt.Printf( "xiaoming.homename=%s\n" ,xiaoming.homename)
     fmt.Printf( "xiaoming.int=%d\n" ,xiaoming.int)
     fmt.Printf( "xiaoming.string=%s\n" ,xiaoming.string)
     fmt.Println(xiaoming)
} 

25、结构体中的方法:Go 方法是作用在接收者(receiver)上的一个函数,接收者是某种类型的变量。因此方法是一种特殊类型的函数。

          接收者几乎可以是任何类型,不单单是结构体类型,任何类型都可以有方法,但是除了接口类型,接收者还不能是个指针类型。

          Go中类型的代码和绑定在它类型中的方法代码不能放置在一起,可以存在不同的源文件中,但是唯一的要求是必须在一个包里面。定义方法的一般格式:func (recv receiver_type) methodName(parameter_list) (return_value_list) { ... }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package  main
import  "fmt"
 
type  instance  struct { //定义结构体
     t1  int
     t2  int
}
  
func  main(){
     test := new(instance)
     test.t1 = 1
     test.t2 = 2
     fmt.Printf( "the sum is %d" ,test.addSum())
     fmt.printf( "the sum add user input param is %d" ,test.addParam(10))
     
}
 
func (t *instance) addSum() int { //结构体中的参数相加
     return  t.t1+t.t2
}
 
func (t *instance) addParam(param int) int{ //增加用户添加的参数
     return  t.t1+t.t2+param
}

26、类型的String()方法:如果类型定义了 String() 方法,它会被用在 fmt.Printf() 中生成默认的输出:等同于使用格式化描述符 %v 产生的输出。还有 fmt.Print() 和 fmt.Println() 也会自动使用 String() 方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package  main
 
import  (
     "fmt"
     "strconv"
)
 
type  TwoInts  struct  {
     a int
     b int
}
 
func  main() {
     two1 := new(TwoInts)
     two1.a = 12
     two1.b = 10
     fmt.Printf( "two1 is: %v\n" , two1)
     fmt.Println( "two1 is:" , two1)
     fmt.Printf( "two1 is: %T\n" , two1)
     fmt.Printf( "two1 is: %#v\n" , two1)
}
 
func  (tn *TwoInts) String() string {
     return  "("  + strconv.Itoa(tn.a) +  "/"  + strconv.Itoa(tn.b) +  ")"
}
 
//运行结果
two1 is: (12/10)
two1 is: (12/10)
two1 is: *main.TwoInts
two1 is: &main.TwoInts{a:12, b:10}

27、Go中的垃圾回收:在 Go 运行时中有一个独立的进程,即垃圾收集器(GC),会处理这些事情,它搜索不再使用的变量然后释放它们的内存。可以通过 runtime 包访问 GC 进程。调用 runtime.GC() 函数可以显式的触发 GC,但这只在某些罕见的场景下才有用,比如当内存资源不足时调用 runtime.GC(),它会在此函数执行的点上立即释放一大片内存,此时程序可能会有短时的性能下降(因为 GC 进程在执行)。

1
2
3
4
5
6
7
//获取当前内存状态
var  m runtime.MemStats
runtime.ReadMemStats(&m)
fmt.Printf( "%d Kb\n" , m.Alloc / 1024)
 
//在一个对象 obj 被从内存移除前执行一些特殊操作
runtime.SetFinalizer(obj,  func (obj *typeObj))

28、Go中的接口:接口定义了一组方法(方法集),但是这些方法不包含(实现)代码:它们没有被实现(它们是抽象的)。接口里也不能包含变量。

  (按照约定,只包含一个方法的)接口的名字由方法名加 [e]r 后缀组成,例如 PrinterReaderWriterLoggerConverter 等等。还有一些不常用的方式(当后缀 er 不合适时),比如 Recoverable,此时接口名以 able 结尾,或者以 I 开头(像 .NET 或 Java 中那样)。

   Go 语言中的接口都很简短,通常它们会包含 0 个、最多 3 个方法。

   类型不需要显式声明它实现了某个接口:接口被隐式地实现。多个类型可以实现同一个接口。

     实现某个接口的类型(除了实现接口方法外)可以有其他的方法。

     一个类型可以实现多个接口。

     接口类型可以包含一个实例的引用, 该实例的类型实现了此接口(接口是动态类型)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package  main
import  "fmt"
 
type  Driver  interface //定义接口
     Driver() int
}
 
type  bike  struct {
     speed  int
     name string
}
type  train  struct {
     speed int
     name string
}
func (b bike) Driver() int{  //实现接口方法
     return  b.speed*2
}
func (t *train) Driver() int{  //实现接口方法
     return  t.speed*5
}
 
func  main(){
     // bikes :=&bike{10,"bike"}
     // trains :=&train{40,"train"}
     // cars :=[]Driver{bikes,trains}   //声明是实现了方法
     // for i,_ :=range cars{
     //  fmt.Printf("the speed is %d \n",cars[i].Driver())
     // }
 
     var  o Driver =bike{10, "bike" }   //这是另一种简约声明方式
     fmt.Printf( "the speed is %d \n" ,o.Driver())
     o =&train{40, "train" }
     fmt.Printf( "the speed is %d \n" ,o.Driver())
}

29、检测和转换接口变量的类型:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package  main
 
import  (
     "fmt"
     "math"
)
 
type  Square  struct  {
     side float32
}
 
type  Circle  struct  {
     radius float32
}
 
type  Shaper  interface  {
     Area() float32
}
 
func  main() {
     var  areaIntf Shaper
     sq1 := new(Square)
     sq1.side = 5
 
     areaIntf = sq1
     // Is Square the type of areaIntf?
     if  t, ok := areaIntf.(*Square); ok {
         fmt.Printf( "The type of areaIntf is: %T\n" , t)
     }
     if  u, ok := areaIntf.(*Circle); ok {
         fmt.Printf( "The type of areaIntf is: %T\n" , u)
     else  {
         fmt.Println( "areaIntf does not contain a variable of type Circle" )
     }
}
 
func  (sq *Square) Area() float32 {
     return  sq.side * sq.side
}
 
func  (ci *Circle) Area() float32 {
     return  ci.radius * ci.radius * math.Pi
}

30、测定某个值是否实现了接口:v 是一个值,然后我们想测试它是否实现了 Stringer 接口

1
2
3
4
5
6
7
type  Stringer  interface  {
     String() string
}
 
if  sv, ok := v.(Stringer); ok {
     fmt.Printf( "v implements String(): %s\n" , sv.String())  // note: sv, not v
}

31、Go中的排序:对于一般性的排序,sort 包定义了一个接口

1
2
3
4
5
type  Interface  interface  {
     Len() int
     Less(i, j int) bool
     Swap(i, j int)
} 

 其中的两个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package  sort
 
type  Sorter  interface  {
     Len() int
     Less(i, j int) bool
     Swap(i, j int)
}
 
func  Sort(data Sorter) {
     for  pass := 1; pass < data.Len(); pass++ {
         for  i := 0; i < data.Len()-pass; i++ {
             if  data.Less(i+1, i) {
                 data.Swap(i, i+1)
             }
         }
     }
}
 
func  IsSorted(data Sorter) bool {
     n := data.Len()
     for  i := n - 1; i > 0; i-- {
         if  data.Less(i, i-1) {
             return  false
         }
     }
     return  true
}
 
// Convenience types for common cases
type  IntArray []int
 
func  (p IntArray) Len() int           {  return  len(p) }
func  (p IntArray) Less(i, j int) bool {  return  p[i] < p[j] }
func  (p IntArray) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
 
type  StringArray []string
 
func  (p StringArray) Len() int           {  return  len(p) }
func  (p StringArray) Less(i, j int) bool {  return  p[i] < p[j] }
func  (p StringArray) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
 
// Convenience wrappers for common cases
func  SortInts(a []int)       { Sort(IntArray(a)) }
func  SortStrings(a []string) { Sort(StringArray(a)) }
 
func  IntsAreSorted(a []int) bool       {  return  IsSorted(IntArray(a)) }
func  StringsAreSorted(a []string) bool {  return  IsSorted(StringArray(a)) }

排序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
package  main
 
import  (
     "./sort"
     "fmt"
)
 
func  ints() {
     data := []int{74, 59, 238, -784, 9845, 959, 905, 0, 0, 42, 7586, -5467984, 7586}
     a := sort.IntArray(data)  //conversion to type IntArray
     sort.Sort(a)
     if  !sort.IsSorted(a) {
         panic( "fails" )
     }
     fmt.Printf( "The sorted array is: %v\n" , a)
}
 
func  strings() {
     data := []string{ "monday" "friday" "tuesday" "wednesday" "sunday" "thursday" "" "saturday" }
     a := sort.StringArray(data)
     sort.Sort(a)
     if  !sort.IsSorted(a) {
         panic( "fail" )
     }
     fmt.Printf( "The sorted array is: %v\n" , a)
}
 
type  day  struct  {
     num       int
     shortName string
     longName  string
}
 
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] }
 
func  days() {
     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" }
     data := []*day{&Tuesday, &Thursday, &Wednesday, &Sunday, &Monday, &Friday, &Saturday}
     a := dayArray{data}
     sort.Sort(&a)
     if  !sort.IsSorted(&a) {
         panic( "fail" )
     }
     for  _, d :=  range  data {
         fmt.Printf( "%s " , d.longName)
     }
     fmt.Printf( "\n" )
}
 
func  main() {
     ints()
     strings()
     days()
}

32、Go中的读和写:io包中提供了读和写的接口

1
2
3
4
5
6
7
type  Reader  interface  {
     Read(p []byte) (n int, err error)
}
 
type  Writer  interface  {
     Write(p []byte) (n int, err error)
}

33、空接口:空接口或则最小接口不包含任何方法,它对实现没有任何要求。可以给一个空接口类型的变量 var val interface {} 赋任何类型的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package  main
import  "fmt"
 
var  i = 5
var  str =  "ABC"
 
type  Person  struct  {
     name string
     age  int
}
 
type  Any  interface {}
 
func  main() {
     var  val Any
     val = 5
     fmt.Printf( "val has the value: %v\n" , val)
     val = str
     fmt.Printf( "val has the value: %v\n" , val)
     pers1 := new(Person)
     pers1.name =  "Rob Pike"
     pers1.age = 55
     val = pers1
     fmt.Printf( "val has the value: %v\n" , val)
     switch  t := val.( type ) {
     case  int:
         fmt.Printf( "Type int %T\n" , t)
     case  string:
         fmt.Printf( "Type string %T\n" , t)
     case  bool:
         fmt.Printf( "Type boolean %T\n" , t)
     case  *Person:
         fmt.Printf( "Type pointer to Person %T\n" , t)
     default :
         fmt.Printf( "Unexpected type %T" , t)
     }
}
 
//输出
val has the value: 5
val has the value: ABC
val has the value: &{Rob Pike 55}
Type pointer to Person *main.Person

34、Go中对变量类型的判断以及获取变量的值:引入reflect 包,使用reflect.TypeOf(变量) 判断变量的类型 使用reflect.ValueOf(变量) 获取变量的值。(了解更多:https://github.com/unknwon/the-way-to-go_ZH_CN/blob/master/eBook/11.10.md

35、遍历出结构体中的字段值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package  main
 
import  (
     "fmt"
     "reflect"
)
 
type  NotknownType  struct  {
     s1, s2, s3 string
}
 
func  (n NotknownType) String() string {
     return  n.s1 +  " - "  + n.s2 +  " - "  + n.s3
}
 
// variable to investigate:
var  secret  interface {} = NotknownType{ "Ada" "Go" "Oberon" }
 
func  main() {
     value := reflect.ValueOf(secret)  // <main.NotknownType Value>
     typ := reflect.TypeOf(secret)     // main.NotknownType
     // alternative:
     //typ := value.Type()  // main.NotknownType
     fmt.Println(typ)
     knd := value.Kind()  // struct
     fmt.Println(knd)
 
     // iterate through the fields of the struct:
     for  i := 0; i < value.NumField(); i++ {
         fmt.Printf( "Field %d: %v\n" , i, value.Field(i))
         // error: panic: reflect.Value.SetString using value obtained using unexported field
         //value.Field(i).SetString("C#")
     }
 
     // call the first method, which is String():
     results := value.Method(0).Call(nil)
     fmt.Println(results)  // [Ada - Go - Oberon]
}
 
//结果
main.NotknownType
struct
Field 0: Ada
Field 1: Go
Field 2: Oberon
[Ada - Go - Oberon]

36、接口的继承:当一个类型包含(内嵌)另一个类型(实现了一个或多个接口)的指针时,这个类型就可以使用(另一个类型)所有的接口方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
type  Task  struct  {
     Command string
     *log.Logger
}
//这个类型的工厂方法像这样
func  NewTask(command string, logger *log.Logger) *Task {
     return  &Task{command, logger}
}
//当 log.Logger 实现了 Log() 方法后,Task 的实例 task 就可以调用该方法
 
task.Log()
 
//类型可以通过继承多个接口来提供像 多重继承 一样的特性
type  ReaderWriter  struct  {
     *io.Reader
     *io.Writer
}

37、Go中的封装、继承、多态:

  • 封装(数据隐藏):和别的 OO 语言有 4 个或更多的访问层次相比,Go 把它简化为了 2 层

    1)包范围内的:通过标识符首字母小写,对象 只在它所在的包内可见

    2)可导出的:通过标识符首字母大写,对象 对所在包以外也可见类型只拥有自己所在包中定义的方法。

  • 继承:用组合实现:内嵌一个(或多个)包含想要的行为(字段和方法)的类型;多重继承可以通过内嵌多个类型实现
  • 多态:用接口实现:某个类型的实例可以赋给它所实现的任意接口类型的变量。类型和接口是松耦合的,并且多重继承可以通过实现多个接口实现。Go 接口不是 Java 和 C# 接口的变体,而且接口间是不相关的,并且是大规模编程和可适应的演进型设计的关键。

38、Sscanf()函数的使用:从一大长串的字符串截取我们需要的数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package  main
 
import  "fmt"
 
var (
     s string
     i int
     f float32
     input =  "56/39.45/Go"
     format = "%d/%f/%s"  //规定想要数据的格式
)
 
func  main(){
     fmt.Sscanf(input,format,&i,&f,&s) //把对应数据的信息赋值给所对应变量
     fmt.Printf( "i=%d,f=%f,s=%s" ,i,f,s)
}

39、 使用bufio 包提供的缓冲读取(buffered reader)来读取数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package  main
import  (
     "fmt"
     "bufio"
     "os"
)
var  readerInput *bufio.Reader
var  input string
var  err error
func  main(){
     readerInput = bufio.NewReader(os.Stdin)  //创建一个读取器并将它与标准输入绑定
     fmt.Println( "please enter you input:" )
     input,err = readerInput.ReadString( '\n' ) //这是读取器中的一个方法,该方法从输入中读取内容,直到碰到 delim 指定的字符,然后将读取到的内容连同 delim 字符一起放到缓冲区。
     if  err==nil{
         fmt.Printf( "the user input is:%s\n" ,input)  //输出用户读取的内容
     }
} 

 40、读文件:文件使用指向 os.File 类型的指针来表示的,也叫做文件句柄。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package  main
import (
     "fmt"
     "os"
     "bufio"
     "io"
)
func  main(){
     content,err :=os.Open( "1.html" ) //打开文件
     if  err!=nil {
         fmt.Printf( "maybe your file not exist or you dont't have the pormission which open the file" )
         return
     }
     defer  content.Close() //在文件末尾关闭文件
     fileContent:=bufio.NewReader(content)  //创建一个读取器并将它与文件读取内容绑定
     for {
         fileString,err :=fileContent.ReadString( '\n' ) //遍历读取内容时按照遇见换行符停止
         fmt.Printf( "the file content is :%s" ,fileString) //输出文件内容
         if  err==io.EOF{
             return
         }
     }
 
}

41、写文件:OpenFile 函数有三个参数:文件名、一个或多个标志(使用逻辑运算符“|”连接),使用的文件权限。

  • os.O_RDONLY:只读
  • os.O_WRONLY:只写
  • os.O_CREATE:创建:如果指定文件不存在,就创建该文件。
  • os.O_TRUNC:截断:如果指定文件已存在,就将该文件的长度截为0。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    package  main
     
    import  (
         "os"
         "bufio"
         "fmt"
    )
     
    func  main () {
         // var outputWriter *bufio.Writer
         // var outputFile *os.File
         // var outputError os.Error
         // var outputString string
         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()
    }  

42、文件拷贝。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package  main
 
import  (
     "fmt"
     "io"
     "os"
)
 
func  main() {
     CopyFile( "target.txt" "source.txt" )
     fmt.Println( "Copy done!" )
}
 
func  CopyFile(dstName, srcName string) (written int64, err error) {
     src, err := os.Open(srcName)
     if  err != nil {
         return
     }
     defer  src.Close()
 
     dst, err := os.Create(dstName)
     if  err != nil {
         return
     }
     defer  dst.Close()
 
     return  io.Copy(dst, src)
}

 

猜你喜欢

转载自www.cnblogs.com/zxs117/p/11736688.html