GO基础快速入门

1、变量和常量

package main

import (
    "fmt"
    "strconv"
)

// 入口函数
func main() {
    fmt.Println("hello world.")

    // 变量,用于在程序中存储可变的值
    // 1、命名只能有,字母、数字、下划线组成
    //        只能以字母或下划线开头
    // 2、变量必须声明后使用,且声明后必须使用

    // 变量声明
    var name string         // 格式 : var 变量名称 变量类型
    var name1, name2 string // 声明多个同类型的变量
    var (                   // 声明多个不同类型的变量
        name3 string
        name4 int
    )

    // 变量赋值
    name = "alice"
    name1, name2 = "bob", "jan" // 给多个同类型的变量赋值

    // 变量类型推导,无需指定变量类型,自动推导变量类型
    // 只能写在函数体内
    name5 := ""
    name6, name7 := "", 1

    fmt.Println("name=" + name)
    fmt.Println("name1=" + name1)
    fmt.Println("name2=" + name2)
    fmt.Println("name3=" + name3)
    fmt.Println("name4=" + strconv.Itoa(name4))
    fmt.Println("name5=" + name5)
    fmt.Println("name6=" + name6)
    fmt.Println("name7=" + strconv.Itoa(name7))

    // 常量,用于在程序中存储可变的值
    // 1、命名只能有,字母、数字、下划线组成
    //        只能以字母或下划线开头
    // 2、声明并且要赋值

    const pi float32 = 3.14 // 声明单个常量
    const (
        n1 int = 1 // 指定常量类型
        n2     = 2 // 自动类型推导
    )

    // iota
    const (
        m1 = iota // 初始值为0
        m2        // 1
        m3        // 2
    )

    fmt.Println("n1=" + strconv.Itoa(n1))
    fmt.Println("n2=" + strconv.Itoa(n2))
    fmt.Println("m1=" + strconv.Itoa(m1))
    fmt.Println("m2=" + strconv.Itoa(m2))
    fmt.Println("m3=" + strconv.Itoa(m3))

}
 

2、基本数据类型和转换

package main

import (
    "fmt"
    "strconv"
)

func main() {
    // GO基本类型有,布尔类型、数值类型、字符串
    // 布尔类型的标识符 : bool
    // 取值范围 : true(真),false(假),默认值是false
    var isSunday bool // false
    isSunday = true   // true
    fmt.Println(isSunday)

    // 数值类型,是用来表示数字的,例如整数、小数
    // 整型、浮点型、复数
    // 整型,用来存储整数的类型
    // unit8、unit16、unit32、unit64、int8、int16、int32、int64
    // unit、int、byte、rune、uintptr
    // unit、int取值范围,取决于所运行的操作系统位数
    // byte 是unit8的别名
    // rune int32的别名
    // uintptr 无符号证书,用于存储指针

    // 浮点数,通俗来讲,用于存储程序中带有小数点的值
    // float32、float64

    // 复数
    // complex64、complex128
    z := 1 + 2i
    x := real(z)
    y := imag(z)
    fmt.Println(x, y)

    // 字符串
    // 一连串的字符组成的片段
    var s1 string = "" // 定义单行字符串
    var s2 string = `多行
    字符串`
    fmt.Println(s1, s2)

    // 字符串的拼接
    var s3 = "hello" + " " + "world."
    fmt.Println(s3)

    // 基本数据类型的转换
    // 数值类型的转换
    var n1 int16 = 1000
    var n2 int32 = int32(n1)
    // 将取值范围小的类型转换为取值范围大类型都是安全的
    fmt.Println(n2)
    // 将取值大的类型转换为取值小的类型都是有风险的,可能会截取
    var n3 int8 = int8(n1)
    fmt.Println(n3)

    // int转换为字符串
    var n4 int = 10
    // Itoa,就是Int转换为ASCII
    var n5 string = strconv.Itoa(n4)
    fmt.Println(n5)

    // 字符串转换为int类型
    var s4 string = "100"
    var n6, e = strconv.Atoi(s4)
    if e != nil {
        fmt.Println(e)
    }
    fmt.Println(n6)

    // int64转换字符串
    var n7 int64 = 10
    fmt.Println(strconv.FormatInt(n7, 10))

    // 字符串转换为int64
    var s5 string = "100"
    // 10 是 10进制,64 是 64位
    var n8, e2 = strconv.ParseInt(s5, 10, 64)
    if e != nil {
        fmt.Println(e2)
    }
    fmt.Println(n8)

}

3、操作符

package main

import "fmt"

func main() {
    // 算数运算符 : + - * / %
    var c = 5 % 3
    fmt.Println(c)

    // 自增 : ++
    // 自减 : --
    var c2 int
    c2++
    c2--
    fmt.Println(c2)

    // 关系运算符 : >、>=、<、<=、==
    // 逻辑运算符 : &&、||、!
    // 位运算符 : & | ^
    // 移位运算符 : >> <<
    // 赋值运算符 : +=、-=、*=、/=、%=
    // 其他运算符 : & 返回变量存储地址 * 指针变量

}

4、流程控制

package main

import (
    "fmt"
)

func main() {

    // 流程控制语句主要有三类 :
    // 条件语句,if/else

    var score = 78
    if score >= 80 {
        fmt.Println("优秀")
    } else if score >= 60 {
        fmt.Println("及格")
    } else {
        fmt.Println("不及格")
    }

    // switch,从上到下,依次匹配,一旦匹配后,就停止,否则都default
    var grade = "A"
    switch grade {
    case "A":
        fmt.Println("优秀")
    case "B":
        fmt.Println("良好")
    case "C":
        fmt.Println("及格")
    default:
        fmt.Println("不及格")
    }

    // 循环语句for
    // for 出事语句;是否进入循环的条件语句,布尔类型;每次循环语句结束
    for i := 0; i <= 10; i++ {
        fmt.Println(i)
    }

    var j = 10
    for ; j >= 0; j-- { // 省略初始语句
        fmt.Println(j)
    }

    var k = 10
    for k >= 0 { // 省略初始和结束语句
        fmt.Println(k)
        k--
    }

    var l = 0
    for { // 不带条件的结构
        if l > 10 {
            break // 终止for循环
        }

        if l == 4 {
            l++
            continue // 跳过本次循环
        }
        fmt.Println(l)
        l++
    }
    // 循环嵌套
    for i := 1; i < 10; i++ {
        for j := 1; j <= i; j++ {
            fmt.Printf("%d x %d = %d ", j, i, j*i)
        }
        fmt.Println("")
    }
}
 

5、数组

package main

import (
    "fmt"
)

func main() {
    // 数组,是一组固定长度的、同类元素的集合
    // 固定长度,声明时指定长度,长度不可改变
    // 同类元素,数组中元素必须是同一类型
    // 数组声明
    var animals [3]string
    // 数组赋值
    // 赋值采用索引(下标),索引从0开始,最后一个索引是长度-1
    animals[0] = "cat" // 给第一个元素赋值
    animals[1] = "cattle"
    animals[2] = "sheep"
    fmt.Println(animals)

    // 声明并赋值
    var numbers = [3]int{1, 2, 3}
    fmt.Println(numbers)

    // 声明时指定索引赋值
    var numbers1 = [3]int{1: 2, 3}
    fmt.Println(numbers1)

    // 声明不指定长度,自动推算其长度
    var cities = [...]string{"北京", "上海", "广州"}
    fmt.Println(len(cities))
    fmt.Println(cities)

    // 数组遍历(循环)
    for i := 0; i < len(cities); i++ {
        fmt.Println(cities[i])
    }

    // 使用for range遍历
    for index, city := range cities {
        fmt.Println(index, city)
    }

    // 求一组数据的和
    var nums = [5]int{1, 2, 3, 4, 5}
    var result int
    for _, num := range nums {
        result += num
    }
    fmt.Println(result)
}
 

6、切片

package main

import (
    "fmt"
)

func main() {
    // 切片,是一组可变长度的,同类元素的集合
    // 与数组相比切片的长度是不固定的
    // 可以追加元素,在追加时可能使切片的容量增大
    // 切片的声明
    var slice []int
    // 切片在使用前必须初始化,未初始化的切片的值为nil
    fmt.Println(slice == nil)

    // 使用make()进行初始化
    slice = make([]int, 3, 5)
    fmt.Println(slice, len(slice), cap(slice))

    // 初始化赋值
    slice = []int{1, 2, 3, 4}
    fmt.Println(slice, len(slice), cap(slice))

    // 对数组进行切片
    var a = []int{4, 5, 6, 7, 8}
    // slice = a[起始索引:截止索引]
    slice = a[1:3]
    fmt.Println(slice)

    // 切片的赋值,指定索引赋值
    slice[1] = 100
    fmt.Println(slice[1]) // 打印第二个元素的值

    // 切片的追加
    slice = append(slice, 200, 300, 400)
    fmt.Println(slice)

    // 批量追加
    slice1 := []int{500, 600}
    // slice = append(slice, 500, 600)
    slice = append(slice, slice1...)
    fmt.Println(slice)

    // 对切片进行切片,截取
    fmt.Println(slice[3:5])

    // 切片的遍历,和数组的遍历方法一样
    for i := 0; i < len(slice); i++ {
        fmt.Println(i, slice[i])
    }

    for i, value := range slice {
        fmt.Println(i, value)
    }

}

7、map

package main

import (
    "fmt"
)

func main() {
    // map(字典) 是一组无序的键值对(key-value)的集合
    // 声明map类型的变量
    var country map[string]string
    // 使用前必须初始化,未初始化的map的值为nil
    fmt.Println(country == nil)
    //country = make(map[string]string)
    country = map[string]string{}
    // map赋值
    country["italy"] = "意大利"
    fmt.Println(country)
    fmt.Println(country["italy"])

    // 判断key是否存在
    val, ok := country["italy"]
    fmt.Println(val, ok)

    // 删除某个key-value
    country["japan"] = "日本"
    fmt.Println(country)
    delete(country, "japan")
    fmt.Println(country)

    // map遍历
    country["india"] = "印度"
    country["france"] = "法国"
    for key, val := range country {
        fmt.Println(key, val)
    }
}

8、函数

package main

import (
    "fmt"
    "math"
    "time"
)

/**
func 函数名称(传入的参数和类型)(返回的参数和类型) {
    函数体代码块
}
*/

// 函数的定义
// sum求两数之和
func sum(x int, y int) int {
    return x + y
}

// 函数可以没有传入参数,也可以没有返回参数
// printTime 打印当前时间
func printTime() {
    fmt.Println(time.Now())
}

const pi float64 = 3.14

// 函数的多值返回,函数的返回值可有多个
// calcCircle 计算园的周长和面积
func calcCircle(r float64) (float64, float64) {
    l := 2 * pi * r
    s := pi * math.Pow(r, 2)
    return l, s
}

func calcCircle2(r float64) (l float64, s float64) {
    l = 2 * pi * r
    s = pi * math.Pow(r, 2)
    return
}

// 可变数量参数的函数,函数的参数的个数不固定
// sumAny 计算任意个数的参数的和
func sumAny(numbers ...int) int {
    res := 0
    for _, v := range numbers {
        res += v
    }
    return res
}

// 匿名函数,就是没有命名的函数
func anonymousFun() {
    var x int = 1
    var y int = 2
    var sum = func(x, y int) int {
        return x + y
    }
    fmt.Println(sum(x, y))
}

// 闭包 func(int, int) int
func colsureFunc() func(int, int) int {
    return func(x int, y int) int {
        return x + y
    }
}
func main() {
    // 函数,是一段执行指定任务的代码块
    // 是一段独立的,完成的,可重复使用的代码片段的定义

    fmt.Println(sum(5, 3))
    fmt.Println(calcCircle(10))
    fmt.Println(calcCircle2(10))
    fmt.Println(sumAny(1, 2, 3, 4, 5))
    anonymousFun()

    sum := colsureFunc()
    fmt.Println(sum(1, 2))

}

9、结构体

package main

import "fmt"

// 结构体是一些相同或不同类型的元素构成的数据集合
// Go语言中没有其他语言中类(class)的概念
// 但可以通过结构体实现面向对象编程

// 结构体的定义
/**
type 类型名称 struct {
    属性字段名称 属性类型
}
*/

type Student struct {
    Name  string
    Age   int
    City  string
    Hobby string
}

// 结构体方法
func (s Student) ShowIntroduction() string {
    return fmt.Sprintf("%s, %d岁,来自%s,爱好是%s", s.Name, s.Age, s.City, s.Hobby)
}

func main() {

    alice := Student{
        Name:  "alice",
        Age:   18,
        City:  "洛杉矶",
        Hobby: "打篮球",
    }
    // 结构体属性的访问及赋值
    fmt.Println(alice.Name)
    alice.Age = 19
    fmt.Println(alice.Age)

    fmt.Println(alice.ShowIntroduction())
}

10、接口

package main

import (
    "fmt"
    "math"
)

// 接口是一组方法定义的集合,如果一个类型实现了这些方法,那么就实现了这个接口
// 它是将所有具有共性的方法定义在一起,这些方法只有函数定义,没有具体实现

type rect struct {
    width  float64 // 宽
    height float64 // 高
}

func (r rect) area() float64 {
    return r.width * r.height
}

type circle struct {
    radius float64 // radius 半径
}

func (c circle) area() float64 {
    return math.Pi * c.radius * c.radius
}

type shape interface {
    area() float64
}

// printArea 函数的参数可以是接口类型
func printArea(s shape) {
    fmt.Println(s.area())
}

func main() {
    var shp shape
    shp = rect{width: 2, height: 3}
    fmt.Println(shp.area())

    shp = circle{radius: 2}
    fmt.Println(shp.area())

    // 其实这里就是将子类传递给方法中的参数(参数是父类)
    printArea(shp)

    // 空接口
    // 空接口是实现了0个方法的接口,空接口可以保存任意类型变量
    var i interface{}
    i = 1
    fmt.Println(i)
    i = "Hello World!"
    fmt.Println(i)

    // 说明key可以是string类型,value是任意类型
    var m = map[string]interface{}{
        "v1": 1,
        "v2": 2,
    }
    fmt.Println(m)

    // 类型断言
    var s interface{} = "str"
    fmt.Println(s.(string))
    v1, ok := s.(string)
    fmt.Println(v1, ok)

    v2, ok := s.(int)
    fmt.Println(v2, ok)
}

11、指针

package main

import "fmt"

func increment(i int) {
    i++
}

func increment2(i *int) {
    *i++
}

// 指针,是一种数据类型,用来表示数据的内存地址
func main() {

    // 声明一个int类型的变量,值为10
    var a int = 10
    // 通过&获取变量a的内存地址
    fmt.Println(&a)
    fmt.Printf("%T\n", &a)

    // 声明一个int类型的指针变量
    var b *int
    b = &a
    fmt.Println(b)
    // 通过*获取指针指向的内存地址上的值
    fmt.Println(*b)

    // 修改指针地址的值
    *b = 11
    fmt.Println(*b)
    fmt.Println(a)

    var i int = 10
    increment(i)
    fmt.Println(i)

    increment2(&i)
    fmt.Println(i)
}

12、异常

package main

import (
    "errors"
    "fmt"
)

// Go语言中的错误,通常是将可以预期的错误(error)作为返回值返回
// 并非其他语言中的throw try/catch的方式

// 面对于非预期的错误,通常称之为异常,发生异常说明程序中存在bug或者不可控的问题
// 比如 数组越界,空指针等

// 两数相除
func div(a, b int) (int, error) {
    if b == 0 {
        return 0, errors.New("divisor cannot be zero.")
    }
    return a / b, nil
}

func div2(a, b int) (int, error) {
    if b == 0 {
        return 0, &DivError{}
    }
    return a / b, nil
}

func div3(a, b int) (int, error) {
    defer func() { // 使用defer,该方法会最后执行
        // 使用recover捕获异常,保证程序不会崩溃退出,能正常继续执行
        if err := recover(); err != nil {
            fmt.Println("捕获异常 :", err)
        }

    }()
    if b == 0 {
        panic("这里发生了异常")
    }
    return a / b, nil
}

// DivError 自定义错误类型
type DivError struct {
}

func (e *DivError) Error() string {
    return "divisor cannot be zero"
}
func main() {

    res, err := div(10, 0)
    fmt.Println(res, err)

    res2, err := div(10, 5)
    fmt.Println(res2, err)

    res3, err := div2(10, 0)
    fmt.Println(res3, err)

    res4, err := div3(10, 0)
    fmt.Println(res4, err)
}
 

13、打包

go get -v github.com/go-sql-driver/mysql

package main

// go mod tidy
// go mod vendor
import (
    "database/sql"
    "fmt"
    _ "github.com/go-sql-driver/mysql"
    "log"
)

func main() {
    db, err := sql.Open("mysql", "root:root@123@tcp(127.0.0.1:3306)/journey")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println("connected.")
    defer db.Close()
}

14、gin

go get -v github.com/gin-gonic/[email protected]

package main

import "github.com/gin-gonic/gin"

func main() {
    r := gin.Default()
    r.GET("/ping", func(c *gin.Context) {
        c.JSON(200, gin.H{
            "message": "pong",
        })
    })

    r.GET("/test", func(context *gin.Context) {
        context.JSON(200, gin.H{
            "message": "test",
        })
    })

    r.Run() // 监听并在 0.0.0.0:8080 上启动服务
}

15、JSON和结构体互相转换

package main

import (
    "encoding/json"
    "fmt"
)

// Golang encoding/json包基于Struct类型的Tag特性,提供了JSON解析方法。这里正是反射的常见用法之一
//包中最重要的两个函数如下:
//* Marshal(v interface{}) ([]byte, error) : 将 Go 数据类型软换为 JSON 格式数据
//* Unmarshal(data []byte, v interface{}):将 JSON 格式数据转换为 Go 数据类型

//针对 Tag 的写法,encoding/json 作了如下约定:
//json:"Foo" : Marshal 时将结构体该字段名以 “Foo” 名输出,Unmarshal 时将 JSON 相应字段赋值给结构体字段
//json:"-": 无论 Marshal 和 Unmarshal 都忽略该字段
//json:",omitempty": 仅用于 Marshal,如果结构体该字段为空,则忽略该字段。注意前面的逗号要保留
//json:"Foo,omitempty": 组合写法,含义同上

// 关于 omitempty,要注意它会屏蔽一切的初始值和空值。比如,整型,即便赋值为 0,其也不会被转换为 JSON 字符串。 有时,你希望结构体存储的是某个状态,而状态中正好有个值为 “零”,这时就要小心了


func main() {
    str := `{"ServerName": "Shanghai_VPN", "ServerAddr": "127.0.0.1", "ServerOwner": "Rainbow", "other": "You can see me!"}`
    var s Server
    // 其实就是把字符串转换为byte数组 []byte(str)
    json.Unmarshal([]byte(str), &s)

    fmt.Printf("s.ServerName: %s\n", s.ServerName)
    fmt.Printf("s.ServerIP: %s\n", s.ServerIP)
    fmt.Printf("s.ServerOwner: %s\n", s.ServerOwner)
    fmt.Printf("s.other: %s\n", s.Other)

    jsonStr, _ := json.Marshal(s)
    fmt.Printf("Marshal: %s", jsonStr)
}
inline这个属性,应该是默认的,为什么这么说,这个属性用作嵌套结构体内,消除嵌套结构体的层级关系,将其转为一个层级

比如 :
type TestField struct {
    Key string `json:"key"`
}

//type TopField struct {
//    T TestField `json:",omitempty"`
//    TestA     string `json:"test_a"`
//    TestB     string `json:"test_b"`
//}

type TopField struct {
    TestField `json:",omitempty,inline"`
    TestA     string `json:"test_a"`
    TestB     string `json:"test_b"`
}

func main() {
    one := TopField{
        TestField: TestField{
            Key: "12321",
        },
        TestA: "a",
        TestB: "b",
    }

    marshal, _ := json.Marshal(one)
    fmt.Println(string(marshal))
}

// {"key":"12321","test_a":"a","test_b":"b"}

你可能感兴趣的:(gogoogle)