前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >来Javaer,学学go吧(二)

来Javaer,学学go吧(二)

作者头像
你呀不牛
发布2021-05-28 11:39:18
8560
发布2021-05-28 11:39:18
举报
文章被收录于专栏:我要变牛我要变牛

前文:

来Javaer,学学go吧(一)

20.Go的内置函数和Java的默认导入包java.lang.*

为了在Java中快速开发,Java语言的创造者把一些常用的类和接口都放到到java.lang包下,lang包下的特点就是不用写import语句导入包就可以用里面的程序代码。

Go中也有类似的功能,叫做Go的内置函数,Go的内置函数是指不用导入任何包,直接就可以通过函数名进行调用的函数。

Go中的内置函数有:

代码语言:javascript
复制
close          关闭channel

len            求长度

make       创建slice,map,chan对象

append       追加元素到切片(slice)中
 
panic         抛出异常,终止程序

recover       尝试恢复异常,必须写在defer相关的代码块中

参考示例代码1:

代码语言:javascript
复制
package main

import "fmt"

func main() {

   array := [5]int{1,2,3,4,5}
   str := "myName"
   //获取字符串长度
   fmt.Println(len(str))
   //获取数组长度
   fmt.Println(len(array))
   //获取切片长度
   fmt.Println(len(array[1:]))

   //make创建channel示例
   intChan := make(chan int,1)
   //make创建map示例
   myMap := make(map[string]interface{})
   //make创建切片
   mySlice := make([]int,5,10)

   fmt.Println(intChan)
   fmt.Println(myMap)
   fmt.Println(mySlice)

   //关闭管道
   close(intChan)
   //为切片添加元素
   array2 := append(array[:],6)
   //输出
   fmt.Println(array2)

   //new案例
   num := new(int)
   fmt.Println(num)

}

参考示例代码2:panic和recover的使用

他们用于抛出异常和尝试捕获恢复异常

代码语言:javascript
复制
func func1() {
 fmt.Println("1")
}

func func2() {
 // 刚刚打开某资源
 defer func() {
  err := recover()
  fmt.Println(err)
  fmt.Println("释放资源..")
 }()
 panic("抛出异常")
 fmt.Println(2")
}

func func3() {
 fmt.Println("3")
}

func main() {
 func1()
 func2()
 func3()
}

Java中的java.lang包下具体有什么在这里就不赘述了,请参考JavaAPI文档:

代码语言:javascript
复制
JavaAPI文档导航:https://www.oracle.com/cn/java/technologies/java-se-api-doc.html

21.Go的标准格式化输出库fmt和java的输出打印库对比

Java的标准输出流工具类是java.lang包下的System类,具体是其静态成员变量PrintStream类。

他有静态三个成员变量:

分别是PrintStream类型的out,in,err

我们常见System.out.println(),实际上调用的就是PrintStream类对象的println方法。


Go中的格式化输出输入库是fmt模块。

fmt在Go中提供了输入和输出的功能,类型Java中的Scanner和PrintStream(println)。

它的使用方法如下:

代码语言:javascript
复制
Print:   原样输出到控制台,不做格式控制。
Println: 输出到控制台并换行
Printf : 格式化输出(按特定标识符指定格式替换)
Sprintf:格式化字符串并把字符串返回,不输出,有点类似于Java中的拼接字符串然后返回。
Fprintf:来格式化并输出到 io.Writers 而不是 os.Stdout

详细占位符号如下:

代码示例如下:

22.Go的面向对象相关知识

1.封装属性(结构体)

Go中有一个数据类型是Struct,它在面向对象的概念中相当于Java的类,可以封装属性和封装方法,首先看封装属性如下示例:

代码语言:javascript
复制
package main

import "fmt"

//示例
type People struct {
   name string
   age int
   sex bool
}
func main(){

   //示例1:
   var l1 People
   l1.name = "li_ming"
   l1.age = 22
   l1.sex = false
   //li_ming
   fmt.Println(l1.name)

   //示例2
   var l2 *People = new(People)
   l2.name = "xiao_hong"
   l2.age = 33
   l2.sex = true
   //xiao_hong xiao_hong
   fmt.Println(l2.name,(*l2).name)

   //示例3:
   var l3 *People = &People{ name:"li_Ming",age:25,sex:true}
   //li_Ming  li_Ming
   fmt.Println(l3.name,(*l3).name)
}
2.封装方法(方法接收器)

如果想为某个Struct类型添加一个方法,参考如下说明和代码:

go的方法和Java中的方法对比,go的函数和go方法的不同

Go中的函数是不需要用结构体的对象来调用的,可以直接调用

Go中的方法是必须用一个具体的结构体对象来调用的,有点像Java的某个类的对象调用其方法

我们可以把指定的函数绑定到对应的结构体上,使该函数成为这个结构体的方法,然后这个结构体的对象就可以通过.来调用这个方法了

绑定的形式是:在func和方法名之间写一个(当前对象变量名 当前结构体类型),这个叫方法的接受器,其中当前对象的变量名就是当前结构体调用该方法的对象的引用,相当于java中的this对象。

参考如下示例为Student学生添加一个learn学习的方法

代码语言:javascript
复制
package main

import "fmt"

type Student struct  {
   num int //学号
   name string //姓名
   class int  //班级
   sex  bool  //性别
}

//给Student添加一个方法
//这里的(stu Student)中的stu相当于java方法中的this对象
//stu是一个方法的接收器,接收是哪个对象调用了当方法
func (stu Student) learn() {
   fmt.Printf("%s学生正在学习",stu.name)
}

func main() {
   stu := Student{1,"li_ming",10,true}
   stu.learn()
}

方法的接收器也可以是指针类型的

参考如下案例:

代码语言:javascript
复制
package main

import "fmt"

type Student struct  {
   num int //学号
   name string //姓名
   class int  //班级
   sex  bool  //性别
}

//这里方法的接收器也可以是指针类型
func (stu *Student) learn() {
   fmt.Printf("%s学生正在学习",stu.name)
}

func main() {
   //指针类型
   stu := &Student{1,"li_ming",10,true}
   stu.learn()
}

注意有一种情况,当一个对象为nil空时,它调用方法时,接收器接受的对于自身的引用也是nil,需要我们做一些健壮性的不为nil才做的判断处理。

3.Go的继承(结构体嵌入)

Go中可以用嵌入结构体实现类似于继承的功能:

参考如下代码示例:

代码语言:javascript
复制
package main

import "fmt"

//电脑
type Computer struct {
 screen string //电脑屏幕
 keyboard string //键盘
}

//计算方法
func (cp Computer) compute(num1,num2 int) int{
 return num1+num2;
}

//笔记本电脑
type NoteBookComputer struct{
 Computer
 wireless_network_adapter string //无线网卡
}
func main() {
 var cp1 NoteBookComputer = NoteBookComputer{}
 cp1.screen = "高清屏"
 cp1.keyboard = "防水键盘"
 cp1.wireless_network_adapter = "新一代无线网卡"
 fmt.Println(cp1)
 fmt.Println(cp1.compute(1,2))
}

需要注意的是,Go中可以嵌入多个结构体,但是多个结构体不能有相同的方法,如果有参数和方法名完全相同的方法,在编译的时候就会报错。所以Go不存在嵌入多个结构体后,被嵌入的几个结构体有相同的方法,最后不知道选择执行哪个方法的情况,多个结构体方法相同时,直接编译就会报错。

参考如下示例:

代码语言:javascript
复制
package main

import "fmt"

func main() {
   man := Man{}
   fmt.Println(man)
   //下面的代码编译会报错
   //man.doEat()

}
type Man struct {
   FatherA
   FatherB
}

func (p FatherA)  doEat() {
   fmt.Printf("FatherA eat")
}
func (t FatherB)  doEat() {
   fmt.Printf("FatherB eat")
}


type FatherB struct {

}

type FatherA struct  {

}
4.Go的多态(接口)

接下来我们讲Go中如何通过父类接口指向具体实现类对象,实现多态:

go语言中的接口是非侵入式接口。

java语言中的接口是侵入式接口。

侵入式接口是指需要显示的在类中写明实现哪些接口。

非侵入式接口是指不要显示的在类中写明要实现哪些接口,只需要方法名同名,参数一致即可。

参考如下代码示例:接口与多态

代码语言:javascript
复制
package main

import "fmt"

//动物接口
type Animal interface{
   eat()     //吃饭接口方法
   sleep()       //睡觉接口方法
}
//小猫
type Cat struct {

}
//小狗
type Dog struct {

}
//小猫吃方法
func (cat Cat) eat() {
   fmt.Println("小猫在吃饭")
}
//小猫睡方法
func (cat Cat) sleep(){
   fmt.Println("小猫在睡觉")
}
//小狗在吃饭
func (dog Dog) eat(){
   fmt.Println("小狗在吃饭")
}
//小狗在睡觉
func (dog Dog) sleep(){
   fmt.Println("小狗在睡觉")
}

func main() {
   var cat Animal = Cat{}
   var dog Animal = Dog{}
   cat.eat()
   cat.sleep()
   dog.eat()
   dog.sleep()
}

接口可以内嵌接口

参考如下代码示例:

代码语言:javascript
复制
package main
//内嵌接口
//学习接口,内嵌听和看学习接口
type Learn interface {
   LearnByHear
   LearnByLook
}
//通过听学习接口
type LearnByHear interface {
   hear()
}
//通过看学习
type LearnByLook interface {
   look()
}

23.Go语言中线程的实现和Java语言中线程的实现

go中的线程相关的概念是Goroutines(并发),是使用go关键字开启。

Java中的线程是通过Thread类开启的。

在go语言中,一个线程就是一个Goroutines,主函数就是(主) main Goroutines。

使用go语句来开启一个新的Goroutines

比如:

普通方法执行

代码语言:javascript
复制
myFunction()

开启一个Goroutines来执行方法

代码语言:javascript
复制
 go  myFunction()

java中是

代码语言:javascript
复制
     new Thread(()->{ 

			//新线程逻辑代码

	 }).start();

参考下面的代码示例:

代码语言:javascript
复制
package main

import (
   "fmt"
)

//并发开启新线程goroutine测试

//我的方法
func myFunction() {
   fmt.Println("Hello!!!")
}
//并发执行方法
func goroutineTestFunc() {
   fmt.Println("Hello!!! Start Goroutine!!!")
}


func main() {
   /*
   myFunction()
   //go goroutineTestFunc()
   //此时因为主线程有时候结束的快,goroutineTestFunc方法得不到输出,由此可以看出是开启了新的线程。
   */
   //打开第二段执行
   /*
   go goroutineTestFunc()
   time.Sleep(10*time.Second)//睡一段时间  10秒
   myFunction()
    */
}

线程间的通信:

java线程间通信有很多种方式:

比如最原始的 wait/notify

到使用juc下高并发线程同步容器,同步队列

到CountDownLatch等一系列工具类

......

甚至是分布式系统不同机器之间的消息中间件,单机的disruptor等等。

Go语言不同,线程间主要的通信方式是Channel。

Channel是实现go语言多个线程(goroutines)之间通信的一个机制。

Channel是一个线程间传输数据的管道,创建Channel必须声明管道内的数据类型是什么

下面我们创建一个传输int类型数据的Channel

代码示例:

代码语言:javascript
复制
package main

import "fmt"

func main() {
   ch := make(chan int)
   fmt.Println(ch)
}

channel是引用类型,函数传参数时是引用传递而不是值拷贝的传递。

channel的空值和别的应用类型一样是nil。

==可以比较两个Channel之间传输的数据类型是否相等。

channel是一个管道,他可以收数据和发数据。

具体参照下面代码示例:

代码语言:javascript
复制
package main

import (
   "fmt"
   "time"
)
//channel发送数据和接受数据用 <-表示,是发送还是接受取决于chan在  <-左边还是右边
//创建一个传输字符串数据类型的管道
var  chanStr  = make(chan string)
func main() {
   fmt.Println("main goroutine print Hello ")
   //默认channel是没有缓存的,阻塞的,也就是说,发送端发送后直到接受端接受到才会施放阻塞往下面走。
   //同样接收端如果先开启,直到接收到数据才会停止阻塞往下走
   //开启新线程发送数据
   go startNewGoroutineOne()
   //从管道中接收读取数据
   go startNewGoroutineTwo()
   //主线程等待,要不直接结束了
   time.Sleep(100*time.Second)
}

func startNewGoroutineOne() {
   fmt.Println("send channel print Hello ")
   //管道发送数据
   chanStr <- "Hello!!!"
}

func startNewGoroutineTwo(){
   fmt.Println("receive channel print Hello ")
   strVar := <-chanStr
   fmt.Println(strVar)
}

无缓存的channel可以起到一个多线程间线程数据同步锁安全的作用。

缓存的channel创建方式是

make(chan string,缓存个数)

缓存个数是指直到多个数据没有消费或者接受后才进行阻塞。

类似于java中的synchronized和lock

可以保证多线程并发下的数据一致性问题。

首先我们看一个线程不安全的代码示例:

代码语言:javascript
复制
package main

import (
   "fmt"
   "time"
)

//多线程并发下的不安全问题
//金额
var moneyA int =1000
//添加金额
func subtractMoney(subMoney int) {
   time.Sleep(3*time.Second)
   moneyA-=subMoney
}

//查询金额
func getMoney() int {
   return moneyA;
}


func main() {

   //添加查询金额
   go func() {
      if(getMoney()>200) {
         subtractMoney(200)
         fmt.Printf("200元扣款成功,剩下:%d元\n",getMoney())
      }
   }()

   //添加查询金额
   go func() {
      if(getMoney()>900) {
         subtractMoney(900)
         fmt.Printf("900元扣款成功,剩下:%d元\n",getMoney())
      }
   }()
   //正常逻辑,只够扣款一单,可以多线程环境下结果钱扣多了
   time.Sleep(5*time.Second)
   fmt.Println(getMoney())
}

缓存为1的channel可以作为锁使用:

示例代码如下:

代码语言:javascript
复制
package main

import (
   "fmt"
   "time"
)

//多线程并发下使用channel改造
//金额
var moneyA  = 1000
//减少金额管道
var synchLock = make(chan int,1)

//添加金额
func subtractMoney(subMoney int) {
   time.Sleep(3*time.Second)
   moneyA-=subMoney
}

//查询金额
func getMoney() int {
   return moneyA;
}


func main() {

   //添加查询金额
   go func() {
      synchLock<-10
      if(getMoney()>200) {
         subtractMoney(200)
         fmt.Printf("200元扣款成功,剩下:%d元\n",getMoney())
      }
      <-synchLock
   }()

   //添加查询金额
   go func() {
      synchLock<-10
      if(getMoney()>900) {
         subtractMoney(900)
         fmt.Printf("900元扣款成功,剩下:%d元\n",getMoney())
      }
      synchLock<-10
   }()
   //这样类似于java中的Lock锁,不会扣多
   time.Sleep(5*time.Second)
   fmt.Println(getMoney())
}

go也有互斥锁

类似于java中的Lock接口

参考如下示例代码:

代码语言:javascript
复制
package main

import (
   "fmt"
   "sync"
   "time"
)

//多线程并发下使用channel改造
//金额
var moneyA  = 1000

var lock sync.Mutex;
//添加金额
func subtractMoney(subMoney int) {
   lock.Lock()
   time.Sleep(3*time.Second)
   moneyA-=subMoney
   lock.Unlock()
}

//查询金额
func getMoney() int {
   lock.Lock()
   result := moneyA
   lock.Unlock()
   return result;
}


func main() {
   //添加查询金额
   go func() {
      if(getMoney()>200) {
         subtractMoney(200)
         fmt.Printf("200元扣款成功,剩下:%d元\n",getMoney())
      }else {
         fmt.Println("余额不足,无法扣款")
      }
   }()

   //添加查询金额
   go func() {
      if(getMoney()>900) {
         subtractMoney(900)
         fmt.Printf("900元扣款成功,剩下:%d元\n",getMoney())
      }else {
         fmt.Println("余额不足,无法扣款")
      }
   }()
   //正常
   time.Sleep(5*time.Second)
   fmt.Println(getMoney())
}

24.Go中的反射与Java中的反射对比

代码语言:javascript
复制
	整体概述:反射是一个通用的概念,是指在程序运行期间获取到变量或者对象,结构体的元信息,比如类型信息,并且能够取出其中变量的值,调用对应的方法。

  首先我们先来回顾一下Java语言用到反射的场景有哪些?

  1.比如说我们的方法参数不能确定是什么类型,是Object类型,我们就可以通过反射在运行期间获取其真实的类型,然后做对应的逻辑处理。

  2.比如动态代理,我们需要在程序运行时,动态的加载一个类,创建一个类,使用一个类。

  3.比如在想要强行破解获取程序中被private的成员。

  4.Java的各种框架中用的非常多,框架中用反射来判断用户自定义的类是什么类型,然后做区别处理。
代码语言:javascript
复制
  Go中的反射大概也是相同的,比如,go中有一个类型 interface,interface类型相当于Java中的Object类,当以interface作为参数类型时,可以给这个参数传递任意类型的变量。

  例如示例1:
代码语言:javascript
复制
package main

import "fmt"

func main() {
   testAllType(1);
   testAllType("Go");
}

//interface{}代表任意类型
func testAllType (data interface{}){
   fmt.Println(data)
}
代码语言:javascript
复制
 那么第一种应用场景就出现了,当我们在go中想实现一个函数/方法,这个函数/方法的参数类型在编写程序的时候不能确认,在运行时会有各种不同的类型传入这个通用的函数/方法中,我们需要对不同类型的参数做不同的处理,那么我们就得能获取到参数是什么类型的,然后根据这个类型信息做业务逻辑判断。

  反射我们需要调用reflect包模块,使用reflect.typeOf()可以获取参数的类型信息对象,再根据类型信息对象的kind方法,获取到具体类型,详细参考下面代码。

  例如示例2:
代码语言:javascript
复制
package main

import (
   "fmt"
   "reflect"
)

func main() {
   handleType(1)
   handleType(true)
}



func handleType(data interface{}) {
   //获取类型对象
   d := reflect.TypeOf(data)
   //kind方法是获取类型
   fmt.Println(d.Kind())
   switch d.Kind() {
      case reflect.Invalid:
         //无效类型逻辑处理
         fmt.Println("无效类型")
      case reflect.Int,reflect.Int8,reflect.Int16,reflect.Int32,reflect.Int64:
         fmt.Println("整形")
      case reflect.Bool:
         fmt.Println("bool类型")
   }
   
}
代码语言:javascript
复制
	 因为传入进来的都是interface类型,所以我们需要用的时候要区分类型,然后取出其中真正类型的值。

      反射取出值得方法就是先通过reflect.ValueOf()获取参数值对象,然后再通过不同的具体方法获取到值对象,比如int和bool

      示例3:
代码语言:javascript
复制
package main

import (
 "fmt"
 "reflect"
)

func main() {
 handleValue(1)
 handleValue(true)
}



func handleValue(data interface{}) {
 //获取类型对象
 d := reflect.ValueOf(data)
 //kind方法是获取类型
 fmt.Println(d.Kind())
 switch d.Kind() {
  case reflect.Invalid:
   //无效类型逻辑处理
   fmt.Println("无效类型")
  case reflect.Int,reflect.Int8,reflect.Int16,reflect.Int32,reflect.Int64:
   //取出值
   var myNum = d.Int()
   fmt.Println(myNum)
  case reflect.Bool:
   //取出bool值
   var myBool = d.Bool()
   fmt.Println(myBool)

 }
 
}
代码语言:javascript
复制
	结构体中的属性和方法怎么获取呢?

    获取结构体属性的个数是先ValueOf获取结构体值对象v后,用v.NumField()获取该结构体有几个属性,通过v.Field(i)来获取对应位置的属性的元类型。

   示例代码4:

   后续反射还有几个api和代码示例和具体应用场景,正在补。。。

25.变量作用域的区别

代码语言:javascript
复制
		Go语言的变量作用域和Java中的一样,遵循最近原则,逐渐往外层找。

        这个比较简单,就不做过多赘述了。	

26.Go语言和Java语言字符串操作的区别

Go语言操作字符串都是通过strings来处理,类似Java中的工具类StringUtils

  • strings.HasPrefix(s string, prefix string) bool:判断字符串s是否以prefix开头
  • strings.HasSuffix(s string, suffix string) bool:判断字符串s是否以suffix结尾。
  • strings.Index(s string, str string) int:判断str在s中首次出现的位置,如果没有出现,则返回-1
  • strings.LastIndex(s string, str string) int:判断str在s中最后出现的位置,如果没有出现,则返回-1
  • strings.Replace(str string, old string, new string, n int):字符串替换
  • strings.Count(str string, substr string)int:字符串计数
  • strings.Repeat(str string, count int)string:重复count次str
  • strings.ToLower(str string)string:转为小写
  • strings.ToUpper(str string)string:转为大写
  • strings.TrimSpace(str string):去掉字符串首尾空白字符
  • strings.Trim(str string, cut string):去掉字符串首尾cut字符
  • strings.TrimLeft(str string, cut string):去掉字符串首cut字符
  • strings.TrimRight(str string, cut string):去掉字符串首cut字符
  • strings.Field(str string):返回str空格分隔的所有子串的slice
  • strings.Split(str string, split string) []string:返回str split分隔的所有子串的slice
  • strings.Join(s1 []string, sep string):用sep把s1中的所有元素链接起来
  • strings.Itoa(i int):把一个整数i转成字符串
  • strings.Atoi(str string)(int, error):把一个字符串转成整数

27.Go语言和Java语言IO操作的区别

在io包中最重要的是两个接口:Reader和Writer接口。本章所提到的各种IO包,都跟这两个接口有关,也就是说,只要实现了这两个接口,它就有了IO的功能。与java中类似都有两个顶层接口

Reader接口

Reader接口的定义如下:

代码语言:javascript
复制
type Reader interface {
Read(p []byte) (n int, err error)
}

Writer接口

Writer接口的定义如下:

代码语言:javascript
复制
type Writer interface {
Write(p []byte) (n int, err error)
}

简单示例

代码语言:javascript
复制
package main

import (
 "fmt"
 "strings"
)

func main() {
 reader := strings.NewReader("abcdefgh")
 p := make([]byte, 6)
 n, err := reader.ReadAt(p, 2)
 if err != nil {
  panic(err)
 }
 fmt.Printf("%s, %d\n", p, n)
}
运行结果:c, 6

28.Go语言中有匿名函数,有闭包,Java中没有(高阶函数用法)

函数也是一种类型,它可以作为一个参数进行传递,也可以作为一个返回值传递。

Go中可以定义一个匿名函数,并把这个函数赋值给一个变量

示例1:匿名函数赋值给变量

代码语言:javascript
复制
package main

import "fmt"

//定义一个匿名函数并赋值给myFun变量
var myFun = func(x,y int) int {
   return x+y
}

func main() {
   //调用myFun
   fmt.Println(myFun(1,2))
}

输出结果:

代码语言:javascript
复制
3

Go的函数内部是无法再声明一个有名字的函数的,Go的函数内部只能声明匿名函数。

示例2:

代码语言:javascript
复制
package main

import "fmt"

func main() {
   myFunc3()
}


func myFun1() {
   /*此处报错,函数内部不能声明带有名称的函数
   func myFunc2() {

   }
    */
}

func myFunc3() {
   //函数内部可以声明一个匿名函数,并把这个匿名函数赋值给f变量
   var f = func() {
      fmt.Println("Hi,boy!")
   }
   //调用f
   f()
   //如果不想赋值给变量,那就必须在最后加上(),表示立即执行
   func() {
      fmt.Println("Hello,girl!")
   }()//有参数可以写在这个小括号中
}

输出:

代码语言:javascript
复制
Hi,boy!
Hello,girl!

Go中有闭包的功能。(闭包是一个通用的编程概念,一些语言有,一些没有,javascript中就有这个概念,Java中没有)

闭包,通俗易懂的讲,就是你有一个A函数,A函数有一个a参数,然后在A函数内部再定义或者调用或者写一个B函数,这个B函数叫做闭包函数。B函数内部的代码可以访问它外部的A函数的a参数,正常A函数调用返回完毕,a参数就不能用了,可是闭包函数B函数仍然可以访问这个a参数,B函数能不受A函数的调用生命周期限制可以随时访问其中的a参数,这个能访问的状态叫做已经做了闭包,闭包闭的是把a参数封闭到了B函数中,不受A函数的限制。

也就是说,我们用程序实现一个闭包的功能,实质上就是写一个让外层的函数参数或者函数内变量封闭绑定到内层函数的功能。

接下来我们看代码示例:

代码语言:javascript
复制
package main

import "fmt"

//我们来看看实现闭包
func main() {
   var f = f1(100)
   f(100) //print 200
   f(100) //print 300
   f(100) //print 400
}

func f1(x int) func(int){
   //此时即使f1函数执行完毕,x也不会消失
   //x在func(y int)这个函数内一直存在并且叠加,
   //这里把x的值封闭到func(y int)这个返回函数中,使其函数一直能使用x的值
   //就叫做闭包,把x变量封闭到fun(y int)这个函数包内。
   return func(y int){
      x+=y
      fmt.Printf("x=%d\n",x)
   }
}

输出:

代码语言:javascript
复制
x=200
x=300
x=400

做下闭包的总结,如何实现一个闭包:

1.定义一个A函数,此函数返回一个匿名函数。(定义一个返回匿名函数的A函数)

2.把在A函数的b参数或A函数代码块中的b变量,放入匿名函数中,进行操作。

3.这样我们调用A函数返回一个函数,这个函数不断的调用就可以一直使用之前b参数,b变量,并且b值不会刷新,有点像在匿名函数外部自定义了一个b的成员变量(成员变量取自Java中类的相关概念)

29.Go中的map和Java中的HashMap

Go中的map也是一个存储key-value,键值对的这么一种数据结构。

我们来看下如何使用:

如何创建一个map?(map是引用类型,默认值是nil,必须用make为其创建才能使用)

创建一个map必须要用make,否则会是nil

格式为: make(map[key类型]value类型) (下面有代码示例)

往Go中的map赋值添加元素用 【 map变量名称[key] = value 】 的方式

示例1:创建map以及添加元素

代码语言:javascript
复制
package main

import "fmt"

func main() {
   //创建一个map必须要用make,否则会是nil
   //格式为:  make(map[key类型]value类型)
   //Java中:   Map<String,Integer> myMap = new HashMap<>();
   myMap := make(map[string]int)
   //往Go中的map赋值添加元素用 【 map变量名称[key] = value 】 的方式
   //区别于Java中的: myMap.put("li_age",20);
   myMap["li_age"] = 20
   myMap["hong_age"] = 30
   //打印一下map
   fmt.Println(myMap)
}

我们从map中取值得格式为: 【 mapValue := map变量名[key]】

当我们填写的key在map中找不到时返回对应的value默认值,int是0,引用类型是nil

当我们的key取不到对应的值,而value的类型是一个int类型,我们如何判断这个0是实际值还是默认值呢

此时我们需要同时取两个值

通过map的key取出两个值,第二个参数为bool类型,false为该值不存在,true为成功取到值

参考下面:

示例2:从map中取值

代码语言:javascript
复制
package main

import "fmt"

func main() {
   //创建一个map必须要用make,否则会是nil
   //格式为:  make(map[key类型]value类型)
   //Java中:   Map<String,Integer> myMap = new HashMap<>();
   myMap := make(map[string]int)
   //往Go中的map赋值添加元素用 【 map变量名称[key] = value 】 的方式
   //区别于Java中的: myMap.put("li_age",20);
   myMap["li_age"] = 20
   myMap["hong_age"] = 30
   //打印一下map
   fmt.Println(myMap)
   //不存在的值
   fmt.Println(myMap["no"])

   //当我们的key取不到对应的值,而value的类型是一个int类型,我们如何判断这个0是实际值还是默认值呢
   //此时我们需要同时取两个值
   //通过map的key取出两个值,第二个参数为bool类型,false为该值不存在,true为成功取到值
   value,existsValue := myMap["no"]
   if !existsValue {
      fmt.Println("此值不存在")
   } else {
      fmt.Printf("value = %d",value)
   }
}

Go中因为返回值可以是两个,所以的map遍历很简单,不像java还得弄一个Iterator对象再逐个获取,它一次两个都能取出来,用for搭配range即可实现。

示例3:遍历

代码语言:javascript
复制
package main

import "fmt"

func main() {
   myMap := make(map[string]int)
   myMap["num1"] = 1
   myMap["num2"] = 2
   myMap["num3"] = 3
   myMap["num4"] = 4
   myMap["num5"] = 5
   myMap["num6"] = 6
   //遍历key,value
   for key,value := range myMap {
      fmt.Println(key,value)
   }
   //写一个参数的时候只取key
   for key := range myMap {
      fmt.Println(key)
   }
   //如果只想取value,就需要用到之前的_标识符进行占位丢弃
   for _,value := range myMap {
      fmt.Println(value)
   }
}

删除函数:用内置函数delete删除

示例4:删除map元素

代码语言:javascript
复制
package main

import "fmt"

func main() {
   myMap := make(map[string]int)
   myMap["num1"] = 1
   myMap["num2"] = 2
   myMap["num3"] = 3
   myMap["num4"] = 4
   myMap["num5"] = 5
   myMap["num6"] = 6
   //第二个参数为删除的key
   delete(myMap,"num6")
   //此时已经没有值了,默认值为0
   fmt.Println(myMap["num6"])
}

在Java中有一些复杂的Map类型,比如:

代码语言:javascript
复制
Map<String,Map<String,Object>> data = new HashMap<>();

实际上,在Go语言中,也有复杂的类型,我们举几个代码示例

示例5:

代码语言:javascript
复制
package main

import "fmt"

func main() {
   //由map组成的切片
   //第一部分 make[] 声明切片
   //第二部分 map[string]int  声明该切片内部装的单个类型是map
   //第三部分 参数 5 表示该切片的长度和容量都是5
   //长度是用索引能取到第几个元素,索引不能超过长度-1,分配长度后都是默认值,int是0,引用类型是nil
   //容量至少比长度大,能索引到几个+未来可添加元素个数(目前没有任何东西,看不见)= 切片容量
   //make([]切片类型,切片长度,切片容量)
   //make([]切片类型,切片长度和容量等同)
   slice := make([]map[string]int,5,10)
   slice0 := make([]map[string]int,0,10)
   //我们看看打印的东西
   fmt.Println("slice=",slice)
   fmt.Println("slice=0",slice0)

   ///*   先看这段
   //因为有5个长度,所以初始化了5个map,但是map没有通过make申请内容空间,所以报错nil map
   //slice[0]["age"] = 10;//报错
   //下面不报错
   slice[0] = make(map[string]int,10)
   slice[0]["age"] = 19
   fmt.Println(slice[0]["age"])
   //*/
}

输出结果:

代码语言:javascript
复制
slice= [map[] map[] map[] map[] map[]]
slice=0 []
19

接下来继续看代码:

代码语言:javascript
复制
package main

import "fmt"

func main() {
   //由map组成的切片
   //第一部分 make[] 声明切片
   //第二部分 map[string]int  声明该切片内部装的单个类型是map
   //第三部分 参数 5 表示该切片的长度和容量都是5
   //长度是用索引能取到第几个元素,索引不能超过长度-1,分配长度后都是默认值,int是0,引用类型是nil
   //append元素到切片时,是添加到最末尾的位置,当元素未超过容量时,都是用的同一个底层数组
   //超过容量时会返回一个新的数组
   //make([]切片类型,切片长度,切片容量)
   //make([]切片类型,切片长度和容量等同)
   slice := make([]map[string]int,5,10)
   slice0 := make([]map[string]int,0,10)
   //我们看看打印的东西
   fmt.Println("slice=",slice)
   fmt.Println("slice=0",slice0)

   /*   先看这段
   //因为有5个长度,所以初始化了5个map,但是map没有通过make申请内容空间,所以报错nil map
   //slice[0]["age"] = 10;//报错
   //下面不报错
   slice[0] = make(map[string]int,10)
   slice[0]["age"] = 19
   fmt.Println(slice[0]["age"])
   */

}

输出:

代码语言:javascript
复制
panic: assignment to entry in nil map

看下面这个报错:

代码语言:javascript
复制
package main

import "fmt"

func main() {
   //由map组成的切片
   //第一部分 make[] 声明切片
   //第二部分 map[string]int  声明该切片内部装的单个类型是map
   //第三部分 参数 5 表示该切片的长度和容量都是5
   //长度是用索引能取到第几个元素,索引不能超过长度-1,分配长度后都是默认值,int是0,引用类型是nil
   //append元素到切片时,是添加到最末尾的位置,当元素未超过容量时,都是用的同一个底层数组
   //超过容量时会返回一个新的数组
   //make([]切片类型,切片长度,切片容量)
   //make([]切片类型,切片长度和容量等同)
   slice := make([]map[string]int,5,10)
   slice0 := make([]map[string]int,0,10)
   //我们看看打印的东西
   fmt.Println("slice=",slice)
   fmt.Println("slice=0",slice0)

   /*   先看这段
   //因为有5个长度,所以初始化了5个map,但是map没有通过make申请内容空间,所以报错nil map
   //slice[0]["age"] = 10;//报错
   //下面不报错
   slice[0] = make(map[string]int,10)
   slice[0]["age"] = 19
   fmt.Println(slice[0]["age"])
   */
   ///*
   //因为初始化了0个长度,所以索引取不到值,报index out of range
   slice0[0]["age"] = 10;

   //*/
}

输出:

代码语言:javascript
复制
slice= [mappanic: runtime error: index out of range

接下来我们看一个:类似于Java中常用的map类型

代码语言:javascript
复制
package main

import "fmt"

func main() {
   //类似于Java中的Map<String,HashMap<String,Object>>
   var myMap = make(map[string]map[string]interface{},10)
   fmt.Println(myMap)
   //记得make
   myMap["li_ming_id_123"] = make(map[string]interface{},5)
   myMap["li_ming_id_123"]["school"] = "清华大学"

   fmt.Println(myMap)
}

输出:

代码语言:javascript
复制
map[]
map[li_ming_id_123:map[school:清华大学]]

30.Go中的time时间包模块和Java中的时间API使用区别

Go中关于时间处理的操作在time包中

1.基本获取时间信息

参考如下代码示例:

代码语言:javascript
复制
package main

import (
   "fmt"
   "time"
)

func main() {
   //获取当前时间
   now := time.Now()
   //获取当前年份
   year := now.Year()
   //获取当前月份
   month := now.Month()
   //获取当前 日期
   day := now.Day()
   //获取当前小时
   hour := now.Hour()
   //获取当前分钟
   min := now.Minute()
   //获取当前秒
   second :=now.Second()

   //获取当前时间戳,和其它编程语言一样,自1970年算起
   timestamp := now.Unix()
   //纳秒时间戳
   ntimestamp := now.UnixNano()


   fmt.Println("year=",year)
   fmt.Println("month=",month)
   fmt.Println("day=",day)
   fmt.Println("hour=",hour)
   fmt.Println("min=",min)
   fmt.Println("second=",second)
   fmt.Println("timestamp=",timestamp)
   fmt.Println("ntimestamp=",ntimestamp)
}

2.格式化时间

Go的时间格式化和其它语言不太一样,它比较特殊,取了go的出生日期作为参数标准

参考如下代码示例:

代码语言:javascript
复制
package main

import (
   "fmt"
   "time"
)

func main() {
   //获取当前时间
   now := time.Now()

   //2006-01-02 15:04:05这个数值是一个标准写死的,只要改格式符号即可
   fmt.Println(now.Format("2006-01-02 15:04:05"))
   fmt.Println(now.Format("2006/01/02 15:04:05"))
   fmt.Println(now.Format("2006/01/02"))//年月日
   fmt.Println(now.Format("15:04:05"))//时分秒
}
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-05-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 你呀不牛 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前文:
  • 20.Go的内置函数和Java的默认导入包java.lang.*
    • 21.Go的标准格式化输出库fmt和java的输出打印库对比
      • 22.Go的面向对象相关知识
        • 23.Go语言中线程的实现和Java语言中线程的实现
          • 24.Go中的反射与Java中的反射对比
            • 25.变量作用域的区别
              • 26.Go语言和Java语言字符串操作的区别
                • 27.Go语言和Java语言IO操作的区别
                  • 28.Go语言中有匿名函数,有闭包,Java中没有(高阶函数用法)
                    • 29.Go中的map和Java中的HashMap
                      • 30.Go中的time时间包模块和Java中的时间API使用区别
                      相关产品与服务
                      消息队列 TDMQ
                      消息队列 TDMQ (Tencent Distributed Message Queue)是腾讯基于 Apache Pulsar 自研的一个云原生消息中间件系列,其中包含兼容Pulsar、RabbitMQ、RocketMQ 等协议的消息队列子产品,得益于其底层计算与存储分离的架构,TDMQ 具备良好的弹性伸缩以及故障恢复能力。
                      领券
                      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档