GO语言基础
主要特性
- 自动垃圾回收
- 丰富的内置类型
- 函数多返回值
- 错误处理
- 匿名函数和闭包
- 类型和接口
- 并发编程
- 反射
- 语言交互性
语言结构
GO语言的基础组成部分
- 包声明
- 引入包
- 函数
- 变量
- 语句 & 表达式
- 注释
Tip:一个文件夹只能声明一个包名
//math1.go
package mathClass
func Add(x,y int) int{
return x + y;
}
//math2.go
package mathClass
func Sub(x,y int) int{
return x*y;
}
math
--math1.go
--math2.go
代码块实例
package main //声明包
import "fmt" //引入包
func main(){ //主函数
var name string //变量
fmt.Println("hello world") //语句
/* 输出hello world 并换行*/ //注释
}
注意语法
package main
import "fmt"
func main()
{
fmt.Println("Hello World!")
//错误执行,{不可单独放于一行
}
基础语法
行分隔符
在GO语言中一行代表一个语句结束,如果要一行写多个语句,才需要在结尾加 ;,否则直接换行就可以。
标识符
标识符是由字母、数字和下划线组成,第一个字符不能是数字,标识符不能和关键字同名。
- 有效标识符
- 无效标识符
- 空白标识符
空白标识符是一个只写变量,你不能得到它的值,即舍弃它的值。
字符串的链接
GO语言中字符串的链接可通过 + 实现
package main
func main(){gehihua
fmt.Println("hello " + "World!")
}
实例输出
格式化字符串
Go语言中可以用 fmt.Sprintf 格式化字符串并赋值给新串
package main
import "fmt"
func main(){
var stockcode = 123
var enddate = "2020-12-31"
var url = "Code = %d & endDate = %s"
var target_url = fmt.Sprintf(url,stockcode,enddate)
fmt.Println(target_url)
}
实例输出
语言数据类型
1.布尔型
布尔值只可为 true 和 false
var flag bool = true
2.数字类型
数字类型有整型、浮点型等
var a int //整型
var b float32 //32位浮点型
var c float64 //64位浮点型
var d uintptr //无符号整型,存放指针
3.字符串类型
Go语言的字符串的字节使用UTF-8编码标识 Unicode 文本
var name string = "Goland"
4.派生类型
var a *int //指针类型
var b [2]int //数组类型
var c struct //结构化类型
var d chan int //Channel通道类型
var e func(string) int //函数类型
var f []int //切片类型
var g interface //接口类型
var g error //error是接口
var h map[string] int //map类型
语言变量
变量的声明
Go的变量声明使用 var 关键字
var 变量名 变量类型
var a type
也可以同时声明多个变量
var 变量名,...,变量名 变量类型
var a,b,c type
变量默认值
int型的变量默认 0 值,bool型的变量默认false,string为空值。
自动判断变量类型
在定义的时候赋值,变量会判断赋值类型定义自身类型
var name = "goland" //name定义为string型
变量声明符
Go中可以用 := 来声明并赋值变量
v_name := value
但 := 不可以在变量声明过后在使用
var num int
num := 1 //编译错误
因为 := 相当于下列代码
var num int
num = 1
多变量声明
//1基本定义
var vname1,vname2,vname3 type
vname1,vname2,vname3 = v1,v2,v3
//2自动判断
var vname1,vname2,vname3 = v1,v2,v3
//3声明标识符
vname1,vname2,vname2 := v1,v2,v3
//4因式分解关键字,一般用于全局变量
var(
vname1 v_type1
vname2 v_type2
)
注意事项
局部变量声明需要使用,否则编译错误。全局变量可声明但不使用。
语言常量
普通常量的定义与声明
常量是什么?常量是定义出来后不可被后续修改的量。
Go的常量定义与C语言相似,都是由const来定义,不过使用const的时候可以不需要var来声明。
const name string = "goland"
const name = "goland"
特殊常量iota
iota是const关键字中的“计数器”,即在一个const定义中可以标记第几个变量,它的值从0开始。
package main
import "fmt"
func main(){
const(
//const中为赋值的变量会按照上次赋值的语句执行
a = iota //0
b //1
d = "good " //2
e //3
h = 100 //4
g //5
h = iota //6
)
}
语言运算符
Go的运算符和C语言一样,不多赘述。
函数
函数的定义类似于C语言,不过返回类型和定义类型一样,在写在后面的
//函数声明func,函数名solution,函数参数parameter list,函数返回类型return type
func solution([parameter list]) [return type]{
}
返回多值
Go语言中函数可以返回多值,且可以按自己顺序返回
package main
import "fmt"
func swap(x,y string) (string,string){
return y,x
}
func main(){
a,b := swap("Google","Runoob")
fmt.Println(a,b)
}
输出实例
这里返回了y,x,ab则按顺序被赋值
*循环语句
在go中没有while循环,一律使用for循环
for循环的基本语句
for init; condition; post
//定义,条件,循环变化
for循环可以省略init和post部分,类似于while
for n <= 10{
}
/*
while(n<=10){
}
*/
全部省略则全为空值
for{
}
等同于
for(;;){
}
指针
指针的定义
在type前加*
空指针
空指针为nil
var ptr *int
ptr == 0 //ptr == nuil
*数组和切片
数组
数组的定义就同c语言一样,如实例
var a [5]int
b := [5]int{1,2,3,4,5}
切片
go的切片就类似于C++中的vector容器,即不定长数组
初始化如下
var a []int
//定义空切片
b := []int{1,2,3}
//定义包含内容切片
c := make([]type,len,cap)
//通过内置函数make定义切片类型长度和容量
在对切片的操作中还有
a := arr[;]
//索引arr从头到尾赋值给a
a := arr[start;]
//从start开始到尾赋值给a
a := arr[;end]
//从头到end赋值给a
a := arr[start;end]
//从start到end赋值给a
其中有几个函数
- 添加函数append
append(切片,添加数据),这个操作会产生一个临时切片,这个临时切片被添加了元素
//一维在结尾添加元素
var a []int
b := []int{1,2,3}
a = append(a,1) //[1]
a = append(b,1) //[1,2,3,1]
//二维数组添加新行
var a [][]int
b := []int{1,2,3}
a = append(a,b) //[1,2,3]
a = append(a,b) //[1,2,3] [1,2,3]
- copy函数
copy(a,b)
//将b的值copy到a中
- len()和cap()
返回对应的数组长度和容量
结构体
结构体的初始化
type Books struct{
name string
author string
id int
}//定义结构体
var book1 Books //定义一个变量
book2 := Books{name:"hahaha",author:"nobody",id:"123"}//初始化
book1.name //调用name,这里为空“”
结构体指针
结构体指针也是通过’ . '来访问,于C中->不同
var book1 Books
var book2 *Books
//下列语句作用相同
book1.name
book2.name
*语言范围
该知识点主要用于for循环之中
package main
import "fmt"
func main(){
nums := []int{1,2,3}
sum := 0
for _,num := range nums{
sum += nums
}//num从nums中顺序获取数据
//类似于c语言中 for(int x:nums)
for i,num := range nums{
sum += nums
fmt.Println(i)
}//一样的获取,不过多了一个i即记录index下标
kvs := map[string]string{"a":"apple"}
for k,v := range kvs{
fmt.Println("%s->%s",k,v)
}//用于map数据中,就可以获取它的键值和对应的值
for i,c := rang "go"{
fmt.Println(i,c)
}//可以枚举unicode字符串
}
*语言接口
接口定义
接口通过定义一个类型的接口,其中包含这个接口的各种处理函数。即一些具有共性方法的集合,例如手机接口,拥有电话短信等功能。
//接口定义
type interface_name interface{
method_name [return_type]
}
//结构体定义
type strcut_name struct{
//运行处理
}
//实现接口函数
func (struct_name_variable struct_name) method_name [return_type]{
//方法实现
}
在接口中接口类型的方法可以多次调用定义,即用不同的结构体对其定义新的函数(可以类比c++中的多态,或模板)。即 接口->多个方法 方法->多个实现函数
实例
package main
import "fmt"
//接口定义
type Phone interface {
call(param int) string
takephoto()
}
//结构体定义
type Huawei struct {
}
//方法定义
func (huawei Huawei) call(param int) string{
fmt.Println("i am Huawei, i can call you!", param)
return "damon"
}
//方法定义
func (huawei Huawei) takephoto() {
fmt.Println("i can take a photo for you")
}
func main(){
var phone Phone
phone = new(Huawei)
phone.takephoto()
r := phone.call(50)
fmt.Println(r)
}
*错误处理
Go语言内部提供了内置的错误接口,提供了简单的错误处理机制。
error类型就是一个错误接口类型,以下是定义
type error interface{
Error() string
}
我们可以在编码中通过实现 error 接口类型来生成错误信息。
函数通常在最后的返回值中返回错误信息。使用errors.New 可返回一个错误信息:
func Sqrt(f float64) (float64,error){
if f < 0{
return 0,errors.New("math: square root of negative number")
}//实现
}
在下面的例子中,我们调用Sqrt并传入一个负数,与nil作比较,若err返回错误信息则会输出,下列为实例
result, err := Sqrt(-1)
if err != nil{
fmt.Println(err)
}
实例运行
package main
import (
"errors"
"fmt"
)
type num struct {
nums int
}
//接口实现方法
//定义返回接口,调用接口就返回相关错误信息。
//一般在之前对数据进行判断,然后判断是否要返回错误信息。
func (n *num) Error() string {
strf := "This num:%d is can't Sqrt"
return fmt.Sprint(strf, n.nums)
}
//函数里运用接口返回信息
//直接在函数里创建一个错误返回类型,errors.New()可以创建对应错误信息
//这里的errors.New就相当于上面的n.Error()
func Sqrt(f float64) (float64, error) {
if f < 0 {
return 0, errors.New("This num is can't Sqrt")
}
return f, errors.New("")
}
func main() {
k, err := Sqrt(1)
fmt.Println(k)
if err != nil {
fmt.Println(err)
}
}
*并发
Go语言也支持并发进行,我们可以通过调用go关键字来开启goroutinue。goroutine是轻量级的线程,goroutine的调度由golang运行时管理。
语法格式
go 函数名(参数列表)
例子
go f(x,y,z) //开启了新的f函数同时运行
开启的轻量级线程,会同主程序一同运行,并执行相应的函数。同一个程序中的goroutine共享同一个地址空间,即共享资源。
实例
package main
import(
"fmt"
"time"
)
func say(s string){
for i:=0; i < 5; i++{
time.Sleep(100*time.Mmillisecond)
fmt.Println(s)
}
}
func main(){
go say("world")
say("hello")
}
输出结果
这里没有固定的先后顺序,运行结果和计算机计算速度有关。且线程同主程序同时运行。
*通道
通道是用来传递数据的一个数据结构,用于两个线程直接的通讯和同步,<- 为方向指定,没有指定为双向通道。
ch := make(chan int)
ch <- v //把v传入到通道ch中
v := <- ch
通道缓冲区
通道的普通定义上是没有缓冲区的,档通道中传入了数据,没有另一方接受数据的话,那么后续的传入数据就会被放入阻塞队列。
以下为示例代码
- 实例一
package main
import (
"fmt"
)
var counts int = 0
func sum(s []int, c chan int) {
sum := 0
for _, v := range s {
sum += v
}
fmt.Println("finish ", counts)
counts += 1
c <- sum // 把 sum 发送到通道 c
fmt.Println("transport finished", counts-1)
}
func main() {
s := []int{7, 2, 8, -9, 4, 0}
c := make(chan int)//make(chan int,3)
go sum(s[:], c)
go sum(s[:len(s)/2], c)
go sum(s[len(s)/2:], c)
x, y := <-c, <-c // 从通道 c 中接收
d := <-c
fmt.Println(x, y, d)
}
- 实例二
package main
import "fmt"
func main() {
// 这里我们定义了一个可以存储整数类型的带缓冲通道
// 缓冲区大小为2
ch := make(chan int, 2)//ch := make(chan int)
// 因为 ch 是带缓冲的通道,我们可以同时发送两个数据
// 而不用立刻需要去同步读取数据
ch <- 1
ch <- 2
// 获取这两个数据
fmt.Println(<-ch)
fmt.Println(<-ch)
}
- 通道运用注意
这里的通道和并发有紧密联系,就例如linux操作系统中的并发中互斥变量的使用,我们要避免死锁状态,就得考虑它的资源分配情况和顺序,并通过计算给出足够的空间与资源。
通道的遍历和关闭
通道的遍历也可以使用 rang 来遍历。
不过通道的遍历也是从头到尾的,如果缓存区大小为11但是数据只有10个的时候,就会去请求接受第十一个数据,从而陷入阻塞甚至死锁。
那么我们就用关闭通道来解决上述问题,通道关闭后就不会进行接收和发送了,那么遍历到第11个数据时就不会去发送请求接收,从而不会造成死锁。
下列为实例代码:
package main
import (
"fmt"
)
func fibonacci(n int, c chan int) {
x, y := 0, 1
for i := 0; i < n; i++ {
c <- x
x, y = y, x+y
}
close(c)
}
func main() {
c := make(chan int, 10)
go fibonacci(cap(c), c)
// range 函数遍历每个从通道接收到的数据,因为 c 在发送完 10 个
// 数据之后就关闭了通道,所以这里我们 range 函数在接收到 10 个数据
// 之后就结束了。如果上面的 c 通道不关闭,那么 range 函数就不
// 会结束,从而在接收第 11 个数据的时候就阻塞了。
for i := range c {
fmt.Println(i)
}
}