Java快速转Go入门案例

Golang语言在2009年诞生于谷歌,相较而言是一门年轻的语言。面对C++等老牌语言众多繁重的特性,几名谷歌员工希望能够甩开历史包袱设计一门更加简洁的编程语言,避免过度的设计,通过较少的特性组合连接就可实现复杂的功能。体现"少即是多"设计哲学。

一、Go入门案例

以下是用一个Go实现的栈数据结构

go 复制代码
package collection //声明当前代码文件所在的包,相同路径下的Go文件包名必须相同
import "errors" //导入error包,Go中错误只能显式的定义/返回/处理,不能抛出/捕获

//结构体struct:类似于Java中的类,但是结构体中只能定义属性,且不存在继承的概念,只有组合和接口
type Stack struct {
	data []string //切片:类似于Java中的数组,但是Go中切片的大小是可动态扩展的,底层基于数组,更像是Java中的ArrayList
}

//函数func:参数前添加*号表示传递引用,不加表示传递数据的副本
func (s *Stack) Push(x string) { //访问权限:Go中函数和变量有两级访问权限,大写字母开头表示包外可访问,小写开头表示只在包内可见,例如Stack中的data变量
	s.data = append(s.data, x) //append()是Go中的内置函数,可直接调用,用于向切片尾部添加一个元素,并返回新的切片
}

//函数支持多重返回值
func (s *Stack) Pop() (string, error) {
	n := len(s.data) //len()也是内置函数 用于获取切片数组长度
	if n == 0 {
		//支持多重赋值
		r, b := "", errors.New("Pop empty stack error!") //定义一个错误
		return r, b //函数中定义的变量必须有使用否则会报错
	}
	res := s.data[n-1] //:=符号用于快速创建并赋值一个新变量(编译时会自动进行类型推断,Go本身是强类型语言)
	s.data[n-1] = "" //为了避免内存泄漏
	s.data = s.data[:n-1] //s.data[:n-1]表示返回对原始数组的一个新的切片视图[0,n-1),不会改变原数组元素
	return res, nil //nil可表示切片、struct、接口等类型的空值
}

//返回栈的大小
func (s *Stack) Size() int {
	return len(s.data)
}

使用这个栈

go 复制代码
package main //main函数只有定义在main包下才可执行

import (
	"HelloGo/collection" //导入collection包
	"fmt" //Go的标准输入输出包
)

//输出: Hello, world!
func main() {
	var s collection.Stack //var表示声明一个变量
	pop, err := s.Pop()
	if err != nil { //if条件可不带括号
		fmt.Println(pop + err.Error()) //打印错误信息
	}
	s.Push("world!") //Go中没有构造器的概念,当声明一个变量而没有明确地初始化时,会自动初始化为默认值
	s.Push("Hello, ") //对于结构体变量,即内部所有字段初始化为默认值,切片默认为nil表示初始化一个空数组。
	for s.Size() > 0 {  //故在这里s可以直接使用
		res, _ := s.Pop() //使用下划线忽略第二个返回值
		fmt.Print(res)
	}
}

二、Go的核心特性

  • 跨平台:Go的标准库提供了一系列与操作系统无关的接口和实现,并且Go的编译器本身就具备支持多种操作系统和硬件架构的能力,通过静态链接的方式可直接将程序编译为对应平台的机器码,故Go可以实现跨平台即"一次编写,到处运行"。此外Go还支持交叉编译,即在一个操作系统上编译出适用于另一个操作系统的可执行文件。Go的跨平台由于不需要虚拟机作为中间人,因此更加轻量级,启动和运行更快。
  • 自动内存管理:Go和Java一样,内存是自动分配和回收的。不过目前Go的垃圾回收器相对简单,就是基于三色标记法的非并发GC。
  • 组合与接口:Go通过使用接口和组合而不是继承来实现代码复用。至于Go是不是面向对象,官网的回答是Yes or no,Go既可以通过将函数绑定在结构体上设计出具有面向对象风格的程序,也可以按照面向过程的方式设计和编程。
  • 高性能并发:Go的并发执行单元是协程(gorountine),可看做用户级的线程,由Go在用户层面实现协程任务的调度,协程和内核线程可以是多对一的关系,内核线程是无感知的,可避免过多的线程带来内核调度和上下文切换开销。例如在面对一组IO密集型任务时,普通的多线程在IO阻塞等待时需要挂起让出CPU切换下一个线程,而Go只需要切换下一个协程,内核线程不需要切换。
  • 函数式编程:函数可以像变量一样被赋值、传递、作为返回值。
  • 简洁:语法简洁,语法细节上有许多强制规定,有助于统一代码风格。Go是静态类型并且是强类型的,变量在编译期就确定类型,且不存在类型的隐式转换。

三、语法特性细节

1. 函数类型

函数是Go的一等公民,被赋予了与其他数据类型(如整数、字符串和结构体)相同的地位。

go 复制代码
//声明函数类型
type binOp func(int, int) int
//定义函数变量op
var op binOp
//声明并初始化函数变量add
add := func(i, j int) int { return i + j }

op = add//赋值
//函数调用
n := op(100, 200)  // n = 100 + 200
2. 并发编程

使用go关键字可直接开启一个协程执行对应函数,channel通道用于在协程间通信

go 复制代码
package main

import (
	"fmt"
	"time"
)

func main() {
	//内置函数make()用于创建指定初始大小的哈希表、通道、切片等
	ch := make(chan int, 10) //创建缓冲区大小为10字节的channel通道
	go func() { //go关键字可直接开启一个协程执行对应函数
		time.Sleep(time.Second*5)
		ch <- 123  //在goroutine中向channel发送数据
	}() //这里定义了一个匿名函数,后面的()为传参
	val := <-ch  //主goroutine阻塞式从channel接收数据
	fmt.Println(val)
	close(ch) //关闭后可读不可写
}

WaitGroup:用于等待一组goroutines的完成。

Mutex:Go中的锁,用于保护共享资源,防止协程同时访问。

go 复制代码
package main

import (
	"fmt"
	"sync" //并发包
)

func main() {
	var wg sync.WaitGroup
	var mu sync.Mutex //独占锁
	var num int
	for i := 1; i <= 5; i++ {
		wg.Add(1) //增加一个计数
		go func() {
			defer wg.Done() //减少一个计数,defer确保函数执行完毕后wg.Done()再执行,类似finally块
			mu.Lock()   // 获取锁
			num++       // 修改共享资源
			mu.Unlock() // 释放锁
		}()
	}
	wg.Wait() // 等待所有goroutine完成,即wg内部计数为0
	fmt.Println(num) //5
}

Cond:条件等待队列,用于在特定条件下阻塞或唤醒一个或多个goroutines。

3. defer关键字

当在函数中使用 defer 关键字时,它会使后面的函数调用被推迟执行,直到当前函数的执行结束。这意味着无论函数是正常返回还是发生了异常,defer 的函数都会被执行,类似Java中的finally块。如果在同一个函数中多次使用 defer,它们的执行顺序是后进先出(LIFO),也就是最后一个 defer 的函数最先执行,依次类推。

defer语句只会影响其声明位置之后的代码。

4. 错误处理

Go有两种错误处理机制:

  • error:大部分函数返回errors
  • panic:只有真正的不可恢复条件,例如超过范围的索引才会产生真正的运行时异常,称为panic
go 复制代码
package main

import "fmt"

func main() {
	arr := []int{1, 2, 3}
	defer func() {
		//if可接受一个初始化语句,该语句通常用于设置局部变量
		if r := recover(); r != nil { //使用recover函数可捕获panic
			fmt.Println("发生了运行时错误:", r)
		}
	}()
	for i := 0; i <= 4; i++ {
		value := arr[i] // 当i=3时数组越界,触发panic
		fmt.Println("数组元素值:", value)
	}
}

参考:https://zhuanlan.zhihu.com/p/492270360

相关推荐
艾迪的技术之路22 分钟前
redisson使用lock导致死锁问题
java·后端·面试
今天背单词了吗98040 分钟前
算法学习笔记:8.Bellman-Ford 算法——从原理到实战,涵盖 LeetCode 与考研 408 例题
java·开发语言·后端·算法·最短路径问题
天天摸鱼的java工程师42 分钟前
使用 Spring Boot 整合高德地图实现路线规划功能
java·后端
东阳马生架构1 小时前
订单初版—2.生单链路中的技术问题说明文档
java
咖啡啡不加糖1 小时前
暴力破解漏洞与命令执行漏洞
java·后端·web安全
风象南1 小时前
SpringBoot敏感配置项加密与解密实战
java·spring boot·后端
DKPT1 小时前
Java享元模式实现方式与应用场景分析
java·笔记·学习·设计模式·享元模式
Percep_gan2 小时前
idea的使用小技巧,个人向
java·ide·intellij-idea
缘来是庄2 小时前
设计模式之迭代器模式
java·设计模式·迭代器模式