Java Demo 对应 Go Demo
很好!既然你有 Java 背景,我会通过对比的方式帮你快速上手 Go。我会展示一些常见场景的 Java 和 Go 代码对比。
1. Hello World & 基本结构
Java:
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}Go:
package main
import "fmt"
func main() {
fmt.Println("Hello, World!")
}关键差异:
- Go 没有类,函数可以独立存在
package main是可执行程序的入口包- 不需要分号(可选)
- 首字母大写表示公开(public),小写表示私有
2. 变量声明与类型
Java:
public class Variables {
public static void main(String[] args) {
// 显式类型
String name = "Alice";
int age = 25;
double salary = 50000.50;
boolean isActive = true;
// 类型推断 (Java 10+)
var city = "Taipei";
// 常量
final int MAX_SIZE = 100;
}
}Go:
package main
func main() {
// 显式类型
var name string = "Alice"
var age int = 25
var salary float64 = 50000.50
var isActive bool = true
// 类型推断
var city = "Taipei"
// 短声明(最常用)
country := "Taiwan"
// 常量
const MaxSize = 100
}关键差异:
- Go 的
:=是最常用的声明方式 - Go 没有隐式类型转换,必须显式转换
- 类型写在变量名后面
3. 结构体 vs 类
Java:
public class Person {
private String name;
private int age;
// 构造函数
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// Getter
public String getName() {
return name;
}
// 方法
public void introduce() {
System.out.println("I'm " + name + ", " + age + " years old");
}
}
// 使用
Person p = new Person("Bob", 30);
p.introduce();Go:
package main
import "fmt"
// 结构体定义
type Person struct {
Name string // 大写=公开
age int // 小写=私有
}
// "构造函数"(惯例)
func NewPerson(name string, age int) *Person {
return &Person{
Name: name,
age: age,
}
}
// 方法(接收者)
func (p *Person) Introduce() {
fmt.Printf("I'm %s, %d years old\n", p.Name, p.age)
}
func main() {
p := NewPerson("Bob", 30)
p.Introduce()
}关键差异:
- Go 没有类,用结构体 + 方法
- 没有构造函数,通常用
NewXxx函数 - 方法通过接收者(receiver)绑定到类型上
- 没有 this/self,用接收者变量名
4. 接口
Java:
public interface Speaker {
void speak();
}
public class Dog implements Speaker {
@Override
public void speak() {
System.out.println("Woof!");
}
}
public class Cat implements Speaker {
@Override
public void speak() {
System.out.println("Meow!");
}
}
// 使用
Speaker dog = new Dog();
dog.speak();Go:
package main
import "fmt"
// 接口定义
type Speaker interface {
Speak()
}
// Dog 类型
type Dog struct{}
func (d Dog) Speak() {
fmt.Println("Woof!")
}
// Cat 类型
type Cat struct{}
func (c Cat) Speak() {
fmt.Println("Meow!")
}
func main() {
var s Speaker
s = Dog{}
s.Speak()
s = Cat{}
s.Speak()
}关键差异:
- Go 的接口是隐式实现(duck typing)
- 不需要
implements关键字 - 只要实现了接口的所有方法就自动满足接口
5. 错误处理
Java:
public class ErrorHandling {
public static int divide(int a, int b) throws ArithmeticException {
if (b == 0) {
throw new ArithmeticException("Division by zero");
}
return a / b;
}
public static void main(String[] args) {
try {
int result = divide(10, 0);
System.out.println(result);
} catch (ArithmeticException e) {
System.err.println("Error: " + e.getMessage());
}
}
}Go:
package main
import (
"errors"
"fmt"
)
func divide(a, b int) (int, error) {
if b == 0 {
return 0, errors.New("division by zero")
}
return a / b, nil
}
func main() {
result, err := divide(10, 0)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println(result)
}关键差异:
- Go 没有异常(exceptions)
- 错误作为返回值返回(通常是最后一个返回值)
- 使用
if err != nil检查错误 nil表示没有错误
6. 集合操作
Java:
import java.util.*;
public class Collections {
public static void main(String[] args) {
// List
List<String> fruits = new ArrayList<>();
fruits.add("apple");
fruits.add("banana");
// Map
Map<String, Integer> ages = new HashMap<>();
ages.put("Alice", 25);
ages.put("Bob", 30);
// 遍历
for (String fruit : fruits) {
System.out.println(fruit);
}
for (Map.Entry<String, Integer> entry : ages.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
}
}Go:
package main
import "fmt"
func main() {
// Slice (动态数组)
fruits := []string{"apple", "banana"}
fruits = append(fruits, "orange")
// Map
ages := make(map[string]int)
ages["Alice"] = 25
ages["Bob"] = 30
// 或者字面量初始化
ages2 := map[string]int{
"Alice": 25,
"Bob": 30,
}
// 遍历 slice
for i, fruit := range fruits {
fmt.Printf("%d: %s\n", i, fruit)
}
// 遍历 map
for name, age := range ages2 {
fmt.Printf("%s: %d\n", name, age)
}
}关键差异:
- Go 的 slice 类似 ArrayList,但更底层
range用于遍历,返回索引/键和值- 用
_忽略不需要的返回值
7. 并发
Java:
public class Concurrency {
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
System.out.println("Thread 1: " + i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
System.out.println("Thread 2: " + i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}Go:
package main
import (
"fmt"
"sync"
"time"
)
func main() {
var wg sync.WaitGroup
wg.Add(2)
// Goroutine 1
go func() {
defer wg.Done()
for i := 0; i < 5; i++ {
fmt.Println("Goroutine 1:", i)
time.Sleep(100 * time.Millisecond)
}
}()
// Goroutine 2
go func() {
defer wg.Done()
for i := 0; i < 5; i++ {
fmt.Println("Goroutine 2:", i)
time.Sleep(100 * time.Millisecond)
}
}()
wg.Wait()
}关键差异:
go关键字启动 goroutine(轻量级线程)WaitGroup等待 goroutine 完成- Go 的并发模型更简单、更轻量
8. Channel 通信(Go 特色)
Go 的 channel 是线程安全的通信机制,这是 Java 没有的内置特性:
package main
import "fmt"
func main() {
// 创建 channel
messages := make(chan string)
// 发送数据到 channel
go func() {
messages <- "Hello"
messages <- "World"
close(messages)
}()
// 从 channel 接收数据
for msg := range messages {
fmt.Println(msg)
}
}快速学习建议
- 先掌握基础:变量、控制流、函数、结构体
- 理解指针:Go 有指针但比 C 简单,没有指针运算
- 接口的隐式实现:这是 Go 最优雅的特性之一
- 错误处理模式:习惯
if err != nil - 并发编程:goroutine 和 channel 是 Go 的杀手级特性
推荐资源:
- 官方 Go Tour: https://go.dev/tour/
- Go by Example: https://gobyexample.com/