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

相关推荐
morris1315 分钟前
【SpringBoot】Xss的常见攻击方式与防御手段
java·spring boot·xss·csp
七星静香30 分钟前
laravel chunkById 分块查询 使用时的问题
java·前端·laravel
Jacob程序员30 分钟前
java导出word文件(手绘)
java·开发语言·word
ZHOUPUYU31 分钟前
IntelliJ IDEA超详细下载安装教程(附安装包)
java·ide·intellij-idea
stewie634 分钟前
在IDEA中使用Git
java·git
Elaine2023911 小时前
06 网络编程基础
java·网络
G丶AEOM1 小时前
分布式——BASE理论
java·分布式·八股
落落鱼20131 小时前
tp接口 入口文件 500 错误原因
java·开发语言
想要打 Acm 的小周同学呀1 小时前
LRU缓存算法
java·算法·缓存
hlsd#1 小时前
go 集成go-redis 缓存操作
redis·缓存·golang