标签 TIOBE 下的文章

Go是一门面向对象编程语言吗

本文永久链接 – https://tonybai.com/2023/03/12/is-go-object-oriented

Go语言已经开源13年了,在近期TIOBE发布的2023年3月份的编程语言排行榜中,Go再次冲入前十,相较于Go在2022年底的排名提升了2个位次:

《Go语言第一课》专栏中关于Go在这两年开始飞起的“预言”也正在逐步成为现实^_^,大家学习Go的热情也在快速提升, 《Go语言第一课》专栏的学习的人数年后也快速增加,快突破2w了。

很多专栏的订阅者都是第一次接触Go,他们中的很多是来自像Java, Ruby这样的OO(面向对象)语言阵营的,他们学习Go之后的第一个问题便是:Go是一门OO语言吗?在这篇博文中,我们就来探讨一下。

一. 溯源

在公认的Go语言“圣经”《Go程序设计语言》一书中,有这样一幅Go语言与其主要的先祖编程语言的亲缘关系图:

从图中我们可以清晰看到Go语言的“继承脉络”:

  • C语言那里借鉴了表达式语法、控制语句、基本数据类型、值参数传递、指针等;
  • Oberon-2语言那里借鉴了package、包导入和声明的语法,而Object Oberon提供了方法声明的语法。
  • Alef语言以及Newsqueak语言中借鉴了基于CSP的并发语法。

我们看到,从Go先祖溯源的情况来看,Go并没有从纯面向对象语言比如Simula、SmallTalk等那里取经。

Go诞生于2007年,开源于2009年,那正是面向对象语言和OO范式大行其道的时期。不过Go设计者们觉得经典OO的继承体系对程序设计与扩展似乎并无太多好处,还带来了较多的限制,因此在正式版本中并没有支持经典意义上的OO语法,即基于类和对象实现的封装、继承和多态这三大OO主流特性。

但这是否说明Go不是一门OO语言呢?也不是! 带有面向对象机制的Object Oberon也是Go的先祖语言之一,虽然Object Oberon的OO语法又与我们今天常见的语法有较大差异。

就此问题,我还特意咨询了ChatGPT^_^,得到的答复如下:

ChatGPT认为:Go支持面向对象,提供了对面向对象范式基本概念的支持,但支持的手段却并不是类与对象。

那么针对这个问题Go官方是否有回应呢?有的,我们来看一下。

二. 官方声音

Go官方在FAQ中就Go是否是OO语言做了简略回应

Is Go an object-oriented language?

Yes and no. Although Go has types and methods and allows an object-oriented style of programming, there is no type hierarchy. The concept of “interface” in Go provides a different approach that we believe is easy to use and in some ways more general. There are also ways to embed types in other types to provide something analogous—but not identical—to subclassing. Moreover, methods in Go are more general than in C++ or Java: they can be defined for any sort of data, even built-in types such as plain, “unboxed” integers. They are not restricted to structs (classes).

Also, the lack of a type hierarchy makes “objects” in Go feel much more lightweight than in languages such as C++ or Java.

粗略翻译过来就是:

Go是一种面向对象的语言吗?

是,也不是。虽然Go有类型和方法,并且允许面向对象的编程风格,但却没有类型层次。Go中的“接口”概念提供了一种不同的OO实现方案,我们认为这种方案更易于使用,而且在某些方面更加通用。还有一些可以将类型嵌入到其他类型中以提供类似子类但又不等同于子类的机制。此外,Go中的方法比C++或Java中的方法更通用:Go可以为任何数据类型定义方法,甚至是内置类型,如普通的、“未装箱的”整数。Go的方法并不局限于结构体(类)。

此外,由于去掉了类型层次,Go中的“对象”比C++或Java等语言更轻巧。

“是,也不是”!我们看到Go官方给出了一个“对两方都无害”的中庸的回答。那么Go社区是怎么认为的呢?我们来看看Go社区的一些典型代表的观点。

三. 社区声音

Jaana DoganSteve Francia都是前Go核心团队成员,他们在加入Go团队之前对“Go是否是OO语言”这一问题也都有自己的观点论述。

Jaana Dogan在《The Go type system for newcomers》一文中给出的观点是:Go is considered as an object-oriented language even though it lacks type hierarchy,即“Go被认为是一种面向对象的语言,即使它缺少类型层次结构”。

而更早一些的是Steve Francia在2014年发表的文章《Is Go an Object Oriented language?》中的结论观点:Go,没有对象或继承的面向对象编程,也可称为“无对象”的OO编程模型。

两者表达的遣词不同,但含义却异曲同工,即Go支持面向对象编程,但却不是通过提供经典的类、对象以及类型层次来实现的

那么Go究竟是以何种方式实现对OOP的支持的呢?我们继续看!

四. Go的“无对象”OO编程

经典OO的三大特性是封装、继承与多态,这里我们看看Go中是如何对应的。

1. 封装

封装就是把数据以及操作数据的方法“打包”到一个抽象数据类型中,这个类型封装隐藏了实现的细节,所有数据仅能通过导出的方法来访问和操作。 这个抽象数据类型的实例被称为对象。经典OO语言,如Java、C++等都是通过类(class)来表达封装的概念,通过类的实例来映射对象的。熟悉Java的童鞋一定记得《Java编程思想》一书的第二章的标题:“一切都是对象”。在Java中所有属性、方法都定义在一个个的class中。

Go语言没有class,那么封装的概念又是如何体现的呢?来自OO语言的初学者进入Go世界后,都喜欢“对号入座”,即Go中什么语法元素与class最接近!于是他们找到了struct类型。

Go中的struct类型中提供了对真实世界聚合抽象的能力,struct的定义中可以包含一组字段(field),如果从OO角度来看,你也可以将这些字段视为属性,同时,我们也可以为struct类型定义方法(method),下面例子中我们定义了一个名为Point的struct类型,它拥有一个导出方法Length:

type Point struct {
    x, y float64
}

func (p Point) Length() float64 {
    return math.Sqrt(p.x * p.x + p.y * p.y)
}

我们看到,从语法形式上来看,与经典OO声明类的方法不同,Go方法声明并不需要放在声明struct类型的大括号中。Length方法与Point类型建立联系的纽带是一个被称为receiver参数的语法元素。

那么,struct是否就是对应经典OO中的类呢? 是,也不是!从数据聚合抽象来看,似乎是这样, struct类型可以拥有多个异构类型的、代表不同抽象能力的字段(比如整数类型int可以用来抽象一个真实世界物体的长度,string类型字段可以用来抽象真实世界物体的名字等)。

但从拥有方法的角度,不仅是struct类型,Go中除了内置类型的所有其他具名类型都可以拥有自己的方法,哪怕是一个底层类型为int的新类型MyInt:

type MyInt int

func(a MyInt)Add(b int) MyInt {
    return a + MyInt(b)
}

2. 继承

就像前面说的,Go设计者在Go诞生伊始就重新评估了对经典OO的语法概念的支持,最终放弃了对诸如类、对象以及类继承层次体系的支持。也就是说:在Go中体现封装概念的类型之间都是“路人”,没有亲爹和儿子的关系的“牵绊”

谈到OO中的继承,大家更多想到的是子类继承了父类的属性与方法实现。Go虽然没有像Java extends关键字那样的显式继承语法,但Go也另辟蹊径地对“继承”提供了支持。这种支持方式就是类型嵌入(type embedding),看一个例子:

type P struct {
    A int
    b string
}

func (P) M1() {
}

func (P) M2() {
}

type Q struct {
    c [5]int
    D float64
}

func (Q) M3() {
}

func (Q) M4() {
}

type T struct {
    P
    Q
    E int
}

func main() {
    var t T
    t.M1()
    t.M2()
    t.M3()
    t.M4()
    println(t.A, t.D, t.E)
}

我们看到类型T通过嵌入P、Q两个类型,“继承”了P、Q的导出方法(M1~M4)和导出字段(A、D)。

关于类型嵌入的具体语法说明,大家可以温习一下《十分钟入门Go语言》《Go语言第一课》专栏

不过实际Go中的这种“继承”机制并非经典OO中的继承,其外围类型(T)与嵌入的类型(P、Q)之间没有任何“亲缘”关系。P、Q的导出字段和导出方法只是被提升为T的字段和方法罢了,其本质是一种组合,是组合中的代理(delegate)模式的一种实现。T只是一个代理(delegate),对外它提供了它可以代理的所有方法,如例子中的M1~M4方法。当外界发起对T的M1方法的调用后,T将该调用委派给它内部的P实例来实际执行M1方法。

以经典OO理论话术去理解就是T与P、Q的关系不是is-a,而是has-a的关系

3. 多态

经典OO中的多态是尤指运行时多态,指的是调用方法时,会根据调用方法的实际对象的类型来调用不同类型的方法实现。

下面是一个C++中典型多态的例子:

#include <iostream>

class P {
        public:
                virtual void M() = 0;
};

class C1: public P {
        public:
                void M();
};

void C1::M() {
        std::cout << "c1.M()\n";
}

class C2: public P {
        public:
                void M();
};

void C2::M() {
        std::cout << "c2.M()\n";
}

int main() {
        C1 c1;
        C2 c2;
        P *p = &c1;
        p->M(); // c1.M()
        p = &c2;
        p->M(); // c2.M()
}

这段代码比较清晰,一个父类P和两个子类C1和C2。父类P有一个虚拟成员函数M,两个子类C1和C2分别重写了M成员函数。在main中,我们声明父类P的指针,然后将C1和C2的对象实例分别赋值给p并调用M成员函数,从结果来看,在运行时p实际调用的函数会根据其指向的对象实例的实际类型而分别调用C1和C2的M。

显然,经典OO的多态实现依托的是类型的层次关系。那么对应没有了类型层次体系的Go来说,它又是如何实现多态的呢?Go使用接口来解锁多态

和经典OO语言相比,Go更强调行为聚合与一致性,而非数据。因此Go提供了对类似duck typing的支持,即基于行为集合的类型适配,但相较于ruby等动态语言,Go的静态类型机制还可以保证应用duck typing时的类型安全。

Go的接口类型本质就是一组方法集合(行为集合),一个类型如果实现了某个接口类型中的所有方法,那么就可以作为动态类型赋值给接口类型。通过该接口类型变量的调用某一方法,实际调用的就是其动态类型的方法实现。看下面例子:

type MyInterface interface {
    M1()
    M2()
    M3()
}

type P struct {
}

func (P) M1() {}
func (P) M2() {}
func (P) M3() {}

type Q int
func (Q) M1() {}
func (Q) M2() {}
func (Q) M3() {}

func main() {
    var p P
    var q Q
    var i MyInterface = p
    i.M1() // P.M1
    i.M2() // P.M2
    i.M3() // P.M3

    i = q
    i.M1() // Q.M1
    i.M2() // Q.M2
    i.M3() // Q.M3
}

Go这种无需类型继承层次体系、低耦合方式的多态实现,是不是用起来更轻量、更容易些呢!

五. Gopher的“OO思维”

到这里,来自经典OO语言阵营的小伙伴们是不是已经找到了当初在入门Go语言时“感觉到别扭”的原因了呢!这种“别扭”就在于Go对于OO支持的方式与经典OO语言的差别:秉持着经典OO思维的小伙伴一上来就要建立的继承层次体系,但Go没有,也不需要。

要转变为正宗的Gopher的OO思维其实也不难,那就是“prefer接口,prefer组合,将习惯了的is-a思维改为has-a思维”。

六. 小结

是时候给出一些结论性的观点了:

  • Go支持OO,只是用的不是经典OO的语法和带层次的类型体系;
  • Go支持OO,只是用起来需要换种思维;
  • 在Go中玩转OO的思维方式是:“优先接口、优先组合”。

“Gopher部落”知识星球旨在打造一个精品Go学习和进阶社群!高品质首发Go技术文章,“三天”首发阅读权,每年两期Go语言发展现状分析,每天提前1小时阅读到新鲜的Gopher日报,网课、技术专栏、图书内容前瞻,六小时内必答保证等满足你关于Go语言生态的所有需求!2023年,Gopher部落将进一步聚焦于如何编写雅、地道、可读、可测试的Go代码,关注代码质量并深入理解Go核心技术,并继续加强与星友的互动。欢迎大家加入!

img{512x368}
img{512x368}

img{512x368}
img{512x368}

著名云主机服务厂商DigitalOcean发布最新的主机计划,入门级Droplet配置升级为:1 core CPU、1G内存、25G高速SSD,价格5$/月。有使用DigitalOcean需求的朋友,可以打开这个链接地址:https://m.do.co/c/bff6eed92687 开启你的DO主机之路。

Gopher Daily(Gopher每日新闻)归档仓库 – https://github.com/bigwhite/gopherdaily

我的联系方式:

  • 微博(暂不可用):https://weibo.com/bigwhite20xx
  • 微博2:https://weibo.com/u/6484441286
  • 博客:tonybai.com
  • github: https://github.com/bigwhite

商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。

2022年Go语言盘点:泛型落地,无趣很好,稳定为王

本文永久链接 – https://tonybai.com/2022/12/29/the-2022-review-of-go-programming-language

早早就计划好在年前写一个Go语言年度盘点,就像2020年2021年那样。但恰逢国内疫情管控放开,一波阳了之后身体十分容易疲劳,再加上工作上的事情挺多,这篇盘点也就迟迟没能下笔。

今年的盘点思路将围绕三个关键字来展开:泛型、无趣(boring)和稳定。下面我们逐一来看看。

1. “泛型”靴子落地

2022年3月中旬,Go社区尤其是那些期盼Go加入泛型特性的Gopher终于迎来了Go 1.18版本的正式发布,这意味着Go泛型这只靴子终于落地了

其实,从Go开源那一天开始,Go核心团队就没有间断过对泛型的探索,并一直尝试寻找一个理想的泛型设计方案,但始终未能如愿。直到近几年Go团队觉得Go已经逐渐成熟,是时候下决心解决Go社区主要关注的几个问题了,包括泛型、包依赖以及错误处理等。其中泛型常年在Go官方用户调查报告的“你最想要的Go语言特性”这项调查的榜单上霸榜,下图摘自2020年度Go官方用户调查结果:

之后,Go团队安排伊恩·泰勒和罗伯特·格瑞史莫花费更多精力在泛型的设计方案上,这才有了Go 1.18 版本中泛型语法特性的落地。

个人觉得:Go泛型是Go核心团队对Go社区的一次迎合与妥协,因为泛型与Go的主要设计哲学“简单”是有悖的。泛型这个语法特性会给语言带来复杂性,这种复杂性不仅体现在语法层面上引入了难于理解和使用的新的语法元素,也体现在类型系统和运行时层面上为支持泛型进行的复杂的实现。

如果从2010年6月份,伊恩·泰勒提出的Type Functions设计方案算起,到2022年3月份的泛型落地,Go加入泛型之路足足走了近12年。不过结果还是不错的,经过近12年的努力与不断地自我否定,Go团队终于找到了一个不违背Go1兼容性承诺(见下图)泛型实现方案

从这方面讲,Go对泛型的支持又是十分成功的。在如此语法巨变的情况下,依然保持向后兼容(backforward compatibility)。

不过如果你因为Go加入了对泛型的支持就打算投入Go阵营,这里先给你一些友情提示:和支持泛型的主流编程语言之间的泛型设计与实现存在差异一样,Go的泛型与其他主流编程语言的泛型也是不同的。在学习Go泛型之前,可以先了解一下Go泛型设计方案已经明确不支持的若干特性,比如:

  • 不支持泛型特化(specialization),即不支持编写一个泛型函数针对某个具体类型的特殊版本;
  • 不支持元编程(metaprogramming),即不支持编写在编译时执行的代码来生成在运行时执行的代码;
  • 不支持操作符方法(operator method),即只能用普通的方法(method)操作类型实例(比如:getIndex(k)),而不能将操作符视为方法并自定义其实现,比如一个容器类型的下标访问 c[k];
  • 不支持变长的类型参数(type parameters);
  • … …。

这些特性如今不支持,后续大概率也不会支持。所以小伙伴们,尤其是来自Java、C++等语言阵营的小伙伴,在进入Go泛型语法学习之前,你一定要先了解Go团队的这些设计决策。

此外,目前的Go泛型实现和最后一版的泛型设计方案相比还有差距,依旧不是完全版,还有一些特性没有加入,还有问题亟待解决

就目前笔者观察来看,Go泛型还处于早期阶段,远非成熟。Go module构建模式从go 1.11版本加入到go 1.16成为默认并逐渐成熟还花了3年多时间呢,何况是Go泛型。这样来看,初步预测Go泛型要到2025年才会成熟,而成熟的标志无非如下几个:

  • 泛型语法特性确定以及稳定下来;
  • 语法问题基本都解决;
  • Go标准库开始广泛使用泛型;
  • Go泛型的运行时性能问题得到基本解决。

目前Go团队对泛型的应用依旧保持谨慎,并在循序渐进地推进泛型在Go团队与Go社区的应用,最新的消息是Go团队已经提出proposal,计划在Go 1.21版本中将用泛型实现的maps包slices包加入Go标准库,这两个包原本计划在Go 1.18版本加入,但因Rob Pike的建议先放到了golang.org/x/exp下面待定。

2. 无趣(boring)很好

和其他主流编程语言如C++、Rust等在新版本中不断有新语言特性刺激程序员的神经,让大家阶段性产生兴奋感(exciting)不同,除了早期版本(比如Go 1.1和Go 1.2)以及里程碑的Go 1.5版本的完成自举和大幅降低GC延迟、Go 1.11版本的go module构建模式、Go 1.18版本的泛型落地之外,大部分版本的发布都很难让Gopher们十分兴奋,甚至业界都称“Go is boring(Go很无趣)”

在今年的线下GopherCon大会上,Go核心团队技术Leader Russ Cox发表名为“Compatibility: How Go Programs Keep Working”的主题演讲,在这个演讲中,Russ Cox借用了Go is boring的这一说法,并称That is good!

国外新冠管控放开早,经过几波疫情后,与病毒共存了,于是2022年的GopherCon大会又重新恢复线下举办。

Russ Cox的原话是:“boring is good. boring is stable. boring means to be able to focus on your work and not ours… We’ll keep doing everything we can to keep go boring for all of you”。

这几句英文不难,相信大家都能看懂。无趣的Go意味着稳定,意味着大家将注意力都集中在自己的工作上而不是Go核心团队身上(去关注新特性)。Go语言不会像其他编程语言那样堆砌新功能特性。

Russ Cox的这一观点代表了Go核心团队,也代表着Go演进未来演进的主基调。同时,Russ明确给出结论:不会有Go2了,Go 1.xy会一直持续下去。Russ甚至提出:兼容性才是Go最重要的feature

并且Russ Cox在Go项目的discussion中也给出保持Go兼容性的backward compatibilityforward compatibility的扩展方案与一个实例

关于“Go is boring”,Russ没有进一步展开说,记得之前译过一篇名为《Go语言很无聊…其实它妙不可言!》的文章,大家可以看看那篇文章进一步体会一下“Go is boring”的含义。

3. “稳定”是主旋律

Go的稳定不仅体现在Go语法特性的演化上,Go语言在各大语言排行榜上的排名也进入了相对稳定区,以TIOBE index为例,下面是2022年12月份的排名截图:

我查了一下《2021年Go语言盘点:厉兵秣马强技能,蓄势待发新征程》一文中2022年1月份Go的排名为13名,上图中2021年12月份是19名的数据应该是错误的,相对2021年12月份,Go实际排名上升1位。

我们看到2021年Go从14升到13,今年又从13升到12。按照TIOBE官方编辑说法,在新兴编程语言中,Go是唯一一个可能在未来冲入前十的后端编程语言。

Go语言在实际应用中的表现与上述排名的变化也十分契合,总体来说就是十分稳定,国内外都波澜不惊,国内大厂该用Go的也都用了,腾讯、字节依旧是这方面的领头羊,先后开源了不少Go实现的项目,最受瞩目的应该是字节将内部的Go框架逐一开源了,包括:netpollkitex(rpc框架)hertz(http框架)等。

为了更好的帮助大家回顾这一年来Go的稳定演化,这里简单整理了2022年Go大事件列表,供大家参考:

Go社区等待了多年的泛型语法特性终于加入Go中。

从调查结果中可以看到,Gopher对Go的满意度依然高达92%;81%的受访者对Go项目的长期方向充满信心。

这篇Go语言的综述文章由Russ Cox,Robert Griesemer,Rob Pike,Ian Lance Taylor和Ken Thompson联合撰写,是Go核心团队对10多年来Go演化发展的复盘,深入分析了那些对Go的成功最具决定性的设计哲学与决策,是Go诞生十多年来最重要的一篇文章。

该文介绍了ThreadSanitizer v2的工作原理,并总结了7类数据竞争模式。

相对于Go 1.18版本而言,Go 1.19是一个“小”版本,它主要针对Go 1.18版本中泛型实现的问题做了修改和优化,引入了Soft memory limit,更新了《Go内存模型》文档。

包括sync.Pool的优化、defer性能提升、基于系统信号的抢占式调度(go 1.14)、调度器性能提升、支持基于寄存器的调用规约、soft memory limit等。

软件供应链安全问题愈发受到各界关注。Go安全团队发布Go官方安全漏洞管理的工具和方案: govulncheck。govulncheck是Go安全漏洞数据库(Go vulnerability database)的一个前端,它通过Go官方维护的vuln仓库下面的vulncheck包对你仓库中的Go源码或编译 后的Go应用可执行二进制文件进行扫描,形成源码的调用图(callgraph)和调用栈(callstack)。

  • 2022年10月,GopherCon大会在芝加哥线下举行

Russ Cox发表《Compatibility: How Go Programs Keep Working》主题演讲,确定了未来Go语言演进的主基调。

  • 2022年11月,Go开源13岁生日

Go官方回顾了2022年Go团队的工作与成果,并简单说明了在新一年的工作,包括继续努力使Go成为用于大规模软件工程的最好的环境。计划特别关注供应链安全,提高兼容性和结构化日志记录(slog),当然还会有很多其他改进,包括profile-guided optimization等。

4. Go语言2023年展望

目前Go语言的演化与发展与我在2020年Go盘点中的预测基本一致。我现在依然坚持我的判断,即我在《Go语言第一课》专栏中所说的那样:

绝大多数主流编程语言将在其诞生后的第15至第20年间大步前进。按照这个编程语言的一般规律,已经迈过开源第13个年头的Go,很可能将进入自己的黄金5-10年。2022年泛型落地就是Go语言进入黄金5-10年的起点,待2025年泛型成熟后,Go将取得更快的发展速度。

前途是美好的,但道路的曲折坎坷的。目前Go更多应用于基础设施、中间件领域和基础微服务领域,在企业级业务系统方面,类似spring这样的“全家桶”框架的缺乏和无法达成一致,让开发者在开发复杂业务系统时依旧首选Java。期待Go在这方面能所有进展。

同时,Go演进道路上还存在另外一个风险,在我的《Go为什么能成功》一文中,我曾经提到过:“Go成也Google,败也Google”。Go团队目前的治理体系太过于依赖google,这是一门双刃剑。当google发展较好时,Go语言将从中受益。但当google开始走下坡路时,Go是否还能像如今这样风光呢?让我么拭目以待吧!


“Gopher部落”知识星球旨在打造一个精品Go学习和进阶社群!高品质首发Go技术文章,“三天”首发阅读权,每年两期Go语言发展现状分析,每天提前1小时阅读到新鲜的Gopher日报,网课、技术专栏、图书内容前瞻,六小时内必答保证等满足你关于Go语言生态的所有需求!2022年,Gopher部落全面改版,将持续分享Go语言与Go应用领域的知识、技巧与实践,并增加诸多互动形式。欢迎大家加入!

img{512x368}
img{512x368}

img{512x368}
img{512x368}

我爱发短信:企业级短信平台定制开发专家 https://tonybai.com/。smspush : 可部署在企业内部的定制化短信平台,三网覆盖,不惧大并发接入,可定制扩展; 短信内容你来定,不再受约束, 接口丰富,支持长短信,签名可选。2020年4月8日,中国三大电信运营商联合发布《5G消息白皮书》,51短信平台也会全新升级到“51商用消息平台”,全面支持5G RCS消息。

著名云主机服务厂商DigitalOcean发布最新的主机计划,入门级Droplet配置升级为:1 core CPU、1G内存、25G高速SSD,价格5$/月。有使用DigitalOcean需求的朋友,可以打开这个链接地址:https://m.do.co/c/bff6eed92687 开启你的DO主机之路。

Gopher Daily(Gopher每日新闻)归档仓库 – https://github.com/bigwhite/gopherdaily

我的联系方式:

  • 微博(暂不可用):https://weibo.com/bigwhite20xx
  • 微博2:https://weibo.com/u/6484441286
  • 博客:tonybai.com
  • github: https://github.com/bigwhite

商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。

如发现本站页面被黑,比如:挂载广告、挖矿等恶意代码,请朋友们及时联系我。十分感谢! Go语言第一课 Go语言精进之路1 Go语言精进之路2 商务合作请联系bigwhite.cn AT aliyun.com

欢迎使用邮件订阅我的博客

输入邮箱订阅本站,只要有新文章发布,就会第一时间发送邮件通知你哦!

这里是 Tony Bai的个人Blog,欢迎访问、订阅和留言! 订阅Feed请点击上面图片

如果您觉得这里的文章对您有帮助,请扫描上方二维码进行捐赠 ,加油后的Tony Bai将会为您呈现更多精彩的文章,谢谢!

如果您希望通过微信捐赠,请用微信客户端扫描下方赞赏码:

如果您希望通过比特币或以太币捐赠,可以扫描下方二维码:

比特币:

以太币:

如果您喜欢通过微信浏览本站内容,可以扫描下方二维码,订阅本站官方微信订阅号“iamtonybai”;点击二维码,可直达本人官方微博主页^_^:
本站Powered by Digital Ocean VPS。
选择Digital Ocean VPS主机,即可获得10美元现金充值,可 免费使用两个月哟! 著名主机提供商Linode 10$优惠码:linode10,在 这里注册即可免费获 得。阿里云推荐码: 1WFZ0V立享9折!


View Tony Bai's profile on LinkedIn
DigitalOcean Referral Badge

文章

评论

  • 正在加载...

分类

标签

归档



View My Stats