其他
Java与Go到底差别在哪,谁要被时代抛弃?
👉腾小云导读
本文针对Golang与Java的基础语法、结构体函数、异常处理、并发编程及垃圾回收、资源消耗等各方面的差异进行对比总结。欢迎阅读。👉看目录,点收藏1 基础语法 1.1 变量 1.2 作用域规则 1.3 逗号 ok 模式 1.4 结构体、函数以及方法 1.5 值类型、引用类型以及指针2 面向对象 2.1 Java的OOP与Golang的结构体组合 2.2 侵入式与非侵入式接口 3 异常处理 3.1 Java的异常处理 3.2 Golang的异常处理4.并发编程 4.1 Java 和 Golang 的基本实现 4.2 Java 和 Golang 的区别 5 垃圾回收 5.1Java的垃圾回收体系 5.2Golang GC特征6 资源消耗对比 6.1 Java的JIT策略比Golang的AOT策略 6.2 内存分配和垃圾回收器 6.3 并发 6.4 反射7 生态8 总结01
data := num // data没有使用者,无法编译
return strconv.Itoa(num)
}
func toString(num int) string {
_ := num // 正常编译
return strconv.Itoa(num)
}
1.1.2 变量声明及初始化
int age;
Object object;
System.out.println(age); // 编译错误
System.out.println(object); // 编译错误
}
func compareVariable() {
var age int
var hashMap *map[string]int
fmt.Println(num) // num = 0
fmt.Println(hashMap) // &hashMap== nil
}
1.2 作用域规则
1.3 逗号 ok 模式
if _, ok := conditionMap["page"]; ok {
//
}
1.4.1 结构体声明及使用
// User 定义User结构体
type User struct {
Name string
Age int
}
// 使用一个结构体
func main() {
personPoint := new(User) // 通过new方法创建结构体指针
person1 := User{} // 通过Person{}创建默认字段的结构体
person2 := User{
Name: "xiaoHong",
Age: 21,
}
fmt.Println(personPoint) // &{ 0 }
fmt.Println(person1) // { 0 }
fmt.Println(person2) // {xiaoHong 21 }
}
class User {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public String print() {
return "{name = " + name + ",age = " + age + "}";
}
}
public class Demo {
public static void main(String[] args) {
User user = new User("xiaohong", 29);
System.out.println("user信息:" + user.print());
}
}
//执行结果
user信息:{name = xiaohong,age = 29}
1.4.2 函数和方法的区别
package entity
import "fmt"
type User struct {
Name string
Age int
}
// User结构体/指针可调用的"方法",属于User结构体
func (user *User) Solve() {
fmt.Println(user)
}
// 任何地方都可调用的"函数",不属于任何结构体,可通过entity.Solve调用
func Solve(user *User) {
fmt.Println(user)
}
func main() {
userPoint := new(entity.User) // 通过new方法创建结构体指针
entity.Solve(userPoint) // 函数调用
userPoint.Solve() // 方法调用
}
1.5 值类型、引用类型以及指针
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;
}
// 不限定长度(即slice):
func main() {
var array = []int{1, 2, 3}
change(array)
fmt.Println(array) // [-1 2 3]
}
func change(array []int) {
array[0] = -1
}
// 限定长度(即数组):
func main() {
var array = [3]int{1, 2, 3}
change(array)
fmt.Println(array) //[1 2 3]
}
func change(array [3]int) {
array[0] = -1
}
1.5.2对象对比
//User 定义User结构体
type User struct {
Name string
Age int
}
// 定义一个全局的User
var globalUser = User {
"xiaoming",
28,
}
// modifyUser 定义一个函数,参数为User结构体“对象”,将全局globalUser指向传递过来的User结构体“对象”
func modifyUser(user User) {
fmt.Printf("参数user的地址 = %p\n",&user)
fmt.Printf("globalUser修改前的地址 = %p\n",&globalUser)
fmt.Println("globalUser修改前 = ",globalUser)
// 修改指向
globalUser = user
fmt.Printf("globalUser修改后的地址 = %p\n",&globalUser)
fmt.Println("globalUser修改后 = ",globalUser)
}
func main() {
var u User = User {
"xiaohong",
29,
}
fmt.Printf("将要传递的参数u的地址 = %p\n",&u)
modifyUser(u)
}
// 执行结果
将要传递的参数u的地址 = 0xc0000ac018
参数user的地址 = 0xc0000ac030
globalUser修改前的地址 = 0x113a270
globalUser修改前 = {xiaoming 28}
globalUser修改后的地址 = 0x113a270
globalUuser修改后 = {xiaohong 29}
class User {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public String print() {
return "{name = " + name + ",age = " + age + "}";
}
}
public class Demo {
private static User globalUser = new User("xiaoming",28);
public static void modifyUser(User user) {
System.out.println("参数globalUser的地址 = " + user);
System.out.println("globalUser修改前的地址 = " + globalUser);
System.out.println("globalUser修改前 = " + globalUser.print());
globalUser = user;
System.out.println("globalUser修改后的地址 = " + globalUser);
System.out.println("globalUser修改后 = " + globalUser.print());
}
public static void main(String[] args) {
User user = new User("xiaohong", 29);
System.out.println("将要传递的参数user的地址 = " + user);
modifyUser(user);
}
}
//执行结果
将要传递的参数user的地址 = com.example.demo.User@5abca1e0
参数globalUser的地址 = com.example.demo.User@5abca1e0
globalUser修改前的地址 = com.example.demo.User@2286778
globalUser修改前 = {name = xiaoming,age = 28}
globalUser修改后的地址 = com.example.demo.User@5abca1e0
globalUser修改后 = {name = xiaohong,age = 29}
1.5.3指针的区别
// User 定义User结构体
type User struct {
Name string
Age int
}
func main() {
p1 := User{
Name: "xiaohong",
Age: 21,
}
changePerson(p1)
fmt.Println(p1.Name) // xiaohong
changePersonByPointer(&p1)
fmt.Println(p1.Name) // xiaoming
}
func changePersonByPointer(user *User) {
user.Name = "xiaoming"
}
func changePerson(user User) {
user.Name = "xiaoming"
}
public class Demo {
public static void changePerson(User user) {
user.setName("xiaoming");
}
public static void main(String[] args) {
User user = new User("xiaohong", 29);
changePerson(user);
System.out.println("user信息:" + user.getName()); // xiaoming
}
}
class User {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
02
2.1 Java的OOP与Golang的结构体组合
public abstract class Animal {
String name;
int age;
}
public class Dog extends Animal {
public void bark() {
System.out.println(age + "岁的" + name + "在汪汪叫");
}
}
public class Demo {
public static void main(String[] args) {
Dog dog = new Dog();
dog.name = "小龙";
dog.age = 2;
dog.bark(); // 2岁的小龙在汪汪叫
}
}
package main
import "fmt"
type Animal struct {
Name string
Age int
}
type Dog struct {
*Animal
}
func (dog *Dog) Bark() {
fmt.Printf("%d岁的%s在汪汪叫", dog.Age, dog.Name)
}
func main() {
dog := &Dog{&Animal{
Name: "小龙",
Age: 2,
}}
dog.Bark() // 2岁的小龙在汪汪叫...
}
2.2 侵入式与非侵入式接口
public interface Dog {
void Bark();
}
public class DogImpl implements Dog{
@Override
public void Bark() {
System.out.println("汪汪叫");
}
}
public class Demo {
public static void main(String[] args) {
Dog dog = new DogImpl();
dog.Bark(); // 汪汪叫
}
}
package entity
type Factory interface {
Produce() bool
Consume() bool
}
type CarFactory struct {
ProductName string
}
func (c *CarFactory) Produce() bool {
fmt.Printf("CarFactory生产%s成功", c.ProductName)
return true
}
func (c *CarFactory) Consume() bool {
fmt.Printf("CarFactory消费%s成功", c.ProductName)
return true
}
// --------------
package main
func main() {
factory := &entity.CarFactory{"Car"}
doProduce(factory)
doConsume(factory)
}
func doProduce(factory entity.Factory) bool {
return factory.Produce()
}
func doConsume(factory entity.Factory) bool {
return factory.Consume()
}
在 Go 中,类没有继承的概念,只需要知道这个类型实现了哪些方法,每个方法是啥行为。 实现类型的时候,只需要关心自己应该提供哪些方法,不用再纠结接口需要拆得多细才合理。接口由使用方按需定义,而不用事前规划。 减少包的引入,因为多引用一个外部的包,就意味着更多的耦合。接口由使用方按自身需求来定义,使用方无需关心是否有其他模块定义过类似的接口。 |
03
异常处理
3.1 Java 的异常处理
public class ExceptionTest {
public static void main(String[] args) {
FileInputStream fileInputStream = null;
try{
fileInputStream = new FileInputStream("test.txt");
}catch (IOException e){
System.out.println(e.getMessage());
e.printStackTrace();
return;
}finally {
if(fileInputStream!=null){
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
System.out.println("回收资源");
}
}
}
3.2 Golang 的异常处理
if err != nil {}
去做判断和处理,不能做到统一捕捉和处理,容易遗漏。func main() {
value, err := Bark()
if err != nil {
// 返回了异常,进行处理
log.error("...异常:", err)
return err
}
// Bark方法执行正确,继续执行后续代码
Process(value)
}
Golang 的 defer 、panic 及 recover
defer func() {
// 清理工作
} ()
// 这是传递参数给匿名函数时的写法
var num := 1
defer func(num int) {
// 做你复杂的清理工作
} (num)
需要注意的是, defer 使用一个栈来维护需要执行的代码,所以 defer 函数所执行的顺序是和 defer 声明的顺序相反的:
defer fmt.Println(a)
defer fmt.Println(b)
defer fmt.Println(c)
执行结果:
c
b
a
func main() {
fmt.Println("main begin")
// 必须要先声明defer,否则不能捕获到panic异常
defer func() {
fmt.Println("defer begin")
if err := recover(); err != nil {
// 这里的err其实就是panic传入的内容
fmt.Println(err)
}
fmt.Println("defer end")
}()
test()
// test中出现错误,这里开始下面代码不会再执行
fmt.Println("main end")
}
func test() {
fmt.Println("test begin")
panic("error")
//这里开始下面代码不会再执行
fmt.Println("test end")
}
//执行结果
main begin
test begin
defer begin
error
defer end
04
并发编程
4.1 Java 和 Golang 的基本实现
Runnable task = ()-> System.out.println("task running");
Thread t = new Thread(task);
t.start();
Runnable task = ()-> System.out.println("task running");
Executor executor = Executors.newCachedThreadPool();
executor.execute(task);
go
关键字调用函数之后,便创建了一个可以运行代码单元的 goroutine 。一旦 CPU 资源就绪,对应的代码单元便会在 goroutine 中执行:go func() {
fmt.Println("test task running")
}()
4.2 Java 和 Golang 的区别
关系 | ||
4.2.1 Java synchronized 与 Golang Mutex
public void method()
{
synchronized(this) {
// todo some thing
}
}
public synchronized void method()
{
// todo some thing
}
修改一个静态方法,作用范围是整个静态方法:
public synchronized static void method() {
// todo some thing
}
class DemoClass {
public void method() {
synchronized(DemoClass.class) {
// todo some thing
}
}
}
func main() {
var mutex sync.Mutex
count := 0
for i := 0; i < 100; i++ {
go func() {
mutex.Lock() // 加锁
count += 1
mutex.Unlock() // 解锁
}()
}
// 休眠,等待2s
time.Sleep(time.Second * 2)
// 100,没有加锁结果不正确
fmt.Println("count = ", count)
}
type count struct {
lock sync.Mutex
value int
}
// 结构体对应的结构方法
func (receiver *count) countOne() {
receiver.lock.Lock()
defer receiver.lock.Unlock()
receiver.value++
}
func main() {
c := count{
lock: sync.Mutex{},
value: 0,
}
group := sync.WaitGroup{}
for i := 0; i < 10; i++ {
group.Add(1)
go func(count2 *count) {
defer group.Done()
for i := 0; i < 100; i++ {
count2.countOne()
}
}(&c)
}
group.Wait()
fmt.Printf("The count value is %d", c.value) // The count value is 1000
}
4.2.2 条件变量
Java 和 Golang 相似点
Java 和 Golang 区别点
4.2.3 CAS/Atomic
一个或者多个操作在 CPU 执行的过程中不被中断的特性,称为原子性(atomicity)。 |
4.2.4 Once 与单例模式
func main() {
var once = sync.Once{}
f := func() {
time.Sleep(10 * time.Millisecond)
fmt.Println("do once")
}
go func() {
fmt.Println("do once start")
once.Do(f)
fmt.Println("do once finish")
}()
time.Sleep(1 * time.Millisecond)
for i := 0; i < 2; i++ {
go func() {
fmt.Println("block...")
once.Do(f)
fmt.Println("resume")
}()
}
time.Sleep(10 * time.Millisecond)
}//~
do once start
block...
block...
do once
do once finish
resume
resume
//懒汉式单例类.在第一次调用的时候实例化自己
public class Singleton {
private Singleton() {}
private static Singleton single=null;
//静态工厂方法
public static synchronized Singleton getInstance() {
if (single == null) {
single = new Singleton();
}
return single;
}
}
饿汉式单例:饿汉式在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以天生是线程安全的。
//饿汉式单例类.在类初始化时,已经自行实例化
public class Singleton {
private Singleton() {}
private static final Singleton single = new Singleton();
//静态工厂方法
public static Singleton getInstance() {
return single;
}
}
05
垃圾回收
因为支持更多的特性和更灵活多样的GC策略, 比如分代,对象可移动,各种参数调节等等. 而Go只做了一种GC方案,不分代,不可移动,没什么参数能调节,而且更注重暂停时间的优化,执行GC的时机更频繁, 所以Go通常更占更少的内存,但代价就是GC性能比JVM差了不少。
5.1 Java 的垃圾回收体系
5.2 Golang GC 特征
5.2.1 三色标记
5.2.3 非紧缩
5.2.4 写屏障
06
资源消耗对比
6.1 Java 的 JIT 策略比 Golang 的 AOT 策略
6.2 内存分配和垃圾回收器
6.3 并发
6.4 反射
07
生态
08
总结最后,上表为各位总结~
优点 | 缺点 | |
静态类型可编译成机器码直接运行 天生多核并行 垃圾收集 跨平台且不依赖运行时环境 简洁的泛型 | 有限的库支持 泛型不够完善 灵活度没Java高(这个可算优点也可算缺点) | |
优秀的三方库 多线程 灵活性高 平台独立性 完善的语言特性 代码结构层次清晰 | 生态的复杂性 复杂的继承机制 |
整体来说,二者各有优劣,各位自取所需。以上是本次分享全部内容,欢迎大家在评论区分享交流。如果觉得内容有用,欢迎转发~
-End-
原创作者|宋欣东
技术责编|宋欣东
公众号后台回复「福利」,一键领腾讯GO语言设计模式教程、GO整洁架构教程、GPT应用教程文集
⬇️⬇️⬇️