• go与java


    基本数据类型

    标识符

    变量名、函数名、常量名 首字母大写,则可被其他包引用

    java则是通过访问修饰符

    public、protected(包内和子类)、default(包)、private(当前类)

    数据类型
    // 基本数据类型
    整型(int、int8、int16、int32、int64、uint、uint8、uint16、uint32、byte)
    浮点型(float32、float64) // 精度损失 浮点数默认为float64
    布尔型(bool)。//不可以用0或非0值替代false和true,Java中也不行,与c/c++不同
    字符串(string)
    单个字符使用byte存储(utf-8)(英文1个字节,汉字3个字节)   
    
    // 复杂数据类型
    指针(Pointer)、数组、结构体(struct)、管道(Channel)、函数(go中函数也是一种类型)、切片(slice)、接口(interface)、map
    
    // Java中四类八种
    byte、short、int、long
    float、double
    char
    boolean
    

    例:

    var n int64 = 10
    fmt.Printf("n 的数据类型是%T 占用字节数是%d",n,unsafe.Sizeof(n))
    
    基本数据类型转换

    1、go必须显示转换 //Java可以自动转换
    2、可以从范围小的 -> 范围大的,也可以从范围大的 -> 范围小的
    3、范围大的 -> 范围小的,编译不会报错,但转换结果和我们希望的不一样

    例:

    var n1 int32 = 12
    var n3 int8
    var n4 int8
    n4 = int8(n1) + 127 // 编译通过,但结果按溢出处理
    n3 = int8(n1) + 128 // 编译不过 128超出n3的范围
    
    字符串

    // go中为基本数据类型 不可以为nil ,同样不可变
    // go中字符串由字节组成
    // 字符串不可变,若想修改,将字符串转换为[]byte,修改之后再转换为字符串,
    // 但是不能处理中文,因为中文是三个字节,若想修改为中文,字符串转换为
    // []rune,修改之后再转换为字符串

    // "" 和 ``
    s := ""
    var s string   //默认为“” 字符串不可以为nil
    var s = ""
    var s string = ""
    
    strings.Join(arr[1:], "") //对切片用空格拼接
    
    //字符串拼接
    var str = "aaa" + "bbb" +    //此加号必须写在上面,否则报错
    				"ccc"
    str += "ddd"
    
    // Java中字符串由字符组成
    String s = null;
    String a = "";
    String str = new String("aa");
    
    //字符串拼接( + 和 StringBuffer的append方法 )
    String str =" world";
    StringBuffer sb = new StringBuffer("hello");
    sb.append(str);
    
    字符串和基本数据类型转换

    1、基本数据类型 转 string ( 两种)

    var b bool = true
    str = fmt.Sprintf("%q", b)  //格式化字符串, 结果为 "true"
    
    strconv.Format..(b ..)string
    

    2、字符串 转 基本数据类型

    // 返回值为bool、float64、int64、uint64等,需要int32 则需要自己 显示转换
    strconv.ParseInt(str string)(value Int64 , err error)
    
    // 若把 “hello”字符串转化为int、float、bool,结果为0、0、false
    
    自定义数据类型
    type myInt int    //相当于别名  但是go认为是两种类型
    type myFunType func(int,int)int
    

    自增自减

    可以i++ 和 i--  // 是语句,不像c是表达式 
    不可以--i 和 ++i 和 j = i++
    
    Java则都可以
    

    取模本质
    a%b -> a - a/b*b

    控制语句

    for循环

    for是go中唯一的循环,没有while
    for 中加 if 实现 while 与 do while

    switch

    匹配项后面不用加break (Java后面需要)

    指针

    值类型:int、float、bool、string、数组、struct
    引用类型:指针、slice、map、chan、interface

    函数

    1、
    go函数 不可以重载
    返回值可以命名

    func cal(n1 int, n2 int)(sum int, sub int) {
    	...
    }
    
    _,sub = cal(10, 20)  //忽略返回值
    

    2、go支持可变参数 (args是slice切片)Java也支持

    func sum(args... int)sum int {
    	...
    }
    

    3、init函数细节

    若一个文件含有全局变量定义、init函数、main函数
    则执行流程为 变量定义 init main

    若main.go含有全局变量定义、init函数、main函数,同时引入另一个utils.go文件
    则执行流程为utils.go的 变量定义 init函数 main.go的 变量定义 init函数 main函数

    4、闭包

    //累加器
    //返回一个匿名函数,但此匿名函数引用到函数外的n,这个匿名函数就和n形成一个整体 闭包
    func AddUpper() func (int) int {
    	var n int = 10
    	return func (x int) int {
    		n = n + x
    		return n
    	}
    }
    
    func main() {
    	f := AddUpper()
    	fmt.Println(f(1)) //11
    	fmt.Println(f(2)) //13
    	fmt.Println(f(3)) //16
    }
    

    5、defer(主要作用:释放资源)

    defer函数会入defer栈(放入栈时相关值也会同时入栈) 先入后出

    6、字符串相关函数…
    7、时间日期相关函数… “2006/01/02 15:04:05”

    8、内置函数
    new: 用来分配内存,主要用来分配值类型 int、float32、struct…返回的是指针
    make:用来分配内存,主要用来分配引用类型 chan、map、slice…

    错误处理

    1、
    panic + recover + defer (defer中使用recover捕获panic异常 并 处理异常)

    panic   //调用一个函数,函数可能会panic异常,造成程序终止,应该怎样  defer中recover处理,避免程序终止 (一般 Must...函数都会panic异常)
    

    2、自定义错误

    errors.New("错误说明")   //返回error类型的值
    

    数组

    package main
    
    import "fmt"
    
    func main() {
    	fmt.Println("array coding........")
    
    	//var array [5]int
    	//array[2] = 2
    
    	//var array = [5]int{0, 1, 2, 3, 4}
    
    	//array := [5]int{0, 1, 2, 3, 4}
    	array := [...]int{0, 1, 2, 3, 4}
    	//array := []int{1: 1, 3: 3} //[]中没有东西的是切片
    
    	//a := 9
    	//array := [5]*int{0: new(int)}
    	//*array[0] = 10
    	//array[1] = &a
    
    	fmt.Println(array)
    
    } 
    

    java中初始化数组

    // 第一种方法:动态
    int [] arr = new int [6];
    int intValue = arr [5];
    System.out.println(intValue); // 0
    
    //第二种方法:静态
    int[] arr1 = {1,2,3,4,5};
    
    //第三种方法:静态
    int[] arr2 = new int [] {1,2,3,4,5};
    

    切片

    package main
    
    import (
    	"fmt"
    )
    
    func main() {
    	fmt.Println("slice coding........")
    
    	//slice := make([]string, 3, 5)
    	//slice[0] = "aaa"
    
    	//slice := []int{10, 20, 30, 40, 50}
    	//newSlice := slice[1:3] //长度为2,容量为4 两个切片共享内存
    	//newSlice = append(newSlice, 60) //长度为3,容量为4 两个切片共享内存
    
    	//slice := []int{10, 20, 30, 40, 50}
    	//newSlice := append(slice, 60) //newSlice拥有全新的底层数组,容量变为原来的两倍,容量超过1000 按照1.25增长
    
    	//source := []string{"aaa", "bbb", "ccc", "ddd", "eee"}
    	//slice := source[2:3:4] //长度为1,容量为2    //slice := source[2:3:4] 长度为1,容量为3   [i:j:k]
    	//好处:若 slice := source[2:3:3] append时会让slice有自己的底层数组,不会改变原有的切片
    
    	//s1 := []int{1, 2}
    	//s2 := []int{3, 4}
    	//fmt.Println(append(s1, s2...))  //将一个切片追加到另一个切片
    
    	//slice := []int{10, 20, 30, 40}
    	//遍历一
    	//for index, value := range slice {    //range创建每个元素的副本,不是直接返回对该元素的引用
    	//	fmt.Printf("Index: %d , Value: %d\n", index, value)
    	//}
    	//遍历二
    	//for _, value := range slice {    // _忽略索引值
    	//	fmt.Printf("Value: %d\n", value)
    	//}
    	//遍历三 传统方式
    	//for index := 0; index < len(slice); index++ {
    	//	fmt.Println(slice[index])
    	//}
    
    }
    
    

    映射

    package main
    
    import (
    	"fmt"
    )
    
    func main() {
    	fmt.Println("map coding........")
    
    	//dict := make(map[string]int,10) //第二个参数是长度,超出长度,自动扩容
    	dict := map[string]string{"a": "aaa", "b": "bbb"} //dict := map[string]string{}
    	//dict := map[string][]string{}
    	dict["c"] = "ccc"
    
    	//方式一
    	//value, exists := dict["a"]
    	//if exists {
    	//	fmt.Println(value)
    	//}
    	//方式二
    	//value := dict["b"]
    	//if value != "" {
    	//	fmt.Println(value)
    	//}
    
    	delete(dict, "b")
    	
    	//map在函数间传递映射时,不会制造副本,函数修改map,所有对这个映射的引用都会察觉到这个修改
    	
    	
    
    	fmt.Println(dict)
    }
    //对map排序,可以先把key遍历放入切片,之后对切片排序,最后根据切片中key,拿数据
    

    变量赋值
    在这里插入图片描述

    方法

    方法调用时,主要看接收者类型,编译时会自动优化

    go中类似继承的概念(嵌入和组合)

    (1) go通过 在结构体中嵌入匿名结构体 实现继承的概念,那就是嵌入
    // 创建结构体实例的时候,可以直接指定匿名结构体字段的值

    //自定义类型 DataResponse 中的属性是两个自定义类型
    type DataResponse struct {
    	*tchttp.BaseResponse    // 访问起中的属性或方法可以简写
    	Response *struct {
    		Data []*MData 
    		RequestId *string 
    	} 
    }   
    

    (2) 若通过 在结构体中嵌入有名结构体 实现继承的概念,那就是组合

    //访问组合的结构体或方法时,必须带上结构体名,不可以简写
    type DataResponse struct {
    	aa *tchttp.BaseResponse
    	Response *struct {
    		Data []*MData 
    		RequestId *string 
    	} 
    }   
    

    1、golang语言中的继承是通过内嵌或组合来实现的,所以可以说,在go语言中,相比较于继承,组合更受青睐。
    2、通过类型 实例名.int 来获取存储在匿名字段中的数据,于是可以得出一个结论:在一个结构体中对于每一种数据类型只能有一个匿名字段。
    3、匿名内嵌的结构体可以直接访问其成员变量 如,ins.a.b.c的访问可以简化为ins.c

    接口

    1、方法集

    1.1、如果使用 指针接收者 来实现一个接口,那么只有指向那个类型的指针才能实现对应的接口
    1.2、如果使用 值接收者 go来实现一个接口,那么那个类型的值和指针接收者都能够实现对应的接口

    2、嵌入类型

    内部类型实现接口,内部类型会被提升
    如果外部类型同样实现了该接口,内部类型不会被提升

    3、interface类型默认是一个指针(引用类型)

    4、所有的类型都实现了 interface{} 空接口

    5、类型断言

    //a是接口类型变量    //b类型实现了a接口
    b = a.(Point)  //表示判断a是否是指向Point类型的变量,如果是就转成Point类型并赋值给b变量,否则报错
    

    注意:
    如果类型不匹配,就会报panic,( 带上检查机制,不要报panic,如下 )

    b, ok = a.(Point)
    if ok == false {
    	fmt.println("convert fail")
    }
    ...
    

    命令行参数

    1、os.Args
    os包下 os.Args 是一个string的切片,用来存储命令行参数

    func main() {
    	fmt.Println("命令行参数有%v个",len(os.Args))
    	
    	// args[0]是程序名,后面是参数名
    	for i, v := range os.Args {
    		fmt.Printf("args[%v]=%v",i, v)
    	}
    }
    

    2、flag包 命令行参数解析

    ...
    var(
    	user string
    	port int
    )
    func init() {
    	flag.StringVar(&user, "u", "", "用户名,默认为空")
    	flag.IntVar(&port, "port", "3306", "端口号,默认为3306")
    	flag.Parse() // 必须
    }
    func main() {
    	...
    }
    ...
    

    序列化与反序列化

    对结构体序列化( tag标签 )

    type People struct {
    	Name string `json:"name"` //反射机制
    	Age  int    `json:"age,omitempty"` //序列化时忽略0值或空值
    }
    p := People{Name: "zs", Age: 18}
    data, _ := json.Marshal(p)
    fmt.Println(data)         //[123 34 110 97 109 101 34 58 34 122 115 34 44 34 97 103 101 34 58 49 56 125]
    fmt.Println(string(data)) //{"name":"zs","age":18}
    

    对结构体反序列化

    str := "{\"name\":\"zs\",\"age\":18}"
    var p1 People
    err := json.Unmarshal([]byte(str), &p1)
    if err != nil {
    	...
    }
    

    单元测试 testing

    cal.go

    package cal
    
    //被测试的函数
    func addUpper(n int)  int {
    	res := 0
    	for i := 1; i <= n - 1; i++ {
    		res += i
    	}
    	return res
    }
    
    //被测试的函数
    func getSub(n1 int, n2 int) int {
    	return n1 - n2
    }
    

    cal_test.go

    package cal
    import (
    	"fmt"
    	"testing" //引入go 的testing框架包
    )
    
    //编写要给测试用例,去测试addUpper是否正确
    func TestAddUpper(t *testing.T) {
    
    	//调用
    	res := addUpper(10)
    	if res != 55 {
    		//fmt.Printf("AddUpper(10) 执行错误,期望值=%v 实际值=%v\n", 55, res)
    		t.Fatalf("AddUpper(10) 执行错误,期望值=%v 实际值=%v\n", 55, res)
    	}
    
    	//如果正确,输出日志
    	t.Logf("AddUpper(10) 执行正确...")
    
    }
    
    //所有TestXxx函数都会被执行
    func TestHello(t *testing.T) {
    
    	fmt.Println("TestHello被调用..")
    
    }
    

    sub_test.go

    package cal
    import (
    	_ "fmt"
    	"testing"
    )
    
    func TestGetSub(t *testing.T) {
    
    	res := getSub(10, 3)
    	if res != 7 {
    		t.Fatalf("getSub(10, 3) 执行错误,期望值=%v 实际值=%v\n", 7, res)
    	}
    	//如果正确,输出日志
    	t.Logf("getSub(10, 3) 执行正确!!!!...")
    }
    
    
    
    1. 测试用例文件名必须以 _test.go 结尾
    2. 测试用例函数 TestXxx
    3. TestAddUpper(t *tesing.T) 的形参类型必须是 *testing.T
    4. 一个测试用例文件中,可以有多个测试用例函数
    5. 运行测试用例指令
      (1)go test [如果运行正确,无日志,错误时,会输出日志]
      (2)go test -v [运行正确或是错误,都输出日志]
    6. 当出现错误时,可以使用 t.Fatalf 来格式化输出错误信息,并退出程序
    7. t.Logf 方法可以输出相应的日志
    8. PASS 表示测试用例运行成功,FAIL 表示测试用例运行失败
    9. 测试单个文件,要带上被测试的原文件 go test -v cal_test.go cal.go
    10. 测试单个方法 go test -v -test.run TestAddUpper

    goroutine和channel

    1、Go协程的特点
    (1) 有独立的栈空间
    (2) 共享程序堆空间
    (3) 调度由程序员控制
    (4) 协程是轻量级的线程

    2、不同goroutine 之间如何通讯

    (1) 全局变量的互斥锁

    import "sync"
    
    var lock sync.Mutex
    
    lock.Lock()
    ...
    lock.Unlocl()
    //不利于多个协程对全局变量的读写操作
    

    (2) 使用管道 channel (本质队列) ( 多goroutine访问时,不需要加锁,channel本身线程安全)

    package main 
    
    import (
    	"fmt" 
    )
    func main() {
    	
    	//创建一个可以存放 3 个 int 类型的管道 var intChan chan int
    	intChan = make(chan int, 3)
    	fmt.Printf("intChan 的值=%v intChan 本身的地址=%p\n", intChan, &intChan)
    	
    	intChan<- 10
    	num := 20 
    	intChan<- num 
    	intChan<- 50 //给管写入数据时,不能超过其容量
    	
    	fmt.Printf("channel len= %v cap=%v \n", len(intChan), cap(intChan))// 3, 3
    	
    	//从管道中读取数据 
    	var num2 int 
    	num2 = <-intChan 
    	
    	fmt.Println("num2=", num2) 
    	fmt.Printf("channel len= %v cap=%v \n", len(intChan), cap(intChan))// 2, 3 
    		
    	num3 := <-intChan 
    	num4 := <-intChan 
    	//在没有使用协程的情况下,如果我们的管道数据已经全部取出,再取就会报告 deadlock
    	num5 := <-intChan 	
    	
    	fmt.Println("num3=", num3, "num4=", num4, "num5=", num5) 
    }
    

    注意:interface{} 的channel 可以存放任意类型变量,但是取出数据时注意类型断言

    管道关闭
    close(intChan) // intChan管道关闭后,不可以再写入数据,但是可以读数据
    
    管道遍历

    遍历管道,使用for range
    1、若遍历取出数据时,channel没有关闭,取完数据则会一直阻塞,出现deadlock的错误(若同时有协程写入数据,则不会死锁)
    2、若遍历时,channel已经关闭,则会正常遍历数据,遍历完后,退出遍历

    注意

    1、管道可以是只读或只写

    var intChan <-chan int
    intChan = make(chan int, 3)
    
    var intChan chan<- int
    intChan = make(chan int, 3)
    

    2、使用select可以解决从管道取数据的阻塞问题

    3、goroutine中使用recover,解决协程中出现panic,导致程序奔溃问题

  • 相关阅读:
    JavaScript【Text 节点、Node 节点属性(nodeName、nodeValue 、textContent、nextSibling、previousSibling)】(十二)
    Windows、Linux应急响应大致流程
    Spring源码之九finishRefresh详解
    Java刷题面试系列习题(四)
    人体关键点识别易语言代码
    Zabbix监控系统 第一部分:zabbix服务部署+自定义监控项+自动发现与自动注册(附详细部署实例)
    VUE实现Office文档在线编辑,支持doc/docx、xls/xlsx、ppt/pptx、pdf等
    关于编辑Windows的右键【新建】删除和添加
    Servlet请求转发与重定向
    .net core 和 WPF 开发升讯威在线客服系统:调用百度翻译接口实现实时自动翻译
  • 原文地址:https://blog.csdn.net/qq_53609683/article/details/126217950