Alvin Liu

  • Home
  • About
  • Privacy Policy
  1. Main page
  2. Golang
  3. Main content

Golang知识汇总

2023-02-28 1466hotness 0likes 0comments

Golang简介

环境安装

源码下载

http://golang.org/dl

安装

直接解压后放在 /usr/local/
编译器是 /usr/local/go/bin/go
源码在 /usr/local/go/src

配置环境变量

编辑 .bashrc

export GOROOT=/usr/local/go
export GOPATH=$HOME/go
export PATH=$PATH:$GOROOT/bin:$GOPATH/bin

运行go version 检查安装是否成功
GOPATH 代表开发代码的默认路径, 后面有其他管理工具

IDE

  1. VS Code
  2. Goland (收费)

Golang的优势

  1. 直接编译成机器码,性能好,类C级性能
  2. 不依赖其它库,内连接
  3. 直接运行不需部署
  4. 静态语言
  5. 底层支持并发, 操作简单性能高
  6. runtime系统调用机制
  7. 高效垃圾回收
  8. 丰富的标准库
  9. 内嵌C语言
  10. 跨平台

Golang适用产品

云原生

docker, k8s, etcd, consul, cloudflare

数据库

tidb, influxdb, cockroachdb

微服务

go-kit, micro, typhon

区块链

以太坊, hyperledger

Golang的不足

  • 包管理不够完善
  • 没有泛型(Generlization)
  • 没有Try Catch, 只有Error

Golang 特色语法

Hello World

src就是workspace, 里面的每一个文件夹就是一个工程
mkdir firstGolang

package main // main function

import (
    "fmt"
    "time"
)

//main function
func main(){
    fmt.Println("Hello Go!")
    time.Sleep(1 * time.Second)
    fmt.Println("Hello Go!")
}

Golang定义变量

//推算类型, 冒等, 不能用来定义全局变量
name := "alvin"

//显式指定
var name string = "alvin"

//var定义推断类型
var name = "alvin"

//默认值a=0
var a int

//常量累加赋值, iota每次换行就会加一
//iota只能在const()内使用, 类似Excel
const(
  BEIJING = iota    //1
  SHANGHAI          //2
  SHENHEN           //3
)

多返回值

//返回值必须有实际类型
//带名称返回值在复制前已经有默认值
func foo3(a string, b int) (r1, r2 int) {
  //命名返回值不用在return里显式给出
  return   // 0, 0
}

func foo4(a string, b int) (int, int) {
  //匿名返回值就是正常用法
  return 1, 2;
}

func main() {
  c, d := foo3("a", 10)
  fmt.Println("c = ", c, "d = ", d)
  e, f := foo4("a", 10)
  fmt.Println("e = ", c, "f = ", d)
}

导入包

package main

import "lib1"

//别名导入, 用别名使用
import mylib "lib3"

//匿名导入, 不使用不会报错, 进调用包的init()方法
import _ "lib2"

//静态导入方法, 容易造成命名空间冲突
import . "lib4"

func main() {
  lib1.Lib1Func()
  mylib.Test()

  //直接使用lib4方法
  Lib4Func()
}

//构造函数
func init() {

}

---
package lib1
func Lib1Func(){

}

包搜索路径 $GOPATH
函数名首字母大写代表公有函数, 首字母小写代表私有函数
导入包必须使用

指针 (不常用)

就是直接传实参的内存引用, 接收函数通过指针对参数的修改在函数外部也生效

//接旨
func changeVal(p *int){
    //修改内存
    *p = 10;
}

func main() {
    var a int = 1;
    //传旨
    changeVal(&a)
    fmt.Println("a=",a)
}

//二级指针, 指向指针的指针
ver pp **int

defer(finalizer)

defer类似类的finalizer和try里面的finally函数, 在声明代码体的最后执行
defer在return之后执行, 这一点是和Java不同的
多条defer后添加的先执行

func main() {
    //finalizer
    defer fmt.Println("Main end 1")
    defer fmt.Println("Main end 2") //后入先出, end 2 先执行

    fmt.Println("main::1")
    fmt.Println("main::2")

    testReturn();  //输出 return called, defer called. 因为defer在函数花括号结束处运行
}

func testReturn() int {
    defer fmt.Println("defer called")

    return returnFunc();
    //defer在这里运行
}

func returnFunc() int {
    fmt.Println("return called");
    return 0;
}

数组

数组的定义

定长数组

缺点

  1. 作为方法入参也需要指定数组长度, 非常不灵活
  2. 作为方法入参是值传递
//定长数组
var myArray [10]int

//初始化值
var myArray2 [10]int{1,2,3,4}

for i := 0; i<10; i++ {
  fmt.Println(myArray[i])
}

for index, value := range myArray2 {
  fmt.Println("index = ", index, ", value = ", value)
}

//定长数组的长度是类型的一部分
//如果要使用定长数组入参要带数组长度
func func10(array [10]int){

}
动态数组(切片)

长度不限
引用传递, 函数内直接修改

var myArray1 = []int{}
var myArray2 = []int{1,2,3,4}

数组遍历

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

//range遍历(foreach)
for index, value := range myArray {
  fmt.Println("index = ", index, "value = ", value)
}

//range遍历放弃index, 声明为匿名变量
for _, value := range myArray {
  fmt.Println("value = ", value)
}

切片

声明方式

切片的长度len表示切片可用长度
切片的容量cap表示内存中保留的总空间

slace1 := []int

var slace2 []int
slace2 = make([]int, 3)

//常用方式
slace3 := make([]int, 3)

//声明额外空间
slace4 := make([]int, 3, 5)

//切片的长度和总空间, 3, 5
fmt.Printf("lens = ", len(slace4), "cap = ", cap(slace4))
切片追加元素
//会使用总空间内额外的部分. 如果总空间不够了, 会创建一个2 * len的空间
slace4 = append(slace4, 1);
截取切片

切片截取的是引用, 修改会影响原始数据

slace1 := []int{1,2,3}
slace2 = slace1[:2] //0~2
深copy
slace3 := make([]int, 3)

//slace1复制到slace3
copy(slace3, slace1)

Map

声明Map
//key和value都是string
//var myMap map[string]string

//第一种方式 创建10个存储空间
myMap1 := make(map[string]string, 10)

myMap1["one"] = "java"
myMap1["two"] = "c++"

fmt.Println(myMap1)

//第二种方式 创建map使用默认存储空间, 不够会自动追加
myMap2 := make(map[string]string)

myMap2["one"] = "java"
myMap2["two"] = "c++"

fmt.Println(myMap2)

//第三种方式 创建同时赋值
myMap3 := map[string]string{
  "one": "java",
  "two": "c++",
}

fmt.Println(myMap3)
遍历Map
cityMap := make(map[string]string)

//添加
cityMap["China"] = "Beijing"
cityMap["Japan"] = "Tokyo"

//遍历
for key, value := range cityMap {
  fmt.Println("key=", key, " value=", value)
}

//删除
delete(cityMap, "China")

//修改
cityMap["Japan"] = "Shanghai"

Struct结构体

结构体对象是值传递的, 如果想在函数内修改结构体对象的值, 需要使用指针赋值

//首字母大写才能被其它包访问
type Book struct {
    title string
    auth  string
}

//把this绑定为当前Book对象的指针. 等于这个方法就是操作Book对象内数据的
//其实就是为Book结构体定义了一个方法
func (this *Book) GetName() string {
  return this.title
}

func (this *Book) SetName(newName string){
  this.title = newName
}

func main() {
    var book1 Book
    book1.title = "Golang"
    book1.auth = "Alvin"

    fmt.Println(book1)
  fmt.Println(book1.GetName());
  book1.SetName("Python")
  fmt.Println(book1.GetName());
}

继承

type Human struct {
    name string
    sex string
}

func (this *Human) Eat() {
    fmt.Println("Human Eat")
}

func (this *Human) Walk() {
    fmt.Println("Human Walk")
}

type SuperMan struct {
    Human //继承了Human

    skill string
}

func (this *SuperMan) Eat() {
    fmt.Println("SuperMan Eat")
}

func (this *SuperMan) Fight() {
    fmt.Println("SuperMan Fight by", this.skill)
}

func main() {
//对象初始化赋值
    // hu0 := Human{}
    // var hu00 Human
    hu := Human{sex:"female", name:"zhang"}
    su := SuperMan{Human{"liu", "male"}, "Laser"}

    hu.Eat()
    su.Eat()
    su.skill = "Laser"

    su.Fight()
}

Interface

接口本身是一个指针, 接收对象实例时只能传入指针&

type AnimalIF interface {
    Sleep()
    GetColor() string
    GetType() string
}

type Cat struct {
  color string
}

//不用this也要定义, 否则方法不能绑定到类上
func (this *Cat) Sleep(){
  fmt.Println("Cat sleep")
}

func (this *Cat) GetColor() string {
  return this.color;
}

func (this *Cat) GetType() string {
  return "Cat";
}

//Dog实现省略

func main() {
    var animal AnimalIF
  //必须用指针
    animal = &Cat{"white"}
  showAnimal(animal)
}

//传入指针完成多态
func showAnimal(animal AnimalIF) {
    fmt.Println(animal.GetColor())
    animal.Sleep()
}

元类/类型判断 interface{} (Object类, 万能接口, instanceof)

//元类型 Object
func acceptAny(arg interface{}){
    fmt.Println(arg)
}

func acceptInt(arg interface{}) {
  //类型判断 instanceof
    value, ok := arg.(int)
    if ok {
        fmt.Println(value)  //Same as arg
    }
}

func main() {
    acceptAny(123)
    acceptAny("abc")

    acceptInt(123)
    acceptInt("abc")
}

变量的内部结构

变量

  1. type
  2. static type (基本类型 int, string)
  3. concrete type (接口类型, 自定义类)
  4. value
func main() {
  var a string
  //pari<type:string, value:"abc">
  a = "abc"

  var allType interface{}
  allType = a
  //读取实例pair里保存的type和value
  value, ok := a.(string)

}

类型转换

//接口1
type Writer interface {
  Write()
}

//接口2
type Reader interface {
  Read()
}

//实现类
type Book struct {
  name string;
}

//实现接口1
func (this *Book) Write() {
  fmt.Println("Write book")
}

//实现接口2
func (this *Book) Read() {
  fmt.Println("Read book")
}

func main() {
    //创建实例, 保存指针
    book := &Book{}

    //把指针赋值给接口1
    var w Writer = book
  //调用接口1方法
    w.Write()

  //强转成接口2, (需要实现接口2)
    var r Reader = w.(Reader)
  //调用接口2方法
    r.Read()
}

反射

import (
  "fmt"
  "reflect"
)

func main() {
  var num float64 = 1.234
  reflectNum(num)
}

//接收任意类型
func reflectNum(arg interface{}) {
  //读取类型
  fmt.Println("type: ", reflect.TypeOf(arg))
  //读取值
  fmt.Println("value: ", reflect.ValueOf(arg))
  //type: float64
  //value: 1.234
}

type User struct {
  Id int
  Name string
  Age int
}

func (this *User) Call() {
  fmt.Println("User is :", this)
}

func main() {
    user := User{1, "Alvin", 10}
    user.Call()
    reflectUser(user)
}

//反射获取对象属性
func reflectUser(input interface{}){
  //获取对象的类型
    inputType := reflect.TypeOf(input)
    fmt.Println("Type is:", inputType)

    inputValue := reflect.ValueOf(input)
    fmt.Println("inputValue is:", inputValue)

  //获取对象的属性, 并遍历
    for i := 0; i < inputType.NumField(); i++ {
        curField := inputType.Field(i)
        fmt.Println("Field Type:", curField.Type)
        fmt.Println("Field Name:", curField.Name)
        fmt.Println("Field Value:", inputValue.Field(i))
    }
}

反射解析结构体标签Tag

type Resume struct {
    //tag和值之间一个冒号,不能有空格. 两个tag之间才是空格
    Name string `info:"name" doc:"我的名字"` //后面的是Tag, 给反射用的
    Sex  string `info:"sex"`
}


func findTag(str interface{}) {
    t := reflect.TypeOf(str).Elem()

    for i := 0; i < t.NumField(); i++ {
        infoTag := t.Field(i).Tag.Get("info")
        docTag := t.Field(i).Tag.Get("info")
        fmt.Println("info:", infoTag, "doc:", docTag)
    }
}

func main() {
    var re Resume
    findTag(&re)
}

使用结构体Tag解析json文件实例

import (
    "encoding/json"
    "fmt"
)

type Movie struct {
    Title string `json:title` //用tag指定json字段名称
    Year  int    `json:year`
    Price int    `json:rmb`
}

func main() {
    movie := Movie{"喜剧之王", 2000, 38}

    //转json字符串
    jsonStr, err := json.Marshal(movie)
    if err != nil {
        fmt.Println("Fail to convert json:", err)
        return
    }
  //必须用printf才能格式化字符串
    fmt.Printf("json str:%s\n", jsonStr)

  //json还原对象
    newMovie := Movie{}
    err = json.Unmarshal(jsonStr, &newMovie)
    fmt.Println(newMovie)
}

Golang 高级内容

Goroutine

总结自刘丹冰的视频 https://www.bilibili.com/video/BV1gf4y1r79E

This article is licensed with Creative Commons Attribution-NonCommercial-No Derivatives 4.0 International License
Tag: Golang
Last updated:2023-03-01

Alvin Liu

Software Developer in Toronto

Like

Comments

razz evil exclaim smile redface biggrin eek confused idea lol mad twisted rolleyes wink cool arrow neutral cry mrgreen drooling persevering
Cancel

COPYRIGHT © 2024 Alvin Liu. alvingoodliu@[email protected] ALL RIGHTS RESERVED.

Theme Made By Seaton Jiang