后端腾讯互娱面经,希望别凉Go语言学习专栏

接口在Golang中扮演着连接不同类型之间的桥梁,它定义了一组方法的集合,而不关心具体的实现。接口的作用主要体现在以下几个方面:

多态性:

接口允许不同的类型实现相同的方法,从而实现多态性。这意味着我们可以使用接口类型来处理不同的对象,而不需要关心具体的类型。

packagemainimport"fmt"typeAnimalinterface{Sound()string}typeDogstruct{}func(dDog)Sound()string{return"Woof!"}typeCatstruct{}func(cCat)Sound()string{return"Meow!"}funcmain(){animals:=[]Animal{Dog{},Cat{}}for_,animal:=rangeanimals{fmt.Println(animal.Sound())}}在上面的示例中,我们定义了一个Animal接口,它包含了一个Sound()方法。然后,我们实现了Dog和Cat两个结构体,分别实现了Sound()方法。通过将Dog和Cat类型赋值给Animal接口类型,我们可以在循环中调用Sound()方法,而不需要关心具体的类型。这就体现了接口的多态性,不同的类型可以实现相同的接口方法。

解耦合:

接口可以将抽象与实现分离,降低代码之间的耦合度。通过定义接口,我们可以将实现细节隐藏起来,只暴露必要的方法,从而提高代码的可维护性和可读性。

packagemainimport"fmt"typePrinterinterface{Print(string)}typeConsolePrinterstruct{}func(cpConsolePrinter)Print(messagestring){fmt.Println(message)}typeFilePrinterstruct{}func(fpFilePrinter)Print(messagestring){//将消息写入文件fmt.Println("Writingmessagetofile:",message)}funcmain(){printer:=ConsolePrinter{}printer.Print("Hello,World!")printer=FilePrinter{}printer.Print("Hello,World!")}在上面的示例中,我们定义了一个Printer接口,它包含了一个Print()方法。然后,我们实现了ConsolePrinter和FilePrinter两个结构体,分别实现了Print()方法。通过将不同的结构体赋值给Printer接口类型的变量,我们可以在主函数中调用Print()方法,而不需要关心具体的实现。这样,我们可以根据需要轻松地切换不同的打印方式,实现了解耦合。

可扩展性:

packagemainimport"fmt"typeShapeinterface{Area()float64}typeRectanglestruct{Widthfloat64Heightfloat64}func(rRectangle)Area()float64{returnr.Width*r.Height}typeCirclestruct{Radiusfloat64}func(cCircle)Area()float64{return3.14*c.Radius*c.Radius}funcmain(){shapes:=[]Shape{Rectangle{Width:5,Height:10},Circle{Radius:3}}for_,shape:=rangeshapes{fmt.Println("Area:",shape.Area())}}在上面的示例中,我们定义了一个Shape接口,它包含了一个Area()方法。然后,我们实现了Rectangle和Circle两个结构体,分别实现了Area()方法。通过将不同的结构体赋值给Shape接口类型的切片,我们可以在循环中调用Area()方法,而不需要关心具体的类型。这样,当我们需要添加新的形状时,只需要实现Shape接口的Area()方法即可,而不需要修改已有的代码。这就实现了代码的可扩展性。

不包含任何字段的结构体,就叫做空结构体。

空结构体的特点:

在Go语言中,虽然没有内置Set集合类型,但是我们可以利用map类型来实现一个Set集合。由于map的key具有唯一性,我们可以将元素存储为key,而value没有实际作用,为了节省内存,我们可以使用空结构体作为value的值。

packagemainimport"fmt"typeSet[Kcomparable]map[K]struct{}func(sSet[K])Add(valK){s[val]=struct{}{}}func(sSet[K])Remove(valK){delete(s,val)}func(sSet[K])Contains(valK)bool{_,ok:=s[val]returnok}funcmain(){set:=Set[string]{}set.Add("程序员")fmt.Println(set.Contains("程序员"))//trueset.Remove("程序员")fmt.Println(set.Contains("程序员"))//false}用于通道信号空结构体常用于Goroutine之间的信号传递,尤其是不关心通道中传递的具体数据,只需要一个触发信号时。例如,我们可以使用空结构体通道来通知一个Goroutine停止工作:

packagemainimport("fmt""time")funcmain(){quit:=make(chanstruct{})gofunc(){//模拟工作fmt.Println("工作中...")time.Sleep(3*time.Second)//关闭退出信号close(quit)}()//阻塞,等待退出信号被关闭<-quitfmt.Println("已收到退出信号,退出中...")}作为方法接收器有时候我们需要创建一组方法集的实现(一般来说是实现一个接口),但并不需要在这个实现中存储任何数据,这种情况下,我们可以使用空结构体来实现:

typePersoninterface{SayHello()Sleep()}typeCMYstruct{}func(cCMY)SayHello(){fmt.Println("你好,世界。")}func(cCMY)Sleep(){fmt.Println("晚安,世界...")}Go原生支持默认参数或可选参数吗,如何实现什么是默认参数默认参数是指在函数调用时,如果没有提供某个参数的值,那么使用函数定义中指定的默认值。这种语言特性可以减少代码量,简化函数的使用。

在Go语言中,函数不支持默认参数。这意味着如果我们想要设置默认值,那么就需要手动在函数内部进行处理。

例如,下面是一个函数用于计算两个整数的和:

funcAdd(aint,bint)int{returna+b}如果我们希望b参数有一个默认值,例如为0,那么可以在函数内部进行处理:

funcAddWithDefault(aint,bint)int{ifb==0{b=0}returna+b}上面的代码中,如果b参数没有提供值,那么默认为0。通过这种方式,我们就实现了函数的默认参数功能。

需要注意的是,这种处理方式虽然可以实现默认参数的效果,但会增加代码复杂度和维护难度,因此在Go语言中不被推荐使用。

可选参数是指在函数调用时,可以省略一些参数的值,从而让函数更加灵活。这种语言特性可以让函数更加易用,提高代码的可读性。

在Go语言中,函数同样不支持可选参数。但是,我们可以使用可变参数来模拟可选参数的效果。

下面是一个函数用于计算任意个整数的和:

funcAdd(nums...int)int{sum:=0for_,num:=rangenums{sum+=num}returnsum}上面的代码中,我们使用...int类型的可变参数来接收任意个整数,并在函数内部进行求和处理。

如果我们希望b和c参数为可选参数,那么可以将它们放到nums可变参数之后:

funcAddWithOptional(aint,nums...int)int{sum:=afor_,num:=rangenums{sum+=num}returnsum}上面的代码中,我们首先将a参数赋值给sum变量,然后对可变参数进行求和处理。如果函数调用时省略了nums参数,则sum等于a的值。

需要注意的是,使用可变参数模拟可选参数的效果虽然能够实现函数的灵活性,但也会降低代码的可读性和规范性。因此在Go语言中不被推荐使用。

在Go中,defer语句用于延迟(defer)函数的执行,通常用于在函数执行结束前执行一些清理或收尾工作。当函数中存在多个defer语句时,它们的执行顺序是“后进先出”(LastInFirstOut,LIFO)的,即最后一个被延迟的函数最先执行,倒数第二个被延迟的函数次之,以此类推。

在Go中,defer语句中的函数在执行时会被压入一个栈中,当函数执行结束时,这些被延迟的函数会按照后进先出的顺序执行。这意味着在函数中的defer语句中的函数会在函数执行结束前执行,包括在return语句之前执行。

协程(Goroutine)之间的信息同步通常通过通道(Channel)来实现。通道是Go语言中用于协程之间通信的重要机制,可以安全地在不同协程之间传递数据,实现协程之间的信息同步。

一些常见的方法来实现协程之间的信息同步:

最开始的是GM模型没有P的,是M:N的两级线程模型,但是会出现一些性能问题:

这里提供几种方案:

explain的用法:

explainselect*fromgateway_apps;返回结果:

下面对上面截图中的字段一一解释:

1、id:select查询序列号。id相同,执行顺序由上至下;id不同,id值越大优先级越高,越先被执行。

2、select_type:查询数据的操作类型,其值如下:

3、table:显示该行数据是关于哪张表

4、partitions:匹配的分区

5、type:表的连接类型,其值,性能由高到底排列如下:

6、possible_keys:显示MySQL理论上使用的索引,查询涉及到的字段上若存在索引,则该索引将被列出,但不一定被查询实际使用。如果该值为NULL,说明没有使用索引,可以建立索引提高性能

7、key:显示MySQL实际使用的索引。如果为NULL,则没有使用索引查询

8、key_len:表示索引中使用的字节数,通过该列计算查询中使用的索引的长度。在不损失精确性的情况下,长度越短越好显示的是索引字段的最大长度,并非实际使用长度

9、ref:显示该表的索引字段关联了哪张表的哪个字段

10、rows:根据表统计信息及选用情况,大致估算出找到所需的记录或所需读取的行数,数值越小越好

11、filtered:返回结果的行数占读取行数的百分比,值越大越好

12、extra:包含不合适在其他列中显示但十分重要的额外信息,常见的值如下:

Redis中提供了三种过期删除的策略

优点:

对CPU是友好的,只有在取出键值对的时候才会进行过期检查,这样就不会把CPU资源花费在其他无关紧要的键值对的过期删除上。

缺点:

如果一些键值对永远不会被再次用到,那么将不会被删除,最终会造成内存泄漏,无用的垃圾数据占用了大量的资源,但是服务器却不能去删除。

惰性删除,当一个键值对过期的时候,只有再次用到这个键值对的时候才去检查删除这个键值对,也就是如果用不着,这个键值对就会一直存在。

定期删除是对上面两种删除策略的一种整合和折中

1、采样一定个数的key,采样的个数可以进行配置,并将其中过期的key全部删除;

2、如果过期key的占比超过可接受的过期key的百分比,则重复删除的过程,直到过期key的比例降至可接受的过期key的百分比以下。

定期删除,通过控制定期删除执行的时长和频率,可以减少删除操作对CPU的影响,同时也能较少因过期键带来的内存的浪费。

执行的频率不太好控制

频率过快对CPU不友好,如果过慢了就会对内存不太友好,过期的键值对不能及时的被删除掉

同时如果一个键值对过期了,但是没有被删除,这时候业务再次获取到这个键值对,那么就会获取到被删除的数据了,这肯定是不合理的。

上面讨论的三种策略,都有或多或少的问题。Redis中实际采用的策略是惰性删除加定期删除的组合方式。

定期删除,获取CPU和内存的使用平衡,针对过期的KEY可能得不到及时的删除,当KEY被再次获取的时候,通过惰性删除再做一次过期检查,来避免业务获取到过期内容。

Redis共有5种基本数据类型:String(字符串)、List(列表)、Set(集合)、Hash(散列)、Zset(有序集合)。

Zset的两种实现方式:

skiplist优势

skiplist原理

如果节点能够跳过一些节点,连接到更靠后的节点就可以优化插入速度:

在上面这个结构中,插入23的过程是

上面这张图就是跳表的初步原理,但一个元素插入链表后,应该拥有几层连接呢?跳表在这块的实现方式是随机的,也就是23这个元素插入后,随机出一个数,比如这个数是3,那么23就会有如下连接:

下面这张图展示了如何形成一个跳表

在上述跳表中查找/插入23的过程为:

总结一下跳表原理:

使用场景:

对数据进行去重,例如将所有重复的单词从文本中删除。

我们搞了一个免费的面试真题共享群,互通有无,一起刷题进步。

THE END
1.从JDK8飞升到JDK17,再到未来的JDK21/*** 定义一个抽象密封类Pet,它的实现类只能是Dog, Cat这两个,其他的实现类均不允许*/publicabstractsealedclassPetpermitsDog,Cat{}finalclassDogextendsPet{}finalclassCatextendsPet{}//密封的类和接口限制了其他类或接口可以扩展或实现它们publicsealedinterfaceShape{finalclassPlanetimplementsShape{}final...https://developer.aliyun.com/article/1084638
1....练习:实现一个接口并使用不同的实现类来展示多态的效果首先,我们定义一个接口PaymentMethod,它包含一个方法pay。 // 定义接口 PaymentMethod interfacePaymentMethod{ voidpay(doubleamount);// 支付方法,接受一个金额参数 } 2. 实现接口的类 接下来,我们定义几个实现了PaymentMethod接口的类,分别代表不同的支付方式。 https://blog.csdn.net/thinking_chou/article/details/143758931
2.Shape接口(Microsoft.Office.Interop.Word)MicrosoftLearn确定在调整形状大小时是否可以彼此独立地更改形状的高度和宽度,或者它是否保留其原始比例。 Name 返回或设置指定对象的名称。 Nodes 返回一个 ShapeNodes 集合,该集合表示指定形状的几何说明。 OLEFormat 返回一个 OLEFormat 对象,该对象表示 OLE 特征 (指定形状的链接) 。 Parent 返回一个对象,代表指定对象的父对象...https://technet.microsoft.com/zh-cn/library/microsoft.office.interop.word.shape(v=office.11).aspx
3.JAVA篇之类和对象编程语言Java是一门面向对象的编程语言(Object Oriented Program,简称OOP),面向对象编程的核心思想是将数据(属性)和操作数据的方法封装成一个整体,即对象。对象之间通过信息传递来相互协作,从而实现特定的功能。 1.1 面向对象和面向过程 面向过程注重解决问题的步骤和操作,面向对象是把构成问题事务分解成各个对象,建立对象的目的...http://www.licqi.com/artikel/24239.html
4.设计一个名为Rectangle的类表示矩形。这个类包括:(1)两个名为...设计一个名为Swimmable的接口,其中包含void swim()方法。设计另一个名为Flyable的接口,其中包含void fly()方法。定义一个Duck类实现上述两个接口。定义测试类,演示接口类型的使用。 设计一个名为Swimmable的接口,其中包含void swim()方法。设计另一个名为Flyable的接口,其中包含void fly()方法。定义一个Duck类实现...https://www.xuesai.cn/souti/IY2DSDTI.html
5.Java中抽象类和接口的用法详解java在Java中,接口可以看成是:多个类的公共规范,是一种引用数据类型。 Java接口是一系列方法的声明,是一些方法特征的集合,一个接口只有方法的特征没有方法的实现,因此这些方法可以在不同的地方被不同的类实现,而这些实现可以具有不同的行为(功能)。 接口可以理解为一种特殊的类,里面全部是由全局常量和公共的抽象方法...https://www.jb51.net/article/259951.htm
6.强化学习八股文温柔一刀的技术博客设计模式的分类 1. 创建型模式 1. 单例模式(一个实例) 2. 工厂模式(工厂提供创建类的方法) 3. 抽象工厂模式(抽象工厂接口,要有实现具体工厂类) 4. 建造者模式(xxxBuilder、是特定的类) 1. 指挥者Director 1. 有个Builder对象() 2. 有个执行组装builder的方法 3. 有个获取Computer的方法(getComputer) ...https://blog.51cto.com/u_14120/12566685
7.Alibaba最新1000多道Java面试题汇总详解,收藏起来慢慢刷!33、接口是否可继承(extends)接口?抽象类是否可实现(implements)接口?抽象类是否可继承具体类(concreteclass)? 34、一个”.java”源文件中是否可以包含多个类(不是内部类)?有什么限制? 35、Java 中的 final 关键字有哪些用法? 二、Java 集合/泛型面试题 ...https://maimai.cn/article/detail?fid=1728969401&efid=esjJLvGGL4fAr1LArgq_cQ
8.一篇文章入门多物理场有限元(全篇)按照面向对象设计方法,可以设计出四个接口基类;按照阶数来分,至少有0阶,1阶和2阶,阶数可以作为属性放在实现类中;引入自由度,单元设计会稍微复杂一点,每个节点自由度根据特定单元类型不同而不同,比如有些节点只考虑平动,不考虑转动,有些节点只考虑磁场不考虑电场;再引入多物理场,单元设计会更加复杂,以三维四面体单元...http://www.360doc.com/content/22/1204/22/80945647_1058861009.shtml
9.使用Spyder进行财务数据分析—Spyder5文档它有很好的绘图库 您可以使用资源,例如 Google Colab 或Binder 在云中进行分析。 我为什么要使用IDE? 虽然您可以在没有IDE(集成开发环境)的情况下使用Python,但是使用IDE(集成开发环境)会更好。Spyder是一个用Python编写的科学集成开发环境,由科学家、工程师和数据分析师设计。Spyder的功能及其与Python的集成使其成为...https://www.osgeo.cn/spyder-docs/workshops/financial.html
10.下一节:第10章python程序开发基础在Python的发展过程中,形成了Python 2.x和Python 3.x两个不同系列的版本,Python 3.x版本相对于Python 2.x版本,是一个较大的升级,Python 3.x在设计的时候没有考虑向下兼容,为了满足不同Python用户的需求,目前是Python 2.x和Python 3.x两个版本并存。2020年1月1日,官方宣布停止Python 2的更新,Python 2.7被...https://cloud.tencent.com/edu/learning/course-3080-56796
11.局域网技术实训IEEE802标准所描述的局域网参考模型对应于OSI参考模型的数据链路层和物理层,它将数据链路层划分为两个子层:逻辑链路控制子层(LLC)和介质控制子层(MAC)。 ? MAC子层是数据链路层的一个功能子层,主要制定管理和分配信道的协议规范。 ? LLC也是数据链路层的一个功能子层,它在MAC子层的支持下向网络层提供服务...http://www.zzxxjs.net/gzsjs/yygzs/kcjs/01/2535.shtml