我是搞JAVA的,所以下面主要说明go和java不一样的地方
语言结构
package main
import "fmt"
func main() {
/* 这是我的第一个简单的程序 */
fmt.Println("Hello, World!")
}
注意:
-
有且只有一个main的package、main方法;
-
每行的结束可以不用;
-
{ 不能单独出现在一行;
-
使用【import _ 包名】来引用该包,仅仅是为了调用该包中的 init() 函数,所以无法通过包名来调用包中的其他函数
变量
-
以下3种定义变量的方式是等价的:
var name string = "wang"
var name = "wang"
name := "wang"
-
声明多个变量:
var b, c int = 1, 2
b, c := 1, 2
-
常量
const b = true
注意:
-
全局变量定义后,可以不使用;但局部变量定义后必须使用,否则编译会报错
-
如果变量名首字母大写,则可以被外部包引入(类似于java的public),否则只能在本包内使用;
-
第一个字符必须是字母或下划线而不能是数字
循环控制
-
普通for循环
for i := 0; i < 10; i ++ {
fmt.Println(i)
}
i := 1
for i < 10 {
fmt.Println(i)
}
// 死循环
for {
fmt.Println("a")
}
-
range循环针对数组
strings := []string{"a", "b"}
for k, v := range strings {
fmt.Println(k, v)
}
strings := []string{"a", "b"}
// 只输出key
for k := range strings {
fmt.Println(k)
}
strings := []string{"a", "b"}
// 只输出value
for _, v := range strings {
fmt.Println(v)
}
函数
-
函数名首字母大写,代表其它包可以访问该函数;
-
支持多返回值
func swap(x, y string) (string, string) {
return y, x
}
-
在函数定义的最后加上(),代表立即调用该函数
package main
import("fmt")
func main(){
str :="Alice"
func(name string){
fmt.Println("Your name is", name)
}(str)
}
-
函数参数传递有两种方式:
-
值传递:默认的,即把值复制一份传递到函数,函数里对参数的修改不会影响原值;
func swap(x, y int) int {}
-
引用传递:即传递引用,函数里对参数的修改会影响原值
func swap(x *int, y *int) {}
init函数
1.init函数可以在所有程序执行开始前被调用,并且每个包下可以有多个init函数
2.init函数先于main函数自动执行
3.每个包中可以有多个init函数,每个包中的源文件中也可以有多个init函数
4.init函数没有输入参数、返回值,也未声明,所以无法引用
5.不同包的init函数按照包导入的依赖关系决定执行顺序
6.无论包被导入多少次,init函数只会被调用一次,也就是只执行一次
7.init函数在代码中不能被显示的调用,不能被引用(赋值给函数变量),否则会出现编译错误
8.导入包不要出现循环依赖,这样会导致程序编译失败
9.Go程序仅仅想要用一个package的init执行,我们可以这样使用:import _ “test_xxxx”,导入包的时候加上下划线就ok了
10.包级别的变量初始化、init函数执行,这两个操作都是在同一个goroutine中调用的,按顺序调用,一次一个包
11.init函数不应该依赖任何在main函数里创建的变量,因为init函数的执行是在main函数之前的
12.在init函数中也可以启动goroutine,也就是在初始化的同时启动新的goroutine,这并不会影响初始化顺序
13.复杂逻辑不建议使用init函数,会增加代码的复杂性,可读性也会下降
14.一个源文件下可以有多个init函数,代码比较长时可以考虑分多个init函数
15.编程时不要依赖init的顺序
指针
func main() {
var a int= 20 /* 声明实际变量 */
var ip *int /* 声明指针变量 */
ip = &a /* 指针变量的存储地址 */
fmt.Printf("a 变量的地址是: %x\n", &a )
/* 指针变量的存储地址 */
fmt.Printf("ip 变量储存的指针地址: %x\n", ip )
/* 使用指针访问值 */
fmt.Printf("*ip 变量的值: %d\n", *ip )
}
语言结构体(类似于Java类)
type Books struct {
title string
author string
subject string
book_id int
}
func main() {
// 创建一个新的结构体
fmt.Println(Books{"Go 语言", "www.runoob.com", "Go 语言教程", 6495407})
// 也可以使用 key => value 格式
fmt.Println(Books{title: "Go 语言", author: "www.runoob.com", subject: "Go 语言教程", book_id: 6495407})
// 忽略的字段为 0 或 空
fmt.Println(Books{title: "Go 语言", author: "www.runoob.com"})
// 访问结构体成员
var Books1 = Books{"Go 语言", "www.runoob.com", "Go 语言教程", 6495407}
fmt.Println(Books1.title)
}
切片(Slice)
Go的数组长度是固定的,切片其实就是可变长度的数组
定义切片:
var s1 []int
s2 := make([]int, 5) // 5是初始长度
s3 := []int{1, 2, 3} // 初始化
s4 := make([]int, 3, 3) // 3是长度,5是容量,容量代表最长可以达到多少
s5 := s3[1,2] // 通过s3初始化s5,1和2是起始位置和结束位置,前闭后开
Map
var map1 map[string]string
var map2 = make(map[string]string)
map3 := make(map[string]string{"k1":"v1", "k2":"v2"})
map1["key1"] = "value1"
delete(map3, "k1") // 从map3中删除key为k1的数据
接口
直接看示例:
package main
import (
"fmt"
)
type Phone interface {
call()
}
type NokiaPhone struct {
}
func (nokiaPhone NokiaPhone) call() {
fmt.Println("I am Nokia, I can call you!")
}
type IPhone struct {
}
func (iPhone IPhone) call() {
fmt.Println("I am iPhone, I can call you!")
}
func main() {
var phone Phone
phone = new(NokiaPhone)
phone.call()
phone = new(IPhone)
phone.call()
}
定义了接口:Phone,接口有个call方法
下面这段,代表了NokiaPhone实现了Phone接口,所以才可以var phone Phone = new(NokiaPhone),进而调用call()方法
func (nokiaPhone NokiaPhone) call() {
fmt.Println("I am Nokia, I can call you!")
}
并发
f是函数名,x y z是参数
go f(x, y, z)
通道
select
其它
go.mod
管理go的依赖包,go.mod是在1.11版本引入的,之前是用GOPATH进行依赖包管理的。当配置了GOPATH后,就不能go.mod文件。
执行下面的命令初始化go.mod文件:
go mod init
vendor
go项目可能会依赖外部的文件,在build时会从远端下载这些依赖文件,但有时候又不能连接远端服务器;这时候就需要vendor模式,执行:go mod vendor,代表把go.mod中定义的依赖,下载到vendor目录下,再build时,直接从本地vendor目录下寻找依赖
编译模式
window安装go后,编译模式默认是windows,即编译exe文件,这时的编译是需要gcc的。
可执行以下命令切换到linux编译模式(生成可执行的二进制文件):
set CGO_ENABLED=0
set GOOS=linux
通过 go env确认修改是否成功
监控(pprof)
在go里用profile代表应用的运行情况数据,比如cpu、内存等
Golang 提供了 runtime/pprof 和 net/http/pprof 两种方式来收集profile信息
-
runtime/pprof:适用于一些一次性应用,即应用启动时开启pprof,应用运行结束后生成pprof文件
-
net/http/pprof:如果你的应用是一直运行的,比如 web 应用,那么可以使用 net/http/pprof 库,它能够对 Http 服务进行分析。示例:
import _ "net/http/pprof"
go func() {
http.ListenAndServe(":6060", nil)
}()
flag
![](https://i-blog.csdnimg.cn/blog_migrate/a24d1556d690f03ed9ec8c582109a622.png)