golang 基礎學習

1.基本概念和語法
  • golang中,若是一個名字的首字母大寫,表明詞名字將被導出。linux

  • golang程序由包(package)組成,程序從main包開始運行,多個源文件能夠屬於同一個包,可是一個目錄中只放置一個包。一般而言,目錄名與包名一致。package main    定義一個包main。 導入代碼包形式以下:golang

  

import "fmt	
import"math/rand"  或寫爲以下形式

import(
	"fmt"
	"math/rand"
)
  • 函數:定義變量時,變量名在類型以前,這與不少語言不一致。且x int,y int能夠表示爲x,y int。 定義一個函數形式以下:
func add(x int,y int)int{
	return x+y
}

  函數能夠返回多個值:數組

package main
import "fmt"
func swap(x,y string)(string,string){
	return y,x
}

func main(){
	a,b:= swap("hello","world")
	fmt.Println(a,b)
}
注:返回值能夠指定變量名,而且像變量同樣使用。
func (p myType) funcName (a,b int, c string) (r,s int){
	return
}

函數也是一個類型,函數類型是go語言中一個重要的類型。func->關鍵字 funcName->方法名 a,b int,c string->傳入參數 r,s int->返回值 {}->函數體 函數的定義可使用var:var m int = 10 或者,var i,j,k = 1,2,3 函數內變量賦值使用:=操做符
var recorder func(name string,age int,seniority int)(done bool)
//後面全部符合這個函數類型的實現均可以賦給變量recorder,以下
recorder = func(name string,age int,seniority int)(done bool){
	//相應語句
	return
} 

上述中,咱們將一個函數字面上賦給了變量recorder,咱們能夠在一個函數類型的變量上直接應用調用表達式來調用它,以下:閉包

done := recorder("Harry",32,10)併發

type Encipher func(planintext string)[]byte //type專門用於聲明自定義類型,此處聲明的
Encipher實際上就是函數類型func(planintext string)[]byte的一個別名類型。函數

  • 數據類型:數據類型的轉換表達式爲T(v),含義將v轉換爲類型T。數據類型包含以下形式:
1.bool 2.string 3.int int8 int16 int32 int64 4.uint uint8 unint16 uint32 uint64 5.uintptr 6.byte(等價於uint8) 7.rune(等價於int32,用於表示一個unicode code point) 8.float32,float64 9.complex64,complex128 注:使用constant來定義常量,constant Pi= 3.14
  • 控制語句:
    1. for語句 golang中使用(且只使用)for來進行循序(沒有while語句)
package main
	func main(){
	sum := 0
	for i := 0;i < 10;i++{
  	sum += i
	}
	//下述寫法等價於C/C++等語言中的while語句
	for sum < 1000{
 	 sum + =sum
	}
	}
	注:for循環語句不須要()且{}是必需要使用的,if、switch語法處理與此同樣。若使用無限循環,則可以使用:
	for{
	}

    2. if語句,if語句能夠在執行條件判斷前帶一個語句(這常被叫作if帶上一個短語句),詞語句中變量的生命週期在if語句結束後結束,以下。ui

package main
	import(
	  "fmt"
	  "math/rand"
	)

	func main(){
	  if n := rand.Intn(6);n <= 2{
	    fmt.Println("[0,2]",n)
	}else{
		fmt.Println("[3,5]",n)
	}

	//此處開始沒法使用變量n
	}

  3. switch語句,golang中不須要使用break語句來跳出switch,且switch中能夠沒有條件。this

package mian
	import(
	  "fmt"
	  "runtime"
	)

	func main(){
	  fmt.Print("Go runs on")
	  //switch相似於if能夠帶上一個短語句
	  switch os := runtime.GOOS;os{
	  case "darwin":
		fmt.Println("OS X.")
	  case "linux":
		fmt.Println("Liunx.")
	  default:
		//others
		fmt.Printf("%s.",os)
	  }
	}

	//無條件使用switc語句
	func main(){
	  t := time.Now()
	  switch{
	  case t.Hour() < 12:
		fmt.Println("Good morning!")
	  case t.Hour() < 17:
		fmt.Println("Good afternoon.")
	  default:
		fmt.Println("Good eventing.")
	  }
	}

  4. defer,defer語句可以將一個函數調用加入一個列表中(這個函數調用被叫作deferred函數調用),在當前函數調用結束時調用列表中的函數。以下:google

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)
	}
	
	//注:deferred函數調用按先進後出的順序執行:
	func main(){
	  //輸出43210
	  defer fmt.Print(i)
	}
  • 其餘數據類型

  1.  結構,(structs),結構是一個域的集合:spa

type Vertex struct{
	  X int
	  Y int
	}

func main(){
	 v := Vertex{1,2}
	  v.X = 4
	  fmt.Println(v)
  }

  2. 數組,[n]T在golang中是一個類型(像*T同樣),表示一個長度爲n的數組其元素類型爲T,數組長度沒法改變

func main(){
	  var a[2]string
	  a[0] = "hello"
	  a[1] = "world"
	  fmt.println(a[0],a[1])
	  fmt.println(a)
	}

  3.指針,golang中的指針不支持算術運算:

p := Vertex{X,Y} //{x,y}爲struct literal
	q := &p //q類型爲*Vertex
	q.X = 2  //直接訪問區域X
	struct的literal由{}包裹,實際過程咱們可使用Name:這樣的語法爲特定域值設置值:
	type Vertex struct{
	  X,Y int
	}

	r := Vertex{X:3}  //此時Y的值爲0  

4.slice,slice是可變長,其是一個指針而不是一個值。[]T爲slice類型,其中元素類型爲T:

p := []int{2,3,4,7,11,13}
	fmt.Println("p==",p)
	for i := 0;i < len(p);i++{
	  fmt.Printf("p[%d] == %d\n",i,p[i])
	  }
	}
	注:表達式s[lo:hi]用於建立一個slice,新建立的slice的元素爲s中的[lo,hi)位置的元素。
	建立slice使用make函數,(不用new了建立)如
	a := make([]int,5) //len(a)爲5
	此處make函數建立一個數組(元素初始化爲0)且返回一個slice指向詞數組。make能夠帶三個參數,用於指定容量:
	b := make([]int,0,5) //len(b)=0,cap(b)=5
	一個沒有值的slice是nil,長度和容量都爲0
	var z[]int
	fmt.Println(z,len(z),cap(z))
	if z == nil{
	  fmt.Println("nil!")
	}

5.map,用於映射key到value(值),map能夠經過make來建立,而不是new

  type Vertex Struct{
	  Lat,Long float64
	}

	var m map[string]Vertex
	
	func main(){
	  m = make(map[string]Vertex)
	  m["Bell Labs"] = Vertex{
		40.23332,-31.32143,
	  }
	  fmt.Println(m["Bell Labs"])
	}
	//注:使用[]訪問map中的值,使用delete刪除map中的值
	m[key] = elem //訪問
	delete(m,key) //刪除
	var m = map[string]Vertex{
	//此處Vertex能夠省略不寫
	  "Bell Labs":Vertex{
	   34.34345,53,32123
	  },
	   "google Labs":Vertex{
	   34.34345,53,32123
	  },
	}
	//若須要檢查map中的key是否存在
	//elem ,ok = m[key] //elem表示key的值(key不存在時,elem爲0),ok表示key是否存在

 6.range,用於在for中迭代一個slice或者一個map

var s = []int{1,2,3}
	
	func main(){
	  for i,v := range s{
		fmt.Println(i,v)
	  }
	  //只須要值,使用_忽略索引
	  for _,v := range s{
		fmt.Println(v)
	  }
	  //只須要索引
	  for i := range s{
		fmt.Println(i)
	  }
	}

7.閉包,golang中函數也是一個值(就像int值同樣),且函數能夠是一個閉包。閉包是一個引用了外部變量的函數。

package mian
	
	import "fmt"

	func adder()func(int)int{
	  sum := 0
	  //返回一個閉包,此閉包引用了外部變量sum
	  return func(x int)int{
	    sum += x
		return sum
	  }
	}

	func main(){
	  a := adder()
	  fmt.Println(a(9527))
	}

  2.方法與接口

  • 方法,方法是附屬於某個自定義的數據類型的函數,具體而言,一個方法就是一個與某個接收者關聯的函數。所以,在方法的簽名中不但包含了函數簽名,還包含了一個與接收者有關的聲明,也便是,方法的聲明中包含關鍵字func、接收者聲明、方法名稱、參數聲明列表、結果聲明列表和方法體。在golang中沒有類,能夠爲結構體定義方法,實例以下:
package main

import(
  "fmt"
  "math"
)

type Vertex struct{
  X,Y float64
}

//結構體Vertex的方法
//此處的方法接收者v的類型爲 *Vertex
func(v *Vertex)Abs()float64{
	return math.Sqrt(v.X*v.X+v.Y*v.Y)
}

func main(){
  v := &Vertex{3,4}
  fmt.Println(v.Abs())
}

  注:此處方法的接收者使用指針類型而非值類型的緣由以下:

1.避免方法每次調用時,對接收者的沒必要要拷貝 2.在方法內能夠修改接收者的值 咱們能夠爲任意類型定義方法,但如下狀況除外: 1.若是類型定義在其餘包中,不能爲其定義方法 2.若是類型是基礎類型,不能爲其定義方法。
  • 接口,接口也是一種類型(像結構體同樣)。一個接口類型包含了一組方法,一個接口類型可以持有那些實現了這些方法的值,以下:
//定義接口Abser
type Abser interface{
  Abs() float
}

//定義結構體Vertex
type Vertex struct{
  X,Y float64
}

//實現方法Abs
func(v *Vertex)Abs()float64{
  return math.Sqrt(v.X*v.X+v.Y+v.Y)
}

func main(){
  v := Vertex{3,4}
  //成功,可以持有*Vertex類型的值
  var a Abser = &v
  //出錯,不能持有Vertex類型的值
  //由於在*Vertex上定義了方法Abs,而在Vertex上未定義
  var b Abser = v
}
  • 匿名域,結構體中能夠存在只有類型而沒有名字的域,它們被叫作匿名域。如:
struct{
  T1
  *T2
}

  一個結構體中的匿名域中的域或者方法能夠被此結構體實例直接訪問:

    package main

	import "fmt"

	type Car struct{
	  wheelCount int
	}

	func(car *Car)numberofWheels()int{
	  return car.wheelCount
	}

	type Ferrari struct{
	  Car
	}
	
	func main(){
	  f := Ferrari{Car{4}}
	  fmt.Println("A Ferrari has this many wheels:",f.numberOfWheels())
	}
  3.併發支持
  • golang在運行時(runtime)管理了一種輕量級線程,被稱爲goroutime。建立數十萬級的goroutine沒有問題。使用go 關鍵字就開啓了一個線程示例以下:
    package main

	import(
	  "fmt"
	  "time"
	)

	func say(s string){
	  for i := 0;i < 5;i++{
		time.Sleep(100 * time.Millesecond)
		fmt.Println(s)
	  }
	}

	func main(){
	  //開啓一個goroutine執行say函數
	  go say("world")
	  say("hello")
	}
  4 其餘
  • panic

panic 是用來表示很是嚴重的不可恢復的錯誤的。在Go語言中這是一個內置函數,接收一個interface{}類型的值(也就是任何值了)做爲參數。panic的做用就像咱們日常接觸的異常。不過Go可沒有try…catch,因此,panic通常會致使程序掛掉(除非recover)。因此,Go語言中的異常,那真的是異常了。你能夠試試,調用panic看看,程序立馬掛掉,而後Go運行時會打印出調用棧。 可是,關鍵的一點是,即便函數執行的時候panic了,函數不往下走了,運行時並非馬上向上傳遞panic,而是到defer那,等defer的東西都跑完了,panic再向上傳遞。因此這時候 defer 有點相似 try-catch-finally 中的 finally。 panic就是這麼簡單。拋出個真正意義上的異常。

  • recover

上面說到,panic的函數並不會馬上返回,而是先defer,再返回。這時候(defer的時候),若是有辦法將panic捕獲到,並阻止panic傳遞,那就異常的處理機制就完善了。Go語言提供了recover內置函數,前面提到,一旦panic,邏輯就會走到defer那,那咱們就在defer那等着,調用recover函數將會捕獲到當前的panic(若是有的話),被捕獲到的panic就不會向上傳遞了,因而,世界恢復了和平。你能夠幹你想幹的事情了。不過要注意的是,recover以後,邏輯並不會恢復到panic那個點去,函數仍是會在defer以後返回。

//go實現相似try catch的事情
package main
func Try(fun func(),handler func(interface{})){
	defer func(){
		if err := recover();err != nil{
			handler(err)
		}
	}()
	fun()
}

func main(){
	Try(func(){
		panic("foo")
},func(e interface{}){
	print(e)
})
}
相關文章
相關標籤/搜索