文章目录
  1. 1. Map key-value形式
  2. 2. function 函数
  3. 3. defer
  4. 4. struct 结构(Go没有class)
  5. 5. 方法(Method)
  6. 6. 接口(interface)
    1. 6.1. 接口的定义和使用
    2. 6.2. 空接口
  7. 7. 并发(Goroutine)

接着上一篇博文的内容,继续瞎侃一下Go语言基础入门。

Map key-value形式

创建map的形式:

var m map[int] string
m=map[int] string{}

var m map[int] string
m=make(map[int]string)

var m map[int]string=make(map[int]string)

m:=make(map[int]string)

存取操作:

m[1]=”OK” 存

a:=m[1] 取

delete(m,1) 删

多级map时每级map都需要初始化

for k,v:=range map{
...
}

注意:k,v只是拷贝,对其的操作不会影响map

function 函数

Go语言中函数不支持嵌套,重载和默认参数。值类型拷贝的是值,引用类型拷贝的是地址。在Go语言中,所有的函数也是值类型,可以作为参数传递。

匿名函数:函数可以像普通变量一样被传递或使用。

// 匿名函数 1  
f := func(i, j int) (result int) { // f 为函数地址  
    result = i+j  
    return  
}  
fmt.Fprintf(os.Stdout, "f = %v  f(1,3) = %v\n", f, f(1, 3))  

/ 匿名函数 2  
x, y := func(i, j int) (m, n int) { // x y 为函数返回值  
    return j, i  
}(1, 9) // 直接创建匿名函数并执行  

fmt.Fprintf(os.Stdout, "x = %d   y = %d\n", x, y)  

闭包:闭包是可以包含自由(未绑定到特定对象)变量的代码块,这些变量不在这个代码块内或者任何全局上下文中定义,而是在定义代码块的环境中定义。要执行的代码块(由于自由变量包含在代码块中,所以这些自由变量以及它们引用的对象没有被释放)为自由变量提供绑定的计算环境(作用域)。

闭包是由函数及其相关的引用环境组合而成的实体(即:闭包=函数+引用环境)。

闭包函数出现的条件:

1.被嵌套的函数引用到非本函数的外部变量,而且这外部变量不是“全局变量”

2.嵌套的函数被独立了出来(被父函数返回或赋值 变成了独立的个体),
而被引用的变量所在的父函数已结束

func closure(x intfunc(int) int{
    return func(y int) int{
        return x*y
    }
}

另一例子:

package main
import "fmt"
func ExFunc(n int) func() {
return func() {
n++ //这里对外部变量加1
fmt.Println(n)
}
}

func main() {
myFunc := ExFunc(10)
myFunc() //这里输出11
myAnotherFunc := ExFunc(20)
myAnotherFunc()  //这里输出21
myFunc()
myAnotherFunc()
//再一次调用myFunc()函数,结果是12,由此得出以下两点
//1.内函数对外函数 的变量的修改,是对变量的引用
//2.变量被引用后,它所在的函数结束,这变量也不会马上被烧毁
}

defer

类似于析构函数,按照调用顺序相反顺序执行,常用于资源清理。特别是当你在进行一些打开资源的操作时,遇到错误需要提前返回,在返回前你需要关闭相应的资源,不然很容易造成资源泄露等问题。

defer 还有一个重要的特性,就是即便函数抛出了异常,也会被执行的。 这样就不会因程序出现了错误,而导致资源不会释放了。

经典例子:

func CopyFile(dst, src string) (w int64, err error) {
 srcFile, err := os.Open(src)
 if err != nil {
  return
 }
 defer srcFile.Close() //每次申请资源时,请习惯立即申请一个defer 关闭资源,这样就不会忘记释放资源了
 dstFile, err := os.Create(dst)
 if err != nil {
  return
 }
 defer dstFile.Close()
 return io.Copy(dstFile, srcFile)
}

Go语言中没有异常机制,但有panic/recover模式来处理错误, panic用于抛出异常, recover用于捕获异常.panic可以在任何地方引发, recover只能在defer语句中使用, 直接调用recover是无效的。

func main() {  
f()  
fmt.Println("Returned normally from f.")  
}  

func f() {  
defer func() {  
if r := recover(); r != nil {  
fmt.Println("Recovered in f", r)  
}  
}()  
fmt.Println("Calling g.")  
g()  
fmt.Println("Returned normally from g.")  
}  

func g() {  
panic("ERROR")  
} 

struct 结构(Go没有class)

定义:
type struct{}

type person struct{
   Name string
      Age int
}

赋值:

a:=&person{}             a:&person{
a.Name="zhengsan"  或        Name:"zhangsan",
a.Age=12                     Age:19,  
                           }  

结构支持指向自身的指针类型成员,允许直接通过指针来读写结构成员,仅支持==与!=,不支持< 或 >

支持匿名结构

a:&struct{
  Name string
  Age int
}{
  Name : "zhangsan",
  Age : 19,
}

支持结构嵌套

type person struct{
    Name string
       Age int
    Contact struct{
            Phone,city string
        }
 }

初始化:

 a:=person{
Name:"zhangsan",
Age:19,
}
a.Contact.Phone="123233"
a.Contact.city="shanghai"

匿名字段:初始化只能按顺序赋值!!

type person struct{
    string
       int
 }
 a:=person{"haha",19}//正确
 b:=person{12,"hah"}//错误

下面是个例子:

type human struct{
        Sex int
}
type student struct{
         human
        Name string
        Age int
}
a:=student{
        Name:"joe",
        Age:19,
        human:human{Sex:0,}
}
a.Sex=100
a.human.Sex=100//两种方式都可以

方法(Method)

Go虽然没有class,但依旧有method,可以访问结构中的私有字段。

type A struct{
       Name string
   }
func (a *A) print(){//方法绑定
a.Name="AA"
 }
a:=A{}
a.print()
(*A).print(&a)

字段访问权限为包内,即在同一个包中字段都是可以访问的。

接口(interface)

接口是一个或多个方法签名的集合
只要某个类型拥有该接口的所有签名方法,即实现该接口,无需显示声明。

接口只有方法声明,没有实现,没有字段。

接口的定义和使用

type I interface{
Get() int
Put(int)
 }//定义了一个接口,它包含两个函数Get和Put

实现这个接口:结构S就是实现了接口I

type S struct {val int}
func (this *S) Get int {
return this.val
}
func (this *S)Put(v int) {
this.val = v
 }

空接口

对于空接口interface{} 其实和泛型的概念很像。任何类型都实现了空接口。空接口可以作为任何数据接口的容器。

很多语言都是这样的逻辑:

function g(obj){
if (obj is I) {
    return (I)obj.Get()
}
 }

Go中是这样实现的:

func g(any interface{}) int {
return any.(I).Get()
}

并发(Goroutine)

当被问到为什么用Go语言,一定不得不提的是Go语言的并发程序编写。Go中并发程序依靠的是两个:goroutine和channel。

并发:主要由切换时间片来实现同时运行

并行:直接利用多核实现多线程的运行

一个goroutine并不相当于一个线程,goroutine的出现正是为了替代原来的线程概念成为最小的调度单位。一旦运行goroutine时,先去当先线程查找,如果线程阻塞了,则被分配到空闲的线程,如果没有空闲的线程,那么就会新建一个线程。注意的是,当goroutine执行完毕后,线程不会回收推出,而是成为了空闲的线程。Goroutine奉行通信来共享内存,而不是共享内存来通信。

channel的意思用白话可以这么理解:主线程告诉大家你开goroutine可以,但是我在我的主线程开了一个管道,你做完了你要做的事情之后,往管道里面塞个东西告诉我你已经完成了。

下面是一个例子:

 c:=make(chan bool)
go func(){
    fmt.Println("GOGOGO")
    c<-true//往channel里输入数据
}()
<-c//从channel中输出数据

1.channel只能使用make来进行创建

基本格式是 c := make(chan int),int是说明这个管道能传输什么类型的数据

2 往channel中插入数据的操作 c <- 1

3 从channel中输出数据 <- c

channel分为两种:一种是有buffer的,一种是没有buffer的,默认是没有buffer的。

ci := make(chan int) //无buffer

cj := make(chan int, 0) //无buffer

cs := make(chan int, 100) //有buffer

有缓冲的channel,因此要注意“放”先于“取”

无缓冲的channel,因此要注意“取”先于“放”

同样要先输出hello world,使用有缓冲的channel和无缓冲的channel分别是这样的:

有缓冲的channel:

var a string
var c = make(chan int, 10)

func f() {
a = "hello, world"
c <- 0
}

func main() {
go f()
<-c
print(a)

}

这里有个缓冲,因此放入数据的操作c<- 0先于取数据操作 <-c

无缓冲的channel:

var a string
var c = make(chan int)

func f() {
a = "hello, world"
<-c
}

func main() {
go f()
c <- 0
print(a)

}

由于c是无缓冲的channel,因此必须保证取操作<-c 先于放操作c<- 0

使用同步包实现多个goroutine:

wg.sync.waitGroup{}
wg.Add(10)
...
wg.wait()
...
wg.Done()//执行一个消除一个

记住,无缓冲的channel永远不会存储数据,只负责数据流通。

从无缓冲信道取出数据,必须要有数据流进来,否则当前线程阻塞,无缓冲信道是一批数据一个一个流进流出。数据流入无缓冲信道,如果没有goroutine来拿走,那么当前线程也会阻塞。而有缓冲信道是一个一个存储数据,然后一起流出一批数据。

文章目录
  1. 1. Map key-value形式
  2. 2. function 函数
  3. 3. defer
  4. 4. struct 结构(Go没有class)
  5. 5. 方法(Method)
  6. 6. 接口(interface)
    1. 6.1. 接口的定义和使用
    2. 6.2. 空接口
  7. 7. 并发(Goroutine)