Java转Golang编程笔记

前言:

    总的来说,Golang具备高级语言的很多特性,虽然语法层面和Java有些不同,但是编程逻辑是一致的

    简单来说,Golang也具备类型转换、字符串操作、数组、Map、文件及IO、反射、接口、包、线程同步、阻塞队列等常用功能。不同点在于:

1)Go保留了指针,而且结构体还是值类型而非引用类型,传参时要注意;Java虽然没有指针,但是引用类型就相当于指针,这样做对编程来说非常友好,而且也很安全,但代价是需要复杂的JVM来支持。还有一款追求极致性能和安全性的语言:Rust,它不需要复杂的VM,但是它需要复杂的语法呀(手动囧 ╯▽╰)。Golang属于原始的形态,它唯一的优势的不需要复杂的VM,和C语言算是有些共性。

2)Go的异常处理与Java的方式不一样,个人觉得有好有坏,try-catch是正统的、健壮的实现、全能的选手,但没有一定功力的人用起来会觉得别扭(实际上我也比较讨厌声明式异常,总是要让我try-catch会让程序很难看,但作为一个老司机,我很少用声明式异常,更多的是用运行时异常和各种异常拦截器),Go将error作为函数的结果返回,是个小心思,想绕过声明式异常,但是始终绕不过运行时异常,而且判断 if err==nil {...} else {...} 跟 try {...} catch(e) {..} 没有本质区别。

3)包管理,Golang依赖于文件夹(相当于Package=private、public),而Java支持文件和文件夹作为多级Package,甚至可以文件套文件(类中类),而不需要文件夹,更灵活、更好用。

    Anyway,虽然Golang的语法和生态库还不尽如人意,但是它链接了以前C语言那一块的市场和资源,既可以做系统底层的工具开发,还可以做Web类业务级应用。而且还能在一定程度上跨平台,还具备优异的性能和不需要VM打包后独立运行的能力,还具备C语言不具备的很多高级语言的特性,这已经算是一种巨大的突破!!

    我已经很多年没有碰C/C++了,但是C/C++发展这么多年,那是一块巨大的宝库,弃之可惜。既然Golang能够链接它们,也能搞一搞系统层面的东西,那就需要抓住。同时也看了Mozilla、微软、Linux社区大力支持的Rust语言,从语法层面可见,Rust所推崇的高效、可靠是名副其实,但是“兼顾开发效率”只是相对于C语言说的——其语法较为复杂,开发效率完全不如Python、Java,也不如Golang。作为编程老手,掌握Rust并不难(需要一段时间熟练),但种种原因,我还是选择Golang,Golang现在的社区要比Rust大10倍以上。


正文:

1、类型转换

1)一个未知类型(interface{})如何转换成map型数组?实际类型:[]map[interface {}]interface {}

分析:

方法1:用到interface assertion方法,语法为 obj.(type)强制类型转换,例如p, ok := t.(bool),p, ok := t.(int64),转换和判断可以一次完成:

if s, ok := command.(string); ok {
    args, err = parseCommand(s)
    if err != nil {
        return nil, err
    }
} else if a, ok := command.([]string); ok {
    args = a
}

首先转换成数组:[]interface{},再将数组元素转换成:map[interface {}]interface {}

config := viper.Get("programs")
arr := config.([]interface{})
for _, elem := range arr {
  fmt.Printf("元素的反射类型是%v\n", reflect.TypeOf(elem))
  val, ok := elem.(map[interface{}]interface{})
  if ok {
    fmt.Printf("转换后类型%T \n", val, val)
  }
}

方法2:如果interface{}包含多种类型,则用到switch type来判断:

switch reflect.TypeOf(origin).Kind() {
case reflect.Slice, reflect.Array:
    s := reflect.ValueOf(origin)
    for i := 0; i < s.Len(); i++ {
        fmt.Println(s.Index(i))
    }
case reflect.String:
    s := reflect.ValueOf(origin)
    fmt.Println(s.String(), "I am a string type variable.")
case reflect.Int:
    s := reflect.ValueOf(origin)
    t := s.Int()
    fmt.Println(t, " I am a int type variable.")
}

参见 https://blog.csdn.net/qq_18293213/article/details/103722973


2、格式化字符

示例如下:

package main
import (
    "fmt"
    "os"
) 
type point struct {
    x, y int
}
func main() {

    // Go提供了几种打印格式,用来格式化一般的Go值,例如
    // 下面的%v打印了一个point结构体的对象的值
    p := point{1, 2}
    fmt.Printf("%v\n", p)

    // 如果所格式化的值是一个结构体对象,那么`%+v`的格式化输出
    // 将包括结构体的成员名称和值
    fmt.Printf("%+v\n", p)

    // `%#v`格式化输出将输出一个值的Go语法表示方式。
    fmt.Printf("%#v\n", p)

    // 使用`%T`来输出一个值的数据类型
    fmt.Printf("%T\n", p)

    // 格式化布尔型变量
    fmt.Printf("%t\n", true)

    // 有很多的方式可以格式化整型,使用`%d`是一种
    // 标准的以10进制来输出整型的方式
    fmt.Printf("%d\n", 123)

    // 这种方式输出整型的二进制表示方式
    fmt.Printf("%b\n", 14)

    // 这里打印出该整型数值所对应的字符
    fmt.Printf("%c\n", 33)

    // 使用`%x`输出一个值的16进制表示方式
    fmt.Printf("%x\n", 456)

    // 浮点型数值也有几种格式化方法。最基本的一种是`%f`
    fmt.Printf("%f\n", 78.9)

    // `%e`和`%E`使用科学计数法来输出整型
    fmt.Printf("%e\n", 123400000.0)
    fmt.Printf("%E\n", 123400000.0)

    // 使用`%s`输出基本的字符串
    fmt.Printf("%s\n", "\"string\"")

    // 输出像Go源码中那样带双引号的字符串,需使用`%q`
    fmt.Printf("%q\n", "\"string\"")

    // `%x`以16进制输出字符串,每个字符串的字节用两个字符输出
    fmt.Printf("%x\n", "hex this")

    // 使用`%p`输出一个指针的值
    fmt.Printf("%p\n", &p)

    // 当输出数字的时候,经常需要去控制输出的宽度和精度。
    // 可以使用一个位于%后面的数字来控制输出的宽度,默认
    // 情况下输出是右对齐的,左边加上空格
    fmt.Printf("|%6d|%6d|\n", 12, 345)

    // 你也可以指定浮点数的输出宽度,同时你还可以指定浮点数
    // 的输出精度
    fmt.Printf("|%6.2f|%6.2f|\n", 1.2, 3.45)

    // To left-justify, use the `-` flag.
    fmt.Printf("|%-6.2f|%-6.2f|\n", 1.2, 3.45)

    // 你也可以指定输出字符串的宽度来保证它们输出对齐。默认
    // 情况下,输出是右对齐的
    fmt.Printf("|%6s|%6s|\n", "foo", "b")

    // 为了使用左对齐你可以在宽度之前加上`-`号
    fmt.Printf("|%-6s|%-6s|\n", "foo", "b")

    // `Printf`函数的输出是输出到命令行`os.Stdout`的,你
    // 可以用`Sprintf`来将格式化后的字符串赋值给一个变量
    s := fmt.Sprintf("a %s", "string")
    fmt.Println(s)

    // 你也可以使用`Fprintf`来将格式化后的值输出到`io.Writers`
    fmt.Fprintf(os.Stderr, "an %s\n", "error")
}


3、数组后面追加数组

config.Programs = append(config.Programs, cProgtmp.Programs...)


4、数值指针赋值

声明:retryTimes *int32

赋值 retryTimes: new(int32)


5、将对象2的值合并到对象1(常用于配置合并、数据合并)

方法如下:

func copy(c1, c2 interface{}) (*interface{}, error) {
  v1 := reflect.ValueOf(c1)              //初始化为c1保管的具体值的v1
  v2 := reflect.ValueOf(c2)              //初始化为c2保管的具体值的v2
  v1_elem := reflect.ValueOf(&c1).Elem() //返回 c1 指针保管的值

  for i := 0; i < v1.NumField(); i++ {
    field := v1.Field(i)  //返回结构体的第i个字段
    field2 := v2.Field(i) //返回结构体的第i个字段
    //field.Interface() 当前持有的值
    //reflect.Zero 根据类型获取对应的 零值
    //这个必须调用 Interface 方法 否则为 reflect.Value 构造体的对比 而不是两个值的对比
    //这个地方不要用等号去对比 因为golang 切片类型是不支持 对比的
    if reflect.DeepEqual(field.Interface(), reflect.Zero(field.Type()).Interface()) {
      //如果第一个构造体某个字段对应类型的默认值

      if !reflect.DeepEqual(field2.Interface(), reflect.Zero(field2.Type()).Interface()) {
        //如果第二个构造体 这个字段不为空

        if !v1_elem.Field(i).CanSet() { //如果不可以设置值 直接返回
          fmt.Println("not set value")
          return nil, errors.New("can not set value")
        }

        v1_elem.Field(i).Set(field2) //设置值
      }
    }
  }
  return &c1, nil
}


6、for循环时,如何改变数组对象的值

示例如下,必须要用数组下标引用才行:

for k, section := range cfg.Programs {

    section.Command = cmd
    section.ProcessName = procName
    section.ProcessNum = i
    section.NumprocsStart = i - 1

    cfg.Programs[k] = section
    
    cfg.Programs[k].Name = "New Name"
}


7、异常堆栈信息

func errors.Wrap(err error, message string) error

Wrap returns an error annotating err with a stack trace at the point Wrap is called, and the supplied message. If err is nil, Wrap returns nil.

示例:errors.Wrap(err, "failed to read config")


+ (点击以下标题显示正文内容)

时间转换

+ (点击以下标题显示正文内容)

字符串替换

+ (点击以下标题显示正文内容)

文件、目录操作

+ (点击以下标题显示正文内容)

数值转换

+ (点击以下标题显示正文内容)

线程同步、等待线程结束

+ (点击以下标题显示正文内容)

结构体数组排序


© 2009-2020 Zollty.com 版权所有。渝ICP备20008982号