官方文档
Golang简称为Go,官方网站https://golang.org已迷失,可以通过如下国内的网站上手入门
-
Go的中文api文档:https://studygolang.com/pkgdoc
-
Go的中文入门文档:http://docscn.studygolang.com/doc/
-
Go的中文社区网址:https://studygolang.com
学习的目的
Go语言目前还是是小而美的语言,夸张的说法:全中国大概60%的开发岗位与java有关,而Go?
作为一个程序员,掌握底层的知识:操作系统、算法、数据结构、计算机原理、网络原理
其实任何一门语言上手都是三天(Java基础),如果有C/C++语言基础,上手时间可以缩短到一天
一定一定不要放弃架构师思维,这个才是更宽、天花板更高的路
招Go语言的都是非常有名的公司,要意识到单纯学Go,只是拓宽个人的知识面
- 讲给Java开发者的Go入门
- 架构知识的加分项
- 并非给小白读者
Go VS Java
指针
-
Java中没有指针
-
C指针繁琐在指针运算
-
Go语言没有指针运算
关键字&
- Go语言这个
&
符号就是取地址 - C++ 引用(别名) 同时也是取地址
package main
import "fmt"
// 指针的使用
func main() {
var i = 8
fmt.Println(&i)
var p = &i
fmt.Println(p)
}
为什么Go语言中要设计指针?引用传递 vs 值传递
关键字*
Java直接传引用
public class ReferenceUseage {
public static void main(String[] args) {
P p = new P();
p.name = "zhang san";
modifyName(p);
System.out.println(p.name);
}
public static void modifyName(P p){
p.name = "si li";
}
}
class P {
String name;
}
以上Java代码最后输出si li
,因为Java在方法中的参数默认是传引用。
Go语言默认是传值
package main
import "fmt"
type P struct {
name string
}
func modifyName(p P){
p.name = "li si"
}
func main() {
var p = P{name: "zhang san"}
modifyName(p)
fmt.Println(p.name)
}
以上Go代码最后输出zhang san
,因为Go的参数传递方式为传值,传递到modifyName函数中,已经复制了整个参数的值,修改的是复制对象,不会影响到原对象。
Go的引用传递
package main
import "fmt"
type P struct {
name string
}
func modifyNameByReference(p *P){
p.name = "li si"
}
func main() {
var p = P{name: "zhang san"}
modifyNameByReference(&p)
fmt.Println(p.name)
}
以上代码声明modifyNameByReference时使用*
关键字,就表示使用引用方式传参,同时注意函数调用时使用&
关键字对参数进行取地址。
使用引用传参后,最后输出结果就为li si
解引用
*
除了表示指针类型,还可以用来表示解引用
package main
import "fmt"
func f(v *int){
*v = 8
}
func main()
m := 0
f(&m)
fmt.Println(m)
}
以上代码输出结果为8
,解引用可以理解为&
取地址的反运算
函数
函数是Go语言中的一等公民
Go语言吸收了很多函数式编程的思想,类似JavaScript
其他函数式语言:JavaScript/python/scala
函数式语言的典型特性:Lambda表达式
指向函数的指针
理解如下代码,通过定义函数,函数指针,可以将函数指针作为参数进行传递,完成功能的调用
package main
import "fmt"
func andBodySay(f func(word string)){
f("hello")
}
func main() {
var ff func(word string)
var f1 = func(word string){
fmt.Println(word, " f1")
}
ff = f1
fmt.Println(ff)
ff("haha")
f1("haha")
andBodySay(f1)
var f2 = func(word string){
fmt.Println(word, " f2")
}
andBodySay(f2)
}
以上代码输出
0x10a6be0
haha f1
haha f1
hello f1
hello f2
对比Java,可在Lambda表达式中找对对应的用法
public class LambdaUsage {
public static void main(String[] args) {
Runnable hello = () -> System.out.println("hello");
new Thread(hello).start();
}
}
Java之前是没有函数式编程的概念的,后续才吸收的Lambda表达式
不准确的理解:Go语言中的函数指针可以类比 Java中的函数签名
通过函数指针的变换就好像可以实现Java中的多态
并发
Go语言抢Java C C++ php的市场的关键点
Go routine(协程) v.s Java中的线程
Go语言最大的特点,非常适合开发基于网络的中间件
Go语言网络通信
Go语言的Tcp客户端代码
package main
import (
"fmt"
"io"
"log"
"net"
"os"
)
func main() {
// 多个返回值(元组/tuple),只取第一个conn
conn, _:= net.Dial("tcp", "localhost:8888")
fmt.Println(conn)
log.Println("connected")
// 类比Java中的finally,保证函数退出之前调用
defer conn.Close()
// 将conn中的数据读出打印到标准输出
io.Copy(os.Stdout, conn)
}
Go语言的Tcp Server端代码
package main
import (
"fmt"
"io"
"log"
"net"
"time"
)
func main() {
listener, err := net.Listen("tcp", "localhost:8888")
if err != nil {
log.Fatal(err)
}
for {
conn, err := listener.Accept()
if err != nil {
log.Fatal(err)
}
fmt.Println("a client connected:", conn.LocalAddr())
// 阻塞,只能处理一个客户端
handle(conn)
}
}
func handle(conn net.Conn) {
defer conn.Close()
for {
_, err := io.WriteString(conn, "hello")
if err != nil {
log.Fatal(err)
}
time.Sleep(1* time.Second)
}
}
目前这样的Server在handle(conn)会直接阻塞,而想要实现类似Java多线程的效果只需要将
handle(conn)
修改为
go handle(conn)
加上一个关键字go
就实现类比Java多线程的机制,那Go语言底层是如何实现的呢?
routing:协程
Gorouting的GPM模型
GPM -> Goroutine Processor Machine
Go语言维护了多个线程,每个线程维护一个协程队列,就好像Java中的线程池(Java Thread Pool/Fork Join Pool)
讨论:
- 为什么协程快?
- 不快,并不比线程池快(Netty > Go)
- 有些情况比线程快,起1万个协程比起1万个线程快
- 节省的是线程切换的支出
- 完成等同于线程池的任务?
- 线程池的task,不具备协程能力
- Golang中可以用channel来做协程协调
- 在用户空间模拟了CPU切换
- 协程怎么回收?
- 执行完回收
- Golang中的异步(没有异步)
- 因为到处都是异步
- 同步的写法完成异步的操作(流行的原因,写起来简单)
- Go语言GC性能不如Java
版权说明
本文章内容为马士兵教育《Golang 4 Javaer》课程的学习笔记