page contents

Golang与Java各方面使用对比

一、基本情况 1.1、Golang基本介绍 Go语言(或 Golang)起源于 2007 年,并在 2009 年正式对外发布。Go 是非常年轻的一门语言,它的主要目标是“兼具 Python 等动态语言的开发速度和 C/C++...

attachments-2021-07-h7uvPuLo60f4ef1db0e5e.png

一、基本情况

1.1、Golang基本介绍

Go语言(或 Golang)起源于 2007 年,并在 2009 年正式对外发布。Go 是非常年轻的一门语言,它的主要目标是“兼具 Python 等动态语言的开发速度和 C/C++ 等编译型语言的性能与安全性”。

Go语言的推出,旨在不损失应用程序性能的情况下降低代码的复杂性,具有“部署简单、并发性好、语言设计良好、执行性能好”等优势,目前国内诸多 IT 公司均已采用Go语言开发项目。

Go语言有时候被描述为“C 类似语言”,或者是“21 世纪的C语言”。Go 从C语言继承了相似的表达式语法、控制流结构、基础数据类型、调用参数传值、指针等很多思想,还有C语言一直所看中的编译后机器码的运行效率以及和现有操作系统的无缝适配。

因为Go语言没有类和继承的概念,所以它和 Java 或 C++ 看起来并不相同。但是它通过接口(interface)的概念来实现多态性。Go语言有一个清晰易懂的轻量级类型系统,在类型之间也没有层级之说。因此可以说Go语言是一门混合型的语言。

此外,很多重要的开源项目都是使用Go语言开发的,其中包括 Docker、Go-Ethereum、Thrraform 和 Kubernetes。

据最新的2020年9月Tiobe编程语言排行榜,目前Golang的使用率排名为第11:

在这里插入图片描述

1.2、Golang使用场景

服务端开发(配合gin、gorm等库就能够完成高性能的后端服务)

容器开发(譬如Docker、K8s都是基于Golang开发的)

脚本开发(由于Golang自身部署简单,并且与操作系统API交互方便,所以还可替代Python作为脚本开发)

底层工具的开发(可代替C或者C++开发操作系统底层工具)

二、基本语法

2.1、编码规约

Golang是一门严格的工程语言,主要体现在编码风格及可见域规则上。在Java中,允许多种编码风格共存,譬如以下两种方法声明,对于Java来说都是允许的:

public String getString(Integer num) {
    return num.toString();
}
public String getString(Integer num) 
{
    return num.toString();
}

1.左右花括号需要符合上下换行风格

在Golang中,只允许出现一种换行风格:

func getString(num int) string {
return strconv.Itoa(num)
}

如果出现以下换行风格则会报错,无法通过编译:

attachments-2021-07-GtTvA8lR60f4ee04e6685.png

2.变量声明后必须使用,不使用需要使用_来代替

在Java中,变量可以声明了却不使用,而Golang中声明的变量必须被使用,否则需要使用_来替代掉变量名,表明该变量不会比使用到:

func getString(num int) string {
temp := num // 没有使用者,无法编译
_ := num// 正常编译
return strconv.Itoa(num)
}

3.可见域规则

Java对方法、变量及类的可见域规则是通过private、protected、public关键字来控制的,而Golang中控制可见域的方式只有一个,当字段首字母开头是大写时说明其是对外可见的、小写时只对包内成员可见。

entity

package entity
type Person struct {
Name string
Age int
id string
}
type student struct {
detail Person
}
func test() {
// 本包内可见
person := &student{detail: Person{
Name: "ARong",
Age:  21,
id:   "211",
}}
fmt.Println(person)
}

main

package main
import (
"fmt"
entity "others/scope"
)
func main() {
// id字段不可见
person := &entity.Person{
Name: "ARong",
Age:  21,
}
fmt.Println(person)
}

2.2、变量声明及初始化

1.变量声明及初始化的文法

在Java中,通常声明变量及初始化的文法为:

    // Object:要声明的类型、v:变量名称、new Object()变量初始化
    Object v = new Object();

而Golang使用var关键字来声明变量:

    // var:变量定义、v1:变量名称、int:变量类型
    var v1 int
var v2 string
var v3 [10]int  // 数组
var v4 []int // 数组切片
var v5 struct {
f int
}
var v6 *int // 指针
var v7 map[string]int  // map,key为string类型,value为int类型
var v8 func(a int) int
var v9,v10 int //v9和v10都声明为int型

也可以采用:=自动推测变量类型:

var v1 int = 10 // 正确的使用方式1
var v2 = 10  // 正确的使用方式2,编译器可以自动推导出v2的类型
v3 := 10  // 正确的使用方式3,编译器可以自动推导出v3的类型

2.对于基本类型,声明即初始化;对于引用类型,声明则初始化为nil

在Java中,如果在方法内部声明一个变量但不初始化,在使用时会出现编译错误:

public void solve() {
    int num;
    Object object;
    System.out.println(num); // 编译错误
    System.out.println(object); // 编译错误
}

而在Golang中,对于基本类型来讲,声明即初始化;对于引用类型,声明则初始化为nil。这样可以极大地避免NPE的发生。

func main() {
var num int
var hashMap *map[string]int
fmt.Println(num) // num = 0
fmt.Println(hashMap) //  &hashMap== nil
}

2.3、值类型及引用类型

Golang的类型系统与Java相差不大,但是需要注意的是Java中的数组是属于引用类型,而Golang中的数组属于值类型,当向方法中传递数组时,Java可以直接通过该传入的数组修改原数组内部值(浅拷贝),但Golang则会完全复制出一份副本来进行修改(深拷贝):

Java

    public static void main(String[] args) {
        int[] array = {1, 2, 3};
        change(array);
        System.out.println(Arrays.toString(array)); // -1,2,3
    }
    private static void change(int[] array) {
        array[0] = -1;
    }

Golang

func main() {
array := [...]int{1, 2, 3}
change(array)
fmt.Println(array) // 1,2,3
}
func change(array [3]int) {
array[0] = -1
}

并且值得注意的是,在Golang中,只有同长度、同类型的数组才可视为“同一类型”,譬如[2]int和[3]int则会被视为不同的类型,这在参数传递的时候会造成编译错误。

所以在Golang中数组很少被直接使用,更多的是使用切片(基于数组指针)来代替数组。

在Golang中,只有切片、指针、channel、map及func属于引用类型,也就是在传递参数的时候,实质上复制的都是他们的指针,内部的修改会直接影响到外部:

func main() {
slice := []int{1, 2, 3}
changeSlice(slice)
fmt.Println(slice) // -1,2,3
mapper := map[string]int {
"num": 0,
}
changeMap(mapper)
fmt.Println(mapper) // num = -1
array := [...]int{1, 2, 3}
changePointer(&array)
fmt.Println(array) // -1,2,3
intChan := make(chan int, 1)
intChan <- 1
changeChannel(intChan)
fmt.Println(<- intChan) // -1
}
func changeChannel(intChan chan int) {
<- intChan
intChan <- -1
}
func changePointer(array *[3]int) {
array[0] = -1
}
func changeMap(mapper map[string]int) {
mapper["num"] = -1
}
func changeSlice(array []int) {
array[0] = -1
}

三、结构体、函数及指针

3.1、结构体声明及使用

在Golang中区别与Java最显著的一点是,Golang不存在“类”这个概念,组织数据实体的结构在Golang中被称为结构体。函数可以脱离“类”而存在,函数可以依赖于结构体来调用或者依赖于包名调用。

Golang中的结构体放弃了继承、实现等多态概念,结构体之间可使用组合来达到复用方法或者字段的效果。

要声明一个结构体只需使用type + struct关键字即可:

type Person struct {
Name string
Age  int
id   string
}

要使用一个结构体也很简单,一般有以下几种方式去创建结构体:

    personPoint := new(entity.Person) // 通过new方法创建结构体指针
person1 := entity.Person{} // 通过Person{}创建默认字段的结构体
person2 := entity.Person{ // 通过Person{Name:x,Age:x}创建结构体并初始化特定字段
Name: "ARong",
Age:  21,
}
fmt.Println(personPoint) // &{ 0 }
fmt.Println(person1)     // { 0 }
fmt.Println(person2) // {ARong 21 }

3.2、函数和方法的区别

使用Java的朋友应该很少使用“函数”这个词,因为对于Java来说,所有的“函数”都是基于“类”这个概念构建的,也就是只有在“类”中才会包含所谓的“函数”,这里的“函数”被称为“方法”。

而“函数”这个词源于面向过程的语言,所以在Golang中,“函数”和“方法”的最基本区别是:

函数不基于结构体而是基于包名调用,方法基于结构体调用。

下面是一个例子,可以直观地看出方法和函数的区别:

entity

package entity
import "fmt"
type Person struct {
Name string
Age  int
id   string
}
// Person结构体/指针可调用的"方法",属于Person结构体
func (p *Person) Solve() {
fmt.Println(p)
}
// 任何地方都可调用的"函数",不属于任何结构体,可通过entity.Solve调用
func Solve(p *Person) {
fmt.Println(p)
}

main

func main() {
personPoint := new(entity.Person) // 通过new方法创建结构体指针
entity.Solve(personPoint) // 函数调用

personPoint.Solve()   // 方法调用
}

3.3、指针的使用

在Java中不存在显式的指针操作,而Golang中存在显式的指针操作,但是Golang的指针不像C那么复杂,不能进行指针运算。

下面从一个例子来看Java的隐式指针转化和Golang的显式指针转换:Java和Golang方法传参时传递的都是值类型,在Java中如果传递了引用类型(对象、数组等)会复制其指针进行传递, 而在Golang中必须要显式传递Person的指针,不然只是传递了该对象的一个副本。

Golang使用 * 来定义和声明指针,通过&来取得对象的指针。

func main() {
p1 := entity.Person{
Name: "ARong1",
Age:  21,
}
changePerson(p1)
fmt.Println(p1.Name) // ARong1
changePersonByPointer(&p1)
fmt.Println(p1.Name) // ARong2
}
func changePersonByPointer(person *entity.Person) {
person.Name = "ARong2"
}
func changePerson(person entity.Person) {
person.Name = "ARong2"
}

注意,如果结构体中需要组合其他结构体,那么建议采用指针的方式去声明,否则会出现更新丢失问题。

以下是Golang方法的一个隐式指针转换,结构体调用方法时,如果传递的是对象,那么会被自动转化为指针调用:

type Person struct {
Name string
Age  int
}
// Person结构体/指针可调用的"方法",属于Person结构体
func (p *Person) Solve() {
fmt.Println(p)
}

func main() {
p := entity.Person{
Name: "ARong",
Age:  21,
}

pp := &p
pp.Solve() // 显式

p.Solve    // 隐式,自动将p转化为&p
}

更多相关技术内容咨询欢迎前往并持续关注六星社区了解详情。

程序员编程交流QQ群:805358732

如果你想用Python开辟副业赚钱,但不熟悉爬虫与反爬虫技术,没有接单途径,也缺乏兼职经验
关注下方微信公众号:Python编程学习圈,获取价值999元全套Python入门到进阶的学习资料以及教程,还有Python技术交流群一起交流学习哦。

attachments-2022-06-gNp9380W62ad4207aa799.jpeg

  • 发表于 2021-07-19 11:19
  • 阅读 ( 653 )
  • 分类:Golang

0 条评论

请先 登录 后评论
轩辕小不懂
轩辕小不懂

2403 篇文章

作家榜 »

  1. 轩辕小不懂 2403 文章
  2. 小柒 1474 文章
  3. Pack 1135 文章
  4. Nen 576 文章
  5. 王昭君 209 文章
  6. 文双 71 文章
  7. 小威 64 文章
  8. Cara 36 文章