0
点赞
收藏
分享

微信扫一扫

Go语言基础笔记

夏天的枫_ 2022-01-16 阅读 81

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)
        }
}
举报

相关推荐

0 条评论