java转go速成入门笔记篇(一)

go 和 java 两者同属静态类型、编译型语言,核心工程理念相通。但Go以"简洁、高效、轻量工程化"为核心设计,剥离了Java中复杂的语法糖与冗余设计。本篇聚焦入门核心知识点,通过"Java对比+可运行Go代码"的模式,快速建立Go语言认知。

一、Go语言核心定位与Java核心差异

对比Java,Go的核心差异集中在三点

  • 语法风格:Go摒弃了Java的类继承、复杂泛型(1.18后引入泛型但用法极简)、注解等厚重特性,采用"结构体+接口"实现组合式编程,代码更简洁直观;也就是显式编程代替 AOP、IOC等隐式方式
  • 编译方式:Go直接编译为机器码,无需JVM依赖,启动速度快、内存占用低;Java需先编译为字节码,再通过JVM运行,跨平台性强但存在一定运行时开销;
  • 工程理念:Go内置go mod、go build等工程化工具,无需额外依赖(类似Java的Maven/Gradle),实现"开箱即用";Java则需依赖构建工具和大量第三方库完成工程化配置。

总结: JAVA 的生态和扩展性比 go 强大,但是 go由于舍弃掉了这些隐式编程以及外部构建工具等,因此性能上会更加强大,其中的协程也是 go 语言的立足根本

二、Go保留关键字:精简语法与Java对标

Go仅含25个关键字(Java有53个),高频常用关键字可直接对标Java语法,核心差异与对应用法如下表,方便快速对照理解:

|-----------|-----------------------------|---------------------------------|---------------------------------------------|------------------------------------------------------------------------------|
| Go关键字 | Java对应语法 | 作用说明 | Go代码示例 | Java对应代码 |
| var | 显式类型声明(如int a) | 声明变量,支持类型后置 | var name string = "GoLang" | String name = "GoLang"; |
| func | 方法/函数定义(如public void xxx()) | 定义函数或结构体方法,无访问修饰符(靠包控制访问) | func sayHello() { fmt.Println("Hello Go") } | public void sayHello() { System.out.println("Hello Go"); } |
| struct | POJO类(无继承的简单JavaBean) | 定义结构体,用于封装数据(类似Java的无继承类) | type User struct { Name string; Age int } | public class User { private String name; private int age; // getter/setter } |
| if | if条件判断 | 逻辑一致,但Go无需括号包裹条件表达式 | if age > 18 { fmt.Println("成年") } | if (age > 18) { System.out.println("成年"); } |
| for | for循环 | Go用for替代Java的for、while、do-while | for i := 0; i < 3; i++ { fmt.Println(i) } | for (int i = 0; i < 3; i++) { System.out.println(i); } |

说明:Go没有public/private/protected关键字,通过"首字母大小写"控制访问权限------首字母大写可跨包访问(类似public),小写仅包内可见(类似private)。

三、Go数据类型:熟悉基础与关键差异

Go的数据类型体系和Java高度重叠,但在声明语法、使用规则上有明确差异,重点关注以下核心类型:

3.1 基础类型(int/string/bool等)

核心差异:Go的数值类型更精细(区分位数),string类型不可变且拼接更简洁,无自动类型转换。

复制代码
package main

import "fmt"

func main() {
    // 1. 字符串:不可变(和Java String一致),拼接直接用+
    str1 := "Java"
    str2 := "转Go"
    fmt.Println(str1 + str2) // 输出:Java转Go
    // 对比Java:String str1 = "Java"; String str2 = "转Go"; System.out.println(str1 + str2);

    // 2. 数值类型:区分int8/int16/int32/int64(对应Java的byte/short/int/long)
    var num1 int32 = 100   // 明确32位整数
    var num2 int64 = 200   // 明确64位整数
    // 注意:Go无自动类型转换,num1 + num2会编译报错(需显式转换)
    fmt.Println(int64(num1) + num2) // 输出:300
    // 对比Java:int num1 = 100; long num2 = 200; System.out.println(num1 + num2);(自动提升为long)

    // 3. 布尔类型:仅bool,无0/1替代(Java可通过0/1间接表示)
    var flag bool = true
    fmt.Println(flag) // 输出:true
}

3.2 复合类型:数组(固定长度)

Go的数组和Java数组核心一致------都是固定长度、连续内存,但声明语法相反(Go是"长度+类型",Java是"类型+[]")。

复制代码
package main

import "fmt"

func main() {
    // Go数组声明:[长度]类型,初始化后长度不可变
    var arr [3]int = [3]int{1, 2, 3}
    fmt.Println(arr[0]) // 输出:1
    fmt.Println(len(arr)) // 输出:3(len()是Go内置函数,获取长度)
    
    // 简化初始化:省略长度,用...自动推导
    arr2 := [...]int{4, 5, 6}
    fmt.Println(len(arr2)) // 输出:3

    // 对比Java:
    // int[] arr = new int[]{1,2,3};
    // System.out.println(arr[0]);
    // System.out.println(arr.length);
}

四、Go操作符:重点差异与实用用法

Go的算术运算符(+、-、*、/)、逻辑运算符(&&、||、!)和Java完全一致,重点关注2个特有/差异操作符:

4.1 短变量声明符 :=

替代var的简化写法,自动推导变量类型,仅能在函数内使用(最常用的Go语法之一)。

复制代码
package main

import "fmt"

func main() {
    // 短变量声明::= 自动推导类型
    name := "张三"  // 等价于 var name string = "张三"
    age := 25      // 等价于 var age int = 25
    fmt.Println(name, age) // 输出:张三 25

    // 对比Java:必须显式声明类型
    // String name = "张三";
    // int age = 25;
    // System.out.println(name + " " + age);
}

4.2 比较运算符 ==:支持结构体/数组直接比较

Go的==除了基础类型比较,还支持数组、结构体的"值比较"(Java需重写equals方法才能实现)。

复制代码
package main

import "fmt"

type User struct {
    Name string
    Age  int
}

func main() {
    // 1. 数组比较:长度和元素完全一致则返回true
    arr1 := [3]int{1,2,3}
    arr2 := [3]int{1,2,3}
    fmt.Println(arr1 == arr2) // 输出:true

    // 2. 结构体比较:所有字段值一致则返回true
    u1 := User{Name: "张三", Age: 25}
    u2 := User{Name: "张三", Age: 25}
    fmt.Println(u1 == u2) // 输出:true

    // 对比Java:
    // 数组:new int[]{1,2,3} == new int[]{1,2,3} → false(比较地址)
    // 结构体(类):new User("张三",25) == new User("张三",25) → false(需重写equals)
}

注意:Go的切片(slice)、字典(map)不支持直接用==比较(编译报错),需手动遍历元素判断。

五、Go错误处理:与Java try-catch的本质区别

Go没有try-catch-finally异常机制,而是通过"error接口"和"if err != nil"的显式判断处理错误,核心思路是"将错误视为普通返回值"。

核心逻辑

  • Go的error是一个接口(类似Java的Exception),任何类型只要实现Error() string方法,就是一个error;

  • 函数通过"多返回值"返回结果和错误(Go支持多返回值,Java需封装为对象);

  • 调用函数后,优先判断err是否为nil(nil等价于Java的null),非nil则表示有错误。

    package main

    import (
    "errors"
    "fmt"
    )

    // 模拟用户查询:返回用户信息和错误
    func getUserById(id int) (User, error) {
    if id <= 0 {
    // 生成错误对象(errors.New是Go内置方法)
    return User{}, errors.New("用户ID必须大于0")
    }
    // 正常返回:错误为nil
    return User{Name: "张三", Age: 25}, nil
    }

    type User struct {
    Name string
    Age int
    }

    func main() {
    // 调用函数,接收结果和错误
    user, err := getUserById(-1)
    // 优先判断错误
    if err != nil {
    fmt.Println("查询失败:", err) // 输出:查询失败: 用户ID必须大于0
    return
    }
    // 无错误则处理结果
    fmt.Println("查询成功:", user)

    复制代码
      // 对比Java try-catch:
      // public static User getUserById(int id) throws Exception {
      //     if (id <= 0) {
      //         throw new Exception("用户ID必须大于0");
      //     }
      //     return new User("张三", 25);
      // }
      // 
      // public static void main(String[] args) {
      //     try {
      //         User user = getUserById(-1);
      //         System.out.println("查询成功:" + user);
      //     } catch (Exception e) {
      //         System.out.println("查询失败:" + e.getMessage());
      //     }
      // }

    }

六、Go字典(map):对标HashMap与并发安全方案

Go的map是"键值对"集合,核心特性和Java的HashMap一致(无序、键唯一、线程不安全),但声明和操作语法更简洁。

核心操作:定义、CRUD

复制代码
package main

import "fmt"

func main() {
    // 1. 定义map:map[键类型]值类型
    // 方式1:先声明,后初始化
    var userMap map[int]User
    userMap = make(map[int]User, 10) // make是Go内置函数,用于初始化map(指定初始容量10)
    
    // 方式2:声明+初始化一步到位
    userMap2 := map[int]User{
        1: {Name: "张三", Age: 25},
        2: {Name: "李四", Age: 30},
    }

    // 2. 新增/修改(键存在则修改,不存在则新增)
    userMap[1] = User{Name: "张三", Age: 25}
    userMap[1] = User{Name: "张三_更新", Age: 26} // 修改

    // 3. 查询:返回值+是否存在(Go特有,避免键不存在时返回零值的歧义)
    user, exists := userMap[1]
    if exists {
        fmt.Println("查询到用户:", user) // 输出:查询到用户: {张三_更新 26}
    } else {
        fmt.Println("用户不存在")
    }

    // 4. 删除:delete( map, 键 )
    delete(userMap, 1)
    _, exists = userMap[1]
    fmt.Println("删除后是否存在:", exists) // 输出:false

    // 对比Java HashMap:
    // Map<Integer, User> userMap = new HashMap<>(10);
    // userMap.put(1, new User("张三",25));
    // userMap.put(1, new User("张三_更新",26));
    // 
    // User user = userMap.get(1);
    // if (user != null) {
    //     System.out.println("查询到用户:" + user);
    // }
    // 
    // userMap.remove(1);
    // System.out.println("删除后是否存在:" + userMap.containsKey(1));
}

说明:Go的map初始化必须用make(或直接字面量赋值),否则为nil,无法直接新增元素(会panic,类似Java的NullPointerException)。

6.2 并发场景下map的数据安全保障

Go的原生map并非线程安全(类似Java的HashMap),在并发读写时会触发panic。针对并发场景,有两种主流解决方案,可对标Java的ConcurrentHashMap:

复制代码
package main

import (
    "fmt"
    "sync"
    "time"
)

func main() {
    // 方案1:使用sync.Mutex加锁(类似Java的Collections.synchronizedMap)
    var mu sync.Mutex
    safeMap := make(map[int]string)

    // 并发写入
    for i := 0; i < 5; i++ {
        go func(idx int) {
            mu.Lock()         // 加锁保护临界区
            defer mu.Unlock() // 延迟解锁,确保函数退出时释放
            safeMap[idx] = fmt.Sprintf("value%d", idx)
        }(i)
    }

    time.Sleep(100 * time.Millisecond)
    mu.Lock()
    fmt.Println("sync.Mutex保护的map:", safeMap)
    mu.Unlock()

    // 方案2:使用sync.Map(Go1.9+内置,类似Java的ConcurrentHashMap)
    var syncMap sync.Map
    // 并发写入
    for i := 0; i < 5; i++ {
        go func(idx int) {
            syncMap.Store(idx, fmt.Sprintf("syncValue%d", idx)) // 内置线程安全存储方法
        }(i)
    }

    time.Sleep(100 * time.Millisecond)
    // 遍历sync.Map
    syncMap.Range(func(key, value interface{}) bool {
        fmt.Printf("sync.Map: key=%d, value=%s\n", key, value)
        return true
    })
}

核心对比:Java中需显式使用ConcurrentHashMap保证并发安全,而Go提供"锁+原生map"和"sync.Map"两种选择------sync.Map针对"读多写少"场景做了优化,无需初始化容量,性能更贴近ConcurrentHashMap;锁方案则更灵活,适配复杂并发逻辑。

七、Go切片(slice):对标ArrayList与并发安全保障

Go的切片是"动态数组",核心特性和Java的ArrayList一致(自动扩容、有序、可动态增删),是Go中最常用的集合类型(比数组更灵活)。

核心认知:切片本质是"数组的视图",包含三个属性------指向底层数组的指针、长度(len)、容量(cap),扩容时会自动创建新的底层数组。

核心操作:定义、扩容、CRUD

复制代码
package main

import "fmt"

func main() {
    // 1. 定义切片:[]类型(无长度,区别于数组)
    // 方式1:make初始化(指定长度和容量,容量可选)
    slice1 := make([]int, 3, 5) // 长度3,容量5(底层数组长度5)
    slice1[0] = 1
    slice1[1] = 2
    slice1[2] = 3

    // 方式2:字面量初始化(长度=容量=元素个数)
    slice2 := []int{1, 2, 3}

    // 2. 新增元素:append(切片, 元素)(返回新切片,必须接收)
    slice2 = append(slice2, 4, 5)
    fmt.Println("slice2新增后:", slice2) // 输出:[1 2 3 4 5]
    fmt.Println("slice2长度:", len(slice2)) // 输出:5
    fmt.Println("slice2容量:", cap(slice2)) // 输出:6(扩容后,默认扩容策略:容量<1024时翻倍,否则增50%)

    // 3. 查询:和数组一致,通过索引
    fmt.Println("slice2[0]:", slice2[0]) // 输出:1

    // 4. 删除元素:Go无内置delete,需通过切片截取实现
    // 删除索引2的元素(3):slice = append(slice[:index], slice[index+1:]...)
    slice2 = append(slice2[:2], slice2[3:]...)
    fmt.Println("slice2删除后:", slice2) // 输出:[1 2 4 5]

    // 5. 切片截取:slice[start:end](左闭右开,不包含end)
    subSlice := slice2[1:3] // 从索引1到2(元素2、4)
    fmt.Println("子切片:", subSlice) // 输出:[2 4]

    // 对比Java ArrayList:
    // List<Integer> list = new ArrayList<>(5);
    // list.add(1);
    // list.add(2);
    // list.add(3);
    // list.add(4);
    // list.add(5);
    // System.out.println("list新增后:" + list);
    // System.out.println("list长度:" + list.size());
    // System.out.println("list.get(0):" + list.get(0));
    // list.remove(2);
    // System.out.println("list删除后:" + list);
}

关键区别:Java的ArrayList通过size()获取长度、ensureCapacity()手动扩容;Go的切片通过len()获取长度、cap()获取容量,append时自动扩容,无需手动干预。

7.2 并发场景下slice的数据安全保障

切片本身也非线程安全,并发读写(尤其是写操作)会导致数据竞争、元素错乱。核心解决方案是通过同步锁(sync.Mutex/sync.RWMutex)保护切片操作,类似Java中用Collections.synchronizedList包装ArrayList:

复制代码
package main

import (
    "fmt"
    "sync"
    "time"
)

func main() {
    // 用sync.RWMutex保护切片(读多写少场景推荐,读锁可共享,写锁排他)
    var rwMu sync.RWMutex
    safeSlice := make([]int, 0, 5)

    // 并发写入
    for i := 0; i < 5; i++ {
        go func(idx int) {
            rwMu.Lock()         // 写操作加写锁
            defer rwMu.Unlock()
            safeSlice = append(safeSlice, idx)
        }(i)
    }

    time.Sleep(100 * time.Millisecond)

    // 并发读取
    for i := 0; i < 3; i++ {
        go func(idx int) {
            rwMu.RLock()         // 读操作加读锁
            defer rwMu.RUnlock()
            fmt.Printf("goroutine%d 读取切片:%v\n", idx, safeSlice)
        }(i)
    }

    time.Sleep(100 * time.Millisecond)
}

补充说明:Java的CopyOnWriteArrayList通过"写时复制"实现并发安全,适合读多写少场景;Go中无内置的"并发安全切片",需手动用锁实现,或基于切片封装自定义安全结构,灵活性高于Java的封装类。

相关推荐
你不是我我2 小时前
【Java 开发日记】我们来说一下 MySQL 的慢查询日志
android·java·mysql
C雨后彩虹2 小时前
ReentrantLock入门:核心特性与基本使用
java·数据结构·reentrantlock·lock
资生算法程序员_畅想家_剑魔2 小时前
Java常见技术分享-27-事务安全-事务日志-事务日志框架
java·开发语言
Noushiki2 小时前
RabbitMQ 进阶 学习笔记2
笔记·学习·rabbitmq
古城小栈2 小时前
内存对决:rust、go、java、python、nodejs
java·golang·rust
代码游侠2 小时前
复习——SQLite3 数据库
linux·服务器·数据库·笔记·网络协议·sqlite
予枫的编程笔记2 小时前
【Java 进阶3】Kafka从入门到实战:全面解析分布式消息队列的核心与应用
java·分布式·kafka
你要飞2 小时前
Part 1 行列式
笔记·线性代数·考研
じ☆冷颜〃7 小时前
分布式系统中网络技术的演进与异构融合架构(HFNA)
笔记·python·物联网·设计模式·架构·云计算