当前位置:首页 > 技术分析 > 正文内容

Java程序员学Go语言指南(中篇)_java程序员自学

ruisui884个月前 (02-22)技术分析14

结构体革命:当类脱下铠甲

1. 类与结构体对比实验

// Java 类继承体系
class Animal {
    String name;
    public Animal(String name) { this.name = name; }
    void speak() { System.out.println("..."); }
}

class Dog extends Animal {
    public Dog(String name) { super(name); }
    @Override void speak() { System.out.println("汪汪!"); }
}
// Go 组合式实现
type Animal struct {
    name string
}

func (a *Animal) Speak() {
    fmt.Println("...")
}

type Dog struct {
    Animal // 嵌入代替继承
}

func (d *Dog) Speak() {
    fmt.Println("汪汪!")
}

// 使用示例
dog := &Dog{Animal{"阿黄"}}
dog.Speak() // 输出:汪汪!

核心差异

  • Go放弃继承,拥抱组合
  • 方法定义与结构体解耦
  • 没有this关键字,显式声明接收器

2. 构造函数的秘密仪式

// Go工厂模式标准写法
type Config struct {
    timeout time.Duration
    retries int
}

// 推荐返回指针避免拷贝
func NewConfig(timeoutSec int) *Config {
    return &Config{
        timeout: time.Duration(timeoutSec)*time.Second,
        retries: 3, // 默认值
    }
}

// 使用方式 vs Java
cfg := NewConfig(5)  // Go
Config cfg = new Config(5); // Java

接口哲学:从契约到自由

1. 接口实现对比实验

// Java显式接口
interface Logger {
    void log(String message);
}

class FileLogger implements Logger { // 必须显式声明
    @Override
    public void log(String message) { /* 写入文件 */ }
}
// Go隐式接口
type Logger interface {
    Log(string)
}

type ConsoleLogger struct{} // 无需声明实现接口

func (c *ConsoleLogger) Log(msg string) { // 实现方法即自动满足
    fmt.Println(msg)
}

// 接口检测技巧(编译时验证)
var _ Logger = (*ConsoleLogger)(nil) // 确保实现完整性

2. 空接口的魔法

// 类似Java Object
func printAny(v interface{}) {
    switch x := v.(type) { // 类型断言
    case int:
        fmt.Printf("整数: %d\n", x)
    case string:
        fmt.Printf("字符串: %s\n", x)
    default:
        fmt.Printf("未知类型: %T\n", x)
    }
}

// 使用示例
printAny(42)        // 整数: 42
printAny("hello")   // 字符串: hello
printAny(3.14)      // 未知类型: float64

错误处理:从异常到返回值

1. 错误处理范式迁移

// Java异常处理
try {
    File file = new File("test.txt");
    BufferedReader br = new BufferedReader(new FileReader(file));
} catch (IOException e) {
    System.err.println("文件操作错误: " + e.getMessage());
} finally {
    br.close(); // 需要显式处理
}
// Go错误处理范式
f, err := os.Open("test.txt")
if err != nil {
    log.Fatalf("打开文件失败: %v", err)
}
defer f.Close() // 确保关闭

data := make([]byte, 1024)
count, err := f.Read(data)
if err != nil {
    if errors.Is(err, io.EOF) {
        fmt.Println("文件读取完毕")
    } else {
        log.Fatal("读取错误:", err)
    }
}

关键要点

  • defer实现资源自动释放(类似Java的try-with-resources)
  • 错误即普通值,通过多返回值传递
  • errors包提供错误处理工具链

包管理:从Maven到Go Module

1. 依赖管理对比



    
        com.google.code.gson
        gson
        2.8.9
    

// go.mod 文件示例
module myproject

go 1.20

require (
    github.com/gin-gonic/gin v1.9.1
    github.com/stretchr/testify v1.8.4 // 间接依赖
)

replace github.com/old/pkg => ./local/pkg // 依赖重定向

2. 依赖管理操作对照表

操作

Maven命令

Go命令

初始化项目

mvn archetype:generate

go mod init

添加依赖

修改pom.xml

go get

更新依赖

mvn versions:update

go get -u

清理缓存

mvn clean

go clean -modcache

工程实践:从理论到战场

1. 项目结构规范

# 典型Go项目布局
├── cmd/           # 主程序入口
│   └── myapp/
├── internal/      # 私有代码(禁止外部引用)
│   └── service/
├── pkg/           # 公共库代码
│   └── utils/
├── go.mod
├── go.sum
└── Makefile       # 构建脚本

2. 单元测试对比

// JUnit测试示例
@Test
void testAdd() {
    assertEquals(4, Calculator.add(2, 2));
}
// Go测试示例
func TestAdd(t *testing.T) {
    got := Add(2, 2)
    want := 4
    if got != want {
        t.Errorf("期望 %d, 实际 %d", want, got)
    }
}

// 表格驱动测试
func TestMultiply(t *testing.T) {
    tests := []struct {
        a, b int
        want int
    }{
        {2, 3, 6},
        {-1, 5, -5},
        {0, 100, 0},
    }

    for _, tt := range tests {
        got := Multiply(tt.a, tt.b)
        if got != tt.want {
            t.Errorf("(%d * %d) 期望 %d, 实际 %d", 
                tt.a, tt.b, tt.want, got)
        }
    }
}

中篇实战项目

任务:构建用户管理系统原型

  1. 使用结构体定义用户模型(包含ID、姓名、邮箱)
  2. 实现用户存储接口(内存存储版)
  3. 编写HTTP接口:GET /users 获取用户列表POST /users 创建新用户
  4. 添加单元测试覆盖率 >70%

技术栈提示

  • Web框架:Gin(类似Spring Boot)
  • 测试框架:标准库testing + testify断言
  • 文档生成:swaggo(类似Swagger)

下篇剧透:我们将深入Go的并发模型,揭秘goroutine调度器的魔法,并对比Java线程池与Go并发模式的性能差异。最终通过实现高并发爬虫系统,体验Go的"并发即简单"哲学。

扫描二维码推送至手机访问。

版权声明:本文由ruisui88发布,如需转载请注明出处。

本文链接:http://www.ruisui88.com/post/2166.html

标签: go swagger
分享给朋友:

“Java程序员学Go语言指南(中篇)_java程序员自学” 的相关文章

前后端分离自动化运维平台开发

运维平台采用前后端分离:前端vue,框架vue-element-admin;后端python,框架django-rest-framework.目前运维平台模块如下:1、 CMDB管理应用管理、环境管理、开发语言管理、产品项目管理、资产管理2、 构建发布持续构建、持续部署、Jar工程依赖构建3、 容器...

身体越柔软越好?刻苦拉伸可能反而不健康 | 果断练

坐下伸直膝盖,双手用力向前伸,再用力……比昨天前进了一厘米,又进步了! 这么努力地拉伸,每个人都有自己的目标,也许是身体健康、线条柔美、放松肌肉、体测满分,也可能为了随时劈个叉,享受一片惊呼。 不过,身体柔软,可以享受到灵活的福利,也可能付出不稳定的代价,并不是越刻苦拉伸越好。太硬或者太软,都不安全...

再来一波黑科技工具,低调使用

静读天下静读天下是一个特别优秀的电子书阅读器。它上面有多个在线书库,像古登堡计划,很多种优秀的书杂志,都可以下载来阅读。它还能智能识别章节功能,还支持外置的语音阅读功能。它支持多种文本格式,比如说txt,pdf,epub,mobi等等。为了便于阅读它还有10 种配色方式,还有夜间模式。不过免费版有广...

js中数组filter方法的使用和实现

定义filter() 方法创建一个新数组, 其包含通过所提供函数实现的测试的所有元素。语法var newArray = arr.filter(callback(element[, index[, selfArr]])[, thisArg])参数callback循环数组每个元素时调用的回调函数。回调函...

Vue进阶(幺叁捌):vue路由传参的几种基本方式

1、动态路由(页面刷新数据不丢失)methods:{ insurance(id) { //直接调用$router.push 实现携带参数的跳转 this.$router.push({ path: `/particulars/${id}`,...

html5迁移到微信小程序的 方法 亲测可用

切图网习惯于在做小程序之前先做成html5+vuejs的形式,因为html5切图是我们比较熟悉的方式,而且有专业的工具 以及浏览器调试也会更加的方便 灵活,效率高,而且html5的方式可以方便预览看效果,方便调整,当html5页面做好确认没问题以后 再转成小程序或者官方出品wepy的方式,这个时候就...