java转go,go语言基础的不同点
-
[go语言标准库中文文档]( Go语言标准库文档中文版 | Go语言中文网 | Golang中文社区 | Golang中国 (studygolang.com) )
-
go build XX.go编译
-
go run xx.go 编译并运行
-
每个.go 文件都要有对应的包,主函数所在文件的包为package main , 否则无法运行
在同一个文件下(同一级别)的.go文件声明包时都必须是同一个名字,否则会报错,主函数所在的包,包名必须为main
-
导入了其他包时,调用包下面的函数的语法:包名.函数名() ;或者在导包时给包取一个别名,用这个别名调用,这时不能再使用原来的包名调用函数,否则会报错
//在某个文件夹下的某个.go文件如下: package utils func GetName(id int) (name string){ name = "aaa" return } //在另一个文件夹下的主函数文件中调用utils包中函数的示例如下: package main import( "fmt" "xx/xx/xx/utils" ) func main(){ utils.GetName(5) fmt.Print("aaa") } //采用给包取别名的方式如下 :也就是说,要么就使用包名,要么使用别名 package main import( "fmt" uu "xx/xx/xx/utils" ) func main(){ uu.GetName(5)//使用别名可以调用 fmt.Print("aaa") utils.GetName(5)//这里会报错,不能使用原来的报名 }
-
go语言的语句结尾不需要写分号,可以写,但推荐不写
-
go语言导入其他文件使用 import + 其他文件名的字符串形式 : 如基础头文件:import “fmt”
-
go语言严格区分大小写
-
导入多个其他文件可以使用花括号:
import( "fmt" "其他的文件名|文件地址" )
-
函数(方法)的定义使用func关键字
-
go语言的代码块花括号的格式必须如下:
func main(){
//codding
}
func main()
{
//这种写法是错误的
}
-
变量的定义需要用到var ,且定义方法形式较多:
var age int = 10 //变量名写在中间,类型写在后面 var age1 int age1 = 10 // 定义后再赋值 var age2 = 10 //直接赋值,程序根据赋值内容自动判断类型 age3 := 10 //省略var的写法
-
go有4大基本类型 , 分别是数据类型、字符类型、布尔类型、字符串类型
- 数据类型分为有符号类型和无符号类型 , 未赋值时的初始值默认为0,
-
有符号的数据基本类型:int , int8 , int16 , int32 , int64 , float32 , float64
-
无符号的数据基本类型:uint , uint8 ,uint16 , uint32 , uint64
-
- 字符类型,go语言使用byte来表示字符类型,与ASCII码相互对应,byte类型长度为一个字节,能表示255个字符 , 默认值为0
- 布尔类型,bool , 值域为true和false , 默认值false
- 字符串,go中字符串为基本数据类型,java中为字符串对象(注意区分) ,默认值为空字符串
- 数据类型分为有符号类型和无符号类型 , 未赋值时的初始值默认为0,
-
变量的公有和私有,即java中的变量的public访问和private访问
var Age = 10 // 变量名首字母大写 ,在其他的文件中可以通过导入该文件的方式访问该变量,相当于public var age = 11 // 变量名首字母小写 ,在其他的文件中不能访问 ,相当于private
-
go语言中有些方法是会有两个返回值的,第一个返回值一般是我们想要的值,第二个为err错误值,不需要第二个字的时候可以用下划线去接收.
-
go语言中没有用到的变量被定义以及没有用到的文件(包)被导入,程序执行会报错
//如下代码 : 由于name未被使用 ,会因为 name变量的定义报错 package main import "fmt" func main(){ var age int = 10 var name string = "1265" fmt.Println(age) } //如下代码 : 由于fmt包文件未被使用 ,会因为导入了该包而报错 package main import "fmt" func main(){ }
-
控制台输出
fmt.Println()
-
控制台的输入
//输入函数有Scanln() 和 Scanf() //Scanln()一次只能获得一个输入,其中需要传入一个用于接收控制台输入内容的地址 , 输入结束的标志为“回车” var age int fmt.Scanln(&age) // &age表示获取age变量的内存地址,此时从控制台获取一个数据类型,如果输入了其他类型的,程序不报错,但是不能成功接收该输入 //Scanf()可以获得一个或多个输入,其中需要给定多个个参数,第一个为输入类型控制转义字符串 ,输入多个时用用空格分开; 第二位之后的参数都是对应第一个参数中用于接收输入的变量的地址 var age1 int var name string var sex bool fmt.Scanf("%d %s %t" , &age1 , &name , &sex)
-
指针使用(java中没有指针)
//指针中存放的是某一类型数据所在的内存空间的地址值(指针变量中只能存放地址) func main(){ //指针定义 var agePtr *int var age int = 10 agePtr = &age //指向age变量的地址 fmt.Println(*agePtr)//获取指针所指向地址的值 *agePtr = 12 //通过指针改变所指向的地址的值 , 此时age变量等于12 }
-
条件判断 if 注意的点:
-
条件判断语句可以不适用括号包裹
-
if关键字和条件判断语句之间必须有空格
-
无论if后面执行的语句是一条还是多条,都必须使用花括号包裹(java中如果只要有一条可以不写花括号)
if 50 > 20 { //codding } else { }
-
-
switch多分支注意的点:
-
case后面的数据类型必须与switch后面的数据类型必须一致 :int ,int8, int32 ,int64都是不同的类型
var a int = 5 var b int8 = 5 switch a{ case b: // 这里会报错,int 和 int8是不一样的类型,即使存放的数据是一样的 fmt.Println("ddd") default: }
-
一个case后面可以进行多个值的比较,只要有一个比较成立即可进入该case , 每个需要必要比较的值之间用逗号区分,因此在go语言中每个case中都加break不是必要的, 且不会因此去执行其后面的case语句
switch 10{ case 5 , 8 ,4: fmt.Println("ddd") case 15 , 168 ,10: fmt.Println("aad") }
-
default语句不是必要的,可写可不写,位置可任意写
-
switch后不带表达式,在case后面中写判断表达式,就可以当做if来使用(不推荐使用,还不如直接使用If)
-
switch后面的表达式中可以直接声明一个变量,但声明语句必须使用分号结束(不推荐使用,个人觉得没必要,直接在外面定义不好吗,搞得这么复杂…)
-
可以在case的执行体代码块中加上fallthrough关键字,程序执行时就会多执行下面的一个case代码块,也叫做是switch穿透(额…感觉这个功能似乎有用,又好像没啥鸟用…)
-
-
for循环:括号可以省略不写,第一个和第三个表达式不需要写时,分号也可以省略,其他都和java一样
i := 0 for i < 5 { //coding i++ }
-
for后面什么都不写,就是死循环
for { //coding 死循环 }
-
for range循环(和java中的迭代器类似)
var str string = "46516ni你好" for key , value := range str{ fmt.Print("%d %c" , key , value) // key每次迭代时的索引,value接收对应的值 }
-
for循环遍历带有中文字符串,中文会出现乱码 , 但使用for range可以正常遍历
var str string = "46516ni你好" for i := 0 ; i < len(str) ; i++ { fmt.Print("%c",str[i]) // 这种方式输出中文字符会乱码 }
-
函数定义 :func 函数名 (参数 1, 参数2…) (第一个返回值类型,第二个返回值类型…){} , 函数名首字母大写,可被其他包访问,小写不可被其他包访问
func setName (name string) {//无返回值的函数 //coding } func SetName (name *string){//无返回值的函数,参数为指针 ,使用指针可以在这个函数直接改变参数内存地址中的值 /coding } func getDate (year int,month int,day int) (string){ // 其他包不可访问的一个函数 //coding return "aaa" } func GetDate (year int,month int,day int) (int,string){ // 其他包可访问的一个函数 ,且返回值有两个,第一个为int类型,第二个为string类型 //coding return 8,"aaaa" } func GetDate1 (year int,month int,day int) (int,int,int){ // 返回值有两个,第一个为int类型,第二个为int类型 ,第三个也为int类型 //coding return 8,9,90 } //重点:返回值的名字也可以定义好,相当于变量,在该函数体中对这些变量赋值,最后return时,return 后面可以不写任何东西了 func getName(id int)(myName string ,yourName string){ yourName = "bbbb"; myName = "aaaa"; return //这里不用写任何东西,因为上面已经对返回值进行了赋值,程序会根据上面的内容进行返回数据。 }
-
go语言函数不支持重载
-
go语言提供了可变数量的参数 ,使用 三个英文点
func getNum (nums...int){ for i := 0 , i < len(nums) , i++ { fmt.Println(nums[i]) } }
-
函数可以作为一个类型赋值给一个变量 , 那么也就可以作为其他函数的参数
func getNum (){ //coding } func setNum (num int , gn getNum){ //函数作为参数 //coding } func main(){ a := getNum //函数赋值给变量 setNum(5 , getNum()) //函数使用 setNum(5, a)//函数使用,两种都是一样的 }
-
go语言中可以通过type自定义类型(就是给已有的函数或者基本类型A ,取另一个名字B,完了之后A和B两类型就是不一样的了,A类型的数据还不能直接赋值给B类型,反之一样)
type long int64 // java中long类型就是go中的int64 var a long = 8 var b int8 = a //这里会报错 //函数取名: func getData(age int , name string){ } type gd getData(age int , name string) //这里取名的意义就是简化使用,再函数作为其他函数的参数时更容易理解 func setData(g gd , num int)(int,string){ //coding }
-
每一个.go源文件中都可以写一个init()函数,该函数,会在调用main()函数之前被调用,用来做一些初始化操作。
package main import "fmt" func init(){ //init函数会先执行,再执行main fmt.Pritnln("aaa") } func main(){ a := 5 fmt.Println("ddddd") }
-
主函数所在源文件中 全局变量的定义、init()函数、main()函数的执行顺序:先是全局变量–>init() -->main()
-
某个源文件A中调用的源文件B中有init()函数,那么B中的init()函数会先被执行,A中的init()后执行,在main()函数中也是一样的
-
匿名函数,函数定义了仅适用一次,并且定义了完了就直接使用
func main(){ result := func (参数1 int , 参数2 int) int{//匿名函数,定义的同时直接调用 return 8 }(传入参数1 , 传入参数2) }
-
闭包:某个匿名函数+其所引用的变量/参数 = 闭包 ; 直观的可以认为实现了类似于java中的静态变量 (对内存消耗较大)
func getSum () func (num int) int{ //该getSum函数解释:该函数返回另一个函数(假设叫做A函数,实际可以是一个匿名函数),A函数有一个int类型的参数,且返回一个int类型的值 var sum int = 0 return func (num int) int{ //这里就是getSum返回了一个匿名函数 sum += num return sum } } func main(){ f := getSum() //函数可以赋值给变量,这里将上面的匿名函数赋值给了f f(1)//返回值为1 f(2)//返回值为3 f(3)//返回值为6 f(2)//返回值为8 //观察上面的返回值发现,f(int)函数调用的时候getSum()中的sum变量未被清零,类似于java中给sum做了静态处理,这就是闭包的体现 }
-
defer关键字:defer关键字后面的语句不会立即被执行,而是程序执行这一句时,将这一行代码和此时所含有的数据都(即:这行代码其中的变量此时的值也会被保存,不会因为后面执行变量值的更改而更改)压入程序栈中(注意栈结构的特性:先进后出),然后继续执行后面的代码,当整个函数体被执行完之后(return也执行完之后),再从栈中按后进先出取出代码执行。这个非常适合用于释放资源(数据库连接资源)
package main import "fmt" func main(){ fmt.Println(add(10 , 10)) } func add(numA int , numB int) int{ defer fmt.Println("numA=",numA) fmt.Println("===1") defer fmt.Println("numB=",numB) fmt.Println("===2") numA += 10 numB += 20 fmt.Println("===3") fmt.Println("numA+numB",numA+numB) return numA + numB } 对应的控制台输出为: ===1 ===2 ===3 numA+numB 50 numB=10 numA=10 50
-
字符串与整数相互转换
//需要导入strconv包 str := "56" num := 45 num1 , _:= strconv.Atoi(str) //字符串转整数 , 该函数有两个返回值 str1 := strconv.Itoa(num)//整数转字符串 fmt.Println(num1) fmt.Println(str1)
-
字符串相关操作的函数在strings包中,(在第一点的文档中可查看)
-
时间相关函数在time包中
-
new(type)函数,参数为某一个类型/结构体类型,用于开辟一块该类型数据的空间,并返回该内存指针。
-
异常捕获:defer关键字 和 recover()函数一起使用,使用defer定义一个函数(匿名函数),在函数用直接使用recover()函数,当程序出现错误时,就会执行recover(),捕获到错误,返回一个error类型的值,正常没有错误recover()返回的值为nil ,nil的类型也是error类型,但它表示没有错误的意思,判断返回值是否为nil,就可以知道是否捕获到了一个异常(错误),返回值可以直接打印。
-
自定义异常,使用error包下的New(错误)函数,返回一个error类型,即可自定义一个错误,错误可以当做函数的返回值返回
-
程序终止函数,go语言内置函数panic(error类型参数),可以直接终止程序
-
数组定义 :
var array [length]int //定义并初始化: var array1 [3]int = []int{1,2,3} var array2 = [3]int{1,2,3} var array3 = [...]int{1,2,3} var array4 = [...]int{2:1,0:2,1:3} // 冒号前面的是下标,后面是数组对应下标位置的数据 //二位数组 var arrayTow [1][2]int var arrayTow2 = [...][...]int{{1,2,3},{1,5,6}} //数组的打印,可以直接打印 fmt.Println(array2) // 结果:[1,2,3]
-
在go语言中数组作为函数的参数时进行的是值传递,即:数组作为参数传递到一个函数中,并在函数中对该数组进行修改,此时修改的只是一个数组的拷贝,而不是原数组。如果想要直接改变原来的数组的内容,那么就传递数组的指针即可
-
切片(slice):是对数组连续内存的引用(即存储了数组中某个片段内每个元素的地址)
int array [6]int = [6]int{1,2,3,4,5,6} slice := array[1:3]//slice就是一个切片 , 将数组下标[1,3)区间内的引用给切片,注意是左闭右开区间 len(slice)//切片的长度 cap(slice)//切片的容量
-
make(切片类型,切片的长度,切片的容量),使用该函数可以创建一个切片
func main(){ splice := make(int[] , 4 , 20)//创建一个底层为数组的,长度为4 , 容量为20 的切片 //该切片中的数组,是不能直接操作到该数组的,需要通过切片间接的操作。而45点中,是先创建了数组,再使用数组创建切片,数组本身是可以拿到的。 }
-
切片可以再次进行切片
-
map映射,key-value形式存储数据,类似于java中的hashmap,声明方式: map[key的类型]value的类型
var amap map[int]string // 这样只是声明了一个map变量,但并未给变量开辟存储空间 amap = make(map[int]string , 10)//通过make()函数创建map , 第二个参数时map的长度 bmap := make(map[string]int)//第二个参数可以不写,默认就分配一个内存 amap[1] = "shuju1" amap[2] = "shuju2" // map的中添加数据的方式 类似于数组 fmt.Println(amap)//打印整个数据 fmt.Println(amap[1])//根据key获取数据 cmap := map[int]string{ //这样也可以创建map 111: "222", 22: "2233" } //key重复时,value会覆盖 amap[1] //会返回两个值,第一个为value ,第二个为是否存在的标志flag(布尔类型) //删除某个map中的某个key-value delete(amap , 1)//删除amap中key为1的数据 len(amap)//len函数获取map中元素得个数 for key,value := reange amap{//迭代获取amap中的全部数据 fmt.Println(key,value) }
-
go语言支持面向对象的特性,支持继承、封装、多态(对接口的重写)
-
结构体struct(类似于javabean)
type Student struct{//结构体的定义 name string age int16 code int32 } func main(){ //结构体的创建方式:,类似于变量的创建 //1 var su1 Student su1.name = "aaaa" //2 var su2 *Student = new(Student) //new函数创建,返回一个空间指针 (*su2).name = "1111" //3 var su3 *Student = Student{}//跟第二种new函数创建效果是一样的 (*su3).name = "2222" //4 Student := Student{"aa" , 2 ,3} //该创建方式必须将每一个值都写清楚,不能缺少 }
package main
import(
"fmt"
)
type Student struct{
name string
age int32
code int32
sex bool
}
func GetName(s Student) string{
return s.name
}
func SetName(s *Student , na string){
(*s).name = na
}
//以上代码可以实现javaBean一样的操作,getter和setter方法
func main(){
var su Student
su.name = "aaaa"
SetName(&su , "3333cc")
fmt.Println(GetName(su))
}
//3
var su3 *Student = Student{}//跟第二种new函数创建效果是一样的
(*su3).name = "2222"
//4
Student := Student{"aa" , 2 ,3} //该创建方式必须将每一个值都写清楚,不能缺少
}
~~~
package main
import(
"fmt"
)
type Student struct{
name string
age int32
code int32
sex bool
}
func GetName(s Student) string{
return s.name
}
func SetName(s *Student , na string){
(*s).name = na
}
//以上代码可以实现javaBean一样的操作,getter和setter方法
func main(){
var su Student
su.name = "aaaa"
SetName(&su , "3333cc")
fmt.Println(GetName(su))
}