golang的特性与区别

本文探讨了Go语言中的数组与切片的区别,包括定义和初始化方式,以及在函数传递中的行为。接着介绍了`make`和`new`的用途和差异,`make`主要用于初始化切片、map和channel,而`new`适用于分配内存。还讨论了`uint`类型溢出、`rune`和`byte`类型以及字符串的底层实现。此外,文章还涵盖了Go语言中的tag解析、函数参数传递、`defer`的工作原理以及类型转换等内容,最后提到了Go语言的并发特性,如goroutine、channel和互斥锁在并发安全中的作用。
摘要由CSDN通过智能技术生成

数组和切片的区别:

  1. 定义方式不一样, 但都只能存储一组相同类型的数据结构
  2. 初始化方式不一样,数组为定长,需要指定大小,切片比数组多一个容量(cap)属性;
  3. 在函数传递中,数组切片都是值传递

golang 中 make 和 new 的区别?

  1. new分配内存返回指针,而make初始化slice、map和channel类型,返回类型本身。
  2. make 仅用于 slice、map和 channel 的初始化,返回值为类型本身,而不是指针.
  3. new一般用于string,int和数字分配内存.

uint 类型溢出问题

  1. 数据类型超过最大容量长度

例如: uint8是0-255,超过255就会发生内存溢出

rune类型

  1. byte类型等同于int8 用来处理ascii字符
  2. rune类型相当于int32 用来处理Unicode或者utf8

golang中的字符串底层实现是通过byte数组的,中文字符在unicode下占2个字节,在utf-8编码下占3个字节,而golang默认编码正好是utf-8

golang 中解析 tag 是怎么实现的?反射原理是什么?

  1. tag通过反射来解析
  2. func TypeOf(i interface{}) Type {...}
  3. func ValueOf(i interface{}) Value {...}

调用函数传入结构体时,应该传值还是指针? (Golang 都是传值)

  1. 首先,go里面没有引用类型,所有的函数传递都是值传递,但是像slice,map,channel由于其内置结构里存在指针,因此传递以上类型会被修改原先数据。
  2. 如果想要修改结构体,就传指针
  3. 如果不修改,传值

defer 的执行顺序

  1. 多个 defer 语句,遵从后进先出(Last In First Out,LIFO)的原则,最后声明的 defer 语句,最先得到执行(压入栈头)。
  2. defer 在 return 语句之后执行,但在函数退出之前,defer 可以修改返回值。

go defer,多个 defer 的顺序,defer 在什么时机会修改返回值?

  1. defer一般用于释放资源,关闭文件,关闭链接,释放锁,收尾工作,捕获panic等
  2. defer是顺序:return->return value->defer 所以defer可以修改最终返回值
  3. 有名返回值和指针在defer里修改时,会函数影响返回值

defer 底层数据结构和一些特性?

  1. 每个 defer 语句都对应一个_defer 实例,多个实例使用指针连接起来形成一个单连表,保存在 gotoutine 数据结构中,每次插入_defer 实>例,均插入到链表的头部,函数结束再一次从头部取出,从而形成后进先出的效果。申请资源后立即使用 defer 关闭资源是个好习惯。

defer 作用域

  1. defer 延迟调用时,需要保存函数指针和参数,因此链式调用的情况下,除了最后一个函数/方法外的函数/方法都会在调用时直接执行。

defer 语句执行时,会将需要延迟调用的函数和参数保存起来。

什么是 rune类型

  1. ASCII 码只需要 7 bit 就可以完整地表示,但只能表示英文字母在内的128个字符,为了表示世界上大部分的文字系统,发明了 Unicode, 它是ASCII的超集,包含世界上书写系统中存在的所有字符,并为每个代码分配一个标准编号(称为Unicode CodePoint),在 Go 语言中称之为 rune,是 int32 类型的别名。
  2. Go 语言中,字符串的底层表示是 byte (8 bit) 序列,而非 rune (32 bit) 序列。

单引号,双引号,反引号的区别?

  1. 单引号,表示byte类型rune类型,对应 uint8int32类型,默认是 rune 类型。byte用来强调数据是raw data,而不是数字;而rune用来表示Unicode的code point。
  2. 双引号,才是字符串,实际上是字符数组。可以用索引号访问某字节,也可以用len()函数来获取字符串所占的字节长度。
  3. 反引号,表示字符串字面量,但不支持任何转义序列。字面量 raw literal string 的意思是,你定义时写的啥样,它就啥样,你有换行,它就换行。你写转义字符,它也就展示转义字符。

go struct 能不能比较?

  1. 使用 reflect.DeepEqual 方法进行比较

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

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

Go语言闭包

  1. 同一个外部调用者,再次调用时会带回上次返回的结果

可以让变量常驻内存,闭包函数不会被GC回收,同一个调用者的返回值会被保存起来,(本人怀疑)因为他转换成了函数。

如何高效地拼接字符串?

  1. Go 语言中字符串是只读的,也就意味着每次修改操作都会创建一个新的字符串。如果需要拼接多次,应使用 strings.Builder,最小化内存拷贝次数。

如何判断 map 中是否包含某个 key ?

  1. dict[“foo”] 有 2 个返回值,val 和 ok,如果 ok 等于 true,则说明 dict 包含 key “foo”,val 将被赋予 “foo” 对应的值。
    例如: if _, ok := map[key]; ok { // 存在 }

如何交换 2 个变量的值?

  1. a, b = b, a

Go 语言 tag 的用处?

  1. tag 可以理解为 struct 字段的注解,可以用来定义字段的一个或多个属性。框架/工具可以通过反射获取到某个字段定义的属性,采取相应的处理方式。tag 丰富了代码的语义,增强了灵活性。

如何判断 2 个字符串切片(slice) 是相等的?

  1. go 语言中可以使用反射 reflect.DeepEqual(a, b) 判断 a、b 两个切片是否相等,但是通常不推荐这么做,使用反射非常影响性能。
  2. 通常采用的方式如下,遍历比较切片中的每一个元素(注意处理越界的情况)

字符串打印时,%v 和 %+v 的区别

  1. %v 和 %+v 都可以用来打印 struct 的值,区别在于 %v 仅打印各个字段的值,%+v 还会打印各个字段的名称。
    但如果结构体定义了 String() 方法,%v 和 %+v 都会调用 String() 覆盖默认值。

Go 语言中如何表示枚举值(enums)

  1. 通常使用常量(const) 来表示枚举值。(iota)

空 struct{} 的用途

  1. 使用空结构体 struct{} 可以节省内存,一般作为占位符使用,表明这里并不需要一个值。
  2. 比如使用 map 表示集合时,只关注 key,value 可以使用 struct{} 作为占位符。如果使用其他类型作为占位符,例如 int,bool,不仅浪费了内存,而且容易引起歧义。
  3. 再比如,使用信道(channel)控制并发时,我们只是需要一个信号,但并不需要传递值,这个时候,也可以使用 struct{} 代替。

init() 函数是什么时候执行的?

  1. init() 函数是 Go 程序初始化的一部分。Go 程序初始化先于 main 函数,由 runtime 初始化每个导入的包,初始化顺序不是按照从上到下的导入顺序,而是按照解析的依赖关系,没有依赖的包最先初始化。
    2.每个包首先初始化包作用域的常量和变量(常量优先于变量),然后执行包的 init() 函数。同一个包,甚至是同一个源文件可以有多个 init() 函数。init() 函数没有入参和返回值,不能被其他函数调用,同一个包内多个 init() 函数的执行顺序不作保证。
    3.一句话总结: import –> const –> var –> init() –> main()

Go 语言的局部变量分配在栈上还是堆上?

  1. 由编译器决定。Go 语言编译器会自动决定把一个变量放在栈还是放在堆,编译器会做逃逸分析(escape analysis),当发现变量的作用域没有超出函数范围,就可以在栈上,反之则必须分配在堆上。

2 个 interface 可以比较吗?

  1. Go 语言中,interface 的内部实现包含了 2 个字段,类型 T 和 值 V,interface 可以使用 == 或 != 比较。2 个 interface 相等有以下 2 种情况
    两个 interface 均等于 nil(此时 V 和 T 都处于 unset 状态)
    类型 T 相同,且对应的值 V 相等。

两个 nil 可能不相等吗?

  1. 可能。
    接口(interface) 是对非接口值(例如指针,struct等)的封装,内部实现包含 2 个字段,类型 T 和 值 V。一个接口等于 nil,当且仅当 T 和 V 处于 unset 状态(T=nil,V is unset)。
    两个接口值比较时,会先比较 T,再比较 V。
    接口值与非接口值比较时,会先将非接口值尝试转换为接口值,再比较。

函数返回局部变量的指针是否安全?

  1. 这在 Go 中是安全的,Go 编译器将会对每个局部变量进行逃逸分析。如果发现局部变量的作用域超出该函数,则不会将内存分配在栈上,而是分配在堆上。

无缓冲的 channel 和 有缓冲的 channel 的区别?

  1. 对于无缓冲的 channel,发送方将阻塞该信道,直到接收方从该信道接收到数据为止,而接收方也将阻塞该信道,直到发送方将数据发送到该信道中为止。
    对于有缓存的 channel,发送方在没有空插槽(缓冲区使用完)的情况下阻塞,而接收方在信道为空的情况下阻塞。
    无缓冲的channel由于没有缓冲发送和接收需要同步.
    有缓冲channel不要求发送和接收操作同步.

什么是协程泄露(Goroutine Leak)?

  1. 协程泄露是指协程创建后,长时间得不到释放,并且还在不断地创建新的协程,最终导致内存耗尽,程序崩溃。
    常见的导致协程泄露的场景有以下几种:
    缺少接收器,导致发送阻塞
    缺少发送器,导致接收阻塞
    死锁(dead lock)
    两个或两个以上的协程在执行过程中,由于竞争资源或者由于彼此通信而造成阻塞,这种情况下,也会导致协程被阻塞,不能退出。
    无限循环(infinite loops)
    这个例子中,为了避免网络等问题,采用了无限重试的方式,发送 HTTP 请求,直到获取到数据。那如果 HTTP 服务宕机,永远不可达,导致协程不能退出,发生泄漏

for循环select时,如果通道已经关闭会怎么样?如果select中的case只有一个,又会怎么样?

  1. for循环select时,如果其中一个case通道已经关闭,则每次都会执行到这个case。
  2. 如果select里边只有一个case,而这个case被关闭了,则会出现死循环。

对已经关闭的的chan进行读写,会怎么样?为什么?

  1. 读已经关闭的 chan 能一直读到东西,但是读到的内容根据通道内关闭前是否有元素而不同。
    如果 chan 关闭前,buffer 内有元素还未读 , 会正确读到 chan 内的值,且返回的第二个 bool 值(是否读成功)为 true。
    如果 chan 关闭前,buffer 内有元素已经被读完,chan 内无值,接下来所有接收的值都会非阻塞直接成功,返回 channel 元素的零值,但是第二个 bool 值一直为 false。
    写已经关闭的 chan 会 panic

对未初始化的的chan进行读写,会怎么样?为什么?

  1. 读写未初始化的 chan 都会阻塞。

json包里使用的时候,结构体里的变量不加tag能不能正常转成json里的字段?

  1. 如果变量首字母小写,则为private。无论如何不能转,因为取不到反射信息。
    如果变量首字母大写,则为public。不加tag,可以正常转为json里的字段,json内字段名跟结构体内字段原名一致。
    加了tag,从struct转json的时候,json的字段名就是tag里的字段名,原字段名已经没用。

json包里使用的时候,会结构体里的字段边上加tag,有没有什么办法可以获取到这个tag的内容呢?

  1. tag信息可以通过反射(reflect包)内的方法获取。

Golang中除了加Mutex锁以外还有哪些方式安全读写共享变量?

  1. Golang中Goroutine 可以通过 Channel 进行安全读写共享变量,还可以通过原子性操作进行.

Go的GPM如何调度?

  1. 新创建的Goroutine会先存放在Global全局队列中,等待Go调度器进行调度,随后Goroutine被分配给其中的一个逻辑处理器P,并放到这个逻辑处理器对应的Local本地运行队列中,最终等待被逻辑处理器P执行即可。
    在M与P绑定后,M会不断从P的Local队列中无锁地取出G,并切换到G的堆栈执行,当P的Local队列中没有G时,再从Global队列中获取一个G,当Global队列中也没有待运行的G时,则尝试从其它的P窃取部分G来执行相当于P之间的负载均衡。

Go的Struct能不能比较

  1. 相同struct类型的可以比较
  2. 不同struct类型的不可以比较,编译都不过,类型不匹配

Go的Slice如何扩容

  1. slice是 Go 中的一种基本的数据结构,使用这种结构可以用来管理数据集合。但是slice本身并不是动态数据或者数组指针。
  2. slice常见的操作有 reslice、append、copy。

Go中的map如何实现顺序读取

  1. Go中map如果要实现顺序读取的话,可以先把map中的key,通过sort包排序.例如sort.Strings(keys)
    通过sort中的排序包进行对map中的key进行排序.

go map 的结构,扩容规则
Go语言中的map是一种哈希表的实现,用于存储键值对。map的结构由两个部分组成:存储指向哈希桶的指针和哈希桶数组。
当我们创建一个map时,Go会为它分配一定数量的初始哈希桶。随着元素的增加,当哈希桶的负载因子(即每个桶中的平均元素数)超过一定阈值时,map会自动进行扩容,以保持高效的性能。
在这里插入图片描述

Go值接收者和指针接收者的区别

  1. 值类型调用者
    值接收者:方法会使用调用者的一个副本,类似于“传值”。
    指针接收者:使用值的引用来调用方法。
    指针类型调用者
    值接收者:指针被引用为值
    指针接收者:实际上也是“传值”,方法里的操作会影响到调用者,类似于指针传参,拷贝了一份指针

rpc、http、tcp的关系 ?
RPC(远程过程调用)和HTTP(超文本传输协议)是两种不同的通信协议在这里插入图片描述

go的常见panic,有哪些panic不能被recover ?
在这里插入图片描述

go 各种类型的空值是什么?
在这里插入图片描述
在这里插入图片描述

设计一个 savego,安全地启动一个协程
在这里插入图片描述
如何等待多个协程执行完毕?用最简单的方式实现
在这里插入图片描述

http长链接和websocket有什么区别?
在这里插入图片描述

map 是线程安全的么?介绍一下线程安全的map
在这里插入图片描述

短链接能不能传递大文件?会有什么问题?
在这里插入图片描述

golang 生产者消费者,如何实现 channel 安全关闭?
在这里插入图片描述

go 一个协程中起另一个协程,子协程panic,主协程的 recover 是否能捕获?
在这里插入图片描述

mysql索引的结构,b树、b+树的区别和优点?如何降低B+树的高度?
在这里插入图片描述

阐述事务隔离级别?他们怎么解决脏读重复读和幻读的?

  1. mysql 可重复读没有彻底解决幻读,用间隙锁解决了当前读下的幻读,但如果没加间隙锁,只是依赖 mvcc,select 时可以不读到幻读,但 update/delete 的当前读状态下,就会出现幻读
  2. 解决的话,select 时手动for update成为当前读,加间隙锁,可以解决阻塞其他事务的插入行为,避免幻读。

package main

import (
	"net/http"
	"sync"
)

func main() {
	wg := sync.WaitGroup{}
	for true {
		for i := 0; i < 1500; i++ {
			wg.Add(1)
			go func(i int) {
				pdfUrl := "http://www..gov.cn/xxgk/fdzdgknr/zdmsxx/fp/201808/P020180807353570565427.pdf"
				_, err := http.Get(pdfUrl)
				if err != nil {
				}
				println(i)
				wg.Done()
			}(i)
		}
		wg.Wait()
	}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

kentrl

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值