Go必知必会五连问
1.Go 语言最主要的特性:
- 自动垃圾回收
- 更丰富的内置类型
- 函数多返回值
- 错误处理
- 匿名函数和闭包
- 类型和接口
- 并发编程
- 反射
- 语言交互性
2.讲讲go关键词的使用
go关键词可以开启一个协程,底层使用的是runtime.newproc(size,f,args),size为参数占用的空间大小,f 为要调用的函数,args为 f 的参数。每次协程获得cpu执行的时候会先进行检测,查看当前栈的大小是否足够,如果不足则会暂停执行,通过go的运行时库获取一个新的足够大的栈空间,将旧的内容拷贝到新栈中,然后恢复函数执行。栈的收缩则是在垃圾回收的过程中实现的,当检测到栈只使用了1/4不到时,栈缩小为原来的1/2.
3.了解go中的fallthrough的用法吗
用法:Go里面switch默认相当于每个case最后带有break,匹配成功后不会自动向下执行其他case,而是跳出整个switch, 但是可以使用fallthrough强制执行后面的case代码。
func GoFallthrough(x int) string { switch x { case 1: fmt.Println("111") fallthrough case 2: fmt.Println("222") fallthrough case 3: fmt.Println("333") fallthrough default: fmt.Println("switch end...") } return "" } func main() { fmt.Println(GoFallthrough(1)) } /* 运行结果: 111 222 333 switch end... Process finished with the exit code 0 */
func GoFallthrough(x int) string { switch x { case 1: fmt.Println("111") fallthrough case 2: fmt.Println("222") fallthrough case 3: fmt.Println("333") fallthrough default: fmt.Println("switch end...") } return "" } func main() { fmt.Println(GoFallthrough(1)) } /* 运行结果: 111 222 333 switch end... Process finished with the exit code 0 */
Tips:作者第一次接触这个关键字还是看同事的代码,然后自己跑了测试用例感受一下也就记住了,这个关键词的用法给我的感觉就是“贯穿伤”,记住这个词就够了。比对两个代码结果 仅注释了一行的fallthrough结果也大有不同,当然 default 分支可以出现在任何顺序,但最好将它放在最后。
4.讲讲defer关键词的使用
defer一般用于资源的释放和异常的捕捉, 作为Go语言的特性之一.
defer关键词会将其后面跟随的语句进行延迟处理.意思就是说跟在defer后面的语句将会在程序进行最后的return后再执行。在defer归属的函数即将返回时,将延迟处理的语句按defer的逆序执行,也就是说,先被defer的语句最后执行,最后被 defer 的语句,最先被执行。当有多个 defer 行为被注册时,它们会以逆序执行(类似栈,即后进先出), 相当于开辟了一个延时调用栈
常用于的地方:
1.资源的释放
//一般在读取文件中 func CopyFile(dstName, srcName string) (written int64, err error) { src, err := os.Open(srcName) if err != nil { return } dst, err := os.Create(dstName) if err != nil { return } dst.Close() src.Close() return } /* 在程序最开始, os.Open及os.Create打开了两个文件资源描述符, 并在最后通过file.Close方法得到释放,在正常情况下,该程序能正常运行, 一旦在dstName文件创建过程中出现错误, 程序就直接返回,src资源将得不到释放。 因此需要在所有错误退出时释放资源, 即修改为如下代码才能保证其在异常情况下的正确性。 dst,err :=os.Create(dstName) if err !=nil { src.Close() return } */
2.异常的捕捉
程序在运行时可能在任意的地方发生panic异常,例如算术除0错误、内存无效访问、数组越界等,这些错误会导致程序异常退出。在很多时候,我们希望能够捕获这样的错误,同时希望程序能够继续正常执行。一些语言采用try…catch语法,当try块中发生异常时,可以通过catch块捕获,Go语言使用了特别的方式处理这一问题。defer的特性是无论后续函数的执行路径如何以及是否发生了panic,在函数结束后一定会得到执行,这为异常捕获提供了很好的时机。异常捕获通常结合recover函数一起使用.
5.讲讲go语言中的panic和err有什么区别?
错误:指的是可能出现问题的地方出现了问题。比如在打开一个文件时失败,这种情况在人们的意料之中 。
异常:指的是不应该出现问题的地方出现了问题。比如引用了空指针,这种情况在人们的意料之外。可见,错误是业务过程的一部分,而异常不是.
go语言中,错误是一种数据类型,内置有error
类型,和其他数据类型一样使用。且内置了error接口,提供了非常简单的错误处理机制
Go必知必会力扣每日一题
《1047. 删除字符串中的所有相邻重复项》
题目:给出由小写字母组成的字符串 S
,重复项删除操作会选择两个相邻且相同的字母,并删除它们。在 S 上反复执行重复项删除操作,直到无法继续删除。在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。
示例:
输入:"abbaca"
输出:"ca"
解释:
例如,在 "abbaca" 中,我们可以删除 "bb" 由于两字母相邻且相同,这是此时唯一可以执行删除操作的重复项。之后我们得到字符串 "aaca",其中又只有 "aa" 可以执行重复项删除操作,所以最后的字符串为 "ca"。
解题思路:(栈)
1.多组相邻重复项,我们无论先删除哪一项,都不会影响最终结果。
2.删除当前项是需要拿上一项出来对比的,所以我们需要用临时栈存放之前的内容。
3.当前项和栈顶一致,弹出栈顶抵消即可。若不一致,压入栈留存,供后续使用。
(感觉可以有更好的解题方法,但是可以看看击败了5%的我怎么做得吧,全网寻找5%)
func removeDuplicates(s string) (res string) { //用栈的思想,用切片最后不用逆置 //用切片起到一个过渡作用 temp :=make([]byte,0) for _,v :=range s { //当用于过渡的temp长度为0,或者 遍历的元素不等于temp的尾元素 if len(temp) == 0|| byte(v) !=temp[len(temp)-1] { //就将这个元素加入到temp temp =append(temp,byte(v)) continue } //如果temp的尾元素等于遍历的元素v 那就将temp尾元素裁掉 if byte(v) == temp[len(temp)-1]{ temp =temp[0:len(temp)-1] continue } } //注意这里用的切片,存进去的顺序是正确的,不用再逆置,如果是栈需要逆置 //将byte类型转成string然后再拼装起来 //用栈的话 for 循环就要这么写(因为栈只能先进后出,尾巴上一个出口) for i:=len(temp)-1; i>=0 ;i--{ //法一:自己这么写的,最后感觉自己是傻子 //for i:=0;i<len(temp) ;i++{ // sss := string(temp[i]) // res +=sss //} //return res //法二: return string(temp) }
分享工作随笔,记录
今天讲的内容太多了,那就下一期让我们轻松点,发一篇好朋友33w天翼云的面经吧~
三道go面试题并进行解答
1、WaitGroup 用法
一个 WaitGroup 对象可以等待一组协程结束。使用方法是:
- main 协程通过调用 wg.Add(delta int) 设置 worker 协程的个数,然后创建 worker 协程;
- worker 协程执行结束以后,都要调用 wg.Done();
3.main 协程调用 wg.Wait() 且被 block,直到所有 worker 协程全部执行结束后返回。
2、WaitGroup 实现原理
1.WaitGroup 主要维护了 2 个计数器,一个是请求计数器 v,一个是等待计数器 w,二者组成一个 64bit 的值,请求计数器占高 32bit,等待计数器占低32bit。
2.每次 Add 执行,请求计数器 v 加 1,Done 方法执行,请求计数器减 1,v 为0 时通过信号量唤醒 Wait()。
3、什么是 sync.Once
1.Once 可以用来执行且仅仅执行一次动作,常常用于单例对象的初始化场景。
2.Once 常常用来初始化单例资源,或者并发访问只需初始化一次的共享资源,或者在测试的时候初始化一次测试资源。
3.sync.Once 只暴露了一个方法 Do,你可以多次调用 Do 方法,但是只有第一次调用 Do 方法时 f 参数才会执行,这里的 f 是一个无参数无返回值的函数。
4、什么操作叫做原子操作
原子操作即是进行过程中不能被中断的操作,针对某个值的原子操作在被进行的过程中,CPU 绝不会再去进行其他的针对该值的操作。为了实现这样的严谨性,原子操作仅会由一个独立的 CPU 指令代表和完成。原子操作是无锁的,常常直接通过 CPU 指令直接实现。事实上,其它同步技术的实现常常依赖于原子操作。
对No.1订正:
文章发出后@smile同学引用文章:https://www.tapirgames.com/blog/golang-has-no-reference-values 对go语言是否有“引用类型”产生讨论,在该文中提出用go底层实现方式“引用类型”也并非引用类型,提出了“值类型和非值类型”的概念,是一个很好的观点,对@smile致以感谢,其他同学怎么理解go语言中的“值类型和引用类型”欢迎在评论区给出讨论!下面是文章重点两处翻译的图片,请大家参考。