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)
})
}
相关文章
相关标签/搜索