已经在java领域学习和工作多年的人,为什么有想要探索一门新语言的想法?
- 多语言对比学习,更有利于对语音的某些特性有更深的理解
- 毫无疑问当前java生态业界无敌,但是不变的只有变化,随着云原生的进一步普及,在十年、二十年后谁也不能保证java的地位仍然固若金汤。所以,在自己精力允许的情况下,积极拥抱另一种语言不是一种坏事。
- java语音面临诸多问题,比如但不限于:启动慢、占用资源多、语法不够简洁等
- 语言会影响开发者的代码逻辑和业务实现思维方式。go语音作为面相工程的语言,在c的基础上结合既往项目中遇到的问题,并尝试解决他们。这是一种积极和前卫的探索方式,比如说go相对于其他语音的区别类似于springmvc和springboot的区别,后者更加人性化,可以更进步提升效率。
- go语言的设计原则,简洁、让用户没有选择就是最好的选择、内置生态。
- 并发关乎结构,并行关乎执行。
比较项 | go | java |
---|---|---|
线程 | 原生并发,轻量高效 表现在:用户层轻量级线程,不需要操作系统进行调度 | 以操作系统线程作为承载分解后的代码片段的执行单元,由操作系统进行调度,创建、销毁以及切换代价较大 |
线程通信方式 | CSP(Communicating Sequential Process,通信顺序进程)模型 对于局部情况,优先使用sync.Metex保证同步 | 共享内存方式(难用、易错) |
特点 | 1. 资源占用小,每个goroutine初始栈仅为2k 2. go运行时自行调度,goroutine上下文切换代价小 3. 原生支持,开发体验更好 4. 内置channel作为goroutine间通信原语 |
go语言中,函数和方法的区别:
函数:
func function_name([parameter list]) [return_types] { 函数体 }
方法:限定了调用者的函数
func (variable_name variable_data_type) function_name() [return_type]{ /* 函数体*/ }
语法细分 | 语法介绍 | ||||
---|---|---|---|---|---|
语言结构 | 包声明、引入包、函数、变量、语句&表达式、注释 换行自动分割,不需显示;声明 fmt.Printf # fmt.Println # | ||||
数据类型 | 1.布尔类型 2.数字类型(uint8-uint64、int8-int64、float32、float64、complex64、complex128、byte、rune、uint、int、uintptr) 3.字符串类型 4.派生类型(指针、数组、结构体、channel、函数、切片、接口、Map) | ||||
变量 | 1.指定变量类型,如果没有初始化,则变量默认为零值 定义:var a string = "Runoob" 字符串零值为“” 派生类型零值为nil 2.根据值自行判定变量类型 var d = true 3.函数专用:=赋值操作符 intVal := 1 等价于 var intVal int; intVal =1 赋值操作仅可以在函数体中使用,且无法对同一变量多次操作; 在函数体中声明的所有变量都需要被使用,否则编译报错; 交换两个变量的值 a, b = b, a 空白标识符 _ 也被用于抛弃值,如值 5 在:_, b = 5, 7 中被抛弃 | ||||
常量 | 常量中的数据类型只可以是布尔型、数字型(整数型、浮点型和复数)和字符串型。 常量还可以用作枚举,常量可以用len(), cap(), unsafe.Sizeof()函数计算表达式的值 iota,特殊常量,可以认为是一个可以被编译器修改的常量。 | ||||
运算符 |
位运算符对整数在内存中的二进制位进行操作。& | ^ << >>
&a; 将给出变量的实际地址。 *a; 是一个指针变量 | ||||
条件语句 | if...else 语句 switch语句 select语句(select 语句类似于 switch 语句,但是select会随机执行一个可运行的case。如果没有case可运行,它将阻塞,直到有case可运行。) 注意:go没有三元运算符 | ||||
循环语句 | for循环 break,中断循环 continue,跳出本次循环,执行下一次循环 goto | ||||
函数体 | 函数定义,例如: func swap(x, y string) (string, string) { return y, x }
默认情况下,go使用的是值传递。 Go 语言中同时有函数和方法,一个方法就是一个包含了接受者的函数,例如: //该 method 属于 Circle 类型对象中的方法 | ||||
数组 | 初始化: var balance = [5]float32{1000.0, 2.0, 3.4, 7.0, 50.0} balance := [5]float32{1000.0, 2.0, 3.4, 7.0, 50.0} 如果数组长度不确定,可以使用 ... 代替数组的长度 // 将索引为 1 和 3 的元素初始化 | ||||
指针 | var a int = 10 var ptr *int | ||||
结构体 | 数组可以存储同一类型的数据,但在结构体中我们可以为不同项定义不同的数据类型。 关键字,type struct type Books struct { fmt.Println(Books{"name", "author", "subject", 123}) | ||||
切片(slice) | 一种灵活,功能强悍的内置类型切片("动态数组") s :=[] int {1,2,3 } #直接初始化 // 初始化 numbers := []int{0, 1, 2, 3, 4, 5, 6, 7, 8} // 追加 | ||||
范围(range) | range 关键字用于 for 循环中迭代数组(array)、切片(slice)、通道(channel)或集合(map)的元素 map1 := make(map[int]float32) //range 也可以用在 map 的键值对上。 | ||||
Map(集合) | 可以使用内建函数 make 或使用 map 关键字来定义 Map(参考楼上,range示例代码) // 获取键值对 v1 := m["apple"] v2, ok := m["pear"] // 如果键不存在,ok 的值为 false,v2 的值为该类型的零值 // 修改键值对 m["apple"] = 5 // 获取 Map 的长度 len := len(m) // 删除键值对 delete(m, "banana") | ||||
类型转换 | var a int = 10 var str string = "10" var num int num, _ = strconv.Atoi(str) | ||||
接口 | Go 语言中的接口是隐式实现的,也就是说,如果一个类型实现了一个接口定义的所有方法,那么它就自动地实现了该接口。因此,我们可以通过将接口作为参数来实现对不同类型的调用,从而实现多态。 // 以结构都定义在包$GOROOT/src/runtime/runtime2.go 非空接口 type iface struct { tab *itab data unsafe.Pointer // 指向动态类型变量的值 } type itab struct{ inter *interfacetype // 存储改接口类型自身信息,包括类型、包路径名、接口方法集合切片 _type *_type // 动态类型变量的类型信息 hash uint32 _ [4]byte fun [1]uintptr // 动态类型已实现的接口方法的调用地址数组 } // 空接口 type eface struct { _type *_type // 指向动态类型变量的类型信息 data unsafe.Pointer // 指向动态类型变量的值 } 在判断两个接口是否相等时,只判断eface._type/iface.tab._type是否相同以及data指针指向的内存空间所存储的数值是否相同,注意不是指针的值。(比较类型和实际数值) | ||||
并发 | 通过 go 关键字来开启 goroutine 即可,goroutine 是轻量级线程,goroutine 的调度是由 Golang 运行时进行管理的。 go 函数名( 参数列表 ) 通道(channel)是用来传递数据的一个数据结构,线程间的一种通信方式,借鉴于erlang。(其他方式:共享内存、管道、socket、、、、) 通道可用于两个 goroutine 之间通过传递一个指定类型的值来同步运行和通讯。操作符 | ||||
select | select { case x := <-c1: // 从channel c1接收数据 …… case y, ok := <-c2: // 从channel c2接收数据,并根据ok判断才是否已经关闭 …… case c3 := <- z: // 将z值发送到channel c3中 …… default: // 当以上channel通信均无法实施,执行改默认分支 } |