序言:本身,我是一个Java开发者,但是最近在学习Java虚拟机,但是看来看去总是不明白Java虚拟机是怎样实现的,而有一本书是《自己动力手写Java虚拟机 》,是用go语言写的,因此,先掌握go语言之后,再来研究《自己动力手写Java虚拟机 》这本书吧。
初识Go语言
Go语言特性
静态类型并具有丰富的内置类型
- 函数多返回值
- 错误处理机制
- 语言层并发
- 面向对象,使用类型,组合,接口来实现面向对象思想
- 反射
- CGO :用户调用 C语言的实现模块
- 自动垃圾回收
- 静态编译
- 交叉编译
- 易于部署
- 基本BSD协议完全开放
Go语言学习库:
https://github.com/golang/go/wiki/projects
Go语言的使用场景
大型服务器后端软件开发,其定位是开发大型软件,常用于:
- 机器学习
- 云平台
- 分页式系统
- 区块链
- 华为云平台
- 服务器编程,日志处理,虚拟机处理,文件系统,分布式系统等。
- 网络编程,Web应用,APIetyi,下载应用
- 内存数据库
- 使用go开发的项目
- Go
- Dock
- kubernetes
…
VoCode 插件下载
go get -d github.com/ramya-rao-a/go-outline
go get -d github.com/acroca/go-symbols
go get -d github.com/mdempsky/gocode
go get -d github.com/rogpeppe/godef
go get -d github.com/zmb3/gogetdoc
go get -d github.com/fatih/gomodifytags
go get -d sourcegraph.com/sqs/goreturns
go get -d github.com/cweill/gotests/…
go get -d github.com/josharian/impl
go get -d github.com/haya14busa/goplay/cmd/goplay
go get -d github.com/uudashr/gopkgs/cmd/gopkgs
go get -d github.com/davidrjenni/reftools/cmd/fillstruct
go get -d github.com/alecthomas/gometalinter
VoCode 安装
go get -d golang.org/x/tools/cmd/godoc
go get -d golang.org/x/tools/cmd/goimports
go get -d golang.org/x/tools/cmd/gorename
go get -d golang.org/x/tools/cmd/guru
go get -d golang.org/x/lint/golint
go get -d github.com/ramya-rao-a/go-outline
go get -d github.com/acroca/go-symbols
go get -d github.com/mdempsky/gocode
go get -d github.com/rogpeppe/godef
go get -d github.com/zmb3/gogetdoc
go get -d github.com/fatih/gomodifytags
go get -d sourcegraph.com/sqs/goreturns
go get -d github.com/cweill/gotests/…
go get -d github.com/josharian/impl
go get -d github.com/haya14busa/goplay/cmd/goplay
go get -d github.com/uudashr/gopkgs/cmd/gopkgs
go get -d github.com/davidrjenni/reftools/cmd/fillstruct
go get -d github.com/alecthomas/gometalinter
插件安装:
go install golang.org/x/tools/cmd/godoc
go install golang.org/x/tools/cmd/goimports
go install golang.org/x/tools/cmd/gorename
go install golang.org/x/tools/cmd/guru
go install golang.org/x/lint/golint
go install github.com/ramya-rao-a/go-outline
go install github.cinstallom/acroca/go-symbols
go install gigetthub.com/mdempsky/gocode
go install github.com/rogpeppe/godef
go install github.com/zmb3/gogetdoc
go install github.com/fatih/ installgomodifytags
go install sourcegraph.com/sqs/goreturns
go install github.com/cweill/gotests/…
go install github.com/josharian/impl
go install github.com/haya14busa/goplay/cmd/goplay
go install github.com/uudashr/gopkgs/cmd/gopkgs
go install github.com/davidrjenni/reftools/cmd/fillstruct
go install github.com/alecthomas/gometalinter
第一个程序
package main import "fmt" func main() { fmt.Println("hello world!") }
【注意】: package main 必须写在代码的第一行,比如在package main 之前加注释或回车换行不影响 ,但是不能写代码 。
运行:
编译过程
go build -x Helloworld.go
重新命名编译 Helloworld.go:
go build -o xx Helloworld.go
编译运行,自己创建一相临时文件夹编译运行
go run Helloworld.go
- Go 源文件以package 声明开头,说明源文件所属的包。
- 接着使用import导入依赖的包,具体为包级别的变量,常量,类型和函数的声明和赋值
- 函数中可以定义局部的变量,常量
go中如何加注释
第一种方式是 : //
第二种方式是 :/* */ 注释的方式和java代码一样
基本组成元素
标识符是编程时所使用的名字,用于给变量,常量函数,类型,等进行命名,以建立名称和使用之间的关系 。
Go语言的标识符的命名规则 :
- 只能由非空字母(Unicode),数字,下划线(_)组成 。
- 只能以字母或下划线隔开头
- 不能Go语言关键字
- 避免使用Go语言预定义的标识符
- 建议使用驼峰式
- 标识符区分大小写
基本组成元素
标识符
Go 语言提供了一些预先定义的标识符用来表示内置的常量,类型,函数
在自定义标识符时应当避免使用:
- 内置常量 :御前 ,false,nil ,iota
- 内置类型:bool byte ,rune ,int ,int8 ,int16,int32 ,int64 ,uint,uint8,unit16 ,unit32,unit64,uintptr,float32,float64,complex64 ,complex128,string,error ,
- 内置函数:make ,len ,cap ,new ,append ,copy ,close ,delete ,complex ,real ,imag ,panic ,recover
- 空白标识符 _
关键字
关键字用于特定的语法结构
Go语言的25个关键字
- 声明 :import ,package
- 实体声明和定义:chan ,const ,func ,interface ,map ,struct ,type ,var
- 流程控制 : break ,case ,continue,default,defer ,else ,fallthrough ,for ,go ,goto ,if ,range ,return ,select ,switch
字面量:
字面量的值的表示方法,常用与对变量/常量进行初始化
主要分为 :
- 标识基础数据类型的值的字面量:0,1.1 ,true ,3 + 4i,‘a’ ,“我爱中国”
- 构造自定义的复合数据类型的字面量,例如 : type interval int
- 用于表示符合数据类型值的复合字面量,用来构造 array ,slice ,map ,例如 {1,2,3}
操作符:
- 算术运算符: + ,- ,* ,/ ,% ,++ ,–
- 关系运算符 : > ,>= ,< ,<= ,== ,!=
- 逻辑运算符 : && ,|| , !
- 位运算符 : & ,| ,^ ,<< ,>> ,&^
- 赋值运算符: = ,+= ,-= ,*= ,/= ,%= ,&= ,|= ,^= ,<<= ,>>=
- 其他运算符 : & (单目),* (单目),.(点),-(单目),… ,<-
*分隔符
小括号() ,中括号,大括号,分号; 逗号 ,
声明
声明语句用于定义程序的各种实体对象,主要 有
- 声明变量 var
- 声明常量 const
- 声明函数func
- 声明类型type
变量
变量就是指对一块存储空间定义名称 ,通过名称对存储空间的内容进行访问或修改声明变量,常用的语法为
- var变量笱变量类型= 值 ,定义变量并进行初始化 ,例如 var name string = “silence”
- var变量名 变量类型 ,定义变量使用零值进行初始化,例如 var age int
- var 变量名= 值 定义变量,变量类型通过值类型进行推导,例如 var isBoy = true
- var 变量1,变量名2 ,…变量名n 的变量类型 ,定义多个相同类型的变量并使用零值进行初始化,例如 var prefix ,suffix
- var 变量名1,变量名2 ,变量名n,变量类型=值1,值2,值n 定义多个相同的变量 并使用对应的值进行初始化,例如 var prev ,next = 1 ,2
- var变量名1,变量名2,… 变量名n = 值1 ,值2 ,… 值n,定义多个变量并使用对应的值进行初始化,变量的类型使用值类型进行推导,类型可以不相同,例如 var name ,age = “silence”,30
package main import "fmt" // 在函数内定义的局部变量一定需要使用,但是在函数外定义的全局变量不一定需要使用 var version string = "1.0" func main() { // 定义变量me为string类型的变量 //变量名需要满足标识符命名规则 // 1.必需是非空的unicode字符串组成,数字 , // 2.不能以数字开头 // 3.不能为go的关键字(25个) // 4.避免和go的预定义标识符冲突,true,false // 5.驼峰 // 6. 标识符区别大小写 // 7. go定义的变量必需是需要使用的,不然会报错 var me = "张三" fmt.Println(me) // 如果一个变量没有赋值的话,默认为零值,每一种数据类型的零值是不一样的 fmt.Println(version) var name,user string = "zhangsan","lissi" fmt.Printf(name) fmt.Printf(user) //定义多个变量,但是类型是不一样的 var ( age int = 11 higxx int = 6 ) fmt.Println(age,higxx) age ,higxx = higxx ,age fmt.Println(age,higxx) var ( s = "kk" a = 32 ) s,a = "bb",32 fmt.Println(a,s) //这是一个简单的声明,只能在函数内部使用 isBoy := true fmt.Println(isBoy) }
常量定义
package main import ( "fmt" ) func main() { const Name = "kk" // 省略类型 const PI = 3.1 // 定义多个常量(类型相同 ) const C1,C2 = 1,2 // 定义多个常量 (类型不同) const ( C3 string = "xxx" C4 int = 1 ) const c5 ,c6 = "xx",1 // 定义多个常量 省略类型 fmt.Println(Name) const a,b = "cc","dd" const ( c = "xx" d = "f" ) fmt.Println(a,b,c,d) fmt.Println(C1,C2,C3,C4) // 常量定义的是一些不可变的值 , 要大写编写 const ( C7 int = 1 C8 C9 C10 float64 = 3.14 C11 C12 C13 string = "kk" //如果赋值和上面的类型一样的话,就可以省略掉 ) fmt.Println(C7,C8,C9,C10,C11,C12,C13) // 1 1 1 3.14 3.14 3.14 kk // 枚举 ,const + iota const ( E1 int = iota E2 E3 ) fmt.Println(E1,E2,E3) // 0 1 2 const ( E4 int = iota E5 E6 ) fmt.Println(E4,E5,E6) // 0 1 2 const ( E7 int = (iota+1)*100 E8 E9 ) fmt.Println(E7,E8,E9) // 100 200 300 }
作用域
作用域指的是变量 可以使用的范围 ,go语言使用大括号显示的标识作用域的范围,大括号内包含一连串的语句,叫做语句块,语句块可以嵌套,语句块内的定义的变量不能在语句块外使用。
常见的隐匿语句块。
全语句块
包语句块
文件语句块
if switch ,for ,select ,case 语句块
package main import "fmt" func main() { // 作用域,定义标识符可以使用的范围 // 在go中用{} 来定义 作用域的范围 // 使用原则,子语句块可以使用父语句块,但是父语句块中不能使用子语句块变量 // 变量在语句块中必需使用 outer := 1 { inner := 2 fmt.Println(outer) fmt.Println(inner) { inner2 := 3 // inner = 3 fmt.Println(outer,inner,inner2) } } //fmt.Println(inner) }
布尔类型
概念:布尔类型用于表示真假,类型名为bool ,只有字节宽度,零值为false
package main import ( "fmt" ) func main() { // 布尔类型,表示真,假,标识符 bool , 字面量 : 可以选择true ,false // 零值为false var zero bool isBoy := true isGirl := true fmt.Println(zero, isGirl, isBoy) // 操作 // 逻辑运算(与,&& ,或 || ,非 ! ) // aBool ,bBool // aBool ,当两个都为true的时候,结果都为True // aBool 的可选 值为true ,false // bBool 的可选 值 true false // true &&true true // true && false false // true || false true // false || false false // 关系运算,(== ,!= ) // && 的运算符 fmt.Println(true && true) fmt.Println(true && false) // || 只要有一个为true ,结果为true fmt.Println(false || true) // fmt.Println(!true) fmt.Println(!false) // 等于判断 fmt.Println(true == false ) fmt.Println(true == true ) fmt.Println( zero == true ) //打印变量的类型,不能用Println ,%t 就是占位bool类型的 fmt.Printf("%T %t\n",zero,zero) }
布尔类型:
常用操作
关系运算,等于(==) ,不等于(!= )
fmt.PrintLn(isBody == isGirl )
fmt.Println( isBody != isGirl )
使用fmr.Printf(进行格式化参数输出 ,点位符)
%t
fmt.Println(isBody,isGirl)
fmt.Printf(“isBody =%t ,isGirl =%t \n”,isBody ,isGirl )
数值类型:
整形 :
Go 语言提供了5种有符号的,5种无符号 ,1 种指针,1种单字节,字符(unicode码点),共13种整数类型,零值均
int :和平台相关,如果系统是32位的,那就是32位,如果系统是64位的,那表示的就是64位的数
uint :无符号int类型
run : 码
int8 :
int16 :
int32:
int64
uint8,
uint16 : 无符号16位的
uint32:
byte :
计算机中,有符号的,高位为1表示负数,高位为0的表示正数,无符号的数据存储中,最高位的表示正数 。
如果一个八位 能表示的数据范围 -2^7 ~ 2^7 - 1
如果是一个无符号的8位,表示的数据范围是 0 ~ 2^8 - 1
package main import "fmt" func main() { // 整数类型 // 标识符: int /int* /unit/unit*/unitptr/byte/rune // 字面量 : 十进制,八进制 0777 = 7 * 8^0 + 7 * 8 ^1 + 7 * 8^2 , 十六进制 var age int = 31 fmt.Println(age) fmt.Printf("%T %d ",age,age) fmt.Println(0777) // 操作 // 算术运算(+,-,*, / ,% ) fmt.Println( 1 + 2 ) fmt.Println( 3 - 10 ) fmt.Println(3 *5 ) fmt.Println(1 / 2 ) fmt.Println(9 % 2 ) age ++ fmt.Println(age) age -- fmt.Println(age) // 关系运算 fmt.Println(2 == 3 ) fmt.Println( 2 != 3 ) fmt.Println(2 > 3 ) fmt.Println(2 >= 3 ) fmt.Println(2 < 3 ) fmt.Println(2 <= 3 ) // 位运算 ,计算机中的所有数字都按位存储的, 10 >= 2 // & | ^ << >> &^ // 十进制 => 2 7 /2 => 1 3 /2 => 1 1 /2 1 // 2 => 0010 // 7 & 2 => 0111 & 0010 => 0010 // 7| 2 => 0111 & 0010 => 0111 // 7 ^ 2 => 0111 ^ 0010 => 0101 // 2 << 1 => // 2 >> 1 fmt.Println( 7 & 2 ) fmt.Println( 7 | 2 ) fmt.Println(7 ^ 2 ) fmt.Println( 2 << 1 ) // 0010 << 1 => 0100 fmt.Println(2 >> 1 ) // 0010 >> 1 => 0001 fmt.Println(7 &^2 ) // // 赋值运算( = ,+= ,-= ,*= ,/= ,%=, |= ,^= ,<<= ,>>= , &^= ) // a+=b => a= a + b , // age = 1 age +=3 fmt.Println(age) // int / uint /byte ,rune /int* var intA int = 10 var unitB uint = 3 // fmt.Println(intA + unitB ) 两种不同的数据类型,需要用户自己强制类型转换 fmt.Println(intA + ((int)(unitB))) fmt.Println(((uint)(intA)) + unitB) // 从大往小转换,可能出现溢出的现象 var intC int = 0XFFFF fmt.Println(intC,uint8(intC),int8(intC)) // byte,rune var a byte = 'A' var w rune = '中' print(a , "-----------",w) fmt.Println("\n") fmt.Printf("%T %d %b %o %x \n",age ,age ,age ,age ,age ) // int 4 100 4 4 fmt.Printf("%T %c \n", a ,a ) //uint8 A fmt.Printf("%T %q \n", w ,w) // int32 '中' fmt.Printf("%10d \n",age) // 4 fmt.Printf("%010d \n",age)//0000000004 }
符点数:
package main import "fmt" func main() { // float32 ,float64 // 字面量: 有两种,一种是十进制表示法,一种是 科学计数法 // 1 E N => 1 // 1.9 E -1 => 0.19 var height float64 = 1.68 fmt.Printf("%T %f\n", height, height) // float64 1.680000 var weight float64 = 0.19E1 fmt.Println(weight) // 1.9 // 操作 // 算数运算( + ,- ,* ,/ , ++ ) fmt.Println(1.2 + 1.3 ) fmt.Println(1.1 -1.2 ) fmt.Println( 1.1 / 1.2 ) // fmt.Println(1.1. % 1.2 ) weight ++ fmt.Println( weight) // 2.9 weight -- fmt.Println(weight) // 1.9 浮点数在计算的时候有精度损失,因此一般不用来计算 ( == 计算) // 一般 只用来计算 (>= ,<= ,> ,< ) fmt.Println(1.2 - 1.2 <= 0.005 ) //true // 赋值(= ,+= ,-= ,/= ,*= ) height += 2.25 fmt.Println(height) fmt.Printf("%T %T \n", 1.1 ,height) //float64 float64 fmt.Printf("%5.2f",height) }
字符串
特殊字符
- \ : 反斜线
- ': 单引号
- ":又引号
- \a: 响铃
- \b:退格
- \f :换页
- \n: 换行
- \r : 回车
- \v :垂直制表符
- \ooo : 3个8位数字给定的八进制码点的Unicode字符
- \uhhhh: 4 个16位数字给定的十六进制码点的Unicode字符
- \Uhhhhhhhh: 8个32位数字给定的十六进制码点的Unicode字符
- \xhh : 2个8位数字给定的十六进制码点的Unicode字符
package main import "fmt" func main() { var name string = "k\tk" //"" 可解释的字符串 var desc string = `我来自\t己中国` // `` 表示原生字符串 // "" 和 `` 的区别, \r \n \t \b \f \v fmt.Println(name) fmt.Println(desc) // 操作 // 算数运算符 :+(链接) fmt.Println(" 我叫" + "KK") // 关系运算(>,>= ,<= , == ,!= ,< ) fmt.Println("a" == "b") // false fmt.Println("a" != "b") //true fmt.Println("a"=="a") // true fmt.Println("aa" <= "bb")//true fmt.Println("aa">="ab") // false fmt.Println("ab">= "abb")// false fmt.Println("bb">="ba") // true s:= "我叫" s +="你是" fmt.Println(s) // 我叫你是 // 字符串操作 ,字符串定义的内容,必须只能是ascii // 索引 0 -n - 1 (n 字符串的长度 ) desc = "abcdef" fmt.Println(len(desc)) fmt.Printf("%T %c \n", desc[2],desc[2]) // uint8 c // 切片 desc[start,end] 得到一个字符串 fmt.Printf("%T %s \n",desc[0:2],desc[0:2]) //string ab desc = "我爱中国" fmt.Printf("%T %s \n",desc[0:2],desc[0:2]) // string � // 得到字符串的长度 fmt.Println(len(desc)) // 12 }
指针
package main import "fmt" func main() { A := 2 B :=A A = 3 fmt.Println(A,B) // 3 2 C:=&A fmt.Printf("%T\n",C) // *int ,看C变量的类型,*int表示是一个指针 var c *int = &A //取地址符 ,定义一个变量c 是指针类型,并且指向A的地址 fmt.Println(c) //打印c fmt.Println(*c) // 打印c所指向地址的值 *c = 4 //修改指向地址的值 fmt.Println(*c,A) }
指针声明需要指定存储地址 ,指针类型空址是nil
本文所有代码的github地址是