专栏首页我要变牛来Javaer,学学go吧(二)

来Javaer,学学go吧(二)

前文:

来Javaer,学学go吧(一)

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

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

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

Go中的内置函数有:

close          关闭channel

len            求长度

make       创建slice,map,chan对象

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

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

参考示例代码1:

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的使用

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

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文档:

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)。

它的使用方法如下:

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

详细占位符号如下:

代码示例如下:

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

1.封装属性(结构体)

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

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学习的方法

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()
}

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

参考如下案例:

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中可以用嵌入结构体实现类似于继承的功能:

参考如下代码示例:

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不存在嵌入多个结构体后,被嵌入的几个结构体有相同的方法,最后不知道选择执行哪个方法的情况,多个结构体方法相同时,直接编译就会报错。

参考如下示例:

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语言中的接口是侵入式接口。

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

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

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

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()
}

接口可以内嵌接口

参考如下代码示例:

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

比如:

普通方法执行

myFunction()

开启一个Goroutines来执行方法

 go  myFunction()

java中是

     new Thread(()->{ 

			//新线程逻辑代码

	 }).start();

参考下面的代码示例:

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

代码示例:

package main

import "fmt"

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

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

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

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

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

具体参照下面代码示例:

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

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

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

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可以作为锁使用:

示例代码如下:

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接口

参考如下示例代码:

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中的反射对比

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

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

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

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

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

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

  例如示例1:
package main

import "fmt"

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

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

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

  例如示例2:
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类型")
   }
   
}
	 因为传入进来的都是interface类型,所以我们需要用的时候要区分类型,然后取出其中真正类型的值。

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

      示例3:
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)

 }
 
}
	结构体中的属性和方法怎么获取呢?

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

   示例代码4:

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

25.变量作用域的区别

		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接口的定义如下:

type Reader interface {
Read(p []byte) (n int, err error)
}

Writer接口

Writer接口的定义如下:

type Writer interface {
Write(p []byte) (n int, err error)
}

简单示例

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:匿名函数赋值给变量

package main

import "fmt"

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

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

输出结果:

3

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

示例2:

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!")
   }()//有参数可以写在这个小括号中
}

输出:

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函数的限制。

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

接下来我们看代码示例:

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)
   }
}

输出:

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以及添加元素

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中取值

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:遍历

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元素

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类型,比如:

Map<String,Map<String,Object>> data = new HashMap<>();

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

示例5:

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"])
   //*/
}

输出结果:

slice= [map[] map[] map[] map[] map[]]
slice=0 []
19

接下来继续看代码:

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"])
   */

}

输出:

panic: assignment to entry in nil map

看下面这个报错:

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;

   //*/
}

输出:

slice= [mappanic: runtime error: index out of range

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

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)
}

输出:

map[]
map[li_ming_id_123:map[school:清华大学]]

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

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

1.基本获取时间信息

参考如下代码示例:

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的出生日期作为参数标准

参考如下代码示例:

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"))//时分秒
}

本文分享自微信公众号 - 你呀不牛(notNiu),作者:不牛

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2021-05-17

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 来Javaer,学学go吧(一)

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

    你呀不牛
  • 用 Type Annotation 提升 Python 代码健壮性

    花下猫语:众所周知,Python 是一门动态类型语言,这也是造成它性能较慢的一大原因。如今 Python 也引入了一些类型检查的辅助,那么,类型检查对于提升 P...

    Python猫
  • 全球排名前十五的编程语言都是什么车?

    C语言是全能手,小巧,强大,所向披靡,可靠,任何事情都能对付。 C++是新的C,双倍的能力,双倍的尺寸,适应险恶的环境,但是你如果没练好就去驾驶,很可能会撞车。...

    小莹莹
  • 异步时代-java的协程路在何方

    很尴尬,但是事实是,很大一部分的程序员不知道协程是啥玩意,更大一部分的程序员,项目中没用到协程。

    生活创客
  • 如何愉快地学习 Go 语言?来场 Go 之旅吧!

    其实官方已经为我们准备了一款在线 Go 语言教程 —— Go 语言之旅(gotour),支持在线敲代码练习,简直入门必备!

    程序员鱼皮
  • Java这么优秀,我当然要深入啦

    今天开始,跃哥将和去年阅读《Head First 设计模式》差不多的模式来,和大家一起学习《Java 实战》的内容。通过前一本书,那本书跃哥二刷,所以读完之后对...

    程序员小跃
  • 如何参与一个开源项目(多图)

    摘要:作为一个 Javaer 一直在享受开源带来的便利,却从未给开源提供任何福报。本周将围绕一个开源项目来讲诉,如何为开源添砖加瓦。

    FoamValue
  • 学习Python,来点PEP吧!

    或许你是一个初入门Python的小白,完全不知道PEP是什么。又或许你是个学会了Python的熟手,见过几个PEP,却不知道这玩意背后是什么。那正好,本文将系统...

    一墨编程学习
  • java程序员为什么使用Groovy?

    一直听说java世界里有个Groovy,但是一直没时间去了解它究竟是一个怎么样子的。我们现在的项目里放了很多开源包,而且项目中做流程的时候,规则就是用Groov...

    秃顶的Java程序员
  • 一起来学Go --- (go的变量)

    变量是几乎所有编程语言中最基本的组成元素,从根本上说,变量相当于是一块数据存储空间的命名,程序可以通过定义一个变量来申请一块数据存储空间,之后可以通过引用变量名...

    Wyc
  • 自学Python十二 战斗吧Scrapy!

    Scrapy是一个为了爬取网站数据,提取结构性数据而编写的应用框架。 可以应用在包括数据挖掘,信息处理或存储历史数据等一系列的程序中。

    叁金
  • Java函数式开发——优雅的Optional空指针处理

    空闲时会抽空学习同在jvm上运行的Groovy和Scala,发现他们对null的处理比早期版本Java慎重很多。在Java8中,Optional为函数式编程的n...

    哲洛不闹
  • 非Java程序员竟鲜有人真正理解DI和IOC

    小编在后端圈也算是阅人无数了, 发现一个现象,Java程序员对于面向对象语言的基础知识整体掌握比较扎实,而类似PHP,Python的初级甚至中级程序员就比较薄弱...

    后端技术探索
  • Vue3 食用方法(赶快来学吧)

    ref 需要用.value 支持任意格式 reactive 只能绑定数组对象 而且不需要.value

    玖柒的小窝
  • 前端er来学习一下webWorker吧

    我们都知道,JavaScript 是单线程的,在同一时刻只能处理一个任务,我们会通过 setTimeout()、setInterval()、ajax 和事件处理...

    OBKoro1
  • Java函数式开发——优雅的Optional空指针处理

        在Java江湖流传着这样一个传说:直到真正了解了空指针异常,才能算一名合格的Java开发人员。在我们逼格闪闪的java码字符生涯中,每天都会遇到各种nu...

    随风溜达的向日葵
  • 【科普】Dart语言

    Dart是一门新的编程语言,如同JAVA、PHP一样,是为了解决编写应用程序中的一些实际问题而被造轮子发明出来的,而这个造轮子的人就是 Google。可能大家都...

    Jean
  • 想学新的编程语言?考虑下Go吧

    【导读】快速的运行时、高效的并发、简单易学的语法,这些都是Go语言最吸引人的特性。

    AI科技大本营
  • Go语言学习(二)| 变量

    使用语法为: var var_name1, var_name2 type , 例:

    Mervyn

扫码关注云+社区

领取腾讯云代金券