来Javaer,学学go吧

220 阅读20分钟

这是我参与更文挑战的第 20 天,活动详情查看: 更文挑战


Go与Java

GoApi文档和中文社区网址

Go的中文api文档:studygolang.com/pkgdoc

Go中文社区网站:studygolang.com/

Go语言圣经: books.studygolang.com/gopl-zh/

一.关于Java

1.Java的用途

	Java的主要用途和应用场景:
	用途一:服务器后端系统开发(web后端、微服务后端支付系统、业务系统、管理后台,各种后台交互的接口服务)。
 	
 	用途二:大数据框架的底层实现和Java的API支持。(Hadoop)。

	用途三:其它中间件的底层开发。(Tomcat、RocketMq、Hbase、Kafka(部分)、SpringCloud,Dubbo...)。

2.Java的优势和特点

	Java语言有什么优势和特点呢?
	*.做服务端系统性能高。

	*.有虚拟机,跨平台。

	*.功能强大,支持的类库多,生态圈类库多,开发框架和工具更易找。

	*.市场占有率高,约60%的中国程序员都是做Java相关的工作。

二.关于Go

1.Go的出生原因

Go语言是Google内部公司大佬开发的,主要起因于Google公司有大量的C程序项目,但是开发起来效率太低,维护成本高,于是就开发了Go语言来提高效率,而且性能只是差一点。

(Go是2007年开始研发,2009推出发布)

2.宏观看Go与Java的差异

	来看一下Go语言与Java的差异之处:
	*.无虚拟机,不跨平台(这里的平台指操作系统)(可以运行多个平台,每个平台打不同的二进制程序包),需要打包编译成对应服务器操作系统版本(windows/linux)的可执行程序(比如windows是exe)。(注:说go跨平台的是指32位和64位相同操作系统之间的跨平台)

	*.因为Go程序直接打包成操作系统可执行的文件,没有虚拟机在中间转换的一层,所以理论上执行效率会更高(理论上更高,实际情况需具体分析)。

	*.相比Java的语言和代码编写风格,Go更简洁,可以用更少的代码实现同样的功能。

	*.Go语言底层也是C实现的,又做了高并发的设计(Java出生时(1995)还没有多核cpu,所以他的并发支持后来添加上去的,Go(2009)出生时已经有了多核cpu的电脑,它在设计语言时就考虑了充分利用多核cpu(英特尔2005首次推出多核)的性能),所以性能高,高并发的支持(高并发支持其中指的一个就是充分利用多核cpu的性能资源,比如go程序默认使用所有cpu(除非自己设置使用多少))也好。

	*.天然的适用一些特定系统的开发,比如区块链类系统(如以太坊底层系统、以太坊上层应用程序),云计算和容器(Docker,K8s底层都是go开发的)开发的(大公司自研运维管理项目也大多是用go做底层的开发),网络编程(类似于java的Netty)。

3.Go和Java的语言类型区别

	计算机编程语言按照运行的方式可以分为编译型编程语言和解释型编译语言。

    举一个例子,你要教别人一门沟通交流的语言,比如英语。

    编译型的教的方式就是录(这里的录相当于计算机中把程序编译成二进制可执行文件)一个视频课程,语音课程,把每一句英语发音录下来,这样学生学的时候只要播放你的录音,然后跟着读就行,你只需要录制一次,学生就可以无数次听。

	 解释性的教的方式就是你亲自到学生家里给他补习,你当面教他,你读(读相当于每次执行都重新用解释器解释一遍)一句他学一句,

这样的话,你想要教他一句你必须就得先读一句,每次教都得重新一遍一遍的读。

	  这两种教学方式还有一个差别,你录(编译)视频语音教他,你录的英语他就只能学英语,空间环境一变,他现在要去日本,要学日语,你的视频语音教程因为已经录好了,是英语类型(英语类型类比操作系统类型)的,所以,你就得再录一套日语的语音教程。

	 而现场教他,你也会日语的话,你只需要读(读相当于解释器解释)日语给他听,让他学就行了,是不用考虑语言环境(操作系统类型环境)不同的问题的。

	 现在再来看编程语言,程序执行有两种方式,一种是编译成操作系统可执行的二进制可执行程序,这样相当于编译一次,之后每次执行都不用再编译了,但是因为不同操作系统对于二进制文件的执行规范不同,不同的操作系统你要编译成不同的可执行文件。

	 解释型语言就是多了一个解释器,解释器我们可以类比为一个老师,你执行一行代码我们类比为学一句话的读音,解释器解释一句,就是老师先读一句,你跟着才能读一句,也就是解释器每解释一行代码为可运行的代码,操作系统执行一行代码,这样的话每次执行都需要解释器重新解释一遍,执行几次就得解释几次。

	Go是编译型的语言,运行在不同的平台需要打包成不同操作系统类型下的可执行文件。

    Java是半编译,半解释型语言。编译是指他的代码都会编译成class类型的文件,class类型的文件只需要编译一次,可以在不同的操作系统的Java虚拟机上执行 ,半解释是指在Java虚拟机中,他还是需要一句一句的将class的二进制代码解释成对应操作系统可执行的代码。

4.Go语言目前的主要应用场景

	*.和Java一样,Go语言最多的应用场景就是服务器后端系统的开发,包括Web后端,微服务后端接口。

	*.Go非常适用需要高性能高并发的网络编程,这里的网络编程是指不需要界面,底层只是用Socket相互传输数据的系统,类似于Java中Netty的用途。

	*.一些云计算容器,比如Docker,K8s,底层就是Go语言开发的,也可以用做底层自研运维项目的开发。

	*.一些游戏系统的开发,可以用Go语言。
	
	*.区块链的一些底层软件和一些应用软件。(区块链程序的第一开发语言)
	

5.现在市场上都有哪些公司在使用Go语言?

	直接BOSS直聘看哪些公司招,招的是干什么系统开发的。

	这是腾讯的一个岗位。

看看岗位描述,是做互联网金融 产品的业务系统开发,业务系统是啥意思,和JAVA后端业务系统一样啊,说明腾讯的一部分项目已经用Go来开发业务系统了, 至少他这个金融团队是这样的。听说腾讯云基本都是Go开发的。

再来看一个字节跳动的,也是开发内部流程自动部署自动运维程序的

再看华为的,好像Java架构师的要求啊,微服务,缓存,消息中间件,数据库。。。

这里不多看,自己看看去吧,大多数你能知道的大公司都有用go语言尝试的新部门,新项目,市场占有率虽然比Java少,但是岗位实际上蛮多的。

三.Go和Java微观对比

1.GoPath和Java的ClassPath

关于Java的classpath:

在我们的开发环境中,一个web程序(war包)有一个classpath,这个classpath在IDEA的开发工具中目录体现为src目录和resource目录,实际上在真正的war包中他定位的是指WEB-INF下的classes文件夹下的资源(比如class文件)。

编译后的文件都放在classpath(类路径)下。多个项目程序会有多个classpath目录。

在Go语言中,GoPath在同一系统上的同一用户,一般规定只有一个,无论这个用户创建多少个go项目,都只有一个GoPath,并且这些项目都放在GoPath下的src目录下。

GoPath下有三个目录:

					1.bin	(用于存放项目编译后的可执行文件)

					2.pkg     (用于存放类库文件,比如.a结尾的包模块)

					3.src    (用于存放项目代码源文件)	

注意:当我们在windows上开发Go程序时,需要新建一个文件夹(文件夹名任意)作为GOPATH的文件目录,在其中新建三个文件夹分别是:bin,pkg,src。如果是在集成开发工具上开发的话,需要在设置中把GOPATH路径设置为你自定义的那个文件夹,之后产生的文件和相关内容都会在其中。

如果是在linux上想跑测试开发执行go程序,需要在/etc/profile添加名为GOPATH的环境变量,目录设置好自己新建的。

例如:全局用户设置GOPATH环境变量

vi /etc/profile
#添加如下 目录可以灵活修改
export GOPATH=/pub/go/gopath
//立即刷新环境变量生效
source /etc/profile

单用户设置GOPATH环境变量

vi   ~/.bash_profile

#添加如下 目录可以自己灵活修改
export GOPATH=/home/user/local/soft/go/gopath
//立即刷新环境变量生效
source vi   ~/.bash_profile

注意:这是在linux上开发go程序才需要的,如果只是生产运行程序的话是不需要任何东西的,直接运行二进制可执行程序包即可,他所有的依赖全部打进包中了。

如果是在windows下的cmd,dos窗口运行相关的go命令和程序,则需要在windows的【此电脑】-->【右键】-->【属性】-->【高级系统设置】-->【环境变量】-【新建一个系统变量】-->【变量名为GOPATH,路径为你自己指定的自定义文件夹】(如果是在IDEA中开发,不需要在此配置环境变量,只需要在IDEA中配置好GOPATH的目录设置即可)

2.Go的开发环境搭建

(配置环境变量GOPATH参考上一节内容)

我们要开发Go的程序,需要如下两样东西:

1.Go SDK

GO中文社区SDK下载地址:https://studygolang.com/dl

go1.14

我们用1.14版就可以,因为1.13后才完全支持Module功能。

有两种安装模式,一种是压缩包解压的方式,一种是图形化安装。    

 推荐使用windows图形安装傻瓜式安装,windows图形安装下载这个

 https://studygolang.com/dl/golang/go1.14.6.windows-amd64.msi

 linux安装如下:

	后续补上。。。

2. Go的集成软件开发环境

参考三(4)中的go集成开发环境选择。

3.Go与Java的文件结构对比

1).go文件结构模板

//主程序必须是写成main包名
package main

//导入别的类库
import "fmt"    

//全局常量定义
const  num = 10
  
//全局变量定义
var name string = "li_ming"

//类型定义
type P struct {

}
  
//初始化函数
func init() {

}

//main函数:程序入口
func main() {
	fmt.Printf("Hello World!!!");
}

2).Java文件结构

//包名
package my_package;
       
//导入包中的类
import java.io.*;

public Class MainTest{ 
	//main方法:程序入口
    public void static main(String[] args) {
	
	}
}
//people类
Class People {
	//成员变量
	public String name;
	public int age;
            
	//成员方法
	public void doSomething() {
	            
	}
    
}

4.Go与Java的集成开发环境

1).Go的集成开发环境

最常用的有三种:

Visual Studio Code(VS Code) 微软开发的一款Go语言开发工具。

LiteIDE 是国人开发的Go语言开发工具。

GoLand 这个非常好用,和Java中的IDEA是一家公司。(推荐使用)

2).Java的集成开发环境

MyEclipse,Eclipse

IntelliJ IDEA(大多数用这个)。

5.Go和Java常用包的对比

Go中文API文档地址:
https://studygolang.com/pkgdoc
     			Go                                              Java         

IO流操作:      bufio/os                                     java.lang.io
字符串操作:      strings                                    java.lang.String
容器           container(heap/list/ring)           	      java.lang.Collection
锁               sync                                       juc
时间              time                                      java.time/java.lang.Date
算数操作          math                                       java.math
底层Unsafe       unsafe                                     unsafe类       

6.Go的常用基础数据类型和Java的基础数据类型对比

1).go中的常用基础数据类型有:

1.布尔型:关键字【bool】: true   false
2.有符号整形:头一位是代表正负
            int   默认整形   48字节         		  32位或64int8             1字节                     8int16            2字节                     16int32            4字节                     32位
            in64             8字节                     64位
       		【int32还是64位取决于操作系统的位数,现在电脑一般都是64位的了,所以一般都是64位】
3.无符号整形
            uint             48字节          	 32位或64uint8             1字节                     8uint16            2字节                     16uint32            4字节                     32uint64            8字节                     644.浮点型
            注:go语言没有float类型,只有float32float64float32           32位浮点数  
            float64           64位浮点数
5.字符串
            string
6. byte     等同uint8,只是类似于一个别名的东西
   rune     等同int32	 只是一个别名,强调表示编码概念对应的数字

2).go中派生数据类型有:

注:这里简单列举一下
指针  	Pointer
数组  	Array[]
结构体 	struct
进程管道: channel 
函数 		 func
切片  	slice
接口 		interface
哈希 		 map

3).Java中的基础数据类型

byte

short

int

long

float

double

boolean

char

7.Go和Java的变量对比

1).go的变量

package main

import(
   //包含print函数
   "fmt"
)
func main() {
   //var  变量名  变量类型 = 变量值
   var name string = "xiao_ming"
   //方法内部可以直接使用 【 变量名 := 变量值 】 赋值,方法外不可以
   name2:="xiao_hong"
   fmt.Println("name = ",name)
   fmt.Println("name2 = ",name2)
}

2).Java的变量

public class MyTest {
    public static void main(String[] args) {
        //变量类型  变量名 = 变量值
        String name = "steven";
        int i = 10;
        System.out.println("name ="+name);
        System.out.println("i ="+i);
    }
}

8.Go和Java的常量对比

1).go的常量

go中的常量和java中的常量含义有一个本质的区别:
go中的常量是指在编译期间就能确定的量(数据),
而java中的常量是指被赋值一次后就不能修改的量(数据)。
所以两者不一样,因为Java中的常量也是JVM跑起来后赋值的,只不过不允许更改;
go的常量在编译后就确实是什么数值了。
package main

import(
   //包含print函数
   "fmt"
)
func main() {
   //const  常量名  常量类型 = 常量值   显示推断类型
   const name string = "const_xiao_ming"
   //隐式推断类型
   const name2 ="const_xiao_hong"
   fmt.Println("name = ",name)
   fmt.Println("name2 = ",name2)
}

2).Java的常量

public class MyTest {
    //【访问修饰符】 【静态修饰符】final修饰符   常量类型   常量名 =  常量值;
    public static final String TAG = "A";    //一般设置为static静态
    public static void main(String[] args) {
        System.out.println("tag= "+TAG);
    }
}

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,有mainmain函数的,.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;
 }