• 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协程执行完成
  • 相关阅读:
    团队项目冲刺第6天
    冲刺阶段第五天
    冲刺阶段前四天总结
    "博客园"用户体验分析
    测试计划
    scrum敏捷开发
    团队开发_软件项目风险管理
    sprint计划会议
    svn 之 svn的两种开发模式
    redis 之 搭建真实集群
  • 原文地址:https://www.cnblogs.com/xiaowangba9494/p/12013091.html
Copyright © 2020-2023  润新知