面试官:说说golang和java有哪些区别?

4,895 阅读9分钟

「时光不负,创作不停,本文正在参加2021年终总结征文大赛」,感谢大家的支持。

image.png

Go语言(Golang)是google在2009年推出的一种编译型编程语言。近些年,使用golang的程序员也越来越多,想必有很多人好奇,golang的特性有哪些呢?和Java语言又有什么区别呢?

聊聊golang

相对于大多数语言,Go语言的推出,旨在不损失应用程序性能的情况下降低代码的复杂性,具有“部署简单、并发性好、执行性能好”等优势。

Go 语言从 C 语言继承了相似的表达式语法、控制流结构、基础数据类型、调用参数传值、指针等很多思想。Go 语言有一个清晰易懂的轻量级类型系统,在类型之间也没有层级之说。因此可以说 Go 语言是一门混合型的语言。此外,很多重要的开源项目都是使用 Go 语言 开发的,其中包括 Docker、Go-Ethereum、Thrraform 和 Kubernetes。

因为 Golang 是面向过程编程,所以它和 Java 或 C++ 看起来并不相同。我们下面通过与java的对比,阐述golang的特点。

与Java的区别

为防止文章大而空,本文暂且以总结介绍为主,详细内容会在后续的系列文章中解读;

  1. 性能 无论在串行还是并发的的业务下,java的性能都比Go差,golang的性能堪比c,c++,被誉为“21 世纪的C语言”;

1. goroutine (协程) 默认占用内存远比 Java 、C 的线程少(goroutine:2KB ,线程:8MB),占用资源更少

2. Go使用goroutine(用户级的轻量级线程), 避免了内核态和用户态的切换导致的成本。

  1. 编译部署 1. Java通过虚拟机编译,使用JVM跨平台编译;

2. Go中不存在虚拟机,golang设计的初衷是极简原则的,省略冗余的设计,字节码到机器码的翻译是有性能损耗的,虽然jvm已经把这种损耗降低到了极限,但远远没到可以忽略的程度,一般认为,go语言写的代码会比jvm语言运行效率更高。golang针对不同的平台,编译对应的机器码;

  1. 访问权限 1. java使用public、protected、private、默认等几种修饰符来控制访问权限;

image.png 2. golang通过大小写控制包外可访问还是不可访问。 golang中根据首字母的大小写来确定可以访问的权限。无论是方法名、常量、变量名还是结构体的名称,如果首字母大写,则可以被其他的包访问;如果首字母小写,则只能在本包中使用。可以简单的理解成,首字母大写是公有的,首字母小写是私有的

  1. 接口 1. java等面向对象编程的接口是侵入式接口,需要明确声明自己实现了某个接口。
interface IFoo { 
void Bar(); 
}

class Foo implements IFoo { 
// Java实现类 // ... 
}

2. 而Golang的非侵入式接口不需要通过任何关键字声明类型与接口之间的实现关系,只要一个类型实现了接口的所有方法,那么这个类型就是这个接口的实现类型。

  1. 异常处理 1. java中错误(Error)和异常(Exception)被分类管理,二者的区别是:

Error(错误):程序在执行过程中所遇到的硬件或操作系统的错误。错误对程序而言是致命的,将导致程序无法运行。常见的错误有内存溢出,jvm虚拟机自身的非正常运行,calss文件没有主方法。程序本生是不能处理错误的,只能依靠外界干预。Error是系统内部的错误,由jvm抛出,交给系统来处理;

EXCEPTION(异常):是程序正常运行中,可以预料的意外情况。比如数据库连接中断,空指针,数组下标越界。异常出现可以导致程序非正常终止,也可以预先检测,被捕获处理掉,使程序继续运行

2. golang中只有error,一旦发生错误逐层返回,直到被处理。Golang中引入两个内置函数panic和recover来触发和终止异常处理流程,同时引入关键字defer来延迟执行defer后面的函数。golang弱化了异常,只有错误,在意料之外的panic发生时,在defer中通过recover捕获这个恐慌,转化为错误以code,message的形式返回给方法调用者,调用者去处理,这也是go极简的精髓;

if err != nil {
//这样的代码块在golang中很常见
}
  1. 继承 1. Java的继承通过extends关键字完成,不支持多继承;
class SuperA { 
} 
class SuperB{ 
} 
class Sub extends SuperA{
//类继承类 只能继承一个类 单继承 
}

2. Go语言的继承通过匿名组合完成:基类以Struct的方式定义,子类只需要把基类作为成员放在子类的定义中,支持多继承。

type demoA struct{
}
type demoB struct{
}
type demoC struct{
     demoA//demoC继承demoA
     demoB//demoC继承demoB
}
  1. 多态 1. java的多态,必须满足继承,重写,向上转型;任何用户定义的类型都可以实现任何接口,所以通过不同实体类型对接口值方法的调用就是多态。

image.png

2. 在Go语言中通过接口实现多态,对接口的实现只需要某个类型T实现了接口中的方法,就相当于实现了该接口。

/* 多态的例子 */
package main

import "fmt"

type message interface {
   call()
}

type people struct {
   name string
   email string
}

type admin struct {
   name string
   age  int
}

//使用指针接收者实现了notofy接口,方法会共享接收者所指向的值people
func (u *people) call()  {
   fmt.Println("sendNotify to people", u.name)
}

//使用值接收者实现了notify接口,方法使用u值的副本,对u的修改不会影响原值
func (u admin) call(){
   fmt.Println("sendNotify to admin:", u.name)
}

//接收一个message接口类型的值,如果一个实体类型实现了该接口,
//sendNotify函数会根据实体类型的值类执行message接口的notify行为,这个函数具有多态的能力。
func sendNotify(m message){
   n.call()
}

func main()  {
   people1 := people{"张三", "qq@qq.com"}
   sendNotify(&people1)

   people2 := admin{"李四", 25}
   sendNotify(user2)

   var m message
   m = &people
   m.call()
}
  1. 值传递和引用传递 java中不存在显式的指针,而Golang中存在显式的指针操作,使用 * 来定义和声明指针,通过&来取得对象的指针。注意,java和golang都是只存在值传递。

Golang的类型系统与Java相差不大,但是需要注意的是Java中的数组是属于引用类型,而Golang中的数组属于值类型,当向方法中传递数组时,Java可以直接通过该传入的数组修改原数组内部值(浅拷贝),但Golang则会完全复制出一份副本来进行修改(深拷贝),在Golang中,只有切片、指针、channel、map及func属于引用类型,也就是在传递参数的时候,实质上复制的都是他们的指针,内部的修改会直接影响到外部。在Golang中,只有同长度、同类型的数组才可视为“同一类型”,譬如int和int32则会被视为不同的类型,这在参数传递的时候会造成编译错误。

  1. 并发 1. 在Java中,通常借助于共享内存(全局变量)作为线程间通信的媒介,通常会有线程不安全问题,使用了加锁(同步化)、使用原子类、使用volatile提升可见性等解决;

2. 但在Golang中使用的是通道(channel)作为协程间通信的媒介,这也是Golang中强调的:不要通过共享内存通信,而通过通信来共享内存。golang使用GMP并发模型,多个goroutine之间通过Channel来通信,chan的读取和写入操作为原子操作,所以是安全的。

  1. 垃圾回收和内存管理机制 Java基于JVM虚拟机的分代收集算法完成GC,golang内存释放是语言层面,对不再使用的内存资源进行自动回收,使用多级缓存,非分代,并发的三色标记算法。

image.png

总结

Go和Java的区别有:

1.性能上: golang的性能比Java更好,占用内存更少,使用goroutine避免内核态和用户态切换成本。

2.编译部署:Java通过虚拟机编译,使用JVM跨平台编译;Go中不存在虚拟机,针对不同的平台,编译对应的机器码;

3.访问权限: java使用public、protected、private、默认等关键字;golang通过大小写控制。

4.接口: java等面向对象编程的接口是侵入式接口,需要明确声明自己实现了某个接口。 而Golang的非侵入式接口不需要通过任何关键字,只要一个类型实现了接口的所有方法,就是这个接口的实现。

5.异常处理:java中错误(Error)和异常(Exception)被分类管理,golang中只有error,一旦发生错误逐层返回,直到被处理。

6.继承:Java的继承通过extends关键字完成,不支持多继承;Go语言的继承通过Struct的方式,子类只需要把基类作为成员放在子类的定义中,支持多继承。

7.多态:java的多态,必须满足继承,重写,向上转型;在Go语言中通过接口实现多态,对接口的实现只需要某个类型T实现了接口中的方法,就相当于实现了该接口。

8.值传递和引用传递:java中不存在显式的指针,而Golang中存在显式的指针操作。java和golang都是只存在值传递。

9.并发:在Java中,通常借助于共享内存(全局变量)作为线程间通信的媒介,通常会有线程不安全问题,使用了加锁(同步化)、使用原子类、使用volatile提升可见性等解决;但在Golang中使用的是通道(channel)作为协程间通信的媒介,多个goroutine之间通过Channel来通信,chan的读取和写入操作为原子操作,所以是安全的。

10.垃圾回收和内存管理机制:Java基于JVM虚拟机的分代收集算法完成GC,golang内存释放语言层面,对不再使用的内存资源进行自动回收,多级缓存,非分代,并发的三色标记算法。