当前位置: 首页 > article >正文

8小时理解go - 基本语法

前言

需求是:有一定的后端其他语言基础(C/C++、Java、Python、PHP等),想要快速了解Golang,可以看B站的这个视频8小时转职Golang工程师

确实讲的很好,对有一定基础的入门golang有很大的帮助,需要代码可以gitee下载

环境

  • Winodws10
  • Go SDK 1.16.2
  • 开发工具:GoLand2020

在电脑安装好Go SDK、设置好系统环境变量
在这里插入图片描述

cmd通过go env查看go环境

在这里插入图片描述

GOPATH或者GOROOT文件夹下的src目录是放go源文件,我们写的代码都要在src文件夹下,不然导不了第三方包

我选择的是放GOPATH下,但是可能会读取不了GOPATH下的第三方包,需要输入命令:go env -w GO111MODULE=off,go命令行将不会使用新的module功能,这是GOPATH模式(后续选择GOMODULE模式)

我的项目结构:
在这里插入图片描述

用GoLand打开src目录,在其下新建文件夹hello,开始hello world

初见Golang

从hello world开始

package mainimport "fmt"func main() {fmt.Println("hello world")
}

golang可执行程序可以分解为一个个包,其中必须存在main包,main包必须存在main函数,程序的执行本质上是执行main函数

执行命令是: go run hello.go,当然用GoLand的执行也行
在这里插入图片描述

变量

声明局部变量

  1. var a int 声明一个变量,默认值为0
  2. var b int = 100 声明一个变量,赋值100
  3. var c =100 省去数据类型,值自动匹配当前变量的数据类型
  4. d := 100 省去var关键字,直接自动匹配(常用)

声明全局变量

除了4,其他3种都可以

  1. var a int 声明一个变量,默认值为0
  2. var b int = 100 声明一个变量,赋值100
  3. var c =100 省去数据类型,值自动匹配当前变量的数据类型

多变量声明

  1. 单行写法
var xx,yy int = 100,200
  1. 多行写法
// 多行多变量声明var (vv int = 100jj bool = true)

数据类型

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

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

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

  4. 派生类型:
    包括:

    (a) 指针类型(Pointer)
    (b) 数组类型
    © 结构化类型(struct)
    (d) Channel 类型
    (e) 函数类型
    (f) 切片类型
    (g) 接口类型(interface)
    (h) Map 类型

简单案例

package mainimport "fmt"// 声明全局变量
var gA int = 100
var gB = 200
//gC := 300 不支持全局变量func main()  {// int默认值 = 0var a intfmt.Println("a = ",a)var b int = 100fmt.Println("b = ",b)// 初始化可以省略数据变量,自动匹配当前变量的数据类型var c = 200fmt.Println("c = ",c)fmt.Printf("type of c = %T\n",c)var bb string = "abcd"var cc = "abcd"fmt.Printf("bb = %s,type of bb = %T\n",bb,bb)fmt.Printf("cc = %s,type of cc = %T\n",cc,cc)// 省略var关键字,直接自动匹配 (常用方法)e := 100fmt.Printf("e = %T\n",e)fmt.Println("e = ",e)fmt.Println("gA = ",gA," gB = ",gB)//fmt.Println("gC = ",gC)// 声明多个变量var xx,yy int = 100,200fmt.Println("xx = ",xx," yy = ",yy)var kk,ll = 200,"ll"fmt.Println("kk = ",kk," ll = ",ll)// 多行多变量声明var (vv int = 100jj bool = true)fmt.Println("vv = ",vv," jj = ",jj)
}

常量

常量,也就是不可变的变量,在go语言中用const关键字修饰

const length int = 10const (BeiJing = 0ShangHai = 1ShenZhen = 2
)

iota关键字

如下的枚举类型,可以用iota来简化

const (BeiJing = 0ShangHai = 1ShenZhen = 2
)

iota关键字和枚举数有关,每行会累加1,第一行iota默认为0 (iota 只能出现在const中)

const (BeiJing = iotaShangHaiShenZhen
)

简单案例

package mainimport "fmt"const (// iota关键字,每行会累加1,第一行iota默认为0 (iota 只能出现在const中)BeiJing = iotaShangHaiShenZhen/*BeiJing = 0ShangHai = 1ShenZhen = 2*/
)const (a,b = iota + 1,iota + 2 // iota = 0, a=iota + 1= 1,b=iota + 2=2c,d					 	// iota = 1, c=iota + 1= 2,d=iota + 2=3e,f 					// iota = 2g,h = iota * 2,iota * 3 // iota = 3, g= iota * 2=6,h= iota * 3= 9i,j						// iota = 4, i= iota * 2= 8,j= iota * 3 = 12
)func main()  {// 常量,不允许被修改const length int = 10fmt.Println("length = ",length)fmt.Println("BeiJin = ",BeiJing)fmt.Println("ShangHai = ",ShangHai)fmt.Println("ShenZhen = ",ShenZhen)fmt.Println("a = ",a,"b = ",b,"c = ",c,"d = ",d,"e = ",e,"f = ",f,"g = ",g,"h = ",h,"i = ",i,"j = ",j)
}

函数

函数func,是程序的基本代码块,程序的执行本质上是执行main函数

函数结构

func (recvarg type) funcname( [parameter list] ) [return_types]  { // 函数体
}

函数的结构:

  • 关键字 func 用于定义一个函数
  • (recvarg type)用于指定此函数可用于的类型,如果设置了,当前函数就是该类型的成员方法(类似于Java中类的方法 - 详情看后续构造体)
  • funcname 为函数名
  • [parameter list]参数列表,函数为值传递(如果需要地址传递,参数需设置为指针)
  • [return_types] 为函数命名返回值列表,可以只有类型而不命名,可以有 0 个或者多个返回值
  • 剩下的为函数体,左花括号必须在同一行,不能新起一行
func foo1(a string,b int)int {fmt.Println("a = ",a)fmt.Println("b = ",b)c := 100return c
}func (this *Hero) Show(){fmt.Println("Name = ",this.Name)
}

函数返回值

go语言函数可以有多个返回值

  1. 无返回值函数
func foo(a string){fmt.Println("a = ",a)
}
  1. 一个返回值
func foo1(a string,b int)int {fmt.Println("a = ",a)fmt.Println("b = ",b)c := 100return c
}
  1. 多返回值:有三种形式
// 返回多个返回值,匿名
func foo2(a string,b int) (int,int) {fmt.Println("a = ",a)fmt.Println("b = ",b)c := 100return c,c*3
}
// 返回多个返回值,有形参名
func foo3(a string,b int) (r1 int,r2 int) {fmt.Println("a = ",a)fmt.Println("b = ",b)// 可以通过直接赋值r1 = 100r2 = 200return
}
// 同类型返回值可以缩写
func foo4(a string,b int) (r1 , r2 int){fmt.Println("a = ",a)fmt.Println("b = ",b)r1 = 100r2 = 200return
}

import导包

当我们想要使用外部函数,通过import关键字导入包

注:Go中函数名/变量名 首字母大写,表示所有包都可访问,首字母小写表示包私有

// 导入单个包
import "fmt"// 导入多个包
import ("init/lib1""init/lib2"
)

fmt包是SDK提供的,Printf等函数在print.go文件里

在这里插入图片描述

别名

当import的包名过长,可以取别名

  • 匿名:导入了包,但是无法使用当前包的方法,会执行包内部的init方法
import (// _ 表示匿名别名,导入但并不使用_ "init/lib1"
)
  • 别名
import (// _ 表示匿名别名,导入但并不使用_ "init/lib1"// mylib2表示别名mylib2 "init/lib2
)
  • 将包中的方法导入到当前包,可以直接调用方法,而不需要fmt.Printf这样包名.函数名 调用,尽量别用,容易歧义,容易导致多个函数同名报错
import (// _ 表示匿名别名,导入但并不使用_ "init/lib1"// mylib2表示别名//mylib2 "init/lib2"// .表示导入到当前包中. "init/lib2"
)
案例

在这里插入图片描述

lib1.go

package lib1import "fmt"func Lib1Test()  {fmt.Println("lib1 test")
}

lib2.go

package lib2
import "fmt"func Lib2Test()  {fmt.Println("lib2 test")
}

main.go

package mainimport ("init/lib1""init/lib2"
)
func main()  {lib1.Lib1Test()lib2.Lib2Test()
}

init函数

Go程序函数启动顺序:
在这里插入图片描述

如果有多个init函数呢?

go允许定义多个init()函数,从上往下按照定义顺序执行

package mainimport "fmt"func init(){fmt.Println("init 1")
}func init()  {fmt.Println("init 2")
}func init()  {fmt.Println("init 3")
}
func init()  {fmt.Println("init 4")
}func main() {fmt.Printf("main")
}

在这里插入图片描述

指针

在go中,函数都是值传递

在这里插入图片描述

可以使用指针地址传递,写法:函数参数p *int,函数传入指针&a

package mainimport "fmt"func change(p *int)  {*p = 10
}func main(){a := 1// 指针传递change(&a)fmt.Println("a = ",a)
}

在这里插入图片描述

defer

defer关键字:可以使函数或者操作压栈,当前函数生命周期结束时,依次出栈

defer在return执行完后

package mainimport "fmt"func foo1() int {fmt.Println("func defer")return 0
}
func foo2() int {fmt.Println("func return")return 1
}func re() int {defer foo1()return foo2()
}func main(){re()// 写入defer关键字,通过压栈实现,当前函数main函数生命周期结束时,依次出栈defer fmt.Println("main end1")defer fmt.Println("main end2")fmt.Println("main :: hello go 1")fmt.Println("main :: hello go 2")
}

在这里插入图片描述

切片slice

slice,也就是动态数组

数组

  • 数组的长度是固定的,在传参时要严格匹配数组类型
    注:int[4],int[10]等不同长度的数组是 不同数组类型

  • 数组作为参数是值传递

定义数组:

  • var myArr1 [10]int 固定长度数组,初始值为0
  • myArr2 := [10]int{1,2,3,4}
package mainimport "fmt"func printArr(myArr [4]int)  {myArr[3] = 100for index,value := range myArr{fmt.Println("index = ",index," value = ",value)}
}func main()  {var myArr1 [10]intfor i := 0;i < len(myArr1);i++{fmt.Print(myArr1[i]," ")}myArr2 := [10]int{1,2,3,4}for index,value := range myArr2{fmt.Println("index = ",index," value = ",value)}fmt.Println("====================")myArr3 := [4]int{11,22,33,44}// 数组是值传递,且[4]int和[10]int类型不同//printArr(myArr1)printArr(myArr3)fmt.Println("====================")for index,value := range myArr3{fmt.Println("index = ",index," value = ",value)}}

在这里插入图片描述

动态数组slice

初始化

可以设置一个不限长度的动态数组:

  • myArr1 := []int{1,2,3,4} 开辟4个数据空间的数组,数组类型为[]int
  • var slice1 []int 声明一个动态数组,不分配数据空间,可以后续通过make()函数分配数据空间
  • slice2 := make([]int,3) 声明动态数组并分配数据空间

动态数组是地址传递:

package mainimport "fmt"func printArr(myArr []int)  {// _表示匿名变量for _,value := range myArr{fmt.Println("value = ",value)}// 地址传递myArr[0] = 100
}func main()  {// 动态数组,切片slicemyArr1 := []int{1,2,3,4}fmt.Printf("myArr1 type is %T\n",myArr1)// 检验是值传递还是地址传递printArr(myArr1)fmt.Println("---------------")for _,value := range myArr1{fmt.Println("value = ",value)}fmt.Printf("len = %d ,slice = %v\n",len(myArr1),myArr1)// 声明slice1是一个切片,但并没有给slice1分配空间var slice1 []int// make函数开辟3个空间,初始值是0slice1 = make([]int,3)slice1[0] =100fmt.Printf("len = %d ,slice = %v\n",len(slice1),slice1)// 声明slice2同时分配空间slice2 := make([]int,3)fmt.Printf("len = %d ,slice = %v\n",len(slice2),slice2)
}
扩容

通过append给动态数组追加元素,数组会自动扩容

make() 用来为 slice,map 或 chan 类型分配内存和初始化一个对象(注:只能用在这三种类型上),它有三个参数:

  • 第一个参数是类型
  • 第二个参数是分配的空间
  • 第三个参数是预留分配空间
package mainimport "fmt"func main() {numbers := make([]int,3,5)fmt.Printf("len = %d, cap = %d , slice = %v\n",len(numbers),cap(numbers),numbers)// 向numbers切片追加元素1numbers = append(numbers,1)fmt.Printf("len = %d, cap = %d , slice = %v\n",len(numbers),cap(numbers),numbers)fmt.Println("===============")numbers2 := make([]int,3)fmt.Printf("len = %d, cap = %d , slice = %v\n",len(numbers2),cap(numbers2),numbers2)numbers2 = append(numbers2,1)fmt.Printf("len = %d, cap = %d , slice = %v\n",len(numbers2),cap(numbers2),numbers2)}

在这里插入图片描述

如上,当make预留的空间不够,就会自动扩容,新的数组长度会是原来的两倍

切片截取

如果只需要数组中的一段,可以截取切片s2 := s[1:2]
切片截取也是地址传递

package mainimport "fmt"func main() {s := []int{1,2,3}// 切片截取s1 := s[:1]fmt.Println(s1)s2 := s[1:2]fmt.Println(s2)// 检验,地址传递s[1] = 20fmt.Println(s2)
}

在这里插入图片描述

map

声明map

  • var myMap1 map[string]string,后续通过make函数分配数据空间
  • myMap2 := make(map[string]string) 不确定数据空间大小
  • myMap3 := map[string]string{ "1" : "java", "2" : "c++", "3" : "python", } 直接赋值
package mainimport "fmt"func main() {// 1. ====> 声明mapvar myMap1 map[string]string// 开辟数据空间myMap1 = make(map[string]string,10)myMap1["one"] = "java"myMap1["two"] = "c++"myMap1["three"] = "python"fmt.Println(myMap1)// 2.======> 不确定数据空间大小myMap2 := make(map[string]string)myMap2["1"] = "java"myMap2["2"] = "c++"fmt.Println(myMap2)//3. ======> 直接赋值myMap3 := map[string]string{"1" : "java","2" : "c++","3" : "python",}fmt.Println(myMap3)
}

在这里插入图片描述

增删改查

  • map是地址传递
  • delete() 函数用于删除集合的某个元素,参数为map和其对应的key
package mainimport "fmt"func main() {cityMap := make(map[string]string)cityMap["China"] = "Beijing"cityMap["USA"] = "NewYork"cityMap["England"] = "EG"// 遍历for key,value := range cityMap{fmt.Println("key = ",key," value =",value)}//删除delete(cityMap,"China")// 修改changeMap(cityMap)fmt.Println("===============")// 遍历for key,value := range cityMap{fmt.Println("key = ",key," value =",value)}
}func printMap(cityMap map[string]string){// 遍历for key,value := range cityMap{fmt.Println("key = ",key," value =",value)}
}func changeMap(cityMap map[string]string)  {// 地址传递cityMap["England"] = "London"
}

在这里插入图片描述

面对对象

面对对象是一种思想,可以说go语言是一门有面对对象思想的语言

官网中对于Go是否为一门面向对象的语言这个问题的表述为:

是,也不是.虽然Go语言可以通过定义类型和方法来实现面向对象的设计风格,但是Go是实际上并没有继承这一说法.在Go语言中,interface(接口)这个概念以另外一种角度展现了一种更加易用与通用的设计方法.在Go中,我们可以通过组合,也就是将某个类型放入另外的一个类型中来实现类似继承,让该类型提供有共性但不相同的功能.相比起C++和Java,Go提供了更加通用的定义函数的方法,我们可以指定函数的接受对象(receiver),它可以是任意的类型,包括内建类型,在这里没有任何的限制.


同样的,没有了类型继承,使得Go语言在面向对象编程的方面会显得更加轻量化

封装

不像Java中有类的概念,Go类似于C++,用结构体来表示对象
首字母大小表示改结构体、属性是公有

// 定义一个结构体
// 结构体首字母大写,其他包也可以访问
type Hero struct {// 属性名首字母大写,为公有属性Name stringAd intLevel int
}

通过func (this *Hero)指定函数用与Hero类型,表示成员方法,如果func (this Hero)是值传递

package mainimport "fmt"// 结构体首字母大写,其他包也可以访问
type Hero struct {// 属性名首字母大写,为公有属性Name stringAd intLevel int
}/* // 当前this是调用该方法对象的一个副本
func (this Hero) Show(){fmt.Println("Name = ",this.Name)
}func (this Hero) GetName() string{return this.Name
}func (this Hero) SetName(newName string){this.Name = newName
}*/func (this *Hero) Show(){fmt.Println("Name = ",this.Name)
}func (this *Hero) GetName() string{return this.Name
}// 当前this是调用该方法对象的一个副本
func (this *Hero) SetName(newName string){this.Name = newName
}func main() {hero := Hero{Name: "zhangsan",Ad: 100,Level: 10}hero.Show()hero.SetName("lisi")hero.Show()}

继承、重写

子类就是在结构体中包含父类,继承父类的方法和属性

type Human struct {Name stringSex string
}type SuperMan struct {Human //SuperMan继承Human的方法和属性level int
}

func (this *SuperMan) Eat 方法和父类同名,即重写父类方法

package mainimport "fmt"
/**继承、重写*/
type Human struct {Name stringSex string
}func (this *Human) Eat()  {fmt.Println("Human eat....")
}func (this *Human) Walk()  {fmt.Println("Human walk....")
}type SuperMan struct {Humanlevel int
}// 重写父类eat方法
func (this *SuperMan) Eat(){fmt.Println("SuperMan eat...")
}// 子类新方法
func (this *SuperMan) Fly(){fmt.Println("SuperMan fly...")
}func (this *SuperMan) print(){fmt.Println("name = ",this.Name)fmt.Println("sex = ",this.Sex)fmt.Println("level = ",this.level)
}func main() {h := Human{"zhangsan","man"}h.Eat()h.Walk()// 定义子类对象//superMan := SuperMan{Human{"lisi","woman"},10}var superMan SuperMansuperMan.Name = "wangwu"superMan.level = 30superMan.Sex = "man"// 调用子类重新的Eat方法superMan.Eat()superMan.print()
}

在这里插入图片描述

多态

接口

interface 关键字定义一个接口,本质上是指针

// 本质上是指针
type AnimalIF interface {Sleep()GetColor() stringGetType() string
}

一个结构体如果实现了接口的所有方法,即可以向上转型为该接口类型
结构体Cat

type Cat struct {color string
}func (this *Cat) Sleep(){fmt.Println("Cat is sleep")
}
func (this *Cat) GetColor() string{return this.color
}
func (this *Cat) GetType() string{return "Cat"
}

结构体Cat实现了接口的所有方法,即可以向上转型为AnimalIF

	var animal AnimalIF // 接口的数据类型cat := Cat{"Black"}animal = &cat

完整案例:

package mainimport "fmt"/**
多态*/// 本质上是指针
type AnimalIF interface {Sleep()GetColor() stringGetType() string
}type Cat struct {color string
}func (this *Cat) Sleep(){fmt.Println("Cat is sleep")
}
func (this *Cat) GetColor() string{return this.color
}
func (this *Cat) GetType() string{return "Cat"
}type Dog struct {color string
}func (this *Dog) Sleep(){fmt.Println("Dog is sleep")
}
func (this *Dog) GetColor() string{return this.color
}
func (this *Dog) GetType() string{return "Dog"
}func ShowAnimal(animal AnimalIF){animal.Sleep()fmt.Println("color = ",animal.GetColor())fmt.Println("type = ",animal.GetType())
}func main() {/*	var animal AnimalIF // 接口的数据类型cat := Cat{"Black"}animal = &catanimal.Sleep()animal = &Dog{"Yellow"}animal.Sleep()*/cat := Cat{"Black"}dog := Dog{"Yellow"}ShowAnimal(&cat)ShowAnimal(&dog)}

在这里插入图片描述

interface

interface{}可以说是通用类型

  • func myFunc(arg interface{}){}函数可以传入任何类型
  • 类型断言value,ok := arg.(string),断言成功,ok为true,value为转换为该类型的值
package mainimport "fmt"/**
interface{}是万能类型*/func myFunc(arg interface{}){fmt.Println("myFunc is called....")fmt.Println(arg)//interface区分不同数据类型// 类型断言 机制value,ok := arg.(string)if !ok {fmt.Println("arg is not string...")}else {fmt.Println("arg is string,value = ",value)fmt.Printf("value tyep is %T\n",value)}}type Book struct {auth string
}func main() {book := Book{"go"}myFunc(book)myFunc("100")myFunc(3.14)myFunc("hello")
}

在这里插入图片描述

反射

反射:在运行时更新和检查变量的值、调用变量的方法和变量支持的内在操作,但是在编译时并不知道这些变量的具体类型

反射功能强大,通过反射可以得到丰富的类型信息

变量的结构pair

变量(pair)=type+value
其中type就是数据类型,也就是前面说的4种-bool、数字类型、字符串类型、派生类型

在这里插入图片描述

package mainimport "fmt"/**变量结构:type+value (叫pair)*/func main() {// pair<(static)type:string,vlaue="abc">var a string = "abc"// pair<(static)type:string,vlaue="abc">var allType interface{}allType = avalue,ok := allType.(string)if ok {fmt.Println(value)}
}

在这里插入图片描述

reflect包的简单使用

  1. 可以通过reflect类反射获得pair的Type、Value

reflect.TypeOf得到Type,reflect.ValueOf得到Value

package mainimport ("fmt""reflect"
)/**
反射*/func reflectNum(arg interface{}){fmt.Println("type",reflect.TypeOf(arg))fmt.Println("value",reflect.ValueOf(arg))}func main() {var num float64 = 1.234reflectNum(num)
}
  1. 得到Filed

关于reflect的各种方法可以去GoStudy网查看

package mainimport ("fmt""reflect"
)type User struct {Id intName stringAge int
}func (this User) Call(){fmt.Println("user is called....")fmt.Println("%v\n",this)
}func main() {user := User{1,"zhangsan",23}DoFiledAndMthod(user)
}func DoFiledAndMthod(input interface{}){inputType := reflect.TypeOf(input)//fmt.Println("input type is :",inputType.Name())inputValue := reflect.ValueOf(input)//fmt.Println("input value = ",inputValue)// 获取属性字段//1. reflect.Type得到NumField,遍历//2. 得到每一个Field,数据类型//3 .通过field的Interface()得到valuefor i:=0;i <inputType.NumField();i++{field := inputType.Field(i)value := inputValue.Field(i).Interface()fmt.Printf("%s : %v = %v\n",field.Name,field.Type,value)}for i := 0;i < inputType.NumMethod(); i++ {m := inputType.Method(i)fmt.Printf("%s : %v\n",m.Name,m.Type)}
}

在这里插入图片描述

结构体标签

在结构体类型后,可以加上键值对标签
注:最外的是esc键下面的那个引号,值需要双引号

type resume struct{Name string `info:"name" doc:"我的名字"`Sex string `info:"sex"`
}

再通过反射可以拿到结构体的标签值:

package mainimport ("fmt""reflect"
)/**
反射得到结构体Tag*/type resume struct{Name string `info:"name" doc:"我的名字"`Sex string `info:"sex"`
}func findTag(str interface{})  {t := reflect.TypeOf(str).Elem()for i:= 0;i<t.NumField();i++{tagInfo := t.Field(i).Tag.Get("info")tagDoc := t.Field(i).Tag.Get("doc")fmt.Println("info : ",tagInfo)fmt.Println("Doc : ",tagDoc)}
}func main() {var re resumefindTag(&re)
}

在这里插入图片描述

应用

标签的作用类似于别名,在json解析时,可以用json:"title"作为某个属性的tag,title替代json的key

type Movie struct{Title string	`json:"title"`Year int		`json:"year"`Price int	`json:"price"`Actors []string `json:"actors"`
}

该结构体转成json时,key是tag中的别名

package mainimport ("encoding/json""fmt"
)type Movie struct{Title string	`json:"title"`Year int		`json:"year"`Price int	`json:"price"`Actors []string `json:"actors"`
}func main() {movie := Movie{"喜剧之王",2000,10,[]string{"xingye","zhangbozhi"}}//结构体 -》jsonjsonStr,err := json.Marshal(movie)if err!= nil {fmt.Println("json marshal error",err)return}fmt.Printf("jsonStr = %s\n",jsonStr)// json ->结构体myMovie := Movie{}err = json.Unmarshal(jsonStr,&myMovie)if err != nil {fmt.Println("json marshal error",err)return}fmt.Println(myMovie)
}

在这里插入图片描述

如果没有加标签json中的key是结构体中属性名
在这里插入图片描述

http://www.lryc.cn/news/2417915.html

相关文章:

  • PC端分享至QQ空间、新浪、微信
  • PHP实现Trim函数功能(附完整源码)
  • .net core 请求外部接口;ABP HttpClientFactory的使用
  • vuex结合mixin在实际项目中的使用(超详细)
  • 前端缓存详解
  • 学习记录333@MySQL问题之server name is already exists解决方案
  • kernel panic 分析解决方法
  • 基于Java游戏论坛平台设计和实现(源码+LW+调试文档+讲解等)
  • Picasa生成图片幻灯片页面图文教程
  • Java集合详解(超详细)
  • 什么是CSharp
  • c语言常量详细解释及简单应用
  • 入门MySQL--0基础,操作详图,简单易懂
  • CDB(ContainerDatabase)与PDB(PluggableDatabase)
  • 【EVPN】EVPN名词简介
  • java环境变量详解_JAVA环境变量配置详解
  • php中file_get_contents如何读取大容量文件
  • 为什么下载的.msi安装文件打不开、运行不了?用mysql的.msi安装文件为例
  • 学习使用Python执行P4操作
  • 8Uftp连接服务器错误
  • 自学电脑编程_有哪些高质量的自学网站
  • javaSE(完整版)
  • 精进不休丨MogDB 数据库预读特性进一步提升20%+查询性能
  • 计算机期刊投稿须知
  • C语言qsort函数的使用详解
  • 2023最新个人博客文章发布系统的设计与实现(毕业设计+论文+开题报告+运行)
  • Android 游戏开发入门指南(一)
  • java Map遍历的5种方法和一些基本使用
  • Cocoa 框架概述
  • alternatives命令总结