zoukankan      html  css  js  c++  java
  • JAVA开发者的Golang快速指南

    Golang作为Docker、Kubernetes和OpenShift等一些酷辣新技术的首选编程语言,越来越受欢迎。尤其它们都是开源的,很多情况下,开源是非常有价值的。深入学习阅Golang等源代码库中的源文件,可以更深地理解它们,同时也有利于其他编程语言的开发者快速映射某些概念,比如Go与Java中常用概念的映射。本文的目的是帮助Java开发人员快速理解一些常见的Go习惯用法。

    项目结构

    Golang项目的一个常见约定是将所有cli二进制源文件或“main”包中的源文件放在根目录cmd文件夹下。通常可以在源根目录的pkg文件夹中找到实现了不同功能的内聚类型、常量、变量和函数集的包。

     

    Golang将其代码组织成包,类似于Java。通过在源文件的顶部引入package来声明源文件所在的包(以及它的所有常量、类型、函数等)。但是与Java不同,不需要输入完整的包名+类名的路径,只需要输入包名即可。例如:

    package api

    假设有一个包“api/endpoint”,那么文件系统上就会有这个目录结构(例如:/pkg/api/endpoint),但是endpoint包在endpoint目录下的源文件中的声明,应该是这样的:

    package endpoints 

    导入包

    使用以下命令可以在程序中导入包,就像在Java中一样:

    import (

    stderrs "errors"

    "time"

    "Golang.org/x/net/context"

    "k8s.io/kubernetes/pkg/auth/user"

    )

    可以根据包路径中的最后一个包名在源代码中使用包。例如,在上面的例子中,我们导入k8s.io/kubernetes/pkg/auth/user,通过代码,可以用user.Foo()引用包中的元素。同样也可以在源文件中重命名包,这样它就不会与其他包名发生冲突,就像上面例子里所示:

    import (

    stderrs "errors"

    )

    并在自己的程序源码中直接引用stderrs.Foo()。

    Main包

    main包是Golang应用程序的入口点。main包必须有一个main()函数,该函数不接受参数,也不提供返回值。例如:

    func main() { … }

    如前所述,这个包通常位于根目录的cmd文件夹中。

     

    类型、常量、函数的作用域/可见性

    在Golang中,对于结构/类型/函数/变量在包外部的作用域和可见性,其标识符的首字符非常重要。例如,在一个foo包中,如果有一个名为func Bar()的函数,那么因为“Bar”的第一个字母是大写的,所以它在包之外是可用的(注:类似于java中的public)。因此,如果导入了foo包,就能够调用foo.Bar()函数。如果“bar”是小写的,它将被隐藏起来(类似于java中的private)。也就是说,第一个字母的大小写决定了其作用域与可见性。

     

    方法可以返回多个值

    Golang中的函数或方法(两者有区别)可以返回“元组”或多个值,与java有明显差异。例如,调用一个返回多个值的函数如下所示:

    internalCtx, ok := foo.bar(context.Context)

    其中,internalCtx表示函数内容,ok可表示函数调用成功或失败标识。

     

    类、结构、方法

    在Java中有类,但在Go中与之相似的概念是结构体(Struct)。struct也可以有成员和方法。如下所示:

    type Rectangle struct {

    width int

    height int

    }

    这是一个名为“Rectangle”的数据结构,它有两个成员变量(也可以称为字段,原文中为fields):宽度和高度。可以像这样创建实例:

    r := new(Rectangle)

    还可以这样引用它的成员变量(fields):

    r.width = 10

    r.height = 5

    我们可以在“Rectangle”数据结构上编写方法,如下所示:

    func (r *Rectangle) area() int {

    return r.width * r.height

    }

    这里的方法名称为area,可以这么来调用上面的方法:

    r := new(Rectangle)

    r.area()

    类型继承

    Golang在设计上未采用Java的“继承(extends)”,它的继承是通过组合来完成的。例如:

    type Rectangle struct {

    Shape

    width int

    height int

    }

    上面Rectangle结构中有一个类型为Shape的匿名成员。Shape中包含的所有字段和方法在Rectangle对象上都是可见的。但是需要注意的是,不像在Java中,可以将Rectangle传递给Shape为参数的函数,这在Go中是行不通的。要获得这种类型的多态性,应该使用Go接口。

    多态性、接口

    在Java中有特定的接口类型,这些接口类型定义了对象的行为。在Go中,也有类似的概念,可以通过intefaces来实现。例如,下面这个接口声明了一个具有Print()方法的Shape类型:

    type Shape interface {

    Print()

    当使用Go来创建结构时,不需要像在Java中那样用“implementation”来声明它。它是隐式的,只需要实现了该接口对应的方法,对应的结构体就可以被传递给需要的函数:

    type Rectangle struct {

    width int

    height int

    }

    func (r *Rectangle) Print() {

    fmt.println("Rectangle!");

    }

    此时,Rectangle对象可以传递给任何接收Shape类型的函数,因为它实现了该类型的所有方法。

    For循环

    Go中的For循环,样例如下:

    for i := 1; i <= 10; i++ {

    fmt.Println(i)

    }

    然而,当迭代一个数组(或类似于数组的东西,例如,字符串,映射,切片等),可以使用range运算符(假设foo是一个列表List):

    for v := range foo {

    fmt.println("value="+v);

    }

    如果在遍历列表时需要知道该列表的索引,则可以这样编写代码:

    for i, v := range foo {

    fmt.println("index " + i +"has value="+v);

    }

    While循环

    Go中还可以像这样再次使用for循环:

    sum := 1

    for sum < 1000 {

    sum += sum

    }

    fmt.Println(sum)

    或者实现一个无限while循环:

    for {

    something...

    }

    指针和引用

    Golang中需要显式地使用指针和引用,而Java通常隐藏这些。例如,Java中可以这样做:

    Shape shape = new Shape();

    shape.foo(); 

    但是在Go中,必须直接处理指针

    type Rectangle struct {

    width int

    height int

    }

    func updateRectangle(r *Rectangle){

    r.width = 5;

    r.height = 10;

    }

    func main() {

    r := Rectangle{20,30}

    updateRectangle(&r)

    }

    当main函数执行完毕时,Rectangle对象中r.width=5,r.height=10。注意:必须显式地引用指针。

     

    垃圾回收机制

    Golang与java类似,也是一种垃圾收集语言。Go开发者不需要手动来释放程序中不再使用的变量和结构占用的内存,在Go的运行时中有一个独立的进程,即垃圾收集器(GC),会处理这些事,它会通过标记算法搜索不再使用的变量然后释放内存。

    通过调用runtime.GC()函数可以显式的触发GC,但这只在某些特殊的场景下才会使用,比如当内存资源不足时调用runtime.GC(),它会在此函数执行的点上立即释放内存,此时程序可能会有短时的性能下降(由于GC进程的执行)。如果想知道当前的内存状态,也可以使用如下代码:

    var mruntime.MemStats

    runtime.ReadMemStats(&m)

    fmt.Printf("%dKb ", m.Alloc / 1024)

    上面的程序会给出已分配内存的总量,单位是 Kb。

    原文作者:Christian Posta  译者:江玮

    原文链接:https://dzone.com/articles/quick-go-lang-for-java-developers

    版权声明:本文版权归作者(译者)及公众号所有,欢迎转载,但未经作者(译者)同意必须保留此段声明,且在文章页面明显位置给出,本文链接如有问题,可留言咨询。

  • 相关阅读:
    跳出iframe
    leetcode 225. Implement Stack using Queues
    leetcode 206. Reverse Linked List
    leetcode 205. Isomorphic Strings
    leetcode 203. Remove Linked List Elements
    leetcode 198. House Robber
    leetcode 190. Reverse Bits
    leetcode leetcode 783. Minimum Distance Between BST Nodes
    leetcode 202. Happy Number
    leetcode 389. Find the Difference
  • 原文地址:https://www.cnblogs.com/davidwang456/p/10327880.html
Copyright © 2011-2022 走看看