Go 基础

1、变量定义三种方法

package main

import "fmt"

func main(){
      var a int = 10  //第一种
      fmt.Println(a)
      b int = 10
      fmt.Println(b)  //第二种
      c := 10
      fmt.Println(c)  //第三种
}        

  

2、数据类型

布尔型:

  布尔型的值只可以是常量 true 或者 false。一个简单的例子:var b bool = true。

数字类型:

  整型 int 和浮点型 float32、float64,Go 语言支持整型和浮点型数字,并且支持复数,其中位的运算采用补码

字符串类型:

  字符串就是一串固定长度的字符连接起来的字符序列。Go 的字符串是由单个字节连接起来的。Go 语言的字符串的字节使用 UTF-8 编码标识 Unicode 文本

3、常量

常量中的数据类型只可以是布尔型、数字型(整数型、浮点型和复数)和字符串型。

定义方式两种:

//第一种
const b string = "abc"

//第二种
const b = "abc"

  

4、函数

1)函数调用

package main

import "fmt"

func main() {
   /* 定义局部变量 */
   var a int = 100
   var b int = 200
   var ret int

   /* 调用函数并返回最大值 */
   ret = max(a, b)

   fmt.Printf( "最大值是 : %d
", ret )
}

/* 函数返回两个数的最大值 */
func max(num1, num2 int) int {
   /* 定义局部变量 */
   var result int

   if (num1 > num2) {
      result = num1
   } else {
      result = num2
   }
   return result
}

  

2)函数返回值

package main

import "fmt"

func swap(x, y string) (string, string) {
   return y, x
}

func main() {
   a, b := swap("Google", "Runoob")
   fmt.Println(a, b)
}

 3)闭包函数:

定义在函数内,对外部作用于有引用

func test(a int) (func()) {
	//var c int =100
	b:=func() {
		fmt.Println(a)
		fmt.Println("我是闭包函数")
	}
	return b

  

5、if-else

package main

import "fmt"

func main(){
	if a :=90;a>90{
		fmt.Print("大于")
	}else if a==90{
		fmt.Print(a)
	}
}

 6、包

//在同一个包下,变量,函数,都不能重复定义
//在包内定义的函数如果是小写字母开头,表示只能在包内部使用
//在外部包想使用,必须首字母大写
// 包的使用
package main

import "mypackage"
import "fmt"



func main() {

    //想使用mypackage包下的test函数和test1函数
    mypackage.Test1()
    fmt.Println("xxx")

}
执行
package mypackage

import "fmt"
//在同一个包下,变量,函数,都不能重复定义
//在包内定义的函数如果是小写字母开头,表示只能在包内部使用
//在外部包想使用,必须首字母大写
func Test1()  {
    fmt.Println(test(1,2))
    fmt.Println("xxxx")
}
供外部使用包
package mypackage  //通常情况下,包名就是文件夹名,在同一个文件夹下,包名必须一致

func test(a,b int) int{
    return a+b

}
内部使用包

7、循环

package main
import "fmt"

func main(){
    for i:=0;i<10;i++{
        fmt.Println(i)
    }
}

ps:for后面三个参数可以省略,当全部省略等同于其他语言的while循环

8、switch语句

switch 是一个条件语句,用于将表达式的值与可能匹配的选项列表进行比较,并根据匹配情况执行相应的代码块。它可以被认为是替代多个 if else 子句的常用方式

-如果条件都不满足,走default默认

a:=11
    switch a {
    case 1:
        fmt.Println("1")
    case 2:
        fmt.Println("2")
    case 10:
        fmt.Println("10")
    default:
        fmt.Println("不知道")
View Code

-fallthrough,穿透,无条件执行下一个case的内容

a:=10
    switch a {
    case 1:
        fmt.Println("1")
        fmt.Println("xxxx")
    case 2:
        fmt.Println("2")
    case 10:
        fmt.Println("10")
        //穿透,无条件执行下一个case的内容
        fallthrough
    case 11:
        fmt.Println("11")
        test5()
        fallthrough
    case 12:
        fmt.Println("12")
    }
View Code

9、数组

1)数组

数组是同一类型元素的集合,Go 语言中不允许混合不同类型的元素,例如包含字符串和整数的数组。

//三种一样
var a [6]int=[6]int{1,2,3}
var a =[6]int{1,2,3}
a :=[6]int{1,2,3}
定义并赋初值方式

 例:

package main

import (
    "fmt"
)

func main() {
    a := [3]int{12} 
    fmt.Println(a)
}
View Code

声明一个长度为 3 的数组,但只提供了一个值 12,剩下的 2 个元素自动赋值为 0。这个程序将输出 [12 0 0]

2)数组是值类型:即所有函数传参都是copy传参

3)数组的长度,内置函数len

4)数组大小是类型的一部分

var a [4]int=[4]int{1,2,}
var b [5]int=[5]int{1,2,}
因为数组大小不一样,所以上面不是同类型
View Code

5)与或非:&&、||、!(Go语言没有and、or和not等判断字符)

6)通过range迭代,迭代可选1-2个参数,第一个为索引,第二个为迭代的值

    for i,v:=range a {
    //for i:=range a {
        fmt.Println("------",i)
        fmt.Println(v)
    }
View Code

7)多维数组

var a [7][2]int
    a[0][1]=100
    fmt.Println(a)

//[[0 100] [0 0] [0 0] [0 0] [0 0] [0 0] [0 0]]
View Code

10、切片

创建
c:= [] int {6,7,8} 

使用 make 创建一个切片
i := make([]int, 5, 5)

创建一个有 3 个整型元素的数组,并返回一个存储在 c 中的切片引用

ps:

1/切片自己不拥有任何数据。它只是底层数组的一种表示。对切片所做的任何修改都会反映在底层数组中

2/切片的长度是切片中的元素数。切片的容量是从创建切片索引开始的底层数组中元素数。

3/追加切片元素append

  -如果添加元素大于切片容量,则容量会翻一倍

  -切片类型的零值为nil,一个nil切片的长度和容量为0

4/多维切片

package main

import (
    "fmt"
)

func main() {  
     pls := [][]string {
            {"C", "C++"},
            {"JavaScript"},
            {"Go", "Rust"},
            }
    for _, v1 := range pls {
        for _, v2 := range v1 {
            fmt.Printf("%s ", v2)
        }
        fmt.Printf("
")
    }
}

//
C C++  
JavaScript  
Go Rust
View Code

11、Maps(是引用类型:当 map 被赋值为一个新变量的时候,它们指向同一个内部数据结构)

map 是在 Go 中将值(value)与键(key)关联的内置类型。通过相应的键可以获取到值。map 的零值是 nil

创建maps:

make(map[type of key]type of value)

给map添加元素:根据key赋值(xxx[key]=value)

获取map中元素:xxx[key],如果不存在,会返回零值(对应该元素类型的零值)

删除map中元素: [delete(map, key)],次函数无返回值

获取map长度:len()

12、字符串

len 统计字节数、utf8.RuneCountInString 统计字符

遍历字符串的好方法:for range

    name :="abc老家伙"

    for _,v:=range name{
    fmt.Println(string(v))
    fmt.Printf("%T",v)
    fmt.Println()
}

# 如果用简单循环,遍历出的是字节,range遍历的是字符
View Code

13、指针

记住三点:   1)& 取地址符号

      2)* 放在类型旁边,表示指向这个类型的指针

      3)* 放变量旁边,表示解引用(反解)

14、结构体

 结构体是用户定义的类型,表示若干个字段(Field)的集合。

 1)结构体的声明

type Employee struct {
    Name,gender    string   #同类型写一行,用逗号隔开
    age,salary      int
}

ps:不声明type,则创建是匿名结构体

2)访问结构体字段

点好操作符 . 用于访问结构体的字段

3)匿名字段(即创建结构体时,字段可以只有类型,而没有字段名)

type Person struct {  
    string
    int
}

4)嵌套结构体

type Address struct {  
    city, state string
}

type Person struct {  
    name string
    age int
    address Address
}

5)结构相等性

结构体是值类型。如果它的每一个字段都是可比较的,则该结构体也是可比较的

如果结构体出现不可比较类型,则不能比较,例如结构体含map类型

15、方法(类似python方法)

方法其实就是一个函数,在 func 这个关键字和方法名中间加入了一个特殊的接收器类型。接收器可以是结构体类型或者是非结构体类型

创建语法:func (a 结构体)函数名(参数)(返回值){}

package main

import (
    "fmt"
)

type Employee struct {
    name     string
    salary   int
    currency string
}

/*
  displaySalary() 方法将 Employee 做为接收器类型
*/
func (e Employee) displaySalary() {
    fmt.Printf("Salary of %s is %s%d", e.name, e.currency, e.salary)
}

func main() {
    emp1 := Employee {
        name:     "Sam Adolf",
        salary:   5000,
        currency: "$",
    }
    emp1.displaySalary() // 调用 Employee 类型的 displaySalary() 方法

值接收器方法:在内部修改值,不会影响外部的值

指针接收器方法:在内部修改至,会改变外部的值

16、接口(一系列方法的集合)

在 Go 语言中,接口就是方法签名(Method Signature)的集合。当一个类型定义了接口中的所有方法,我们称它实现了该接口。

1)定义一个鸭子类型接口

package main

import "fmt"

//定义一个鸭子类型接口
type Duck interface {
    speak()
}

//一个鸭子和方法
type Tduck struct {
    name string
    age int
}

func (a Tduck) speak(){
    fmt.Println("Tduck方法",a.name)
}

//另一只鸭子和方法
type PDuck struct {
    wife,name string
}
func (a PDuck) speak(){
    fmt.Println("PDuck",a.name)
}

func main(){
    pD:=PDuck{name:"水鸭子"}
    tD:=Tduck{name:"唐老鸭"}
    speak(pD)
    speak(tD)
}

func speak(a Duck){
    a.speak()
}
View Code

2)空接口

没有包含方法的接口称为空接口。空接口表示为 interface{}。由于空接口没有方法,因此所有类型都实现了空接口。

3)断言

//断言
func speak(p Tduck) {
    a:=p.(PDuck)
    fmt.Println(a.wife)
    p.speak()
}
View Code

想取出类型其他的属性,需用到判断,用switch

//承接1)鸭子接口

func speak(p Duck) {
    switch a:=p.(type) {
    case PDuck:
        fmt.Println("")
        fmt.Println(a.wife)
    case Tduck:
        fmt.Println("普通")
        fmt.Println(a.name)
    }
}
View Code

4)多接口和接口嵌套

5)接口的零值

package main

import "fmt"

//接口的零值 nil  接口是引用类型
type Describer interface {
    Describe()
}

func main() {
    var d1 Describer
    if d1 == nil {
        fmt.Println("xxxx")
    }
}
View Code

17、异常处理

//异常处理
//defer  panic  recover
//defer 表示延迟调用,即便程序出现严重错误,也会执行
//panic  就是python中的raise(主动抛出异常)
//recover 恢复程序,继续执行
package main

import "fmt"

//异常处理
//defer  panic  recover
//defer 表示延迟调用,即便程序出现严重错误,也会执行
//panic  就是python中的raise(主动抛出异常)
//recover 恢复程序,继续执行
func main() {
    //先注册,后调用
    //defer fmt.Println("xxxx")
    //defer fmt.Println("yyy")
    f1()
    f2()

    f3()

}

func f1()  {
    fmt.Println("f1...")
}

func f2()  {
    defer func() {
        if a:=recover();a!=nil{
            //a 如果不等于nil,表示程序出了异常,a 就是异常信息
            //a 等于nil,表示没有异常
            //fmt.Println("出错了")
            fmt.Println(a)
        }
        //用于会被执行(相当于finally)

    }()
    fmt.Println("f2...")
    //var a =make([]int,3,3)
    //fmt.Println(a[4])
    panic("你给我出去")
}
func f3()  {

    fmt.Println("f3...")
}
View Code

18、错误处理

package main

import (
    "errors"
    "fmt"
)

//错误

func circleArea(radius int) (int, error) {
    if radius < 0 {
        return 0, errors.New("错误信息")
        //panic("xddd")
    }
    return 100, nil
}

func main() {
    a,_:=circleArea(-10)
    if err!=nil{
        fmt.Println(err)
    }
    //fmt.Println(err)
    fmt.Println(a)
    _,err:=fmt.Println()
    if err!=nil{
        fmt.Println("打印出错")
    }
}
View Code

 19、Go并发和协程

1、Go 编程语言原生支持并发。Go 使用 Go 协程(Goroutine) 和信道(Channel)来处理并发。

2、Go协程:一起并发的函数或方法,轻量级线程,而且成本很小

  1)go协程会复用数量更少的os线程

  2)协程之间通过信道来通信

  3)启动:调用函数或方法时,在前面加上关键字go即可

package main

import (
    "fmt"
)

func hello() {
    fmt.Println("Hello world goroutine")
}
func main() {
    go hello()
    fmt.Println("main function")
}

20、信道(管道)

1、介绍

信道可以想像成 Go 协程之间通信的管道。如同管道中的水会从一端流到另一端,通过使用信道,数据也可以从一端发送,在另一端接收

ps:所有信道关联了一个类型,只能运输这种类型的数据,而运输其他类型的数据都是非法的。

2、定义

chan T 表示 T 类型的信道。

信道的零值为 nil。信道的零值没有什么用,应该像对 map 和切片所做的那样,用 make 来定义信道。

a := make(chan int)

3、信道读取值(默认是阻塞的)

data := <- a // 读取信道 a  
a <- data // 写入信道 a

4、死锁现象

5、单向信道(了解)

package main

import "fmt"

func sendData(sendch chan<- int) {  
    sendch <- 10
}

func main() {  
    sendch := make(chan<- int)
    go sendData(sendch)
    fmt.Println(<-sendch)
}
View Code

6、关闭信道和使用for range遍历信道

  1)当从信道接收数据时,接收方可以多用一个变量来检查信道是否已经关闭。

v, ok := <- ch
package main

import (  
    "fmt"
)

func producer(chnl chan int) {  
    for i := 0; i < 10; i++ {
        chnl <- i
    }
    close(chnl)
}
func main() {  
    ch := make(chan int)
    go producer(ch)
    for {
        v, ok := <-ch
        if ok == false {
            break
        }
        fmt.Println("Received ", v, ok)
    }
}
例:关闭信道

  2)也可以用range,从信道接收,只有一个变量

package main

import (  
    "fmt"
)

func producer(chnl chan int) {  
    for i := 0; i < 10; i++ {
        chnl <- i
    }
    close(chnl)
}
func main() {  
    ch := make(chan int)
    go producer(ch)
    for v := range ch {
        fmt.Println("Received ",v)
    }
}

21、缓冲信道

信道是阻塞的,我们可以利用缓冲信道,当信道缓冲为空时,才会阻塞信道接收数据

当信道缓冲已满,会阻塞信道发送数据。

1、创建缓冲信道(capacity表示容量)

ch := make(chan type, capacity)
package main

import (  
    "fmt"
    "time"
)

func write(ch chan int) {  
    for i := 0; i < 5; i++ {
        ch <- i
        fmt.Println("successfully wrote", i, "to ch")
    }
    close(ch)
}
func main() {  
    ch := make(chan int, 2)
    go write(ch)
    time.Sleep(2 * time.Second)
    for v := range ch {
        fmt.Println("read value", v,"from ch")
        time.Sleep(2 * time.Second)

    }
}

2、死锁

当超出了信道的容量,产生阻塞,又继续添加值时会发生死锁

程序会在运行时触发 panic错误信息

3、长度和容量

长度:即信道内含数据个数

容量:最多能放几个数据

4、Waitgroup (即等待所有go协程执行完成)

假设我们有 3 个并发执行的 Go 协程(由 Go 主协程生成)。Go 主协程需要等待这 3 个协程执行结束后,才会终止。这就可以用 WaitGroup 来实现。

ps:因为sync包下的WaitGroup,是个值类型,当参数传递是需要取地址

package main

import (  
    "fmt"
    "sync"
    "time"
)

func process(i int, wg *sync.WaitGroup) {  
    fmt.Println("started Goroutine ", i)
    time.Sleep(2 * time.Second)
    fmt.Printf("Goroutine %d ended
", i)
    wg.Done()
}

func main() {  
    no := 3
    var wg sync.WaitGroup
    for i := 0; i < no; i++ {
        wg.Add(1)
        go process(i, &wg)
    }
    wg.Wait()
    fmt.Println("All go routines finished executing")
}
方法一:通过WG实现
//通过信道实现
func main() {
    var a =make(chan bool)
    for i:=0;i<5;i++{
        go test6(a,i)
    }
    for i:=0;i<5;i++{
        <-a
    }

    fmt.Println("都执行完了")
}
func test6(a chan bool,i int)  {
        time.Sleep(time.Second*2)
        fmt.Println(i)
        a<-true

}
方法二:通过信道实现

22、select

数据先回来先取,同时回来时随机选取一个,

一般防止单个任务遇到阻塞情况,所以开多任务提取数据,提高性能

import (  
    "fmt"
    "time"
)

func server1(ch chan string) {  
    time.Sleep(6 * time.Second)
    ch <- "from server1"
}
func server2(ch chan string) {  
    time.Sleep(3 * time.Second)
    ch <- "from server2"

}
func main() {  
    output1 := make(chan string)
    output2 := make(chan string)
    go server1(output1)
    go server2(output2)
    select {
    case s1 := <-output1:
        fmt.Println(s1)
    case s2 := <-output2:
        fmt.Println(s2)
    }
}

23、mutex

 Mutex 用于提供一种加锁机制(Locking Mechanism),可确保在某时刻只有一个协程在临界区运行,以防止出现竞态条件

package main

import (
    "fmt"
    "sync"
)

//通过锁实现
var x  = 0
func increment(wg *sync.WaitGroup,m *sync.Mutex) {
    m.Lock()
    x = x + 1
    m.Unlock()
    wg.Done()
}
func main() {
    var w sync.WaitGroup
    var m sync.Mutex   //值类型,传递地址
    for i := 0; i < 1000; i++ {
        w.Add(1)
        go increment(&w,&m)
    }
    w.Wait()
    fmt.Println("final value of x", x)
}
加锁

也可以用信道实现

package main  
import (  
    "fmt"
    "sync"
    )
var x  = 0  
func increment(wg *sync.WaitGroup, ch chan bool) {  
    ch <- true
    x = x + 1
    <- ch
    wg.Done()   
}
func main() {  
    var w sync.WaitGroup
    ch := make(chan bool, 1)
    for i := 0; i < 1000; i++ {
        w.Add(1)        
        go increment(&w, ch)
    }
    w.Wait()
    fmt.Println("final value of x", x)
}
信道实现
等待所有go协程执行完成
原文地址:https://www.cnblogs.com/xiaowangba9494/p/12013091.html