golang语法基础

golang语法基础

此文章只做总结,以记住语法为准,学习go的同学应该都不是第一次学习编程语言了,既然玩了go,就要以生产实践为主。

导入

import "fmt"

起别名

import f "fmt"

批量导入,使用()来表示

import ( "fmt" "math" )

只导入不调用

import ( "fmt" _ "math" )

在go中禁止循环导入,无论直接还是间接,编译无法通过

导出

该规则适用于整个Go语言:对外暴露,首字母大写;不对外暴露,首字母小写

私有

go中约定,一个包内名为internal包为私有包,其他的包将无法访问私有包中的任何东西

数据类型

bool

true & false

在go中,整数0并不代表假值,非零整数也不能代表真值,即数字无法代替布尔值进行逻辑判断,两者是完全不同的类型

整型

uint8、uint16、uint32、uint64

int8、int16、int32、int64

uint、int

uintptr 等价于无符号64位整型

浮点型

float32、float64

字符类型

byte 等价于uint8表示ANSCII字符

rune 等价于int32表示Unicode字符

string 字节序列,可以转换为[]byte类型即字节切片

派生类型

数组 [5]int,长度为5的整型数组

切片 []float64,64位浮点数切片

映射表 map[string]int ,键为字符串类型、值为整型的映射表

结构体 type Gopher struct{} Gopher结构体

指针 *int 一个整型指针

函数 type f func(),一个参数,没有返回值的函数类型

接口 type Gopher interface{},Gopher接口

通道 chan int,整型通道

零值

数字类型 0

布尔类型 false

字符串类型 ""

数组 固定长度对应类型的零值集合

结构体 内部字段都是零值的结构体

切片、映射表、函数、接口、通道、指针 nil

常量

const 修饰的必须初始化的字面量、常量表达式

变量

var 修饰的

声明

只声明不赋值,则为零值

简单声明

var intNum int

同时声明相同的数据类型

var numA,numB,numC int

同时声明多个变量

var (
name string
age int
)

赋值

先声明再赋值

var name string
name = "jack"

声明直接赋值

var name string = "jack"

同时赋值多个变量

var name string
var age int
name,age = "jack",1

语法糖

a := 1

特殊情况,允许编译通过

go 复制代码
func main() {
	a := 1
	a, b := 2, 2
	fmt.Println(a)
	fmt.Println(b)
}

交换

go 复制代码
func main() {
	num1, num2 := 10, 20
	num1, num2 = num2, num1
	fmt.Println(num1)
	fmt.Println(num2)
}

匿名变量

a, b, _ := 1, 2, 3

比较

类型必须相同
minVal := min(1, 2, -1, 1.2)

数组

go中的数组是值类型,而非引用,并不是指向头部元素的指针

初始化

go 复制代码
// 声明
var nums [5]int
// 元素初始化
nums := [5]int{1,2,3}
// new函数获取一个指针
nums := new([5]int)

使用

go 复制代码
fmt.Println(nums[0])
nums[0] = 1
// 元素数量
len(nums)
// 数组容量
cap(nums)

切割

左闭右开

go 复制代码
nums :=[5]{1,2,3,4,5}
nums[1:]
nums[:5]
nums[2:3]

切片

初始化

go 复制代码
var nums []int // 值
nums := []int{1,2,3} // 值
nums := make([]int,0,0) // 值
nums := new([]int) // 指针

使用

切片的使用和数组完全一样

append

go 复制代码
package main

import "fmt"

func main() {
	nums := make([]int, 0, 0)
	nums = append(nums, 1, 2, 3, 4, 5, 6, 7, 8, 9)
	fmt.Println(len(nums), cap(nums))
}

插入元素

头插入

go 复制代码
func main() {
	nums := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
	nums = append([]int{-1, 0}, nums...)
	fmt.Println(nums)
}

中间插入

例如在i的位置插入

nums = append(nums[:i+1], append([]int{999, 999}, nums[i+1:]...)...)

尾部插入
nums = append(nums,99,100)

删除元素

从头部删除n个元素

nums = nums[n:]
fmt.Println(nums) //n=3 [4 5 6 7 8 9 10]

从尾部删除n个元素

nums = nums[:len(nums)-n]
fmt.Println(nums) //n=3 [1 2 3 4 5 6 7]

从中间指定下标i位置开始删除n个元素

nums = append(nums[:i], nums[i+n:]...)
fmt.Println(nums)// i=2,n=3,[1 2 6 7 8 9 10]

删除所有元素

nums = nums[:0]
fmt.Println(nums) // []

拷贝

copy

clear

clear会将切片内所有的值置为零值

清空切片

go 复制代码
func main() {
	s := []int{1, 2, 3, 4}
    s = s[:0:0]
	fmt.Println(s)
}

字符串

在Go中,字符串本质上是一个不可变的只读的字节数组,也是一片连续的内存空间

字面量

普通字符串

支持转义、不支持多行书写

go 复制代码
"这是一个普通字符串\n"
"abcdefghijlmn\nopqrst\t\\uvwxyz"

原生字符串

支持多行书写,不支持转义

访问

go 复制代码
func main() {
	str := "hello world"
	fmt.Println(str[0])
	fmt.Println(str[0:4])
}

不支持修改

str[0]='a'

转换

go 复制代码
func main() {
	str := "this is a string"
	// 字符串转切片
	bytes := []byte(str)
	fmt.Println(bytes)
	// 切片转字符串
	fmt.Println(string(bytes))
}

字符串的内容是只读的不可变的,无法修改,但是字节切片是可以修改的

go 复制代码
func main() {
	str := "this is a string"
	// 字符串转切片
	bytes := []byte(str)
	fmt.Println(bytes)
	bytes = append(bytes, 96, 97, 98)
	// 切片转字符串
	fmt.Println(string(bytes))
}

提示:两种类型之间的转换都需要进行数据拷贝,其性能损耗会随着长度的增加而增长。

长度

字符串的长度,并不是字面量的长度,而是字节数组的长度

go 复制代码
func main() {
	str := "中国人"
	fmt.Println(len(str))
}

拷贝

本质是字节切片的拷贝

go 复制代码
func main() {
	var dst, src string
	src = "this is a string"
	desBytes := make([]byte, len(src))
	copy(desBytes, src)
	dst = string(desBytes)
	fmt.Println(src)
	fmt.Println(dst)
}

也可以使用string.clone函数,本质内部实现差不多

go 复制代码
func main() {
	var dst, src string
	src = "this is a string"
	dst = strings.Clone(src)
	fmt.Println(src, dst)
}

拼接

+ 拼接

转为字节使用append添加

go 复制代码
package main

import "fmt"

func main() {
	var str string = "hello world"
	bytes := []byte(str)
	bytes = append(bytes, "您好 世界"...)
	fmt.Println(string(bytes))
}

strings.Builder

以上两种的性能都很差,如果对性能有要求,使用strings.Builder

go 复制代码
package main

import (
	"fmt"
	"strings"
)

func main() {
	builder := strings.Builder{}
	builder.WriteString("hello world")
	builder.WriteString("hello world")
	fmt.Println(builder.String())
}

遍历

go 复制代码
func main() {
	str := "hello world 您好世界"
	for _, r := range str {
		fmt.Printf("%c", r)
	}
}

rune 本质上是int32的别名

映射表

一般来说,映射表数据结构实现通常有两种,哈希表和搜索树,区别在于前者无序,后者有序,在Go中,map的实现是基于哈希通,所以也是无序的

初始化

字面量

go 复制代码
func main() {
	mp := map[int]string{
		0: "a",
		1: "b",
		2: "c",
		3: "d",
	}
	fmt.Println(mp)
}

make

go 复制代码
func main() {
	mp := make(map[string]int, 8)
	mp["a"] = 1
	fmt.Println(mp)
}

访问

不存在,返回对应的零值

go 复制代码
func main() {
	mp := make(map[string]int, 8)
	mp["a"] = 1
	fmt.Println(mp["b"])
}

访问方式

go 复制代码
func main() {
	mp := make(map[string]int, 8)
	//mp["a"] = 1
	fmt.Println(mp["b"])

	if val, exist := mp["a"]; exist {
		// 如果存在
		fmt.Println(val)
	} else {
		// 如果不存在
		fmt.Println("mp['a'] not exist")
	}
}

删除

go 复制代码
delete(mp,"a")

遍历

go 复制代码
func main() {
	mp := map[string]int{
		"a": 1,
		"b": 2,
		"c": 3,
		"d": 4,
	}
	for k, v := range mp {
		fmt.Println(k, v)
	}
}

清空

clear(mp)

Set

Set是一种无序的,不包含重复元素的集合,Go中并没有提供类似的数据结构的实现,但是map的键正是无序且不能重复的,所以也可以使用map来替代set

go 复制代码
func main() {
	set := make(map[int]struct{}, 10)
	for i := 0; i < 10; i++ {
		set[rand.Intn(100)] = struct{}{}
	}
	fmt.Println(set)
}

提示:一个空的结构体不会占用内存

并发安全

map并不是一个并发安全的数据结构 ,Go团队认为大多数情况下map的使用并不涉及高并发的场景,引入互斥锁会极大的降低性能,map内部有读写检测机制,如果冲突会触发fatal error

在这种情况下,需要使用sync.Map来替代。

指针

Go保留了指针,在一定程度上保证了性能,同时为了更好的GC和安全考虑,又限制了指针的使用

创建

go 复制代码
func main() {
	num := 2
	p := &num
	fmt.Println(p)
}

解引用符号

  1. 访问指针所指向元素
  2. 声明一个指针
go 复制代码
func main() {
	num := 2
	p := &num
	fmt.Println(*p)

	var numPtr *int
	numPtr = &num
	fmt.Println(*numPtr)

}

指针声明初始化

go 复制代码
func main() {
   var numPtr *int = new(int)
   fmt.Println(*numPtr)

   numPtr1 := new(int)
   fmt.Println(*numPtr1)

}

new 和 make

  • new
  1. 返回值是类型指针
  2. 接收参数是类型
  3. 专用于给指针分配内存空间
  • make
  1. 返回值是值,不是指针
  2. 接收的第一个参数是类型,不定长参数根据传入类型的不同而不同
  3. 专用于给切片,映射表,通道分配内存

结构体

Go中抛弃了类与继承,同时抛弃了构造方法,刻意弱化了面向对象的功能,Go并非是一个OOP的语言,但是Go依旧有着OOP的影子,通过结构体和方法可以模拟出一个类。结构体可以存储一组不同类型的数据,是一种复合类型

声明

go 复制代码
type Person struct {
	name string
	age int
}

对于相同类型的字段,如下声明

go 复制代码
type Rectangle struct {
	height, width float32
}

注意:在声明结构体字段时,字段名和方法名不应该重复

创建

go 复制代码
package main

import "fmt"

type Programmer struct {
	Name     string
	Age      int
	Job      string
	Language string
}
 
func main() {
	programmer := Programmer{
		Name:     "jack",
		Age:      42,
		Job:      "bash",
		Language: "python",
	}
	fmt.Println(programmer)

}

组合

在Go中,结构体之间的关系是通过组合来表示的,可以显式组合,也可以匿名组合,后者使用起来更类似于继承

go 复制代码
import "fmt"

type Person struct {
	name string
	age  int
}

type Student struct {
	p      Person
	school string
}

type Employee struct {
	p   Person
	job string
}

func main() {
	student := Student{
		p:      Person{name: "jack", age: 18},
		school: "lili school",
	}
	fmt.Println(student.p.name)
}
go 复制代码
type Person struct {
	name string
	age  int
}

type Student struct {
	Person
	school string
}

type Employee struct {
	Person
	job string
}

func main() {
	student := Student{
		Person: Person{name: "jack",age: 18},
		school: "lili school",
	}
	fmt.Println(student.name)
}

指针

对于结构体而言,不需要解引用就可以直接访问结构体的内容

go 复制代码
type Person struct {
	name string
	age  int
}

func main() {
	p := &Person{
		name: "jack",
		age:  18,
	}
	fmt.Println(p.age)
}

标签

结构体标签是一种元编程的形式,结合反射可以做出很多奇妙的功能

标签是一种键值对的形式,使用空格进行分隔。

结构体的容错性很低,如果没能按照正确的格式书写结构体,那么将导致无法正常读写,但是编译时不会有任何报错

go 复制代码
type Programmer struct {
	Name string `json:"name"`
	Age  int `json:"age"`
}

空间结构体

空间结构体没有字段,不占用内存空间,可以通过unsafe.SizeOf函数计算占用字节大小

go 复制代码
func main() {
	type Empty struct {
	}
	fmt.Println(unsafe.Sizeof(Empty{}))
}

应用:例如 将map作为set来使用

相关推荐
西猫雷婶1 分钟前
python学opencv|读取图像(二十一)使用cv2.circle()绘制圆形进阶
开发语言·python·opencv
kiiila1 分钟前
【Qt】对象树(生命周期管理)和字符集(cout打印乱码问题)
开发语言·qt
初晴~2 分钟前
【Redis分布式锁】高并发场景下秒杀业务的实现思路(集群模式)
java·数据库·redis·分布式·后端·spring·
盖世英雄酱581367 分钟前
InnoDB 的页分裂和页合并
数据库·后端
小_太_阳27 分钟前
Scala_【2】变量和数据类型
开发语言·后端·scala·intellij-idea
直裾30 分钟前
scala借阅图书保存记录(三)
开发语言·后端·scala
唐 城1 小时前
curl 放弃对 Hyper Rust HTTP 后端的支持
开发语言·http·rust
星就前端叭1 小时前
【开源】一款基于Vue3 + WebRTC + Node + SRS + FFmpeg搭建的直播间项目
前端·后端·开源·webrtc
小林coding2 小时前
阿里云 Java 后端一面,什么难度?
java·后端·mysql·spring·阿里云
AI理性派思考者2 小时前
【保姆教程】手把手教你在Linux系统搭建早期alpha项目cysic的验证者&证明者
后端·github·gpu