Golang语言在2009年诞生于谷歌,相较而言是一门年轻的语言。面对C++等老牌语言众多繁重的特性,几名谷歌员工希望能够甩开历史包袱设计一门更加简洁的编程语言,避免过度的设计,通过较少的特性组合连接就可实现复杂的功能。体现“少即是多”设计哲学。
一、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)
}
使用这个栈
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只需要切换下一个协程,内核线程不需要切换。
- 函数式编程:函数可以像变量一样被赋值、传递、作为返回值。
- 简洁:语法简洁,如多重赋值、类型自动推断、for range语法等,但语法细节上有许多强制规定,这有助于统一代码风格。Go是静态类型并且是强类型的,变量在编译期就确定类型,且不存在类型的隐式转换。
三、语法特性细节
1. 函数类型
函数是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通道用于在协程间通信
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中的锁,用于保护共享资源,防止协程同时访问。
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有两种错误处理机制:文章来源:https://www.toymoban.com/news/detail-815060.html
- error:大部分函数返回errors
- panic:只有真正的不可恢复条件,例如超过范围的索引才会产生真正的运行时异常,称为panic
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文章来源地址https://www.toymoban.com/news/detail-815060.html
到了这里,关于Java快速转Go入门案例的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!