-
一、基础数据类型
-
二、声明语法
-
三、逻辑控制
-
四、出错处理
-
五、总结
一、基础数据类型
1.布尔类型
bool,只有true和false
2.有符号整型
int,int8,int16,int32,int64,rune(等于int32)
3.无符号整型
uint,uint8,uint16,uint32,uint64,byte(等于uint8),uintptr(存放指针)
4.浮点型
float32,float64,complex64,complex128
5.字符串
string,比如 "abc"
对比go和java的不同?
java的基础类型都是值类型,java的值类型都是基础类型。
java的基础类型是不可拆分的,不能由其他基础类型或指针组成。
java除了八个基础数据类型之外,其他都是引用类型,都是对象。
java的基础类型分配在栈上,引用类型在没有开启逃逸分析的情况下都是分配堆上的。
go因为有指针的存在,所以内存分配在栈上还是堆上是和逃逸分析有关。
go的字符串实现其实是由一个指针加int,如果按照java的思路应该算是复合类型,因为go读取字符串内容需要多次内存访问。
二、声明语法
1.变量声明
完整语法:var 变量名 类型 = 初始化值
或:var 变量名= 初始化值 (编译器类型进行推导,类型都可以不用写)
简便语法:变量名:= 初始化值 (一般都是这么写)
2.常量声明
① const 变量名 = 初始化值 (同样因为类型推导,不用声明类型)
② iota:常量生成器,初始化值为0,依次加1递增
对比go和java的不同?
java的变量声明是类型 变量名 = 初始化值,不能简写 。
java的常量声明是通过final关键字。
java的语法确实比较繁琐,好的地方就是繁琐也意味着读代码时不需要在脑子里类型推断,可读性,可维护性都会相对强些。不好的地方就是写起来麻烦,又臭又长。
三、逻辑控制
1.if条件语句
if i>5{
fmt.Println("i大于5")
}else if i>-5 && i<=0{
fmt.Println("i大于-5且i小于等于0")
}else{
fmt.Println("i是个啥")
}
2.三目表达式
go语言不支持三目表达式。
官方给出的解释:
The reason ?: is absent from Go is that the language's designers had seen the operation used too often to create impenetrably complex expressions. The if-else form, although longer, is unquestionably clearer. A language needs only one conditional control flow construct.
golang中不存在?:运算符的原因是因为语言设计者已经预见到三元运算符经常被用来构建一些极其复杂的表达式。虽然使用if进行替代会让代码显得更长,但这毫无疑问可读性更强。一个语言只需要有一种条件判断结构就足够了。
3.switch选择语句
go的switch不用加break。命中条件后执行完即跳出switch逻辑。如果命中条件后还想继续执行下一个条件判断,用fallthrough关键字。
switch i {
case 5:
fmt.Println("i等于5,并且执行下一个选择判断")
fallthrough
case 2:
fmt.Println("i等于2")
default:
fmt.Println("i是个啥")
}
4.for循环语句
for 判断语句 {
}
或
for 初始化语句;判断语句;后处理语句 {
}
比如:
for true {
fmt.Println("i是个啥")
}
for i:=0;i<100;i++ {
fmt.Println("i是个啥")
}
一样可以用continue和break进行逻辑控制。
对比go和java的不同?
java的判断逻辑都是用括号框起来的。
java的switch也需要break配合。
go觉得去掉括号更简洁,我的强迫症啊。。。- -!
整体来看,java的逻辑控制语法和c/c++差不多。
四、出错处理
1.错误和异常
错误 Error:业务逻辑上的出错,可控的出错,在编译期,从代码逻辑上就进行出错时的处理。
异常 Panic:一种很严重的出错,运行时无法预料的出错,不可控的出错,比如数组越界访问这样的异常。
2.error接口
错误通过实现error接口来表示。
type error interface {
Error() string
}
通过工厂函数 errors.New("some error msg")返回一个错误
通过定义实现error的结构体携带更多异常相关信息,比如错误码:
type commonError struct {
errorCode int //错误码
errorMsg string //错误信息
}
func (e commonError) Error() string{
return e.errorMsg
}
3.error嵌套
在实际开发中,错误可能是链式的结构。比如一个错误引发了另外一个错误。所以就会对error进行嵌套。
嵌套对应的函数:fmt.Errorf(format string, a ...interface{}) error
解开嵌套的函数:errors.Unwrap(err error) error
断言函数:errors.As(err error, target interface{}) bool
比如:
func main() {
e1:=commonError{100,"it is a commonError"}
e2:=fmt.Errorf("处理时出错,%w",e1)//一定要写%w
fmt.Println(e2)
e3:=errors.Unwrap(e2)
fmt.Println(e3)
var c =commonError{}
if errors.As(e2,&c){
fmt.Println(c)
}else{
fmt.Println(e2)
}
}
4.panic异常
go语言在运行时可能会抛出panic异常。比如数据越界访问,则会自动抛出panic异常。
panic是go语言内置的函数:func panic(v interface{})
可以通过在代码里调用panic函数,抛出一个panic异常。但是一般情况不要这么做,panic异常会中断程序的运行,导致进程崩溃,危害极大,慎用。
5.defer和recover异常捕获
defer是一个关键字,修饰一个函数,该函数保证会在当前代码体return之前得到执行,哪怕发生了panic异常。
recover是go语言内置的函数,通过recover()函数可以接收一个panic异常,并进行处理。
通过defer配合recover进行异常捕获:
func main() {
defer func() {
if p:=recover();p!=nil{
fmt.Println(p)
}
}()
a:=[]int{1}
fmt.Println(a[2])
}
对比go和java的不同?
java统一采用try{}catch{}的编程范式进行出错处理。
对go而言,简单的错误通过代码的返回参数携带错误信息,而严重的异常则使用recover进行捕获。
java在异常超类Throwable中定义cause字段来构造一个错误链。
而go则是简单实现了错误嵌套机制。
java默认只要发生异常都会打印堆栈信息,比较笨重。好处方便排查问题,不好的地方是有一定的性能影响。
而go的Error不携带堆栈信息,不太方便排查问题,只能自定义Error去携带堆栈信息。
如何让程序员尽可能地对可能发生的出错程序进行容错的处理?
java定义受检异常(checked exception)强制程序员处理,但是大多数时候程序员也不清楚要怎么处理,选择了把异常往上继续抛- -!
而go的错误处理则是采用方法返回信息中携带错误信息的方式,更加直观地让程序员了解调用该方法需要做的容错处理是什么。
go的错误处理虽然让代码写起来繁琐了,存在了大量的if err != nil,但是更加强迫程序员去处理这些错误,繁琐和程序严谨安全运行,该如何权衡呢?
五、总结
初学下来的体会是,go语言的语法很简单,门槛低,容易入门。
go的语法很简洁,相比之下java的会更为繁琐些。