9.Go与Java的赋值对比
1).go的赋值
Go方法内的赋值符号可以用 := ,也可以用 =,方法外只能用 = 。 例如:
package main import( //包含print函数 "fmt" ) //方法外只能用 = 赋值 var my_name string = "my_name" var my_name2 = "my_name2" //my_name3:="my_name3" 不在方法内,错误 func main() { fmt.Println("name = ",my_name) fmt.Println("name2 = ",my_name2) }
Go支持多变量同时赋值:
package main import( //包含print函数 "fmt" ) func main() { //多变量同时赋值 var name,name2 = "li_ming","xiao_hong" fmt.Println("name = ",name) fmt.Println("name2 = ",name2) }
Go的丢弃赋值
package main import( //包含print函数 "fmt" ) func main() { //丢弃赋值 把 1和2丢弃 只取3 //在必须一次取两个以上的值的场景下,又不想要其中一个值的时候使用,比如从map中取key,value var _,_,num = 1,2,3 fmt.Println("num = ",num) }
2).java的赋值
public class MyTest { public static void main(String[] args) { //直接用 = 赋值 String name = "xiao_ming"; int i = 10; System.out.println("name ="+name); System.out.println("i ="+i); } }
10.Go与Java的注释
Go中的注释写法和Java中的基本一样。 例如: //单行注释,两者相同 /* Go的多行注释 */ /** Java多行注释 */
11.Go和Java的访问权限设置区别
首先我们来回忆一下,Java的权限访问修饰符有哪些?
public 全局可见
protected 继承相关的类可见
default 同包可见
private 私有的,本类可见
关于Java中的访问权限修饰符,是用于修饰变量,方法,类的,被修饰的对象被不同的访问权限修饰符修饰后,其它程序代码要想访问它,必须在规定的访问范围内才可以,比如同包,同类,父子类,全局均可访问。
那么,Go中的访问权限设置又有什么区别呢?
要理解这个问题,首先来看一下一个Go程序的程序文件组织结构是什么样子的?
一个可运行的编译后的Go程序,必须有一个入口,程序从入口开始执行,这个入口必须是main包,并且从main包的main函数开始执行。
但是,为了开发的效率和管理开发任务的协调简单化,对于代码质量的可复用,可扩展等特性的要求,一般采用面向对象的,文件分模块式的开发。
比如,一个游戏程序,main函数启动后,首先要启动UI界面,那么关于UI界面相关的代码我们一般会专门分出一个模块去开发,然后这个模块有很多个程序文件,这里关于UI模块比如有3个文件,a.go,b.go,c.go,那么我们在实际当中会建一个以ui为名的包文件夹,然后把a.go,b.go,c.go全部放到ui这个包文件夹下,然后因为这个包没有main包,没有main函数,所以它打出来的一个程序文件就是以.a结尾的工具包,类似于Java中的jar包,工具包文件名为 ui.a。
参考如下:
----com.ababa.niu.ui
------------------------------------a.go ------------------------------------b.go ------------------------------------c.go
a.go文件如下示例:
//这里的ui,也就是package后面的名称尽量和包文件夹的名称一致,不一致也可以 package ui //相关方法和业务 func main() { } //启动游戏UI func StartGameUI() { }
这里需要注意一个点,在程序中的 package后面的 ui包名可以和文件夹com.ababa.niu.ui中最后一层的ui文件夹名称不一致,
一般按规范写是要求写一致的,不一致时的区别如下:
把ui.a打包完毕后,就可以在别的程序中用import导入这个包模块 ,然后使用其中的内容了。
上面两个ui不同之处在于,在import 的代码后面,需要写的模块名称是在 ${gopath}/src/下的文件夹名,也就是com.ababa.niu.ui中的ui。
例如:
//游戏主程序 package main //这里的ui是com.ababa.niu.ui的最后一层文件夹名 import "ui" //相关方法和业务 func main() { //这里的ui不是文件夹名,而是之前a.go程序中package后面写的包名 ui.StartGameUI() }
接下来进入主题,我们的go语言关于访问修饰符的是指的限制什么权限,以及如何实现?
之前可以看出来,实战中的go程序是有一个main程序import很多其它包模块,每个模块实现对应的功能,最后统一在main程序中组合来完成整个软件程序,那么有一些其它模块的函数和变量,我只想在本程序文件中调用,不想被其它程序import能调用到,如何实现?
import后是否能调用对应包中的对象(变量,结构体,函数之类的)就是go关于访问权限的定义,import后,可以访问,说明是开启了访问权限,不可以访问,是说明关闭了其它程序访问的权限。
在go中,为了遵循实现简洁,快速的原则,用默认的规范来规定访问权限设置。
默认规范是:某种类型(包括变量,结构体,函数,类型等)的名称定义首字母大写就是在其它包可以访问,首字母非大写,就是只能在自己的程序中访问。
这样我们就能理解为什么导入fmt包后,他的PrintF函数的首字母P是大写的。
参照如下代码:
package ui import "fmt" func main() { //这里的P是大写 //所有调用别的包下的函数,都是首字母大写 fmt.Printf("aa") } //这里的Person的首字母P也是表示外部程序导入该包后可以使用此Person类 type Person struct{ } //这里的D同上 var Data string = "li_ming"
12.Go与Java程序文件的后缀名对比
Java的编译文件是.class结尾,多个.class打成的一个可执行文件是.jar结尾,.jar不能直接在windows和linux上执行,得用java命令在JVM中执行。 Go语言的程序文件后缀为.go,有main包main函数的,.go文件打包成二进制对应操作系统的可执行程序,如windows上的.exe结尾的可执行程序。 Java的类库会以.jar结尾,Go语言非main包没有main函数的程序编译打包会打成一个类库,以.a结尾,也就是说Go语言的类库以.a结尾。 Go的类库如下: 包名.a my_util.a 注:my_util是最顶层文件夹名,里面包含着一个个程序文件。
13.Go与Java选择结构的对比
1).if
Go中的if和Java中的if使用相同,只不过是把小括号给去掉了。
示例1:
package main import ( "fmt" ) func main() { /* 单分支结构语法格式如下: if 条件判断 { //代码块 } */ var num int fmt.Printf("请输入数字") fmt.Scan(&num) if num > 10 { fmt.Println("您输入的数字大于10") } }
示例2:
package main import ( "fmt" ) func main() { /* if else分支结构语法格式如下: if 条件判断 { //代码块 } else { //代码快2 } */ var num int fmt.Printf("请输入数字") fmt.Scan(&num) if num > 10 { fmt.Println("您输入的数字大于10") } else { fmt.Println("您输入的数字不大于10") } }
示例3:
package main import ( "fmt" ) func main() { /* if else分支结构语法格式如下: if 条件判断 { //代码块 } else if 条件判断{ //代码块2 } else { //代码块3 } */ var num int fmt.Printf("请输入数字") fmt.Scan(&num) if num > 10 { fmt.Println("您输入的数字大于10") } else if num == 10{ fmt.Println("您输入的数字等于10") } else { fmt.Println("您输入的数字小于10") } }
2).switch
示例1(go语言case语句默认只执行一个,不需要break):
package main import ( "fmt" ) func main() { var a = "xiao_ming" switch a { case "xiao_ming": fmt.Println("Hello!XiaoMing") case "xiao_hong": fmt.Println("Hello!XiaoHong") default: fmt.Println("No!") } }
示例2:一分支多值
package main import ( "fmt" ) func main() { var name = "xiao_ming" var name2 = "xiao_hong" switch name { //li_ming或xiao_hong 均进入此 case "xiao_ming", "xiao_hong": fmt.Println("xiao_ming and xiao_hong") } switch name2 { case "xiao_ming", "xiao_hong": fmt.Println("xiao_ming and xiao_hong") } }
示例3:switch表达式
package main import ( "fmt" ) func main() { var num int = 11 switch{ case num > 10 && num < 20: fmt.Println(num) } }
示例4:fallthrough下面的case全部执行
package main import ( "fmt" ) func main() { var num = 11 switch { case num == 11: fmt.Println("==11") fallthrough case num < 10: fmt.Println("<12") } }
14.Go与Java循环结构的对比
1).for循环
示例1:省略小括号
package main import ( "fmt" ) func main() { for i := 1; i < 10; i++ { fmt.Println(i) } }
示例2:和while相同,break,continue同java
package main import ( "fmt" ) func main() { i := 0 //省略另外两项,相当于java中的while for i < 3 { i++ } //break用法相同 for i == 3 { fmt.Println(i) break } }
示例3:死循环,三项均省略
package main func main() { for { } for true { } }
示例4:嵌套循环和java也一样,不演示了
示例5: range循环
package main import "fmt" func main() { var data [10]int = [10]int{1,2,3,4,5,6,7,8,9,10} for i, num := range data { fmt.Println(i,num) } }
2).goto
package main import "fmt" func main() { //goto可以用在任何地方,但是不能跨函数使用 fmt.Println("start") //go to的作用是跳转,中间的语句不执行,无条件跳转 goto my_location //goto是关键字, my_location可以自定义,他叫标签 fmt.Println("over") my_location: fmt.Println("location")
15.Go与Java的数组对比
1)go的一维数组
var 数组名 [数组长度]数组类型 = [数组长度]数组类型{元素1,元素2...}
示例1:
package main import "fmt" //全局 var my_arr [6]int var my_arr_1 [3]int = [3]int{1,2,3} func main() { //方法内: this_arr := [2]int{1, 2} fmt.Println(my_arr) fmt.Println(my_arr_1) fmt.Println(this_arr) }
2)二维数组
package main import "fmt" //全局 var my_arr [4][6]int var my_arr_1 [2][3]int = [...][3]int{{1, 2, 3}, {4, 5, 6}} func main() { //方法内: this_arr := [2][3]int{{1, 2, 3}, {8, 8, 8}} // 第 2 纬度不能用 "..."。 this_arr2 := [...][2]int{{1, 1}, {2, 2}, {3, 3}} fmt.Println(my_arr) fmt.Println(my_arr_1) fmt.Println(this_arr) fmt.Println(this_arr2) }
16.Go有指针概念,Java没有指针概念
Go中有指针的概念,Java中没有指针的概念。 指针简单的说就是存储一个【变量地址】的【变量】。
Go中使用指针的方法 //*+变量类型 = 对应变量类型的指针类型,&+变量名 = 获取变量引用地址 var 指针变量名 *指针变量类型 = &变量名 例如: var my_point *int = &num //通过&+指针变量 = 修改原来的变量真实值 &指针变量名 = 修改的变量值 例如: &my_point = 100;
示例:
package main import "fmt" func main() { // 声明实际变量 var name string="xiao_ming" // 声明指针变量 var name_point *string // 指针变量的存储地址 name_point = &name //直接访问变量地址 fmt.Println("name 变量的地址是:", &name ) // 指针变量的存储地址 fmt.Println("name_point变量储存的指针地址:", name_point ) // 使用指针访问值 fmt.Println("*name_point 变量的值:", *name_point ) }
输出结果:
name 变量的地址是: 0x10ae40f0 name_point变量储存的指针地址: 0x10ae40f0 *name_point 变量的值: xiao_ming
17.Go语言的中new,make和Java中的new对象有什么区别?
首先,Java中的new关键字代表创建关于某一个类的一个新的对象。
如:
List list = new ArrayList();
Go中的创建一个struct结构体的对象,是不需要用new关键字的,参考【20】中有代码示例讲解如何创建结构体对象。
Go中new的概念是和内存相关的,可以通过new来为基础数据类型申请一块内存地址空间,然后把这个把这个内存地址空间赋值给
一个指针变量上。(new主要就是为基础数据类型申请内存空间的,当我们需要一个基础数据类型的指针变量,并且在初始化这个基础指针变量时,不能确定他的初始值,此时我们才需要用new去内存中申请一块空间,并把这空间绑定到对应的指针上,之后可以用该指针为这块内存空间写值。new关键字在实际开发中很少使用,和java很多处用new的情况大不相同)
参考如下示例代码:
package main import "fmt" func main() { var num *int //此处num是nil fmt.Println(num) //此处会报空指针异常,因为num为nil,没有申请内存空间,所以不能为nil赋值 *num = 1 fmt.Println(*num) }
改为如下代码即可:
package main import "fmt" func main() { //在内存中申请一块地址,并把内存地址存入num var num = new(int) //此处num的值是申请出来的内存空间地址值,一个十六进制的数字 fmt.Println(num) //正常 *num = 1 fmt.Println(*num) } 复制代码
go中的make是做什么用的?
go中的make是用来创建**slice(切片),map(映射表),chan(线程通信管道)**这三个类型的对象的,返回的就是对应类型的对象,他的作用就相当于Java中new一个ArrayList,new一个HashMap时候的new的作用,只不过是go语法规定用make来创建slice(切片),map(映射表),chan(线程通信管道)。
示例代码如下:
package main import "fmt" func main() { //make只能为map,channel,slice申请分配内存,只有这三种,没有第四种 //所有通过make创建的这三种类型都是引用类型,传递参数时虽然是引用值传递, //但是对方法内引用变量参数的修改可以影响到外部的引用变量 //1.通过make创建map对象 如下代码类似于Java中 Map<String,Integer> myMap = new HashMap<>(); //在这里make就是申请分配map的内存,和java中创建map的new一样 myMap := make(map[string]int) myMap["xiao_ming"] = 20 //2.通过make创建channel,make函数内可以有一个参数,也可以有两个参数,有两个参数时第二个参数 //为通道的缓存队列的长度 //2.1) 只有一个参数,通道的缓存队列长度此时为0,也就是无缓存。 //创建一个传输int类型数据的通道 myChan := make(chan int) fmt.Println(myChan) //2.2) 有两个参数,第二个参数2代表此时代表缓存队列的长度为2 //创建一个传输int类型数据的通道,缓存为2 mychan2 := make(chan int,2) fmt.Println(mychan2) //此处暂时不做通道缓存队列数多少有何区别的讲解 //3.通过make创建slice切片 //有两种方式,一种是两个参数,一种是三个参数 //我们只有在创建一个空的切片时才会使用make //如果通过一个已有的数组创建切片往往是下面的形式 //创建一个底层数组 myArr := []int{1,2,3,4,5} //如果通过一个数组创建切片,往往是用 原始数组变量名[切片起始位置:切片结束位置] 创建一个切片 mySlice1 := myArr[2:4] fmt.Println(mySlice1) //我们如果是想创建一个空的slice,则用make创建切片 //如下形式 make(int[],num1,num2) //num1 = 切片的长度(默认分配内存空间的元素个数) //num2 = 切片的容量(解释:底层数组的长度/切片的容量,超过底层数组长度append新元素时会创建一个新的底层数组, //不超过则会使用原来的底层数组) //代表底层数组的长度是4,默认给底层数组的前两个元素分配内存空间 //切片指向前两个元素的地址,如果append新元素,在元素数小于4时都会 //在原来的底层数组的最后一个元素新分配空间和赋值, //append超过4个元素时,因为原数组大小不可变,也也存储不下了, //所以会新创建一个新的底层数组,切片指向新的底层数组 mySliceEmpty := make([]int,2,4) fmt.Println(mySliceEmpty) //两个参数,代表切片的长度和切片的容量(底层数组长度)均为第二个参数那个值 mySliceEmpty2 := make([]int,5) fmt.Println(mySliceEmpty2) }
18.Go相关的数据容器和Java的集合框架对比
Go中有的数据结构:数组,切片,map,双向链表,环形链表,堆 Go自己的类库中没有set,没有集合(List),但是第三方库有实现。 Java中有: Map,Set,List,Queue,Stack,数组 Java中没有切片的概念。 Go中的数组打印格式是[1,2,3,4,5] Go中的切片打印格式是[[1,2,3]] Go中切片的概念:切片是数组的一个子集,就是数组截取某一段。 Go的map和Java的map大致相同
19.Go中的函数,Go的方法和Java中的方法对比
1).Go中的函数定义
Go中返回值可以有多个,不像Java中多个值得封装到实体或map返回 //注:【】内的返回值可不写,无返回值直接把返回值部分全部去掉即可。 func 函数名(变量1 变量类型,变量2 变量2类型...)【(返回值1 类型1,返回值2 类型2...)】 { //注意:这个方法的右中括号必须和func写在同一行才行,否则报错,不能按c语言中的换行写
示例1:
package main import "fmt" func main() { //定义局部变量 var a int = 100 var b int = 200 var result int //调用函数并返回最大值 result = max(a, b) fmt.Println( "最大值是 :", result ) } /* 函数返回两个数的最大值 */ func max(num1, num2 int) int { /* 定义局部变量 */ var result int if (num1 > num2) { result = num1 } else { result = num2 } return result }
示例2:返回多个值
package main import "fmt" func main() { a, b := swap("xiao_ming", "xiao_hong") fmt.Println(a, b) } func swap(x, y string) (string, string) { //返回多个值 return y, x }
注意点:函数的参数:基础类型是按值传递,复杂类型是按引用传递
示例3: 函数的参数:变长参数传递
package main import "fmt" func main() { manyArgs(1,2,"2","3","4") manyArgs(1,2,"5","5","5") dataStr := []string{"11","11","11"} //传数组也可以,加三个点 manyArgs(1,2,dataStr...) } //可变参数必须放在最后面 func manyArgs(a int,b int ,str ...string ){ for i,s := range str { fmt.Println(i,s) } }
注意点:函数的返回值:如果有返回值,返回值的类型必须写,返回值得变量名根据情况可写可不写。
示例4: defer:推迟执行(类似于java中的finally)
package main import "fmt" func main() { testMyFunc(); } func testDefer1() { fmt.Println("print defer1") } func testDefer2() { fmt.Println("print defer2") } func testMyFunc() { //defer会在方法返回前执行,有点像java中的finally //defer写在任意位置均可,多个defer的话按照逆序依次执行 defer testDefer2() defer testDefer1() fmt.Println("print my func") }
示例5 :丢弃返回值
package main import "fmt" func main() { //方式一丢弃:丢弃num1和str _,num2,_:= testFun(1,2,"3"); fmt.Println(num2) //方式二丢弃: _,num3,_:= testFun(1,3,"4"); fmt.Println(num3) } func testFun(num1,num2 int,str string) (n1 int,n2 int,s1 string){ n1 = num1 n2 = num2 s1 = str return } func testFun2(num1,num2 int,str string) (int,int,string){ return num1,num2,str }
2).Java中的方法定义
访问修饰符 返回值类型 方法名(参数1类型 参数1,参数2类型 参数2...) { return 返回值; }
示例:
public Integer doSomething(String name,Integer age) { return 20; }