28

Java程序员Go语言入门指南

 4 years ago
source link: https://studygolang.com/articles/24729
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
  • 类C的语法,这意味着Java、C#、JavaScript程序员能很快的上手
  • 有自己的垃圾回收机制
  • 跨平台、编译即可执行无需安装依赖环境
  • 支持反射

Go语言简介

Go 语言(或 Golang)起源于 2007 年,并在 2009 年正式对外发布。Go 是非常年轻的一门语言,它的主要目标是“兼具Python等动态语言的开发速度和 C/C++ 等编译型语言的性能与安全性”。

数据类型

数据类型 说明 bool 布尔 string 字符串 int uint8,uint16,uint32,uint64,int8,int16,int32,int64 float float32,float64 byte byte

参考: www.runoob.com/go/go-data-…

基本语法

HelloWorld

在线运行示例: play.golang.org/p/-4RylAqUV…

package main

import "fmt"

var name string

func init() {
  name = "world"
}

func main() {
  fmt.Println("hello " + name)
}
复制代码

我们来执行一下:

$ go run main.go # main.go 为刚刚创建的那个文件的名称
$ hello world
复制代码

变量

变量声明

在线运行示例: play.golang.org/p/zPqCkRZgr…

package main

import (
	"fmt"
)

func main() {
	var name string   // 声明
	name = "da'mao'mao" // 赋值
	fmt.Println(name)

	var age int = 18 // 声明并赋值
	fmt.Println(age)
}
复制代码

类型推断

在线运行示例: play.golang.org/p/0My8veBvt…

package main

import (
	"fmt"
)

func main() {
	name := "damaomao"
	fmt.Println(name)

	age := 18
	fmt.Println(age)
}
复制代码

函数

  • 函数可以有多个返回值
  • 隐式的指定函数是private还是public,函数首字母大写的为public、小写的为private
  • 没有类似Java中的 try cachethrow ,Go语言是通过将 error 作为返回值来处理异常。
  • 不支持重载

下面我们通过一个示例来了解一下,在线运行示例: play.golang.org/p/PYy3ueuPF…

package main

import (
	"errors"
	"fmt"
	"strconv"
)

func main() {
	log1()

	log2("hello world")

	ret1 := add1(1, 1)
	fmt.Println("add1 result:" + strconv.Itoa(ret1))

	ret2, err := Add2(0, 1)
	if err == nil {
		fmt.Println("Add2 result:" + strconv.Itoa(ret2))
	} else {
		fmt.Println("Add2 error", err)
	}
}

// 私有、无入参、无返回值
func log1() {
	fmt.Println("execute func log1")
}

// 私有、入参、无返回值
func log2(msg string) {
	fmt.Println("execute func log2:" + msg)
}

// 私有、两个入参、一个返回值
func add1(count1, count2 int) int {
	total := count1 + count2
	fmt.Println("execute func add3, result=" + strconv.Itoa(total))
	return total
}

// Public、两个入参、多个返回值
func Add2(count1, count2 int) (int, error) {
	if count1 < 1 || count2 < 1 {
		return 0, errors.New("数量不能小于1")
	}
	total := count1 + count2
	return total, nil
}
复制代码

该示例输出结果为:

execute func log1
execute func log2:hello world
execute func add3, result=2
add1 result:2
Add2 error 数量不能小于1
复制代码

但函数有多个返回值的时候,有时你只关注其中一个返回值,这种情况下你可以将其他的返回值赋值给空白符: _ ,如下:

_, err := Add2(1, 2)
if err != nil {
  fmt.Println(err)
}	
复制代码

空白符特殊在于实际上返回值并没有赋值,所以你可以随意将不同类型的值赋值给他,而不会由于类型不同而报错。

结构体

Go语言不是像Java那样的面向对象的语言,他没有对象和继承的概念。也没有 class 的概念。在Go语言中有个概念叫做结构体( struct ),结构体和Java中的 class 比较类似。下面我们定义一个结构体:

type User struct {
	Name   string
	Gender string
	Age    int
}
复制代码

上面我们定义了一个结构体 User ,并为该结构体分别设置了三个公有属性:Name/Gender/Age,下面我们来创建一个User对象。

user := User{
	Name:   "hahaha",
	Gender: "男",
	Age:    18, // 值得一提的是,最后的逗号是必须的,否则编译器会报错,这就是go的设计哲学之一,要求强一致性。
}
复制代码

结构体的属性可以在结构体内直接声明,那么如何为结构体声明函数(即Java中的方法)呢,我们来看下下面的示例:在线运行示例: play.golang.org/p/01_cTu0Rz…

package main

import "fmt"

type User struct {
	Name   string
	Gender string
	Age    int
}

// 定义User的成员方法
func (u *User) addAge() {
	u.Age = u.Age + 1
}

func main() {
	user := User{
		Name:   "哈", // 名称
		Gender: "男", // 性别
		Age:    18,  // 值得一提的是,最后的逗号是必须的,否则编译器会报错,这就是go的设计哲学之一,要求强一致性。
	}
	user.addAge()
	fmt.Println(user.Age)
}
复制代码

指针类型和值类型

Java中值类型和引用类型都是定死的,int、double、float、long、byte、short、char、boolean为值类型,其他的都是引用类型,而Go语言中却不是这样。

在Go语言中:

  • & 表示取地址,例如你有一个变量 a 那么 &a 就是变量 a 在内存中的地址,对于Golang指针也是有类型的,比如a是一个string那么&a是一个string的指针类型,在Go里面叫&string。
  • * 表示取值,接上面的例子,假设你定义 b := &a 如果你打印 b ,那么输出的是 &a 的内存地址,如果要取值,那么需要使用: *b

下面我们来看下例子,在线运行: play.golang.org/p/jxAKyVMjn…

package main

import (
	"fmt"
)

func main() {
	a := "123"
	b := &a

	fmt.Println(a)
	fmt.Println(b)
	fmt.Println(*b)
}

输出结果为:
123
0x40c128
123
复制代码

并发编程

Go语言的并发是基于 goroutine 的,goroutine 类似于线程,但并非线程。可以将 goroutine 理解为一种虚拟线程。Go语言运行时会参与调度 goroutine,并将 goroutine 合理地分配到每个 CPU 中,最大限度地使用CPU性能。

Go 程序从 main 包的 main() 函数开始,在程序启动时,Go 程序就会为 main() 函数创建一个默认的 goroutine。

2YNvMjI.jpg!web

下面我们来看一个例子(在线演示: play.golang.org/p/U9U-qjuY0…

package main

import (
	"fmt"
	"time"
)

func main() {
	// 创建一个goroutine
	go runing()
	// 创建一个匿名的goroutine
	go func() {
		fmt.Println("喜特:" + time.Now().String())
	}()

	// 这里sleep一下是因为main方法如果执行完了,main该程序创建的所有goroutine都会退出
	time.Sleep(5 * time.Second)
}

func runing() {
	fmt.Println("法克:" + time.Now().String())
	time.Sleep(3 * time.Second)
}

输出:
法克:2009-11-10 23:00:00 +0000 UTC m=+0.000000001
喜特:2009-11-10 23:00:00 +0000 UTC m=+0.000000001
复制代码

执行结果说明fuck函数中的sleep三秒并没有影响 喜特 的输出。

如果说 goroutine 是Go语言程序的并发体的话,那么 channel 就是它们之间的通信机制。一个 channel 是一个通信机制,它可以让一个 goroutine 通过它给另一个 goroutine 发送值信息。每个 channel 都有一个特殊的类型,也就是 channel 可发送数据的类型。一个可以发送 int 类型数据的 channel 一般写为 chan int。

下面我们利用goroutine+channel来实现一个生产消费者模型,示例代码如下:(在线执行: play.golang.org/p/lqUBugLdU…

package main

import (
	"fmt"
	"time"
)

func main() {
	// 创建一个通道
	channel := make(chan int64)

	// 异步去生产
	go producer(channel)

	// 数据消费
	consumer(channel)
}

// 生产者
func producer(channel chan<- int64) {
	for {
		// 将数据写入通道
		channel <- time.Now().Unix()
		// 睡1秒钟
		time.Sleep(time.Second)
	}
}

// 消费者
func consumer(channel <-chan int64) {
	for {
		timestamp := <-channel
		fmt.Println(timestamp)
	}
}

输出为如下:(每秒钟打印一次)
1257894000
1257894001
1257894002
1257894003
复制代码

Java程序员觉得不好用的地方

tag

参考

作者简介

大猫猫,互联网公司老码农、不折腾不舒服斯基,多年千万日活服务端研发和架构经验。

  • 码农俱乐部: mlog.club
  • 关注公众号查看更多干货
Jry6zm2.jpg!web

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK