Golang入门(3):一天学完GO的进阶语法

摘要

在前一篇文章中,我们聊了聊Golang的一些基础的语法,如变量的定义、条件语句、循环语句等等。他们和其他语言很相似,我们只需要看一看它们之间的区别,就差不多可以掌握了,所以我称它们为“基础语法”。在这篇文章中,我们将聊一聊Golang的一些语言特性,这也是Golang和其他语言差别比较大的地方。除此之外,还有一部分内容是关于Golang的并发,这一部分将在下一篇文章中介绍。

1 结构体

在Java中,我们已经体会过了面向对象的方便之处。我们只需要将现实中的模型抽象出来,就成为了一个类,类里面定义了描述这个类的一些属性。

而在Golang中,则没有对象这一说法,因为Golang是一个面向过程的语言。但是,我们又知道面向对象的便捷性,所以我们在Golang中有了结构体这一类型。

结构体是复合类型,当需要定义类型,它由一系列属性组成,每个属性都有自己的类型和值的时候,就应该使用结构体,它把数据聚集在一起。

组成结构体类型的那些数据成为字段(fields)。每个字段都有一个类型和一个名字;在一个结构体中,字段名字必须是唯一的。

我们可以近似的认为,一个结构体就是一个类,结构体内部的字段,就是类的属性。

注意,在结构体中也遵循用大小写来设置共有私有的规则。如果这个结构体名字的第一个字母是大写,则他可以被其他包访问,否则,只能包内访问。而结构体内的字段也一样,也是遵循一样的规则。

1.1 定义

对于结构体,他的定义方式如下:

type 结构体名 struct {
    字段1 类型
    字段2 类型
}

1.2 声明

对于结构体的声明和初始化,有以下几种形式:

使用var关键字

var s T
s.a = 1
s.b = 2

注意,在使用了var关键字之后不需要初始化,Golang会自动分配内存空间,我们只需要按需进行赋值即可。

使用new函数

type people struct {
    name string
    age int
}

func main() {
    ming := new(people)
    ming.name = "xiao ming"
    ming.age = 18
}

使用字面量

type people struct {
    name string
    age int
}

func main() {
    ming := &people{"xiao ming", 18}
}

1.3 区别

上面我们提到了几种结构体的声明的方法,但其实这几种是有些区别的。

先说结论,第一种使用var声明的方式,返回的是该实例的结构类型,而第二第三种,返回的是一个指向这个结构类型的指针

注意,这一部分作者可以保证是观点是正确的。但是作者的解释其实有些问题,因为作者还没开始研究Golang的源码,所以不能很好的解释“返回的是实例的结构类型”这一句话。在作者的理解中,返回类型有两种,一种是具体的数值,一种是指向这个数值的指针。

所以,对于第二第三种返回指针的声明形式,在我们需要修改他的值的时候,其实应该使用的方式是:

(*ming).name = "xiao wang"

也就是说,对于指针类型的数值,应该要先用*取值,然后再修改。

但是,在Golang中,可以省略这一步骤,直接使用ming.name = "xiao wang"。尽管如此,我们应该知道这一行为的原因,分清楚自己所操作的对象究竟是什么类型,掌握这点对下面方法这一章节至关重要。

2 方法

在上一节的内容中,我们也提到了面向对象的优势,而Golang又是一种面向过程的语言。在上一章节中,提到了用结构体实现了对象这一概念。在这一章中,提到的是对象对应的方法

在Go语言中有一个概念,它和方法有着同样的名字,并且大体上意思相同,Go 方法是作用在接收器(receiver)上的一个函数,接收器是某种类型的变量,因此方法是一种特殊类型的函数。

说白了,方法就是函数,只不过是一种比较特殊的函数。

我们都知道,在Golang中,定义一个函数是这样的:

func 函数名(args) 返回类型

而在此基础上,在func函数名之间,加上接受者的类型,就可以定义一个方法。

type Vertex struct {
	X, Y float64
}

func (v Vertex) Abs() float64 {
	return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

func main() {
	v := Vertex{3, 4}
	fmt.Println(v.Abs())
}

可以看到,我们定义了一个Vertex为接收者的方法。也就是说,这个方法,仅仅可以被Vertex的结构体数值调用。

注意,接受者有两种类型,即指针接收者和非指针接受者。

我们来看下面的代码:

type Vertex struct {
	X, Y float64
}

func (v Vertex) test1(){
    v.X++;
    v.Y++;
}

func (v *Vertex) test2(){
    v.X++;
    v.Y++;
}

在这里我们定义了两个方法,test1test2,他们唯一的区别就是方法名前面的接收者不同,一个是指针类型的,一个是值类型的。

并且,执行这两个方法,也需要定义不同的结构体类型。

v1 := Vertex{1, 1}
v2 := &Vertex{1, 1}

v1.test1()
v2.test2()

fmt.Println(v1)
fmt.Println(v2)

执行之后我们可以查看结果:

{1 1}
&{2 2}

也就是说,只有指针接收者类型的方法,才能修改这个接收器的成员值,非指针接收者,方法修改的只是这个传入的指针接收者的一个拷贝

那么为什么会这样,我们同样拿代码说话:

type Vertex struct {
	X, Y float64
}

func (v Vertex) test1(){
	fmt.Printf("在方法中的v的地址为:%p\n", &v)
	v.X++;
	v.Y++;
}

func main()  {
	v1 := Vertex{1, 1}
	fmt.Printf("自己定义的v1内存地址为:%p\n", &v1)
	v1.test1()
}

在上述的代码中,我定义了一个非指针类型接收者的方法,然后打印方法外的v1和方法内的v的内存地址,结果如下:

自己定义的v1内存地址为:0xc00000a0e0
在方法中的v的地址为:0xc00000a100

我们可以看出,这两个结构体数值的内存地址是不一样的。既然这样的话,我们修改了方法内的数值,对方法外的原变量也就不能起到任何作用了。

但是,如果使用的是指针接收者,他们的内存地址就是一样的了,下面看代码:

type Vertex struct {
	X, Y float64
}

func (v *Vertex) test2(){
	fmt.Printf("在方法中的v的地址为:%p\n", v)
	v.X++;
	v.Y++;
}

func main()  {
	v1 := &Vertex{1, 1}
	fmt.Printf("自己定义的v1内存地址为:%p\n", v1)
	v1.test2()
}

执行之后的结果为:

自己定义的v1内存地址为:0xc00000a0e0
在方法中的v的地址为:0xc00000a0e0

所以我们可以知道,指针接收器,是可以修改原来的数值的,这也和Java中的对象调用方法更加相似;而对于非指针,它是拷贝原来的数据。至于使用哪一种,需要按照实际的业务来处理。

但是,如果是一个大对象,如果也采用拷贝的方式,将会耗费大量的内存,降低效率。

还有一点需要补充说明:不管是指针接收者还是非指针接收者,他在接受一个对象的时候,会自动将这个对象转换为他所需要的类型。也就是说,如果我现在有一个非指针类型的对象,去调用一个指针接收者的方法,那么这个对象将会自动被取地址然后调用。

换句话说,方法的调用类型不重要,重要的是定义方法是怎么定义的。

3 接口

在聊接口怎么用之前,我们先来聊聊接口的作用。

在作者看来,接口是一种规范,一种约定。举个例子:一个商品只要是符合某种种类的约定,遵循某种种类的个规范,那么我就可以认为这个商品是属于这个种类的。这样做的目的是为了把生产这个商品的生产者和使用这个商品的消费者分开。用编程里面的术语来讲,我们可以把实现和调用解耦。

下面举个鸭子模型的例子,来自于知乎,可以说特别的形象生动了。注意,在这里先不研究语法,语法的问题我们后面会提到,你只需要跟随作者的思路去思考:

首先定义一个规范,也就是说定义一个接口:

type Duck interface {
    Quack()   // 鸭子叫
    DuckGo()  // 鸭子走
}

这个接口是鸭子的行为,我们认为,一个鸭子他需要会叫,会走。然后我们再定义一只鸡:

type Chicken struct {
}

假设这只鸡特别强,他会像鸭子叫,也会像鸭子那样走路,那么我们定义一下这只鸡的行为:

func (c Chicken) Quack() {
    fmt.Println("嘎嘎")
}

func (c Chicken) DuckGo() {
    fmt.Println("大摇大摆的走")
}
注意,这里只是实现了 Duck 接口方法,并没有将鸡类型和鸭子接口显式绑定。这是一种非侵入式的设计。

然后我们让这只鸡,去叫,去像鸭子那样走路:

func main() {
	c := Chicken{}
	var d Duck
	d = c
	d.Quack()
	d.DuckGo()
}

执行之后我们可以得到结果:

嘎嘎
大摇大摆的走

也就是说,这只鸡,他能做到鸭子能做的所有事情,那么我们可以认为,这只鸡,他就是一个鸭子。

这里牵涉到了一个概念,任何类型的数据,他只要实现了一个接口中方法集,那么他就属于这个类型。所以,当我们在实现一个接口的时候,需要实现这个接口下的所有方法,否则编译将不能通过。

理解了接口是什么之后,我们再来聊聊语法,首先是定义一个接口:

type 接口名 interface {
    方法1(参数) 返回类型
    方法2(参数) 返回类型
    ...
}

这一部分和结构体的定义很相似,但是里面的元素换成了函数,但是这个函数不需要func

定义完接口之后,需要实现这些接口。我们需要定义方法去实现这些接口。注意,这里新定义的方法名参数返回类型,必须和接口中所定义的完全一致

其次,这里的方法中的接受者,就是你所需的调用这个方法的对象。

还有最重要的一点,实现某个接口,必须要实现这个接口的全部方法。

在调用接口的时候,我们需要先声明这个接口类型的变量,如我们上面定义了一个Duck接口,就应该声明一个Duck类型的变量。

var d Duck

然后我们把实现了这个方法的接收器对象赋值给这个变量d

d := Chicken{}

随后,我们就可以用这个变量d,是调用那些方法了。

写在最后

首先,还是很感谢你能看到这里,谢谢你!

这是《Golang入门》系列的第三篇了,也差不多要讲完Golang的基本语法了。在这篇文章中是介绍了一些Golang的比较特殊的用法,希望能够对你有所帮助。

当然了,作者也只是个初学者,也才刚刚开始学习Golang这门发展势头特别快的语言,在学习的过程中,难免会有错误的理解,或者会有遗漏的地方。如果你发现了,请你留言指正我,谢谢!除此之外,如果有作者讲的不清楚不明白的地方,也欢迎留言,我们一起交流学习。

在下一篇中,作者将介绍一下Golang的并发。我们下篇文章见。

再次感谢~

原文地址:https://www.cnblogs.com/hongjijun/p/12670584.html

时间: 2024-10-29 04:14:02

Golang入门(3):一天学完GO的进阶语法的相关文章

GoLang入门4-编译应用mymath测试

上面我们已经建立了自己的应用包mymath,如何进行编译安装呢?有两种方式可以进行安装 1.只要进入对应的应用包目录,然后执行go install,就可以安装了 2.在任意的目录执行如下代码go install mymath 安装完之后,我们可以进入如下目录 在任意目录运行 go install mymath  如下,则说明 编译安装包成功! mymath.a 就是编译后的包 这个.a文件是应用包,那么我们如何进行调用呢? 接下来我们新建一个应用程序来调用 GoLang入门4-编译应用mymat

Java程序员的Golang入门指南(下)

Java程序员的Golang入门指南(下) 4.高级特性 上面介绍的只是Golang的基本语法和特性,尽管像控制语句的条件不用圆括号.函数多返回值.switch-case默认break.函数闭包.集合切片等特性相比Java的确提高了开发效率,但这些在其他语言中也都有,并不是Golang能真正吸引人的地方.不仅是Golang,我们学习任何语言当然都是从基本语法特性着手,但学习时要不断地问自己:使这门语言区别于其他语言的"独到之处"在哪?这种独到之处往往反映了语言的设计思想.出发点.要解决

北京Python培训班学完能做什么?

深圳Python培训班学完可以从事哪些岗位? Python是一种计算机程序设计语言,又被称为胶水语言,可以用混合编译的方式使用c/c++/java等语言的库.你可能已经听说过很多种流行的编程语言,比如在大学里感觉非常难学的C语言,进入社会非常流行的Java语言,以及适合初学者的Basic语言,非常适合网页编程的Java语言等,Python是他们其中的一种. 1.网站后端程序员:使用它单间网站,后台服务比较容易维护.如:Gmail.Youtube.知乎.豆瓣 2.自动化运维:自动化处理大量的运维任

C/C++要学什么东西?C/C++学完能干什么?学了又能当饭吃吗?

前言 普遍问题 学校的课程设计不完善,涉及面广,什么都不深入. 老师缺乏实际的工作经验,比如说我经常看到老师经常教学生做数学题之类的小程序,但是,实际工作中,我们并不玩这个. 在我看来,要学习某种编程语言,没必要去局限课堂,一方面是进度慢,另一方面,如果你要学习某种语言,关键在学而不在教,你的态度和决心是关键,只要你有决心,没人教也能学会,现在互联网这么发达,什么内容都可以找得到.但是,如果有人教,并且方法合适,肯定会快很多.好了说了这么多,先进入我们今天的主题:C/C++要学什么东西?C/C+

学完Python好找工作吗?为什么有人学完找不到工作?

学完Python好找工作吗?很多人学了Python还是找不到工作,为什么?自己在学习Python,怕以后不好找工作,想问问前辈们,现在Python的工作好找吗?也看到很多人找不到Python工作,是为什么呢?创一个小群,供大家学习交流聊天如果有对学python方面有什么疑惑问题的,或者有什么想说的想聊的大家可以一起交流学习一起进步呀.也希望大家对学python能够持之以恒python爱好群,如果你想要学好python最好加入一个组织,这样大家学习的话就比较方便,还能够共同交流和分享资料,给你推荐

十年架构师留下最完整的Java学习路线,学完年薪40W

文章有点长,请大家耐心看完,话不多说直接上干货! 永不过时的编程语言--Java 编程开发. Java编程语言占比: 据官方数据统计,在全球编程语言工程师的数量上,Java编程语言以900万的程序员数量位居首位. 而且很多软件的开发都离不开Java编程,因此其程序员的数量最多.而在以Java编程为核心的开发领域中,javaEE程序员的需求量10年来一直居于首位!创一个小群,供大家学习交流聊天如果有对学java方面有什么疑惑问题的,或者有什么想说的想聊的大家可以一起交流学习一起进步呀.也希望大家对

没有基础该怎么学Python 学完后好不好找工作

没有基础该怎么学Python?学完后好不好找工作?Python是人工智能时代最佳的编程语言,凭借高可读性以及高开发效率的优势,Python受到各大开发者的欢迎,同时在运维领域也被大量运用到自动化运维场景中.且看小编的分析. 入门简单是Python的主要特点,这让很多人纠结究竟是自学还是参加专业学习.事实上,这个问题很简单,你只需要关注两点:你有没有编程基础.你能不能确保学习效率. 对于没有编程基础的人来说,他们连最基本的代码都不了解,完全摸不着头脑也找不到学习规律,参加专业学习无疑是学习效果最好

Java程序员的Golang入门指南(上)

Java程序员的Golang入门指南 1.序言 Golang作为一门出身名门望族的编程语言新星,像豆瓣的Redis平台Codis.类Evernote的云笔记leanote等. 1.1 为什么要学习 如果有人说X语言比Y语言好,两方的支持者经常会激烈地争吵.如果你是某种语言老手,你就是那门语言的"传道者",下意识地会保护它.无论承认与否,你都已被困在一个隧道里,你看到的完全是局限的.<肖申克的救赎>对此有很好的注脚: [Red] These walls are funny.

GoLang入门-安装-配置

Go的三种安装方式 Go有多种安装方式,你可以选择自己喜欢的.这里我们介绍三种最常见的安装方式: Go源码安装:这是一种标准的软件安装方式.对于经常使用Unix类系统的用户,尤其对于开发者来说,从源码安装是最方便而熟悉的.Go标准包安装:Go提供了方便的安装包,支持Windows.Linux.Mac等系统.这种方式适合初学者,可根据自己的系统位数下载好相应的安装包,一路next就可以轻松安装了.第三方工具安装:目前有很多方便的第三方软件包工具,例如Ubuntu的apt-get.Mac的homeb