【Go语言编程】基础知识篇

文章目录

  • 一、Go语言简介
    • 1.优势
    • 2.劣势
    • 3.前景
  • 二、数据集的导入及编辑
    • 1.包及其导入
    • 2.变量
    • 3.预定义常量
    • 4.基础数据类型
    • 5.派生数据类型
    • 6.类型转换与类型别名
  • 三、流程控制
  • 四、数组、切片和映射
    • 1.数组
    • 2.切片
    • 3.映射
  • 五、string操作
    • 1.连接字符串
    • 2.解析字符串
      • 1.遍历字符串
      • 2.字符串操作
    • 3.检查字符串长度
    • 4.数据复制
  • 六、函数
  • 七、指针
    • 1.指针的定义
    • 2.Go语言中的指针
  • 八、结构体和方法
    • 1.结构体
    • 2.方法
  • 九、接口
    • 1.错误的接口实现方法
    • 2.空接口
    • 3.类型断言、类型查询


一、Go语言简介

Go(又称 Golang)是 Google 的 Robert Griesemer,Rob Pike 及 Ken Thompson 开发的一种静态强类型、编译型语言。Go 语言语法与 C 相近,但功能上有:内存安全,GC(垃圾回收),结构形态及 CSP-style 并发计算。

1.优势

  • 兼顾C语言级别的编译、运行效率,容易部署,PHP级别的开发效率
  • 强大的标准库,内置高效的垃圾回收机制
  • 简单的并发编程,goroutine和channel极易上手
  • 静态类型语言,拥有强大的编译检查、严格的编码规范
  • 背景强大,由三位骨灰级大师创造,google公司运营维护

2.劣势

  • 没有异常处理,只有Error
  • 依赖管理(已解决√) 1.11推出GoModules
  • 缺少框架(已解决√)Gin、Beego、Echo…等等
  • 无泛型(正在解决。。。)官博确认正在考虑泛型设计,最快在1.18测试版中加入

3.前景

  • 在云计算、微服务、基础后端软件上具有强大优势
  • 得到诸多大厂的"青睐",很多部门由java转golang,新公司甚至直接使用golang
  • 优秀开源项目不断涌现,以docker和k8s为代表

二、数据集的导入及编辑

1.包及其导入

  • 点操作
import (."fmt")
//之后调用这个包函数时,可以省略前缀包名
Println("hello world")
  • 别名操作
import (f"fmt")

f.Println("hello world")
  • _ 操作
    _ 操作只是引入该包,当导入一个包时,它所有的init()函数就会被执行,无法通过包名调用包中的导出函数,只是为了简单地调用其init()函数。

2.变量

//变量声明

//1.指定变量类型,声明后不赋值
var a int
a=12
//2.根据值自动推导变量类型
var b=12
//3.使用:=直接取代var关键字和变量类型
c:=10
//4.多变量声明中用于声明全局变量的写法
var (
	vname1 v_type1
	vname2 v_type2
)

//匿名变量:_(下划线)是特殊的变量名,任何赋予的值都会被丢弃
_ ,b := 34,35

3.预定义常量

iota是一个常量计数器,可以认为是一个被编译器修改的常量,每个const关键字出现时被重置为0,然后在下一个const出现前,每出现一次iota,其代表的数字会自动加1,可以用于枚举。

package main 
import "fmt"
func main(){
	const(
		a=iota
		b
		c
	)
	fmt.Println(a,b,c)
}

//输出0 1 2

4.基础数据类型

  • 布尔类型不能接受其他类型的赋值,不支持自动或强制的类型转换。
  • int和int32在Go语言中被认为是两种不同的类型,但可以使用强制类型转换。
  • 复数的表示
package main
import "fmt"
func main(){
	var v1 complex64
	v1=3.2+12i
	v2 :=3.2+12i
	v3:=complex(3.2,12)
	//获取实部和虚部
	fmt.Println(real(v1),imag(v1))
}

5.派生数据类型

  1. 指针pointer
  2. 数组array
  3. 切片slice
  4. 映射map
  5. 通道channel
  6. 结构体struct
  7. 接口interface

6.类型转换与类型别名

  • Go语言不存在隐式转换,必须显式转换,且只能在两种互相兼容的类型间转换。
  • Go语言支持使用关键字type为数据类型起别名。
package main
import "fmt"
type text string
func main(){
	var t text = "hello world"
	fmt.Println(t)
}

三、流程控制

  • if、switch语句支持一个初始化语句,初始化语句和变量本身以分号(;)分隔。
package main
import("fmt")
func main(){
	if a:=3;a==3{
		fmt.Println("a==3")
	}
}
  • Go语言默认每个case最后带有break关键字,匹配成功后不会自动向下执行,而是跳出switch语句。但是可以使用fallthrough关键字强制执行后面case的语句。
  • Go语言只支持for循环,而不支持while和do…while结构。
  • for…range可返回字符串索引、键值数据。
  • goto关键字可用于任何语句中,作用是定点跳转到本函数内的标签。goto关键字不能跨函数跳转,也不能跳转至内层代码块中。

四、数组、切片和映射

1.数组

//声明与初始化
array1 :=[5] int{10,20,30,40,50}
array2 :=[...]int{10,20,30}
//初始化索引为1和2的元素
array3 :=[5]int{1:10,220}
//for...range遍历数组元素
package main
import("fmt")
func main(){
	var a[2]int
	a[0]=10
	a[1]=12
	for i,v:=range a{
		fmt.Println("Array element[", i, "]=",v)
	}
}

在go语言中数组是一个值类型,在函数体中无法修改传入的数组的内容。

2.切片

  • 切片的三个属性:指针、长度和容量。
  • 基于数组:数组切片可以只使用数组的一部分元素或者整个数组来创建,甚至可以创建一个比原数组还要大的数组切片。
  • myArray[index1,index2]:包含index1,不包含index2,左闭右开。
  • 直接创建切片:make()可以用于直接创建数组切片而不需要事先准备一个数组。
//创建一个初始元素个数为5的数组切片,元素初始值为0,并预留10个元素的存储空间。
mySlice := make([]int,5,10)
myslice1 :=[]int{1,2,3,4,5}
  • 切片共享同一段底层数组,如果一个切片修改了该底层数组的共享部分,那另一个切片也会相应发生改变。
  • 切片只能访问到长度内的元素,试图访问超出其长度的元素会导致语言运行时异常。
  • Go的内建函数:cap()和len(),返回容量和长度。
  • append函数修改有余量的数组时,由于共享了底层数组,所以数组也会被修改。
  • append函数会智能处理底层数组的容量增长。切片容量小于1000个元素时会成倍地增加容量;元素个数超过1000时,容量的增长因子会设为1.25。
  • 切片复制时,加入的数组切片不一样大,就会按较小的数组切片的元素个数进行复制。

3.映射

//映射例子
inf :=map[int]string{
	110:"警察局",
	114:"查号中心",
	119:"消防局",
	120:"急救中心"
}
  • 切片、函数以及包含切片的结构类型由于具有引用语义,不能作为映射的键。
  • 映射的赋值:inf[110]=“警察局”。
  • 元素的查询:同时获得值以及一个表示这个键是否存在的标志;只返回键对应的值,然后进行判断是否存在。
  • 元素删除:delete(inf,110)。如果键不存在,这个调用什么都不会发生。但如果传入的map变量的值是nil,那么调用导致程序抛出异常。

五、string操作

字符串可以使用双引号“”定义,也能使用反引号’'定义。反引号最大的好处是支持跨行操作

1.连接字符串

  1. 使用加法运算符

较少字符串连接的场景下直接使用加号运算符会使代码简短清晰,可读性好。

str1:="abcd"
str2:="efg"
str=str1+str2
  1. 调用fmt.Sprintln()函数

如果需要拼接的不仅仅是字符串,还有数字之类的其他需求的话,可以考虑fmt.Sprintln()函数。

str1:="abcd"
str2:="efg"
str:=fmt.Sprintln("%s%s",str1,str2)
  1. 调用string.Join()函数

在已有字符串数组的场合,使用strings.Join()函数有比较好的性能。

//需要import("strings")
str:=[]string{"ab","cd","ef"}
str:=strings.Join(str,"")
  1. 调用buffer.WriteString()函数

在一些性能要求较高的场合,尽量使用buffer.WriteString()函数以获得更好的性能。

//需要import("bytes")
var buf bytes.Buffer
buf.WriteString("abcd")
buf.WriteString("efg")
//输出buf.String()

这种方式比较理想,可以当作一个可变长字符串来使用,对内存的增长也有优化。如果能够预估字符串的长度,还可以用buffer.Grow()设置缓存容量。

2.解析字符串

1.遍历字符串

  • 常规方式遍历字符串
  • for…range方式遍历字符串
  • 切片方式遍历字符串
    先将字符串转成 []rune切片,然后用常规方式遍历。

2.字符串操作

  1. strings.Contains(s,substr string) bool
    判断字符串s是否包含substr,返回一个布尔值。
  2. strings.Index(s,sep string) int
    子串sep在字符串s中第一次出现的位置,不存在则返回-1。
  3. strings.Repeat(s string,count int) string
    返回count个s串联的字符串。
  4. strings.Replace(s,old,new string,n int) string
    返回将s中前n个不重叠old子串都替换为new,如果n<0,则替换所有old子串。
  5. strings.Split(s,sep string) []string
    用sep分割s,会分割到结尾,并返回切片。如果sep为空,则Split将s切分成每一个unicode码值一个字符串。

3.检查字符串长度

除了“先将字符串转成 []rune切片,然后用常规方式遍历”方法外,还有其他三种方法。

  • bytes.Count(s,sep []byte) int
    如果参数sep为nil,返回s的Unicode代码点数+1;不为空则计算s有多少个不重叠的sep子切片。
  • strings.Count(s,sep string) int
    如果sep为空字符串,返回字符串的长度+1,否则计算字符串sep在s中出现的次数。
  • utf8.RuneCountInString(s string) (n int)
    返回s中的utf-8编码的码值的个数。错误或者不完整的编码会被视为宽度为1字节的单个码值。

4.数据复制

copy函数将第二个切片的元素复制到第一个切片中,复制长度为两切片中长度较小的那个。


六、函数

  • 不定参数是指函数传入的参数个数为不定数量。如果参数列表中若干个相邻的参数类型相同,则可以在参数列表中省略前面变量的类型变量,在参数类型前面加上三个点“…”。
  • 调用变参函数时,也可以将切片作为实参,不过需要展开,就是在切片后面加“…”。
  • 如果希望传递任意类型,那么可以指定参数类型为接口interface{}。
  • 匿名函数:不需要定义函数名的一种函数实现方法。
  • 所有的匿名函数都是闭包。
  • 闭包就是一个函数捕获了和它在同一作用域的其他变量或常量,只要闭包还在使用它,这些变量或常量就还存在。

七、指针

1.指针的定义

//声明指针
package main
import("fmt")
func main(){
	var ptrA *int
	var ptrB = new(float32)
	var c float64
	ptrC := &c
	//分别为0,指针b,指针c
	//即未被初始化的指针为nil
}

Go语言指针不同于C/C++地方:不能在程序中对指针的值进行运算,即不支持指针的自增、偏移等。

但是它也为Go语言的垃圾回收机制提供了便利,不会因为垃圾回收而悬空指针。

2.Go语言中的指针

不支持"—>"运算符,直接用"."访问目标成员。

数组元素全为指针的数组称为指针数组。数组指针是指只想数组首元素的地址的指针,其本质为指针;指针数组是数组元素为指针的数组,其本质为数组。

区分数组指针和指针数组的方法:看"*"和谁结合。
p *[5] int,*与数组结合说明是数组的指针;如p [5] *int*与int结合,说明数组都是int类型的指针,是指针数组。


八、结构体和方法

1.结构体

type Student struct{
	ID int
	Name string
	Address string
	Age int
	Class string
	Teacher string
} 
var lm Student
  • 如果一个结构体的成员变量名称的首字母大写,那么这个变量就是可导出的,导出是指这个变量可以在其他包进行读写。
//结构体的初始化
//第一种格式:顺序为每个成员变量指定一个值
type Point struct{X,Y int}
p:=Point{1,2}
// 第二种格式:指定部分或者全部成员变量的名称和值来初始化结构体变量,如果成员被忽略,默认用零值
lm := Student{ID:2021201}

2.方法

  • Go语言的方法是运作在一个特定类型变量上的函数,该对象被称为方法的接收者。
  • 方法属于函数,所以方法也是不能重载的;对于一个给定的类型和一个给定的名称,只会有一个方法。
  • 根据方法接收者的不同类型,会出现重载的这种情况。
  • 接收者不可以是接口,因为接口这种抽象的定义不能包含函数的具体实现。
  • 方法的接收者类型本身不可以是指针类型,虽然方法接收者本身可以是一个指针。
type Person struct{}
type Ptr *Person
func (p Ptr) getName(){}   //编译错误
func (p Person) getName(){}
func (p *Person)getName(){}

九、接口

接口是一系列方法的集合。接口定义了一组方法,但这些方法不包含代码,它们并不在接口中实现。

1.错误的接口实现方法

  • 没有完全实现接口所定义的方法
  • 用某一类型的变量直接对接口赋值
  • 对不同的包中具有不可导出方法的接口进行实现

2.空接口

type Any interface{}
func funcA(args...interface{}) interface{}{
}

空接口被称为"最小接口",没有任何方法,实现他们不需要满足任何要求。一个空接口变量可以被任意赋值,从而指向任何类型的对象。

最典型的例子是fmt库中的输出函数,通过配合空接口和类型查询,能够实现可接受任意对象实例并进行处理的函数。

3.类型断言、类型查询

一个接口变量所包含的值的类型有多种可能,为了动态判断接口代表的实际类型,需要利用Go语言的类型断言机制。

//检查变量varI是否是TypeA类型,判断并赋值
varC , ok := varI.(TypeA)
//类型查询
switch varC := varI.(type){
	case int:
		carC++
	case string:
		varC = "It's a string"
	...
	default:
		...
}

你可能感兴趣的:(golang,开发语言,后端)