GoLang基础

1 与其他语言相比,使用go有什么好处?

  1. 简洁易学:相比其他编程语言,Go语言具有清晰简洁的语法和规范,减少了代码的复杂性。Go语言拥有较少的关键字和一致的格式,使得代码易于编写、阅读和维护。新手可以很快上手并开始开发应用程序。
  2. 并发编程:Go语言内置了强大的并发编程功能,使得编写高效、可扩展的并发程序变得容易。它通过“goroutine”来实现并发,而不是使用传统的线程。goroutine是一种轻量级的执行单元,可以在一个Go程序中创建成千上万个goroutine,而且切换的开销非常低。此外,Go语言还提供了丰富的并发相关的工具和库,比如通道(channel)和互斥锁(mutex),以帮助开发人员更好地控制并发操作。
  3. 高效性能:Go语言的编译器(Go编译器)在编译代码时能够生成高度优化的机器码,从而提供了出色的性能。Go语言还拥有垃圾回收器(garbage collector),能够自动处理内存管理,减轻了开发人员的负担。此外,Go语言的运行时库(runtime library)是非常轻量级的,不仅能够减少内存占用,还提供了许多基本功能的支持,比如网络、文件操作等。
  4. 跨平台支持:Go语言可以在不同的操作系统和硬件架构上运行,包括Windows、Linux、MacOS等。这使得开发人员可以轻松地将Go程序部署到不同的平台上,而不需要进行大量的修改和适配。同时,Go语言提供了一个工具(称为交叉编译器),可以在一个平台上编译出可以运行在另一个平台上的可执行文件。
  5. 开发效率:Go语言通过提供丰富的标准库和工具,使开发人员能够更快地构建应用程序。Go语言的标准库提供了各种常用的功能和工具,比如文件处理、网络编程、加密、并发等,使开发人员能够快速构建稳定和高效的应用程序。此外,Go语言还有许多第三方库和框架,提供了更多的功能和工具,进一步提高了开发效率。

        综上所述,Go语言具有简洁易学、并发编程、高效性能、跨平台支持和高开发效率等优点。这些优点使得Go语言在不同领域的应用程序开发中变得越来越受欢迎。无论是构建Web应用程序、后端服务、分布式系统还是云原生应用程序,Go语言都是一个强大而可靠的选择。

2 Golang使用什么数据类型?

        go语言的数据类型有:1、布尔型;2、数值类型(可分为整型和浮点型);3、字符串类型;4、指针类型;5、数组类型;6、结构化类型;7、Channel类型;8、函数类型;9、切片类型;10、接口类型;11、Map类型。

GoLang基础_第1张图片

基本数据类型说明:  

类型 描述
uint 32位或64位
uint8 无符号 8 位整型 (0 到 255)
uint16 无符号 16 位整型 (0 到 65535)
uint32 无符号 32 位整型 (0 到 4294967295)
uint64 无符号 64 位整型 (0 到 18446744073709551615)
int 32位或64位
int8 有符号 8 位整型 (-128 到 127)
int16 有符号 16 位整型 (-32768 到 32767)
int32 有符号 32 位整型 (-2147483648 到 2147483647)
int64 有符号 64 位整型 (-9223372036854775808 到 9223372036854775807)
byte uint8的别名(type byte = uint8)
rune int32的别名(type rune = int32),表示一个unicode码
uintptr 无符号整型,用于存放一个指针是一种无符号的整数类型,没有指定具体的bit大小但是足以容纳指针。
uintptr类型只有在底层编程是才需要,特别是Go语言和C语言函数库或操作系统接口相交互的地方。
float32 IEEE-754 32位浮点型数
float64 IEEE-754 64位浮点型数
complex64 32 位实数和虚数
complex128 64 位实数和虚数

整型:

  • 整型数据分为两类,有符号无符号两种类型。有符号: int, int8, int16, int32, int64。无符号: uint, uint8, uint16, uint32, uint64, byte
  • 不同位数的整型区别在于能保存整型数字范围的大小;有符号类型可以存储任何整数,无符号类型只能存储自然数
  • int和uint的大小和系统有关,32位系统表示int32和uint32,如果是64位系统则表示int64和uint64
  • byte与uint8类似,一般用来存储单个字符
  • 在保证程序正确运行下,尽量使用占用空间小的数据类型
  • fmt.Printf("%T", var_name)输出变量类型
  • unsafe.Sizeof(var_name)查看变量占用字节

浮点型:浮点型也就是小数类型,可以存放小数。比如6.6,-12.34

  • 关于浮点数在机器中存放形式的简单说明,浮点数=符号位+指数位+尾数位
  • 尾数部分可能丢失,造成精度损失。-123.0000901,float64的精度要比float32的要准确,如果我们要保存一个精度高的数,则应该选择float64。golang的浮点型默认为float64类型,
  • 0.123可以简写成.123,也支持科学计数法表示:5.1234e2 等价于512.34

字符:Golang中没有专门的字符类型,如果要存储单个字符(字母),一般使用byte来保存。

字符串就是一串固定长度的字符连接起来的字符序列。Go的字符串是由单个字节连接起来的,也就是说对于传统的字符串是由字符组成的,而Go的字符串不同,它是由字节组成的

  • 字符只能被单引号包裹,不能用双引号,双引号包裹的是字符串
  • 当我们直接输出type值时,就是输出了对应字符的ASCII码值
  • 当我们希望输出对应字符,需要使用格式化输出
  • Go语言的字符使用UTF-8编码,英文字母占一个字符,汉字占三个字符
  • 在Go中,字符的本质是一个整数,直接输出时,是该字符对应的UTF-8编码的码值。
  • 可以直接给某个变量赋一个数字,然后按格式化输出时%c,会输出该数字对应的unicode字符
  • 字符类型是可以运算的,相当于一个整数,因为它们都有对应的unicode码
  • 如果我们保存的字符大于255,比如存储汉字,这时byte类型就无法保存,此时可以使用uint或int类型保存

布尔型:

  • 布尔类型也叫做bool类型,bool类型数据只允许取值true或false
  • bool类型占1个字节
  • bool类型适用于逻辑运算,一般用于流程控制

字符串:

  • 字符串一旦赋值了,就不能修改了:在Go中字符串是不可变的。
  • 字符串的两种标识形式
    • 双引号,会识别转义字符
    • 反引号,以字符串的原生形式输出,包括换行和特殊字符,可以实现防止攻击、输出源代码等效果

指针:

  • 基本数据类型,变量存的就是值,也叫值类型
  • 获取变量的地址,用&,比如var num int,获取num的地址:&num
  • 指针类型,指针变量存的是一个地址,这个地址指向的空间存的才是值,比如:var ptr *int = &num
  • 获取指针类型所指向的值,使用:*,比如,var ptr *int,使用*ptr获取ptr指向的值
  • 值类型,都有对应的指针类型,形式为*数据类型,比如int对应的指针就是*int,float64对应的指针类型就是*float64,依此类推。
  • 值类型包括:基本数据类型数组结构体struct

值类型与引用类型:

  • 值类型:变量直接存储值,内存通常在中分配
  • 引用类型:变量存储的是一个地址,这个地址对应的空间才真正存储数据(值),内存通常在上分配,当没有任何变量应用这个地址时,该地址对应的数据空间就成为一个垃圾,由GC来回收。
  • Golang中值类型和引用类型的区分
    • 值类型:基本数据类型(int系列、float系列、bool、string)、数组和结构体
    • 引用类型:指针、slice切片、map、管道chan、interface等都是引用类型

3 Go程序的包是什么?

        Go语言是使用包来组织源代码的,包(package)是多个 Go 源码的集合,是一种高级的代码复用方案。Go语言中为我们提供了很多内置包,如 fmt、os、io 等。

        Golang 中的包与文件夹是一一对应的,必须创建在 GOPATH 目录下才可以被使用。Golang 中的一个包需要引用另一个包的内容,那么必须在开始使用 import 关键字进行导入才可以使用。

        任何源代码文件必须属于某个包,同时源码文件的第一行有效代码必须是 package pacakgeName 语句,通过该语句声明自己所在的包。

4 Go支持什么类型的数据转换?

        Golang支持有符号整数、无符号整数、浮点数、布尔、字符串等类型的转换,类型转换的语法为:T(x),其中T表示要转换成的类型,x表示要转换的值。

整型类型转换:

        在Go语言中整数类型包括有符号整数和无符号整数,支持转换的整数类型有int8、int16、int32、int64、uint8、uint16、uint32和uint64。其中,int8和uint8称为字节类型,int16和uint16称为短整数类型,int32和uint32称为长整数类型,int64和uint64称为长长整数类型。

整数类型的转换需要注意以下两点:

  • 转换时如果值范围超过要转换到的类型值范围,则会溢出,导致结果不准确。例如,将一个比int8范围大的值转换成int8类型,结果则会在[-128, 127]范围内出现不准确的值。需要避免整数类型的溢出问题。
  • 只有同种类型、或者从低精度类型向高精度类型的转换是安全的。例如,从int8转换成int16是安全的,而从int16转换成int8是不安全的,因为在转换成int8时,可能会截取部分数据,导致结果不准确。

浮点型类型转换:

        在Go语言中,浮点数类型包括float32和float64,支持转换的浮点数类型只能是float32和float64。浮点数类型的转换也需要注意两点:

  • 转换时如果值范围过大或者过小,可能会溢出。
  • 只能从低精度类型向高精度类型转换,从高精度类型向低精度类型转换可能会丢失精度。

布尔类型转换:

        在Go语言中,布尔类型只有true和false两个值,支持转换的类型只有int和字符串类型。将布尔值转换成int时,true会转换成1,false转换成0。将布尔值转换成字符串时,true转换成"true",false转换成"false"。

字符串类型转换:

        在Go语言中,字符串是由字符序列组成的(不可变的)数组,支持转换的类型只有原始类型。字符串转换可以通过strconv包实现。将整数转换成字符串时,可以使用 strconv.Itoa() 函数,将浮点数转换成字符串时,可以使用 strconv.FormatFloat() 函数。

        在Go语言中,类型转换是一个非常重要的概念。类型转换可以将不同类型之间的值进行转换,从而满足程序的需要。但需要注意,在进行类型转换时,需要避免数据类型范围溢出和精度丢失的问题,同时需要保证转换后的类型和转换前的类型兼容。

5 什么是Goroutine?如何停止它?

        Goroutine是Golang语言中轻量级线程的实现,可以在一个或多个线程上运行。它是由Go运行时系统管理的,是一个独立、轻量级的执行单元。与传统线程不同,goroutine运行时,它是由Go运行时系统自动进行调度,而且它的调度模型也是非常高效的,它不仅可以很好地支持高并发,而且还能够充分利用多核CPU的优势。大多数 Go 语言程序同时使用数千个 Goroutine。要创建 Goroutine,可以在函数声明之前添加 go 关键字。

        停止goroutine是一个比较复杂的问题,因为goroutine在运行时没有提供显式的终止和暂停的方法。这是由于Go语言的设计目标之一就是让goroutine在运行时尽量不被阻塞,因此需要一些特殊的技巧来停止它们。

  1. 使用channel
    使用channel是最简单、最安全的停止goroutine的方法之一。可以创建一个bool类型的channel,当我们需要停止goroutine时,给这个channel发送一个信号,goroutine收到信号后就可以正常退出了。
    注:每次给channel发送一个信号,只能退出一个协程方法。并不能退出所有的。
    package main
    
    import (
    	"fmt"
    	"time"
    )
    
    func worker1(stopChan chan bool) {
    
    	for {
    		select {
    		case <-stopChan:
    			fmt.Println("worker1 stopped")
    			return
    		default:
    			fmt.Println("worker1 is running")
    			time.Sleep(time.Second)
    		}
    	}
    }
    
    func worker2(stopChan chan bool) {
    
    	for {
    		select {
    		case <-stopChan:
    			fmt.Println("worker2 stopped")
    			return
    		default:
    			fmt.Println("worker2 is running")
    			time.Sleep(time.Second)
    		}
    	}
    }
    func main() {
    	stopchan := make(chan bool)
    	go worker1(stopchan)
    	go worker2(stopchan)
    	time.Sleep(3 * time.Second)
    	stopchan <- true
    	//stopchan <- true
    	time.Sleep(2 * time.Second)
    }
    
  2. 使用Context
    在Go1.7中,标准库中加入了context包,提供了一种新的挂起、取消goroutine的方法。我们可以在每个goroutine中传入一个Context参数,然后在需要停止goroutine时,对这个Context变量执行cancel操作。
    注:跟channel方式不同,cancle操作执行一次,停止所有协程。
    package main
    
    import (
    	"context"
    	"fmt"
    	"time"
    )
    
    func worker3(ctx context.Context) {
    	for {
    		select {
    		case <-ctx.Done():
    			fmt.Println("worker3 stopped")
    			return
    		default:
    			fmt.Println("worker3 is running")
    			time.Sleep(time.Second)
    		}
    	}
    }
    
    func worker4(ctx context.Context) {
    	for {
    		select {
    		case <-ctx.Done():
    			fmt.Println("worker4 stopped")
    			return
    		default:
    			fmt.Println("worker4 is running")
    			time.Sleep(time.Second)
    		}
    	}
    }
    func main() {
    	ctx, cancle := context.WithCancel(context.Background())
    	go worker3(ctx)
    	go worker4(ctx)
    	time.Sleep(3 * time.Second)
    	cancle()
    	time.Sleep(2 * time.Second)
    }
    
  3. 使用Mutex和WaitGroup
    通过共享变量的方式来控制goroutine的停止,这种方法需要使用sync包中的Mutex和WaitGroup。Mutex用于保护共享变量的读写,WaitGroup用于等待所有goroutine完成。
    package main
    
    import (
    	"fmt"
    	"sync"
    	"time"
    )
    
    var wg = sync.WaitGroup{}
    var stop bool
    var mutex = sync.Mutex{}
    
    func worker5() {
    	defer wg.Done()
    	for {
    		mutex.Lock()
    		if stop {
    			mutex.Unlock()
    			fmt.Println("work5 stopped")
    			return
    		}
    		fmt.Println("work is running")
    		mutex.Unlock()
    		time.Sleep(time.Second)
    	}
    }
    func main() {
    	for i := 0; i < 3; i++ {
    		wg.Add(1)
    		go worker5()
    	}
    	time.Sleep(3 * time.Second)
    	mutex.Lock()
    	stop = true
    	mutex.Unlock()
    	wg.Wait()
    	time.Sleep(2 * time.Second)
    }
    
  4. 使用runtime包
    使用runtime包的方法则要稍微麻烦一些,在goroutine运行的代码中,需要定期地检查全局变量,然后在需要停止goroutine时,使用runtime包中的函数强制将其终止。
    package main
    
    import (
    	"fmt"
    	"runtime"
    	"time"
    )
    
    var stop bool
    
    func work6() {
    	for {
    		if stop {
    			fmt.Println("work6 stopped")
    			return
    		}
    		fmt.Println("work6 is running")
    		time.Sleep(time.Second)
    
    	}
    }
    func main() {
    	go work6()
    	time.Sleep(3 * time.Second)
    	stop = true
    	time.Sleep(2 * time.Second)
    	runtime.Goexit()
    }
    

6 如何在运行时检查变量类型?

整理了三种方法,interface{}.(type)、reflect.TypeOf(varibale)、fmt.Printf("%T\n", varibale)。详情查看下面代码。

package main

import (
	"fmt"
	"reflect"
)

// 方法1
func typeofObject1(variable interface{}) string {
	switch variable.(type) {
	case int:
		return "int"
	case float32:
		return "float32"
	case float64:
		return "float64"
	case bool:
		return "boolean"
	case string:
		return "string"
	default:
		// 如果是结构体类型,返回结构体名称
		v := reflect.ValueOf(variable)
		if v.Kind() == reflect.Struct {
			return v.Type().Name() + " struct{}"
		}
		return "unknown"
	}
}

// 方法2
func typeofObject2(variable any) interface{} {
	r := reflect.TypeOf(variable)
	return r
}

func main() {
	var num float64 = 3.14
	type1 := typeofObject1(num)
	type2 := typeofObject2(num)
	// 方法3:
	fmt.Printf("%T\n", num)
	fmt.Println(type1)
	fmt.Println(type2)
}

7 Go两个接口之间可以存在什么关系?

  • 如果两个接口有相同的方法列表,那么他们就是等价的,可以相互赋值。
  • 如果接口A的方法列表是接口B的方法列表的子集,那么接口B可以赋值给接口A。
  • 接口查询是否成功,要在运行期才能够确定。

8 Go中同步锁有什么特点?作用是什么?

  • 当一个goroutine(协程)获得了Mutex后,其他gorouline(协程)就只能乖乖的等待,除非该gorouline释放了该Mutex
  • RWMutex在 读锁 占用的情况下,会阻止写,但不阻止读
  • RWMutex在 写锁 占用情况下,会阻止任何其他goroutine(无论读和写)进来,整个锁相当于由该goroutine独占

同步锁的作用是保证资源在使用时的独有性,不会因为并发而导致数据错乱,保证系统的稳定性。

9 Go语言当中channel有什么特点?

  • 如果给一个 nil 的 channel 发送数据,会造成永远阻塞
  • 如果从一个 nil 的 channel 中接收数据,也会造成永久爱阻塞
  • 给一个已经关闭的 channel 发送数据, 会引起 pannic
  • 从一个已经关闭的 channel 接收数据, 如果缓冲区中为空,则返回一个零值

10 Go语言中的channel缓冲有什么特点?

无缓冲的 channel是同步的,而有缓冲的channel是非同步的。

11 Go语言中的cap函数可以作用于哪些内容?

cap函数用于获取切片、数组或通道的容量,即可以存储的元素数量的最大值。可以作用于的类型有:

  • array(数组)
  • slice(切片)
  • channel(通道)

12 Go convey是什么?一般用来做什么?

  • go convey是一个支持golang的单元测试框架
  • go convey能够自动监控文件修改并启动测试,并可以将测试结果实时输出到Web界面
  • go convey提供了丰富的断言简化测试用例的编写

13 Go语言当中new的作用是什么?

new关键字是用来分配内存的函数,new(Type)作用是为T类型分配并清零一块内存,并将这块内存地址作为结果返回。也就是说new(T)会为类型为T的新项分配已置零的内存空间,并返回它的地址。在go中,返回一个指针,指针指向新分配的内存,类型为T类型的零值。

14 Go语言中make的作用是什么?

make的作用是为slice, map or chan的初始化 然后返回引用 make函数是内建函数,函数定义:

func make(Type, size IntegerType) Type

make(T, args)函数的目的和new(T)不同 仅仅用于创建slice, map, channel 而且返回类型是实例

15 Go语言中切片和数组的区别是什么?

数组:

  1. 数组固定长度
  2. 数组长度是数组类型的一部分,所以[3]int[4]int是两种不同的数组类型
  3. 数组需要指定大小,不指定也会根据处初始化对的自动推算出大小,不可改变
  4. 数组是通过值传递的

切片:

  1. 切片可以改变长度
  2. 切片是轻量级的数据结构,三个属性,指针,长度,容量
  3. 不需要指定大小
  4. 切片是地址传递(引用传递)
  5. 可以通过数组来初始化,也可以通过内置函数make()来初始化,初始化的时候len=cap,然后进行扩容

16 Go语言中的值传递、地址传递(引用传递)

  1. 值传递只会把参数的值复制一份放进对应的函数,两个变量的地址不同,不可相互修改。
  2. 地址传递(引用传递)会将变量本身传入对应的函数,在函数中可以对该变量进行值内容的修改。

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