Skip to content

入门篇

与其他语⾔相⽐,使⽤ Go 有什么好处?

  • Golang 针对并发进⾏了优化,并且在规模上运⾏良好。
  • ⾃动垃圾收集明显⽐ Java 或 Python 更有效,因为它与程序同时执⾏。
  • 由于单⼀的标准代码格式,Golang 通常被认为⽐其他语⾔更具可读性。

Golang使⽤什么数据类型?

  • Method
  • Boolean
  • Numeric
  • String
  • Array
  • Slice
  • Struct
  • Pointer
  • Function
  • Interface
  • Map
  • Channel

Go程序中的包是什么?

  • 在Go语言中,包是一个或多个源码文件的集合。
  • 每个文件必须包含 package <name> 包名声明。
  • go程序入口必须为main包。且必须包含main函数。
  • 除了main包外,其他包的名称必须与对应的目录同名。
  • 同一个目录下所有源码文件包名必须相同。

nil切片和空切片有什么区别?

go
// 定义一个nil切片,即完成定义,没有初始化的切片
var s1 []int;
// 定义一个空切片,即定义并且初始化的切片
var s2:=make([]int,0);
// 参照物
var s3:=make([]int,0);
//比较是否相等
fmt.Println(reflect.DeepEqual(s1, s2))
fmt.Println(reflect.DeepEqual(s2, s3))
//输出详细数据
fmt.Printf("data: %+v  address: %p cap: %d len: %d \r\n", s1, s1, cap(s1), len(s1))
fmt.Printf("data: %+v  address: %p cap: %d len: %d \r\n", s2, s2, cap(s2), len(s2))
fmt.Printf("data: %+v  address: %p cap: %d len: %d \r\n", s3, s3, cap(s3), len(s3))

//结果1
false
true
data: []  address: 0x0 cap: 0 len: 0
data: []  address: 0x8aed300 cap: 0 len: 0
data: []  address: 0x8aed300 cap: 0 len: 0
// 结果2
false
true
data: []  address: 0x0 cap: 0 len: 0
data: []  address: 0x163e300 cap: 0 len: 0
data: []  address: 0x163e300 cap: 0 len: 0
...
// 结果n
false
true
data: []  address: 0x0 cap: 0 len: 0
data: []  address: 0x99ba300 cap: 0 len: 0
data: []  address: 0x99ba300 cap: 0 len: 0
  • 对比发现结果
  1. nil切片指定的是一个固定的地址0x0(没有实际地址)。
  2. 所有空切片地址是一样的。
  3. nil切片和空切片地址是不一样。

字符串转成byte数组,会发⽣内存拷⻉吗?

  • 字符串转成切片,会产生拷贝。
  • 严格来说,只要是发生类型强转都会发生内存拷贝。

翻转含有中⽂、数字、英⽂字⺟的字符串

  • 中文字符码超出byte长度(-128~127)。所以要进行转换rune(-2^31~2^31-1)。
  • go没有内置翻转函数,需要自定义翻转函数。
go
func main() {
	a := "hello 世界"
	b := reverse([]rune(a))
	fmt.Println(string(b))
}

func reverse(s []rune) []rune {
	for i, j := 0, len(s)-1; j > i; i, j = i+1, j-1 {
		s[i], s[j] = s[j], s[i]
	}
	return s
}

拷⻉⼤切⽚⼀定⽐⼩切⽚代价⼤吗?

  • 首先确认这个代价是什么?
  • 拷⻉代价:将一个 slice 变量分配给另一个变量,只会复制切片三个字段data,len,cap,不会产空间大小,长度变更。
  • 所以 拷贝大切片跟小切片的代价应该是一样的。
go
//SliceHeader切片在go的底层结构。
type SliceHeader struct {
 Data uintptr //指向切片底层数组的指针,切片的存储空间
 Len  int //切片的长度
 Cap  int //容量
}

json包变量不加tag会怎么样?

  • 如果变量首字母小写,则为private。无论如何不能转,因为取不到反射信息。
  • 大写不加tag,可以正常转为json里的字段,json内字段名跟结构体内字段原名一致。
  • 大写加了tag,从struct转json的时候,json的字段名就是tag里的字段名,原字段名已经没用。
go
import (
	"encoding/json"
	"fmt"
)
type J struct {
	a string // 小写无tag
	b string `json:"B"` // 小写+tag
	C string // 大写无tag
	D string `json:"DD"` // 大写+tag
}
func main() {
	j := J{
		a: "1",
		b: "2",
		C: "3",
		D: "4",
	}
	fmt.Printf("转为json前j结构体的内容 = %+v\n", j)
	jsonInfo, _ := json.Marshal(j)
	fmt.Printf("转为json后的内容 = %+v\n", string(jsonInfo))
}

reflect(反射包)如何获取字段tag?为什么json包不能导出私有变量的tag?

go
package main

import (
	"fmt"
	"reflect"
)
type J struct {
	a string //小写无tag
	b string `json:"B"` //小写+tag
	C string //大写无tag
	D string `json:"DD" otherTag:"good"` //大写+tag
}
func main() {
	j := J{
		a: "1",
		b: "2",
		C: "3",
		D: "4",
	}

	t := reflect.TypeOf(&j).Elem()
	for i := 0; i < t.NumField(); i++ {
		fmt.Printf("结构体内第%v个字段 %v 对应的json tag是 %v , 还有otherTag? = %v \n", i+1, t.Field(i).Name, t.Field(i).Tag.Get("json"), t.Field(i).Tag.Get("otherTag"))
	}
}

go struct能不能⽐较

  • 相同struct类型的可以⽐较
  • 不同struct类型的不可以⽐较,编译都不过,类型不匹配

go四种类型转换

  • 断言
  • 强制
  • 显示
  • 隐示
go
// 显示转换:顾名思义,显示转换需要在代码中明确的编写转换语句,语法为: T(x),其中x为变量,T为转换的目标类型
// 表现方式1:数字类型-数字类型 互转
// 表现方式2:数字类型-字符串类型 互转
var a int = 123
b := int32(a)
cStr := strconv.Itoa(a)
fmt.Println(b, cStr)
// 隐示转换 与显式转换相对应的,不需要开发人员编写转换代码,由编译器自动完成。
// 表现方式1:数值常量初始化赋值、传参
// 表现方式2:结构体类型转接口类型和interface{}
...
// 类型断言: 转换语法为:newT, ok := x.(T), x为待转换的变量,T为目标类型
// 只能对interface数据进行断言
type People interface{}
type Student struct {
    Name string
}
var p People = Student{"lisi"}
s, ok := p.(Student) // 接口类型断言结构体
if ok {
    fmt.Println(s.Name)
} else {
    fmt.Println("断言p为Student类型失败")
}
// 强制转换:unsafe.Pointer强制转换
// 不推荐使用
type myBigInt int64
var n int64 = 123
var ptr *int64 = &n
fmt.Println(ptr)
// 将ptr转为unsafe.Pointer,然后转为*myBigInt
myBigIntPtr := (*myBigInt)(unsafe.Pointer(ptr))
fmt.Println(myBigIntPtr, *myBigIntPtr)

Goroutine和线程的区别?

  • Goroutine 是一个轻量级线程.
  • Goroutine所需要的内存通常只有2kb,而线程则需要1Mb.
  • 由于线程创建时需要向操作系统申请资源,并且在销毁时将资源归还,因此它的创建和销毁的开销比较大。相比之下,goroutine的创建和销毁是由go语言在运行时自己管理的,因此开销更低。
  • Goroutine调度是独立于线程的,采用GMP模型调用。

⽆缓冲 Chan 的发送和接收是否同步?

go
// ⽆缓冲的channel由于没有缓冲发送和接收需要同步.
// channel⽆缓冲时,发送阻塞直到数据被接收,接收阻塞直到读到数据
ch := make(chan int)
// 有缓冲channel不要求发送和接收操作同步.
// channel有缓冲时,当缓冲满时发送阻塞,当缓冲空时接收阻塞。
ch := make(chan int, 2)