[面试] Golang 面试题

[面试] Golang 面试题本文章收录于:后端工程师面试题目总结(提供参考答案)目录1.make与new的区别2.简要描述go中的main和init函数的区别3.下面的代码输出什么,若会报错报什么错?4.这段代码会输出什么?5、简述channel和mutex锁机制的原理异同与使用场景6、sync.WaitGroup的使用场景?7、写一段闭包代码,阐述其作用8、执行这段代码会发生什…

大家好,又见面了,我是你们的朋友全栈君。

本文章收录于:后端工程师面试题目总结(提供参考答案)

目录

1. make与new的区别

2. 简要描述go中的main和init函数的区别

3. 下面的代码输出什么,若会报错报什么错?

4.  这段代码会输出什么?

5、简述channel和mutex锁机制的原理异同与使用场景

6、sync.WaitGroup的使用场景?

7、写一段闭包代码,阐述其作用

8、执行这段代码会发生什么?

9、单例实现

10、这段代码输出什么?

11、这段代码可以编译过吗,如果会错是在哪一行?

12、ABCD哪一行会报错?

13、下面的代码会怎样输出?每次输出结果一样吗?


1. make与new的区别

Make 用于map、slice 和channel几种类型的内存分配。并且返回一个有初始值的对象,注意不是指针。

注:channel在make之后打印出来的也是内存地址,是个特殊类型。

New 用于使用type声明的类型的内存分配。new(T)分配了零值填充的T类型的内存空间,并且返回其地址,即一个T类型的值。用Go的术语说,它返回了一个指针,指向新分配的类型T的零值。有一点非常重要:new返回指针。

2. 简要描述go中的main和init函数的区别

首先,这两个函数应用位置不同,init函数可以应用于所有的package,main只能应用于 package main,需要注意的是虽然一个package中可以写任意多个init,但是无论是从可读性还是可维护性来说,都是不推荐的; 其次,这两个函数定义时都不能有任何的参数和返回值, 最后,个人理解,init函数为初始化操作,main函数为程序入口。

一图胜千言

[面试] Golang 面试题

3. 下面的代码输出什么,若会报错报什么错?

func main(){
	/*
	先defer的后执行
	recover后输出panic中的信息
	*/
	defer func(){
		If err:=recover();err!=nil{
			fmt.Print(err)
		} else {
			fmt.Print("no")
		}
	}()
	defer func(){
		panic("1111111111111")
	}()
	panic("22222222222")
}

答案:不会报错,而是被recover捕捉,打印出1111111111111

4.  这段代码会输出什么?

[面试] Golang 面试题

答案:先打印出111111111,然后报错(死锁)

说明:两个协程都是读取c2中的元素,然后塞入c1, 然而c2又是无缓冲且没有任何协程在往c2中写数据,所以第一个协程读c2的时候就导致死锁,因为c2永远读不出数据。 如何改能让程序不报错?在第一个协程前加一个协程:

gofunc(){ 
    c2<-1 
}()

5、简述channel和mutex锁机制的原理异同与使用场景

channel原理: 当channel能存放的元素数量为0时表示为阻塞型channel。当管道无数据时,需要从管道取数据的协程会被阻塞,不会向下执行。 所以可以通过多个协程应用同一个channel,从而实现协程间的同步。

channel使用场景: 1. 需要协程通信时     2. 需要管道传输数据时。

Mutex原理: 互斥锁用来防止资源竞争,多个协程使用同一把排它锁时,原来是并发运行的话将变为线性运行。

排它锁针对任意操作都是排它的,没有读写区分。 若一个goroutine获得锁,则其他goroutine会一直阻塞到他释放锁后才能获得锁。

mutex使用场景: 解决协程并发时对同一资源的竞争问题。

6、sync.WaitGroup的使用场景?

程序中需要并发,需要创建多个goroutine,并且一定要等这些并发全部完成后才继续接下来的程序执行.WaitGroup的特点是Wait()可以用来阻塞直到队列中的所有任务都完成时才解除阻塞,而不需要sleep一个固定的时间来等待.但是其缺点是无法指定固定的goroutine数目(也就是协程池功能).

7、写一段闭包代码,阐述其作用

package main

import "fmt"

type logClosure func(format string, v...interface{})

func LoggerWrapper(logType string)logClosure{
	return func(format string, v...interface{}) {
		fmt.Printf(fmt.Sprintf("[%s] %s", logType, format), v...,)
		fmt.Println()  // 换行
	}
}

func main(){
	info_logger := LoggerWrapper("INFO")
	warning_logger := LoggerWrapper("WARNING")
	info_logger("this is a %s log", "info")
	warning_logger("this is a %s log", "warning")
}

作用:给访问外部函数定义的内部变量创造了条件,将关于的函数的一切封闭到了函数内部,减少了全局变量。

使用场景:

每次调用函数A时都要改变全局变量B,且B只与A相关,以往没有闭包时只能将B定义为全局变量;而现在可以将B定义为A的内部变量,将真正的执行函数作为闭包放在A内部去执行。

8、执行这段代码会发生什么?

type ConfigOne struct {
	Daemon string
}

func (c *ConfigOne) String() string {
	return fmt.Sprintf("print: %v", c)
}
c := &ConfigOne{}
_ = c.String()

答案:如果类型实现 String(),%v 和%+v 格式将使用 String() 的值, 这里会造成死递归

9、单例实现

var once sync.Once

type manager struct {name string}
var single *manager

func Singleton() *manager{
	once.Do(func() {
		single = &manager{"a"}
	})
	return single
}

注意一个once实例只能用一次。

10、这段代码输出什么?

fmt.Println(len(“你好bj!”)) 

答案:是9, len方法返回字符串的字节长度。

11、这段代码可以编译过吗,如果会错是在哪一行?

type Test struct {
	Name string
}
var list map[string]Test
func main() {
	list = make(map[string]Test)
	name := Test{"xiaoming"}
	list["name"] = name
	
	fmt.Println(list["name"].Name)
	list["name"].Name = "Hello"
	fmt.Println(list["name"])
}

不能!倒数第三行报错: cannot assign to struct field list[“name”].Name in map

因为map的value不是指针。首先,map本来存储的就是value的“初始指针值”,可以打印list[“name”].Name,  但不能通过取值的方式来修改。 因为当map扩容时,内部元素会在内存中移动, 移动之后list[“name”].Name获取到的值依然有效,但获取到的指针是无效的,如果允许这样赋值,那之后再打印list[“name”].Name 是获取不到修改后的值的。 而当value是指针时,也就是说list[“name”]是指针,list[“name”].Name就是指针内部的指针,值改变后,list[“name”]仍然获取到的是原始数据指针,也就仍然可以获取到list[“name”].Name。

12、ABCD哪一行会报错?

type S struct {
}

func f(x interface{}) {
}

func g(x *interface{}) {
}

func main() {
	s := S{}
	p := &s
	f(s) //A
	g(s) //B
	f(p) //C
	g(p) //D
}

B和D行。interface可以接受任意类型参数,包括指针。但是*interface{} 就只能接受*interface{}

13、下面的代码会怎样输出?每次输出结果一样吗?

const N = 10
var wg = &sync.WaitGroup{}
func main() {

	for i := 0; i < N; i++ {
		go func(i int) {
			wg.Add(1)
			println(i)
			defer wg.Done()
		}(i)
	}
	wg.Wait()
}

大部分时候可以输出0~9,但是顺序都不一样,有时只能输出部分数字。 因为wg.Add被包含在协程中,到达wg.Wait时,已经执行到wg.Add(1)的协程数量是无法确定的,这中间没有耗时也没有等待的操作。 虽然不会报错,但waitgroup不应该被这样使用!Wg.Add语句应该在协程之外。

 

文中的题目部分来自网络,由个人整理而成,如有错误请指出。

 

—————————————————————-  saosao的分割线  ————————————————————————

博主的Coding部落群 588757596,快来一起玩耍!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/148315.html原文链接:https://javaforall.net

赞 (0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • MATLAB实现线性插值interp1的功能

    MATLAB实现线性插值interp1的功能1.关于插值插值,它根据已知的数据序列(也可以理解为坐标中一连串离散的点),找到其中的规律;然后根据找到的这个规律,来对其中尚未有数据记录的点进数值的估计。2.关于线性插值线性插值是一种针对一维数据的插值方法,它根据一维数据序列中需要插值的点的左右邻近两个数据点来进行数值的估计。当然了它不是求这两个点数据大小的平均值(当然也有求平均值的情况),而是根据到这两个点的距离来分配它们的比重的。而对于一些边缘处的点也需要使用到外插:即通过找出最近的两个点,通过建立该两点之间的一元一次线性方程通过带入x即可以得

    2022年5月20日
  • linux识别sd卡_kafka存储结构

    linux识别sd卡_kafka存储结构浅析Linux下sdio接口对sd卡硬件检测流程staticstructpxamci_platform_dataluther_mci_platform_data={.detect_delay=20,//检测到sd设备插入之后,延时detect_delay个tick之后,执行函数.ocr_mask=MMC_VDD_32_33|MMC_VDD_33_34,.init…

    2022年10月3日
  • 基于51单片机+LD3320语音模块+SYN6288语音合成——语音识别智能分类垃圾桶「建议收藏」

    基于51单片机+LD3320语音模块+SYN6288语音合成——语音识别智能分类垃圾桶「建议收藏」语音识别智能分类垃圾桶基本介绍器件51单片机LD3320语音模块SYN6288语音合成SG90舵机(4个)usb-ttl模块垃圾桶四个(4个)面包板(建议用)实现思路与接线实现流程图接线呈现图代码编写语音模块(部分代码)语音模块串口调试结果51单片机代码(部分代码 )项目展示基本介绍这个一个基于51单片机做的一个语音识别分类智能垃圾桶,通过我们说话来对垃圾词语进行分类。比如:垃圾桶(一级指令)易拉罐(垃圾词语),我们通过说话说出关键字让语音模块接收到——语音模块通过串口发指令给51单片机,针对

    2022年6月26日
  • 服务器安全-使用ipset 和iptables禁止国外IP访问[通俗易懂]

    服务器安全-使用ipset 和iptables禁止国外IP访问[通俗易懂]服务器遭受ddos攻击,发现发部分IP来自国外……IPSET安装yuminstallipset//安装ipsetipsetcreatechinahash:nethashsize10000maxelem1000000//创建地址表ipsetaddchina172.18.0.0/16ipsetlistchina获取国内IP地址段并导入viipset_china.sh#!/bin/bashrm-rfcn.zonewget..

    2022年10月7日
  • fp5138升压电路图_大电流升压芯片

    fp5138升压电路图_大电流升压芯片外置MOS大功率升压IC

    2022年10月6日
  • Tomcat的下载及安装

    Tomcat的下载及安装一、Tomcat下载1、打开Tomcat官网,默认进入官网首页,官网地址为:https://tomcat.apache.org/2、左侧的导航栏可以看到Download(下载),以及最近相关版本的Tomcat(9、8、7…),这里选择的是Tomcat8,点击进入3、进入tomcat8的相关页面后会显示一些该版本的信息,可以忽略,鼠标下滑进行查找4、这里我们可以看到…

    2022年5月19日

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号