Java程序员的Go语言快速入门指南

交流 Go语言  收藏
0 / 269

本文介绍

Go语言已成为一种流行的非常流行编程语言,它是 DockerKubernetesOpenShift 等一些炫酷新开源项目的首选编程语言,掌握Go语言意味着我们都可以为这些开源项目做贡献。

我在日常学习中学习过很多编程语言,例如 C,C ++,C#,Java,Python,Scala,Groovy,Assembly,JavaScript 等。我花了几周的时间学习Go语言和阅读DockerKubernetesOpenShift的源代码。我认为对于一个程序员来说,学习另外一种新的编程语言,可以将你所熟悉的编程语言(例如Java)中的概念带入新的编程语言中,这对你学习新的编程语言有很大的帮助和参考意义。

本文当然不是单纯的 Go语言 的简介,也不是完整的 Java-> Go 的映射。本文的目的是快速帮助 Java 开发人员快如的了解和入门 Go语言开发。

项目结构

在Go语言项目中,通常直接将源代码放到项目的根目录中,而不是像Java那样将源码放入项目的src目录中。在项目根目录中也可以根据业务划分不同的package

Packages

与 Java 类似,Go语言将其代码组织到package中。您可以通过package <package_name>在源码的顶部来声明源文件所在的package(及其所有常量,类型,函数等);但是与 Java 不同,你不需要不输入完整package路径,只用输入包名即可。例如:

package api

所以,如果你的包名为:“api/endpoints”,那么对应文件系统上的目录结构就是“./pkg/api/endpoints”,endpoints包在源代码中声明方式如下:

package endpoints

这点要和Java区分开,如果按照Java的包声明规则,那么声明可能就是下面这种了:

// 注意着是错误的声明方式
package api.endpoints

导入Packages

Go语言和Java一样,可以通过import的方式导入package,在Go语言中导入包的方式如下:

import (
    stderrs "errors"
    "time"
    "golang.org/x/net/context"
    "k8s.io/kubernetes/pkg/auth/user"
)

我们可以根据包路径中的最后一个包名称在源代码中使用包。例如在上面的示例中,我们导入了包k8s.io/kubernetes/pkg/auth/user,那么在代码中可以可以使用user.xxx调用该包下的静态方法、类、常量,例如:user.Foo()。如果我们想在源文件中重命名包,以免与其他名称冲突,那么可以使用下面这种方式:

import (
    stderrs "errors"
)

那么我们在代码中调用errors包下的程序就需要通过别名,例如:stderrs.Foo()

main包

main包是Go语言的应用程序入口点。main包中必须有一个main()函数,它没有参数和返回值:

func main() {
    …
}

这个main函数就是项目的启动入口。

类型、常量、函数的的作用域

Go语言中没有类似Java中privateprotectedpublic关键字,Go语言是通过命名的首字母大小写来定义struct/type/function/variable的作用域。首字母小写表示private、大写表示public

例如,在foo包中,如果你有一个这样的函数func Bar(),由于“Bar”的首字母大写,因此可以在包外部调用。如果你导入foo包,则可以访问foo.Bar()。如果将函数修改为首字母小写:func bar(),那么包外是无法访问该函数的。

函数支持多返回值

Go语言中的函数(可以理解为Java中的方法)可以有多个返回值。例如,调用返回多个值的函数如下所示:

// 函数定义
func foo() (string, bool) {
    ...
}

// 函数调用
str, ok := foo()

其中:strokfoo的两个返回值。

类,结构,方法

在Java中我们有class,Go语言中是没有class的,但是有和class概念、作用相似的东西:结构体(struct)。在struct中可以定义函数和成员变量。

例如:

type Rectangle struct {
    width int
    height int
}

这是一个名为Rectanglestruct,具有两个字段:widthheight。我们可以这样创建一个新的Rectangle

r := new(Rectangle)

我们可以像下面这样访问他的字段:

r.width = 10
r.height = 5

我们可以像下面这样为结构体添加方法:

func (r *Rectangle) area() int {
    return r.width * r.height
}

类型继承

Go语言不想Java那样有关键字extends来实现类的继承。类型的继承是匿名字段组合来完成的。如下:

type Rectangle struct {
    Shape
    width int
    height int
}

这这个示例中,Rectangle有一个匿名字段Shape,那么Shape中的字段和方法都会在Rectangle对象中可见。

需要注意的是,如果一个函数的要求的参数是Shape,虽然Rectangle继承自Shape,也不能直接将Rectangle对象作为该函数的参数。这点是和Java不同的,Java中是允许将更具体的子类型作为参数的。如果要想实现类型的多态,请使用Go语言中的接口(下个章节会讲到)

多态、接口

在Java中,我们有特定的接口类型:interface,接口中的方法由继承了该接口的class来具体实现。Go语言同样有接口功能,但是Go语言中接口的实现是不需要显式的去声明,只需要结构体(即Java中的类)去实现接口中方法即可。

如下,有一个Shape接口,该接口中声明了函数Print()

type Shape interface {
    Print()
}

当我们创建我们的结构体时,我们不用像在Java中那样通过“implements”来声明它。我们只需实现Shape接口中的方法,Go语言就会认为该结构体实现类Shape接口,例如:

type Rectangle struct {
    width int
    height int
}

func (r *Rectangle) Print() {
    fmt.println("Rectangle!");
}

在这种情况下,Rectangle对象可以传递给参数为Shape类型的函数了,因为Rectangle实现了Shape所有的方法。

for循环

GO语言循环例子:

for i := 1; i <= 10; i++ {
    fmt.Println(i)
}

当迭代数组(或者字符串、map、slice等等)时,可以使用range来进行迭代,假设foo是一个列表,那么迭代方式如下:

for v := range foo {
    fmt.println("value="+v);
}

如果你需要在迭代过程中获取数据的索引,那么可以使用以下方式:

for i, v := range foo {
    fmt.println("index " + i + "has value="+v);
}

while循环

Go语言中没有while循环,但是你可以使用for循环来实现while循环相同的功能,如下:

sum := 1
for sum < 1000 {
    sum += sum
}
fmt.Println(sum)

下面是无限循环的例子:

for {
    something...
}

指针与引用

Java中值类型和引用类型都是隐式的,int、double、float、long、byte、short、char、boolean为值类型,其他的都是引用类型,而Go语言中却不是这样。

在Go语言中:

  • &表示取地址,例如你有一个变量a那么&a就是变量a在内存中的地址,对于Golang指针也是有类型的,比如a是一个string那么&a是一个string的指针类型,在Go里面叫&string。
  • *表示取值,接上面的例子,假设你定义b := &a 如果你打印b,那么输出的是&a的内存地址,如果要取值,那么需要使用:*b

下面我们来看一个例子:

package main

import (
    "fmt"
)

func main() {
    a := "123"
    b := &a
    fmt.Println(a)
    fmt.Println(b)
    fmt.Println(*b)
}

输出结果为:
123
0x40c128
123

垃圾回收

和Java一样,Go语言是有自己的垃圾回收机制的,不需要我们显式的释放内存,垃圾回收机制能够大大的减轻开发的复杂度。

本文由 码农俱乐部 翻译自:https://dzone.com/articles/quick-go-lang-for-java-developers 转载请在文章正文内容中注明出处!本文链接:https://mlog.club/topic/686

关注公众号阅读更多技术干货

码农俱乐部