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 main

import "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 = 100
		jj 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 main

import "fmt"

// 声明全局变量
var gA int = 100
var gB = 200
//gC := 300 不支持全局变量

func main()  {
	// int默认值 = 0
	var a int
	fmt.Println("a = ",a)

	var b int = 100
	fmt.Println("b = ",b)

	// 初始化可以省略数据变量,自动匹配当前变量的数据类型
	var c = 200
	fmt.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 := 100
	fmt.Printf("e = %T\n",e)
	fmt.Println("e = ",e)

	fmt.Println("gA = ",gA," gB = ",gB)
	//fmt.Println("gC = ",gC)

	// 声明多个变量
	var xx,yy int = 100,200
	fmt.Println("xx = ",xx," yy = ",yy)

	var kk,ll = 200,"ll"
	fmt.Println("kk = ",kk," ll = ",ll)

	// 多行多变量声明
	var (
		vv int = 100
		jj bool = true
	)
	fmt.Println("vv = ",vv," jj = ",jj)
}

常量

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

const length int = 10

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

iota关键字

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

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

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

const (
	BeiJing = iota
	ShangHai
	ShenZhen
)

简单案例

package main

import "fmt"

const (
	// iota关键字,每行会累加1,第一行iota默认为0 (iota 只能出现在const中)
	BeiJing = iota
	ShangHai
	ShenZhen

	/*
	BeiJing = 0
	ShangHai = 1
	ShenZhen = 2
	*/
)

const (
	a,b = iota + 1,iota + 2 // iota = 0, a=iota + 1= 1,b=iota + 2=2
	c,d					 	// iota = 1, c=iota + 1= 2,d=iota + 2=3
	e,f 					// iota = 2
	g,h = iota * 2,iota * 3 // iota = 3, g= iota * 2=6,h= iota * 3= 9
	i,j						// iota = 4, i= iota * 2= 8,j= iota * 3 = 12
)

func main()  {
	// 常量,不允许被修改
	const length int = 10

	fmt.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 := 100
	return 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 := 100
	return c
}
  1. 多返回值:有三种形式
// 返回多个返回值,匿名
func foo2(a string,b int) (int,int) {
	fmt.Println("a = ",a)
	fmt.Println("b = ",b)
	c := 100
	return c,c*3
}
// 返回多个返回值,有形参名
func foo3(a string,b int) (r1 int,r2 int) {
	fmt.Println("a = ",a)
	fmt.Println("b = ",b)
	// 可以通过直接赋值
	r1 = 100
	r2 = 200
	return
}
// 同类型返回值可以缩写
func foo4(a string,b int) (r1 , r2 int){
	fmt.Println("a = ",a)
	fmt.Println("b = ",b)
	r1 = 100
	r2 = 200
	return
}

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 lib1

import "fmt"

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

lib2.go

package lib2
import "fmt"


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

main.go

package main

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

init函数

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

如果有多个init函数呢?

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

package main

import "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 main

import "fmt"

func change(p *int)  {
	*p = 10
}

func main(){
	a := 1
	// 指针传递
	change(&a)
	fmt.Println("a = ",a)
}

在这里插入图片描述

defer

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

defer在return执行完后

package main

import "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 main

import "fmt"

func printArr(myArr [4]int)  {
	myArr[3] = 100
	for index,value := range myArr{
		fmt.Println("index = ",index," value = ",value)
	}
}

func main()  {
	var myArr1 [10]int

	for 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 main

import "fmt"

func printArr(myArr []int)  {
	// _表示匿名变量
	for _,value := range myArr{
		fmt.Println("value = ",value)
	}
	// 地址传递
	myArr[0] = 100
}

func main()  {
	// 动态数组,切片slice
	myArr1 := []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个空间,初始值是0
	slice1 = make([]int,3)
	slice1[0] =100
	fmt.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 main

import "fmt"

func main() {
	
	numbers := make([]int,3,5)

	fmt.Printf("len = %d, cap = %d , slice = %v\n",len(numbers),cap(numbers),numbers)

	// 向numbers切片追加元素1
	numbers = 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 main

import "fmt"

func main() {
	s := []int{1,2,3}

	// 切片截取
	s1 := s[:1]
	fmt.Println(s1)
	s2 := s[1:2]
	fmt.Println(s2)
	// 检验,地址传递
	s[1] = 20
	fmt.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 main

import "fmt"

func main() {
	// 1. ====> 声明map
	var 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 main

import "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 string
	Ad int
	Level int
}

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

package main

import "fmt"

// 结构体首字母大写,其他包也可以访问
type Hero struct {
	// 属性名首字母大写,为公有属性
	Name string
	Ad int
	Level 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 string
	Sex string
}

type SuperMan struct {
	Human //SuperMan继承Human的方法和属性

	level int
}

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

package main

import "fmt"
/**
	继承、重写
 */
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

	level 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 SuperMan
	superMan.Name = "wangwu"
	superMan.level = 30
	superMan.Sex = "man"

	// 调用子类重新的Eat方法
	superMan.Eat()
	superMan.print()
}

在这里插入图片描述

多态

接口

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

// 本质上是指针
type AnimalIF interface {
	Sleep()
	GetColor() string
	GetType() 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 main

import "fmt"

/**
多态
 */

// 本质上是指针
type AnimalIF interface {
	Sleep()
	GetColor() string
	GetType() 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 = &cat
	animal.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 main

import "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 main

import "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 = a

	value,ok := allType.(string)
	if ok {
		fmt.Println(value)
	}
}

在这里插入图片描述

reflect包的简单使用

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

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

package main

import (
	"fmt"
	"reflect"
)

/**
反射
 */

func reflectNum(arg interface{}){
	fmt.Println("type",reflect.TypeOf(arg))
	fmt.Println("value",reflect.ValueOf(arg))

}

func main() {

	var num float64 = 1.234

	reflectNum(num)
}

  1. 得到Filed

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

package main

import (
	"fmt"
	"reflect"
)

type User struct {
	Id int
	Name string
	Age 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()得到value
	for 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 main

import (
	"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 resume
	findTag(&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 main

import (
	"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"}}

	//结构体 -》json
	jsonStr,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是结构体中属性名
在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值