go语言基础语法vs java

基础

  • 语句分割:go可以直接用换行作为语句分割符,也可以和java一样用分号
  • 代码块:go的{不能在单独行(会编译错误),java也有这样的格式化规范(只是规范,编译不会报错)
  • 字符串拼接:和java一样通过+号进行
  • 空格:和java类似,用于分隔标识符、关键字、运算符和表达式,以提高代码的可读性

go没有真正意义的类,一切皆方法,方法可以聚合在一起,可导出

关于包

package main

import "fmt"
func main() {
   fmt.Println("Hello, World!")
}
  • 通过package定义包,每个 Go 应用程序都包含一个名为 main 的包
  • 通过 import导入包
  • main和java的main类似

注释

  • 单行注释:// 和java一样
  • 多行注释:/* */ 和java一样

命名规范

  • 驼峰命名
  • 首字母大写代表外部可见,类似java的public关键字
  • 首字母小写代表包内可见,类似java的protected
  • 标识符:和java类似,第一个字符必须是字母或下划线而不能是数字

关键字

下面列举了 Go 代码中会使用到的 25 个关键字或保留字:

break:同javadefaultfunc:定义函数interface:同javaselect
casedefergomapstruct:定义结构体
chanelse:同javagotopackage:类似javaswitch:同java
const:定义常量fallthroughif:同javarangetype
continuefor:同javaimport:同javareturn:同javavar:定义变量

除了以上介绍的这些关键字,Go 语言还有 36 个预定义标识符:

appendboolbytecapclosecomplexcomplex64complex128uint16
copyfalsefloat32float64imagintint8int16uint32
int32int64iotalenmakenewnilpanicuint64
printprintlnrealrecoverstringtrueuintuint8uintptr

数据类型

  • 布尔类型:var b bool = true,类似java boolean

  • 整数数字类型:go有无符号类型,java没有
    | 序号 | 类型和描述 |
    | — | — |
    | 1 | uint8
    无符号 8 位整型 (0 到 255) |
    | 2 | uint16
    无符号 16 位整型 (0 到 65535) |
    | 3 | uint32
    无符号 32 位整型 (0 到 4294967295) |
    | 4 | uint64
    无符号 64 位整型 (0 到 18446744073709551615) |
    | 5 | int8
    有符号 8 位整型 (-128 到 127)
    等于java的byte |
    | 6 | int16
    有符号 16 位整型 (-32768 到 32767)
    等于java的short |
    | 7 | int32
    有符号 32 位整型 (-2147483648 到 2147483647)
    等于java的int |
    | 8 | int64
    有符号 64 位整型 (-9223372036854775808 到 9223372036854775807)
    等于java的long |

  • 浮点类型

序号类型和描述
1float32
IEEE-754 32位浮点型数
等于java的float
2float64
IEEE-754 64位浮点型数
等于java的double
3complex64
32 位实数和虚数
java没有相应的类型
4complex128
64 位实数和虚数
java没有相应的类型
  • 其他数字类型
序号类型和描述
1byte 类似 uint8
2rune 类似 int32
3uint 32 或 64 位 32位处理器为32位,64位处理器为64位
4int 与 uint 一样大小
5uintptr 无符号整型,用于存放一个指针

运算符

算术运算符

运算符描述备注
+相加
-相减
*相乘
/相除
%求余
++自增go没有java中 ++i这种语法
同时只能作为表达式,不能赋值
a=a++编译不过
自减go没有java中 --i这种语法

关系运算符

运算符两边的值类型要一致,不一致编译会报错(java的可以不一致)

运算符描述备注
==检查两个值是否相等,如果相等返回 True 否则返回 False。
!=检查两个值是否不相等,如果不相等返回 True 否则返回 False。
>检查左边值是否大于右边值,如果是返回 True 否则返回 False。
<检查左边值是否小于右边值,如果是返回 True 否则返回 False。
>=检查左边值是否大于等于右边值,如果是返回 True 否则返回 False。
<=检查左边值是否小于等于右边值,如果是返回 True 否则返回 False。

逻辑运算符

和java语法一致

运算符描述备注
&&逻辑 AND 运算符。 如果两边的操作数都是 True,则条件 True,否则为 False。
||逻辑 OR 运算符。 如果两边的操作数有一个 True,则条件 True,否则为 False。
!逻辑 NOT 运算符。 如果条件为 True,则逻辑 NOT 条件 False,否则为 True。

位运算符

和java语法一致

  • &:且运算符,双1才1,有0则0
  • |:或运算符,双0才0,有1则1
  • ^:异或运算符,相同0,相异1

假定 A 为60,B 为13

运算符描述实例
&按位与运算符"&"是双目运算符。 其功能是参与运算的两数各对应的二进位相与。(A & B) 结果为 12, 二进制为 0000 1100
|按位或运算符"|"是双目运算符。 其功能是参与运算的两数各对应的二进位相或(A | B) 结果为 61, 二进制为 0011 1101
^按位异或运算符"^"是双目运算符。 其功能是参与运算的两数各对应的二进位相异或,当两对应的二进位相异时,结果为1。(A ^ B) 结果为 49, 二进制为 0011 0001
<<左移运算符"<<“是双目运算符。左移n位就是乘以2的n次方。 其功能把”<<“左边的运算数的各二进位全部左移若干位,由”<<"右边的数指定移动的位数,高位丢弃,低位补0。A << 2 结果为 240 ,二进制为 1111 0000
>>右移运算符">>“是双目运算符。右移n位就是除以2的n次方。 其功能是把”>>“左边的运算数的各二进位全部右移若干位,”>>"右边的数指定移动的位数。A >> 2 结果为 15 ,二进制为 0000 1111

赋值运算符

和java语法一致

运算符描述备注
=简单的赋值运算符,将一个表达式的值赋给一个左值
+=相加后再赋值
-=相减后再赋值
*=相乘后再赋值
/=相除后再赋值
%=求余后再赋值
<<=左移后赋值
>>=右移后赋值
&=按位与后赋值
^=按位异或后赋值
|=按位或后赋值

其他运算符

与指针相关(和c类似,java无此运算符)

运算符描述实例
&返回变量存储地址&a; 将给出变量的实际地址。
*指针变量。*a; 是一个指针变量

运算符优先级

有些运算符拥有较高的优先级,二元运算符的运算方向均是从左至右。下表列出了所有运算符以及它们的优先级,由上至下代表优先级由高到低

优先级运算符
4* / % << >> & &^
3+ - | ^
2== != < <= > >=
1&&、||

可以通过()来提升优先级(建议方式)

变量

变量声明

  • 声明变量的一般形式是使用 var 关键字
    • var identifier type(java的是type identifier)
    • 可以一次声明多个变量,var identifier1, identifier2 type
var name string ="jake"
var a,b = 1,2
  • 变量只声明,不赋值,则默认为零值:零值就是变量没有做初始化时系统默认设置的值(和java类似)
    • 数值类型(包括complex64/128)为 0
    • 布尔类型为 false
    • 字符串为 “”(空字符串)
    • 以下几种类型为 nil(nil类型java的null)
var a *int
var a []int
var a map[string] int
var a chan int
var a func(string) int
var a error // error 是接口
  • 声明变量时如果省略了type,则根据值进行判定
var a = true
var b = "test"
var c = 1
  • 推荐方式,通过 := (本身是一个声明+赋值的关键字)可以省略var关键字, :=左侧的关键字不能是被声明过的,否则会编译错误,同时只能在函数体重存在
a := true
b := "test"
c := 1
  • 因式分解关键字:一般用于全局变量声明
package main
import "fmt"
var (  // 这种写法一般用于全局变量声明
    a int
    b string
)
func main(){
    
}
  • 函数体内定义的变量必须被使用,否则会报错(java不会报错),全局变量允许声明但不使用的
  • 多个变量可以在同一行赋值:通常用于获取函数的多个返回值
a, b, c := 1, true, "test"
  • 抛弃值:通过空白标识符进行,如值 1 在:_, b = 1, 2 中被抛弃,通常用于获取函数多个返回值的一个

常量

  • 常量是一个简单值的标识符,在程序运行时,不会被修改的量(类似java的 final值)
  • 常量中的数据类型只可以是布尔型、数字型(整数型、浮点型和复数)和字符串型(java的常量可以是任何类型)
  • 定义格式:const identifier [type] = value
const A string = "aa"
const B = "aa"
  • 常量命名规范和java类型,单词大写,下划线分割单词

值类型和引用类型

和java类似,

  • int、float、bool 和 string 这些基本类型都属于值类型,使用这些类型的变量直接指向存在内存中的值
  • 一个引用类型的变量 r1 存储的是 r1 的值所在的内存地址(数字),或内存地址中第一个字所在的位置。这个内存地址称之为指针,这个指针实际上也被存在另外的某一个值中

变量作用域

Go 语言中变量可以在三个地方声明

  • 函数内定义的变量称为局部变量
  • 函数外定义的变量称为全局变量
  • 函数定义中的变量称为形式参数

控制语句

条件语句

if语句

和java类似,不过条件不需要使用()

package main

import "fmt"
func main() {
    a  := 10
   if a < 20 {
       fmt.Printf("a 小于 20\n" )
   }
   fmt.Printf("a 的值为 : %d\n", a)
}

和java不同,if 条件语句还可以进行变量定义,变量的作用范围在语句块里面

package main

import "fmt"

func main() {
	if i, j := 20, 30; i > 20 && j > 20 {
		fmt.Println(i, j)
	}
}

switch语句

语法如下,变量 var1 可以是任何类型,而 val1 和 val2 则可以是同类型的任意值。类型不被局限于常量或整数,但必须是相同的类型;或者最终结果为相同类型的表达式

switch var1 {
    case val1:
        ...
    case val2:
        ...
    default:
        ...
}

整体上看,表达力比java的强,同时不需要break语句,进入case就会break

func main() {
   /* 定义局部变量 */
   var grade string = "B"
   var marks int = 90

   switch marks {
      case 90: grade = "A"
      case 80: grade = "B"
      case 50,60,70 : grade = "C"
      default: grade = "D"  
   }

   switch {
      case grade == "A" :
         fmt.Printf("优秀!\n" )    
      case grade == "B", grade == "C" :
         fmt.Printf("良好\n" )      
      case grade == "D" :
         fmt.Printf("及格\n" )      
      case grade == "F":
         fmt.Printf("不及格\n" )
      default:
         fmt.Printf("差\n" );
   }
   fmt.Printf("你的等级是 %s\n", grade );      
}

switch 语句还可以被用于 type-switch 来判断某个 interface 变量中实际存储的变量类型interface{}类似java中的Object类型

package main

import "fmt"

func main() {
   var x interface{}
     
   switch i := x.(type) {
      case nil:  
         fmt.Printf(" x 的类型 :%T",i)                
      case int:  
         fmt.Printf("x 是 int 型")                      
      case float64:
         fmt.Printf("x 是 float64 型")          
      case func(int) float64:
         fmt.Printf("x 是 func(int) 型")                      
      case bool, string:
         fmt.Printf("x 是 bool 或 string 型" )      
      default:
         fmt.Printf("未知型")    
   }  
}

fallthrough:类似java case没有break语句,因为go不需要写break,所以强制执行下一个只能用fallthrough标识

package main

import "fmt"

func main() {
	i := 6
	switch {
	case i == 6:
		fmt.Println(6)
		fallthrough
	case i == 5:
		fmt.Println(5)
	}
}

输出:6 5,和一下java代码类似

int i = 6;
    switch (i) {
        case 6:
            System.out.println(6);
        case 5:
            System.out.println(5);
            break;
 }

select语句

go特有,java无,用于通道操作,每个 case 必须是一个通道操作,要么是发送要么是接收,后续学了通道再不出

循环语句

go的循环语句只有for,java除了for还有while
go的for循环有三种形式

  • for init; condition; post { }:和java类似,init和post是可选的
  • for condition { }:和java的while类似
  • for { }:和java的for(;;)或while(true)类似

for 循环的 range 格式可以对 slice、map、数组、字符串等进行迭代循环。格式如下:和java的foreach语法

for key, value := range oldMap {
    newMap[key] = value
}

 numbers := [6]int{1, 2, 3, 5}
//i是数组下标,v是值
for i,v:= range numbers {
  fmt.Println(i,v)
}

以上代码中的 key 和 value 是可以省略

  • 如果只想读取 key,格式为:for key := range oldMap或者for key, _ := range oldMap
  • 如果只想读取 value,格式如下:for _, value := range oldMap

函数

函数语法

函数类似java的方法,定义如下

func function_name( [parameter list] ) [return_types] {
   //函数体
}
package main

import "fmt"

func main() {
	fmt.Println(add(1, 2))
}

func add(a, b int) int {
	return a + b
}

函数可以返回多个值,java的方法只能返回一个值

func swap(x, y string) (string, string) {
   return y, x
}

func main() {
   a, b := swap("a", "b")
   fmt.Println(a, b)
}

函数参数

默认为值传递,可以通过传指针,来达到在行数中修改参数值(java无指针)

package main

import "fmt"

func main() {
	x := 0
	unModify(x)
	fmt.Println(x)
	modify(&x)
	fmt.Println(x)
}

func unModify(x int) {
	x++
}
func modify(x *int) {
	*x++
}
//运行的输出结果为
0
1

函数用法

函数可以作为实参传递,类似java中的Function

package main

import (
   "fmt"
   "math"
)

func main(){
   getSquareRoot := func(x float64) float64 {
      return math.Sqrt(x)
   }

   fmt.Println(getSquareRoot(9))
}
//结果为4

闭包:闭包是一个匿名函数,匿名函数是一个"内联"语句或表达式。匿名函数的优越性在于可以直接使用函数内的变量,不必申明,java无对应语法

package main

import "fmt"

func getSequence() func() int {
   i:=0
   return func() int {
      i+=1
     return i  
   }
}

func main(){
   /* nextNumber 为一个函数,函数 i 为 0 */
   nextNumber := getSequence()  

   /* 调用 nextNumber 函数,i 变量自增 1 并返回 */
   fmt.Println(nextNumber())
   fmt.Println(nextNumber())
   fmt.Println(nextNumber())
   
   /* 创建新的函数 nextNumber1,并查看结果 */
   nextNumber1 := getSequence()  
   fmt.Println(nextNumber1())
   fmt.Println(nextNumber1())
}
//执行结果为
1
2
3
1
2

函数方法:类似java重的方法,go中没有类的概念,java中的类包含两部分,数据和方法,go语法中这两部分是分离的,数据通过结构体定义,方法通过函数方法定义,函数方法格式为

func (variable_name variable_data_type) function_name([parameter list]) [return_type]{
   /* 函数体*/
}
package main

func main() {
	r := Rectangle{2, 3}
	println(r.test(2))
}

type Rectangle struct {
	l, w int
}

func (r Rectangle) test(i int) int {
	return r.l * r.w * i
}
//输出结果为12

数据结构

指针

go的指针和c语言指针类似,指针代表的就是变量的内存地址,指针通过&获取

package main

import "fmt"

func main() {
   var a int = 10  
   fmt.Println(&a)
}

指针声明:var var_name *var-type

var ip *int        /* 指向整型*/
var fp *float32    /* 指向浮点型 */

指针使用

package main

import "fmt"

func main() {
	var a int = 1
	var p *int = &a
	*p = 2
	fmt.Println(a, *p, p, &a)
}
//输出结果为:2 2 0xc0000a6058 0xc0000a6058

空指针:当一个指针被定义后没有分配到任何变量时,它的值为 nil

package main

import "fmt"

func main() {
	var a *int

	fmt.Println(a == nil)
}
//输出:true

空指针判断

var ptr *int
if(ptr != nil)     /* ptr 不是空指针 */
if(ptr == nil)    /* ptr 是空指针 */

指向指针的指针:如果一个指针变量存放的又是另一个指针变量的地址,则称这个指针变量为指向指针的指针变量(指针是一个地址,也需要存储的)

package main

import "fmt"

func main() {
	var a int = 3
	var ptr *int = &a
	var pptr **int = &ptr

	fmt.Println(ptr, *pptr, pptr, *ptr, **pptr)
}
//输出:0xc000018098 0xc000018098 0xc00000a028 3 3

指针参数:类似java的引用传递,通过指针参数就可以修改指针值了

package main

import "fmt"

func main() {
	x, y := 1, 2
	swap(&x, &y)
	fmt.Println(x, y)
}
func swap(x, y *int) {
	*x, *y = *y, *x
}
//输出:2 1

数组

声明数组:var variable_name [SIZE] variable_type

var arr [10] int

初始化数组

balance := [5]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
//如果数组长度不确定,可以使用 ... 代替数组的长度,编译器会根据元素个数自行推断数组的长度:
balance := [...]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
//...也可以省略
balance := []float32{1000.0, 2.0, 3.4, 7.0, 50.0}
//通过下标来初始化元素,将索引为 1 和 3 的元素初始化
balance := [5]float32{1:2.0,3:7.0}

多维数组:和java类似

package main

import "fmt"

func main() {
    // Step 1: 创建数组
    values := [][]int{}

    // Step 2: 使用 append() 函数向空的二维数组添加两行一维数组
    row1 := []int{1, 2, 3}
    row2 := []int{4, 5, 6}
    values = append(values, row1)
    values = append(values, row2)

    // Step 3: 显示两行数据
    fmt.Println("Row 1")
    fmt.Println(values[0])
    fmt.Println("Row 2")
    fmt.Println(values[1])

    // Step 4: 访问第一个元素
    fmt.Println("第一个元素为:")
    fmt.Println(values[0][0])
    //二维数组初始化
    a := [][]int{
		{0, 1, 2, 3},  
		{4, 5, 6, 7},  
		{8, 9, 10, 11},
	}
}

数组作为参数传递:数组作为参数传递,依然是值传递,这里和java不一样,java是引用传递

import "fmt"

func main() {

	a := [3]int{1, 2, 3}
	test(a)
	fmt.Println(a)
}

func test(arr [3]int) {
	arr[0] = 3
}
//输出结果为:[1 2 3]

要想在传入函数中修改数组值,需要用指针

package main

import "fmt"

func main() {

	a := []int{1, 2, 3}
	test(&a)
	fmt.Println(a)
}

func test(arr *[]int) {
	(*arr)[0] = 3
}
//输出结果为[3 2 3]

结构体

和c的结构体类似,类似java中只有属性没有方法的类
定义格式如下

type struct_variable_type struct {
   member definition
   member definition
   ...
   member definition
} 

结构体使用示例代码

package main

import "fmt"

func main() {
	fmt.Println(Student{"tom", 18})
	fmt.Println(Student{name: "tom"})
	fmt.Println(Student{age: 18})
	stu := Student{"tom", 18}
	fmt.Println(stu.name, stu.age)
	fmt.Println(stu.Desc())
}

type Student struct {
	name string
	age  int
}

//给结构增加函数方法
func (stu Student) Desc() string {
	return fmt.Sprintf("name=%s,age=%d", stu.name, stu.age)
}
//输出为
{tom 18}
{tom 0}
{ 18}
tom 18
name=tom,age=18

结构体指针:格式如下

stu := Student{"tom", 18}
var stu_ptr *Student = &stu

结构体指针使用示例

func main() {

	stu := Student{"tom", 18}
	valueRef(stu)
	fmt.Println(stu)
	pointerRef(&stu)
	fmt.Println(stu)

}

type Student struct {
	name string
	age  int
}

func valueRef(stu Student) {
	stu.name = "bob"
}
func pointerRef(stu *Student) {
	stu.name = "bob"
}
//输出
{tom 18}
{bob 18}

对于结构体属性命名,首字母大小写决定了访问访问,如果要在其他包访问,属性名称首字母要大写

go语言切片(Slice)

:::info
有点类似java的ArrayList,语言切片是对数组的抽象,数组的长度不可改变,内置类型切片(“动态数组”),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大
:::
切片定义

//未指定大小的数组切片
var identifier []type
//使用 make() 函数来创建切片
slice1 := make([]type, len)
//也可以指定容量,其中 capacity 为可选参数
make([]T, length, capacity)

切片初始化:

s :=[] int {1,2,3 } //cap=len=3
s := arr[:] //初始化切片 s,是数组 arr 的引用
s := arr[startIndex:endIndex] //将 arr 中从下标 startIndex 到 endIndex-1 下的元素创建为一个新的切片
s := arr[startIndex:] //默认 endIndex 时将表示一直到arr的最后一个元素
s := arr[:endIndex] //默认 startIndex 时将表示从 arr 的第一个元素开始

len()和cap()函数:len() 代表元素数量,cap() 底层数组中能够容纳的元素数量
详解见:https://blog.csdn.net/bibihenmuc/article/details/131135133

Map集合

数据结构和java的HashMap类似,但是在获取 Map 的值时,如果键不存在,返回该类型的零值,例如 int 类型的零值是 0,string 类型的零值是 “”(java返回的是null)
Map 是引用类型,如果将一个 Map 传递给一个函数或赋值给另一个变量,它们都指向同一个底层数据结构,因此对 Map 的修改会影响到所有引用它的变量
Map定义

// 创建一个空的 Map
m := make(map[string]int)
// 创建一个初始容量为 10 的 Map
m := make(map[string]int, 10)
// 使用字面量创建 Map
m := map[string]int{
    "apple": 1,
    "banana": 2,
    "orange": 3,
}

Map使用

// 获取键值对
v1 := m["apple"]
v2, ok := m["pear"]  // 如果键不存在,ok 的值为 false,v2 的值为该类型的零值
// 修改键值对
m["apple"] = 5
// 获取 Map 的长度
len := len(m)
// 遍历 Map
for k, v := range m {
    //
}
// 删除键值对
delete(m, "banana")

递归

语法和java类似,示例,实现斐波那契数列

package main

import "fmt"

func main() {
	for i := 0; i < 10; i++ {
		fmt.Printf("%d ", fibonacci(i))
	}
}
func fibonacci(n int) int {
	if n < 2 {
		return n
	} else {
		return fibonacci(n-2) + fibonacci(n-1)
	}
}

类型转换

语言类型转换基本格式:type_name(expression),数值类型java语法可以自动转换

var a int = 10
var b float64 = float64(a)

字符串转换

var str string = "10"
var num int
num, _ = strconv.Atoi(str)

整数转换为字符串

num := 123
str := strconv.Itoa(num)

字符串转换为浮点数

str := "3.14"
num, err := strconv.ParseFloat(str, 64)

浮点数转换为字符串

num := 3.14
str := strconv.FormatFloat(num, 'f', 2, 64)

接口类型转换
接口类型转换有两种情况:类型断言和类型转换
类型断言用于将接口类型转换为指定类型,其语法为:value.(type) 或value.(T)

package main

import "fmt"

func main() {
    var i interface{} = "Hello, World"//interface{} 有点像java的Object
    str, ok := i.(string)
    if ok {
        fmt.Printf("'%s' is a string\n", str)
    } else {
        fmt.Println("conversion failed")
    }
}

接口
Go 语言提供了另外一种数据类型即接口,它把所有的具有共性的方法定义在一起,任何其他类型只要实现了这些方法就是实现了这个接口。接口可以让我们将不同的类型绑定到一组公共的方法上,从而实现多态和灵活的设计。go中没有java继承这种语法,继承的复用通过组合模式来实现。
Go 语言中的接口是隐式实现的,也就是说,如果一个类型实现了一个接口定义的所有方法,那么它就自动地实现了该接口。因此,我们可以通过将接口作为参数来实现对不同类型的调用,从而实现多态。

/* 定义接口 */
type interface_name interface {
   method_name1 [return_type]
   method_name2 [return_type]
   method_name3 [return_type]
   ...
   method_namen [return_type]
}

/* 定义结构体 */
type struct_name struct {
   /* variables */
}

/* 实现接口方法 */
func (struct_name_variable struct_name) method_name1() [return_type] {
   /* 方法实现 */
}
...
func (struct_name_variable struct_name) method_namen() [return_type] {
   /* 方法实现*/
}

使用示例

package main

import (
	"fmt"
)

type Person interface {
	Say()
}
type Teacher struct {
}

func (teacher Teacher) Say() {
	fmt.Println("I'm a teacher")
}

type Stucent struct {
}

func (student Stucent) Say() {
	fmt.Println("I'm a student")
}

func main() {
	var p1 Person = new(Teacher)
	p1.Say()
	var p2 Person = new(Stucent)
	p2.Say()

}
//输出
I'm a teacher
I'm a student

错误处理

Go 语言通过内置的错误接口提供了非常简单的错误处理机制。
error 类型是一个接口类型,这是它的定义

type error interface {
    Error() string
}

使用示例,感觉还是java thow 异常的方式比较简单

package main

import (
	"errors"
	"fmt"
)

func testError(i int) (int, error) {
	if i == 0 {
		return 0, errors.New("mock error")
	} else {
		return i, nil
	}
}

func main() {
	i, error := testError(0)
	fmt.Println(i, error)

}

panic,defer 和 recover:据说可以实现类似java try catch finally的效果,后续研究了在写

go并发

go语言的并发是通过goroutine(协程) 调度额,是轻量级的线程,使用起来比java的多线程更简单

并发开启

使用示例

package main

import (
	"fmt"
	"time"
)

func say(s string) {
	for i := 0; i < 5; i++ {
		time.Sleep(10 * time.Millisecond)
		fmt.Println(s)
	}
}
func main() {
	go say("world")//通过go开启并发执行
	go say("hello")
	time.Sleep(200 * time.Millisecond)
}
//输出:输出是随机交错的
world
hello
hello
world
world
hello
hello
world
world
hello

goroutine通信

两个 goroutine 之间通过传递一个指定类型的channel来同步运行和通讯

ch := make(chan int)
ch <- v    // 把 v 发送到通道 ch
v := <-ch  // 从 ch 接收数据

示例

package main

import "fmt"

func sum(s []int, c chan int) {
	sum := 0
	for _, v := range s {
		sum += v
	}
	c <- sum // 把 sum 发送到通道 c
}

func main() {
	s := []int{1, 2, 3, 4, 5}

	c := make(chan int)
	go sum(s[:len(s)/2], c)
	go sum(s[len(s)/2:], c)
	x, y := <-c, <-c // 从通道 c 中接收

	fmt.Println(x, y, x+y)
}
//输出:12 3 15

默认情况下,通道是不带缓冲区的。发送端发送数据,同时必须有接收端相应的接收数据,如果没被接收,发生会被阻塞,如果没有发送,接收会被阻塞
通道可以设置缓冲区,通过 make 的第二个参数指定缓冲区大小:ch := make(chan int, 100),类似java的缓冲队列效果
通道可以通过close来关闭,通道关闭后,放数据会报错,数据取完后,会返回0值

package main

import "fmt"

func main() {

	ch := make(chan int, 2)

	ch <- 1
	ch <- 2
	close(ch)

	fmt.Println(<-ch)
	fmt.Println(<-ch)
	v, ok := <-ch //通过ok可以判断是否取完了
	fmt.Println(v, ok)
	ch <- 2 //已经关闭了,再放会报错

}
//输出:
1
2
0 false
panic: send on closed channel

字符串格式化

Go 语言中使用 fmt.Sprintffmt.Printf 格式化字符串并赋值给新串

  • Sprintf 根据格式化参数生成格式化的字符串并返回该字符串
  • Printf 根据格式化参数生成格式化的字符串并写入标准输出

格式化语法

格 式描 述
%v按值的本来值输出
%+v在 %v 基础上,对结构体字段名和值进行展开
%#v输出 Go 语言语法格式的值
%T输出 Go 语言语法格式的类型和值
%%输出 % 本体
%b整型以二进制方式显示
%o整型以八进制方式显示
%d整型以十进制方式显示
%x整型以十六进制方式显示
%X整型以十六进制、字母大写方式显示
%UUnicode 字符
%f浮点数
%p指针,十六进制方式显示

小结

以上为我学习的go的基础语法点以及和java语法的简单对比,整体看起来还是比较简洁的,后续继续学习高级特性会继续记录

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值