这篇博文是我持续学习Golang时的个人总结
官网下载msi后缀的64位程序后,像Java一样把bin目录追加到环境变量PATH中即可
IDE使用JetBrains GoLand
Go语言现在叫Golang
Hello world:
main方法必须放在main包下,否则编译出错
package main
import "fmt"
var age = 1
var id int = 1
var num int
func main() {
fmt.Println("Hello, world; こんにちは世界")
println(age, id, num)
}
变量:
由数字、字母、下划线组成,首个字符不能是数字
var identifier type
三种定义方法:
var age = 1//根据值自行判定变量类型
var id int = 1
var g ,h int //分开的方式只能用在局部变量里
g, h = 1, 2
初始化声明(a := 20
):
这种方式只能使用在函数体中
我自己感觉可以养成习惯,全局变量用var定义(JavaScript是局部变量用var别搞混淆了),局部变量用初始化声明a := 20的方式!
注意在代码块中:
1.如果你声明了一个局部变量却没有在相同的代码块中使用它,会得到编译错误
2.不能对相同名称的变量再次使用初始化声明(a := 20),但是 a = 20 是可以的,因为这是给相同的变量赋予一个新的值。
当使用等号 = 将一个变量的值赋值给另一个变量时,如 j = i,实际上是在内存中将 i 的值进行了拷贝
更复杂的数据通常会需要使用多个字,这些数据一般使用引用类型保存。
这个内存地址称之为指针,这个指针实际上也被存在另外的某一个字中。
同一个引用类型的指针指向的多个字可以是在连续的内存地址中(内存布局是连续的),这也是计算效率最高的一种存储形式;也可以将这些字分散存放在内存中,每个字都指示了下一个字所在的内存地址。
常量:
const LENFTH int = 10
const WIDTH int = 5
var area int
const a, b, c = 1, 2, 3
area = LENFTH * WIDTH
还可用作枚举
const (
Unknown = 0
Female = 1
Male = 2
)
常量可以用len(), cap(), unsafe.Sizeof()函数计算表达式的值。常量表达式中,函数必须是内置函数,否则编译不通过
iota用法:
第一个 iota 等于 0,每当 iota 在新的一行被使用时,它的值都会自动加 1,所以 a=0, b=1, c=2 可以简写为:
const (
a = iota
b
c
)
——
func main() {
const (
a = iota //0
b //1
c //2
d = "ha" //独立值,iota += 1
e //"ha" iota += 1
f = 100 //iota +=1
g //100 iota +=1
h = iota //7,恢复计数
i //8
)
fmt.Println(a,b,c,d,e,f,g,h,i)
}
//结果为:0 1 2 ha ha 100 100 7 8,常量赋值会继承上面的表达式
* 指针变量
& 返回变量存储地址 &a 将给出变量的实际地址。
*a 是一个指针变量
func main() {
var a int = 6
var ptr *int
ptr = &a
fmt.Printf("a的值为 %d\n", a) //6
fmt.Printf("*ptr为 %d\n", *ptr) //6
}//后面会有详细的解释
注意go语言的for后面没有(),if后面有()
对于go来说是值传递可以直接有swap()方法
func max(num1, num2 int) int {
var result int
if num1 > num2 {
result = num1
} else {
result = num2
}
return result
}
func swap(x, y string) (string, string) {
return y, x
}
func main1() {
a := 10
b := 20
var ret int
ret = max(a, b)
fmt.Printf("最大的值是:%d", ret)
}
func main() {
a, b := swap("Bob", "Sam")
fmt.Println(a, b)
}
函数作为值:
函数定义后可作为值来使用:
func main(){
/* 声明函数变量 */
getSquareRoot := func(x float64) float64 {
return math.Sqrt(x)
}
/* 使用函数 */
fmt.Println(getSquareRoot(9))
}
函数闭包:
Go 语言支持匿名函数,可作为闭包。匿名函数是一个"内联"语句或表达式。匿名函数的优越性在于可以直接使用函数内的变量,不必申明。
func getSequence() func() int {
i:=0
return func() int {
i+=1
return i
}
}
func main(){
/* nextNumber 为一个函数,函数 i 为 0 */
nextNumber := getSequence()
/* 调用 nextNumber 函数,i 变量自增 1 并返回 */
fmt.Println(nextNumber())
fmt.Println(nextNumber())
fmt.Println(nextNumber())
/* 创建新的函数 nextNumber1,并查看结果 */
nextNumber1 := getSequence()
fmt.Println(nextNumber1())
fmt.Println(nextNumber1())
} // 1 2 3 1 2
对于for循环:
切记如果在for循环外用var a = 0定义了一个a ,for初始化用a := 0定义了a,for循环结束后在循环外面打印a的时候还是0,如果想让外面的a打印的结果就是a循环时的变化,则循环初始化用a = 0定义(因为a := 0这样的定义仅仅只是局部的)
数组:
定义方式:
//第一种
//var <数组名称> [<数组长度>]<数组元素>
var arr [2]int
arr[0]=1
arr[1]=2
//第二种
//var <数组名称> = [<数组长度>]<数组元素>{元素1,元素2,...}
var arr = [2]int{1,2}
//或者
arr := [2]int{1,2}
//第三种
//var <数组名称> [<数组长度>]<数组元素> = [...]<元素类型>{元素1,元素2,...}
var arr = [...]int{1,2}
//或者
arr := [...]int{1,2}
//第四种
//var <数组名称> [<数组长度>]<数组元素> = [...]<元素类型>{索引1:元素1,索引2:元素2,...}
var arr = [...]int{1:1,0:2}
//或者
arr := [...]int{1:1,0:2}
func main() {
var a = [5]int{1,2,3,4,5}
var b = a[0]
fmt.Println(b)
var c = [5][2]int{ {0,0}, {1,2}, {2,4}, {3,6},{4,8}}
d := c[1][1]
fmt.Print(d)
}
指针:
* 号用于指定变量是作为一个指针
在已经是指针类型前面加上 * 号(前缀)来获取指针所指向的内容
一个指针变量指向了一个值的内存地址这句话体现在定义和形参中
var ptr *int = &a
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 )
}
指针数组:
func main() {
//a := [3]int{10,100,1000}
//for i := 0; i < len(a); i++ {
// fmt.Println(a[i])
//}
const MAX int = 3
a := [3]int{1, 2, 3}
var ptr [MAX]*int
for i := 0; i < MAX; i++ {
ptr[i] = &a[i]
}
}
//a[0] = 1 a[1] = 2 a[2] = 3
指向指针的指针:
func main() {
var a int
var ptr *int
var pptr **int
a = 3000
/* 指针 ptr 地址 */
ptr = &a
/* 指向指针 ptr 地址 */
pptr = &ptr
/* 获取 pptr 的值 */
fmt.Printf("变量 a = %d\n", a )
fmt.Printf("指针变量 *ptr = %d\n", *ptr )
fmt.Printf("指向指针的指针变量 **pptr = %d\n", **pptr)
}
//变量 a = 3000
//指针变量 *ptr = 3000
//指向指针的指针变量 **pptr = 3000
指针作为函数参数:
func main() {
/* 定义局部变量 */
var a int = 100
var b int= 200
fmt.Printf("交换前 a 的值 : %d\n", a )
fmt.Printf("交换前 b 的值 : %d\n", b )
/* 调用函数用于交换值
* &a 指向 a 变量的地址
* &b 指向 b 变量的地址
*/
swap(&a, &b);
fmt.Printf("交换后 a 的值 : %d\n", a )
fmt.Printf("交换后 b 的值 : %d\n", b )
}
func swap(x *int, y *int){
*x, *y = *y, *x
}
func swap(x *int, y *int) {
var temp int
temp = *x /* 保存 x 地址的值 */
*x = *y /* 将 y 赋值给 x */
*y = temp /* 将 temp 赋值给 y */
}
结构体(类似于自定义对象):
import "fmt"
type Books struct {
title string
author string
subject string
book_id int
}
func main() {
var Book1 Books /* 声明 Book1 为 Books 类型 */
var Book2 Books /* 声明 Book2 为 Books 类型 */
/* book 1 描述 */
Book1.title = "Go 语言"
Book1.author = "www.runoob.com"
Book1.subject = "Go 语言教程"
Book1.book_id = 6495407
/* book 2 描述 */
Book2.title = "Python 教程"
Book2.author = "www.runoob.com"
Book2.subject = "Python 语言教程"
Book2.book_id = 6495700
/* 打印 Book1 信息 */
printBook(Book1)
/* 打印 Book2 信息 */
printBook(Book2)
}
//结构体作为函数参数
func printBook( book Books ) {
fmt.Printf( "Book title : %s\n", book.title);
fmt.Printf( "Book author : %s\n", book.author);
fmt.Printf( "Book subject : %s\n", book.subject);
fmt.Printf( "Book book_id : %d\n", book.book_id);
}
结构体指针看明白使用方式:
先func printBook( book *Books )
再printBook(&Book2)
//还是指针变量指向了值的内存地址的使用方式
Go 语言函数方法
Go 语言中同时有函数和方法。一个方法就是一个包含了接受者的函数,接受者可以是命名类型或者结构体类型的一个值或者是一个指针。所有给定类型的方法属于该类型的方法集(自己重点用下面的例子体会这句话)。语法格式如下:
func (variable_name variable_data_type) function_name() [return_type]{
/* 函数体*/
}
下面定义一个结构体类型和该类型的一个方法:
package main
import (
"fmt"
)
/* 定义结构体 */
type Circle struct {
radius float64
}
func main() {
var c1 Circle
c1.radius = 10.00
fmt.Println("圆的面积 = ", c1.getArea())
}
//该 method 属于 Circle 类型对象中的方法
func (c Circle) getArea() float64 {
//c.radius 即为 Circle 类型对象中的属性
return 3.14 * c.radius * c.radius
} //圆的面积 = 314
切片:
数组: a := [...]int{1,2,3} a := [3]int{1,2,3}
切片: a:= []int{1,2,3} a := make([]int, 5) a := make([]int, 5, 10)
//var a [...]int = [...]int{1, 2, 3} //error就算数组能自动推断长度也不能这么写
a := [...]int{1, 2, 3}
var a2 [3]int = [3]int{4, 5, 6}
//通过切片s初始化切片s1
s1 := s[startIndex:endIndex]
通过内置函数==make()==初始化切片s(该函数有三个参数,第一个参数是类型,第二个参数是分配的空间,第三个参数是预留分配空间),[]int 标识为其元素类型为int的切片,原来预留的空间需要重新切片才可以使用
func main(){
a := make([]int, 10, 20)
//就是说你预留了20的空间但也要重新切片才能使用
fmt.Printf("%d, %d\n", len(a), cap(a))
fmt.Println(a)
b := a[:cap(a)]
fmt.Println(b)
}
注意make()里第三个容量参数的计算:
基于原数组或者切片创建一个新的切片后,那么新的切片的大小和容量是多少呢?
这里有个公式,对于底层数组容量是 k 的切片 slice[i:j] 来说:
长度: j-i
容量: k-i
numbers := []int{0,1,2,3,4,5,6,7,8}
number3 := numbers[2:5]
printSlice(number3)
结果为:
len=3 cap=7 slice=[2 3 4]
capacity 为 7 是因为 number3 的 ptr 指向第三个元素,
后面还剩 2,3,4,5,6,7,8, 所以 cap=7。
range简单用法:
1.
func main(){
nums := []int{1,2,3,4};
length := 0;
for range nums {
length++;
}
fmt.Println( length);
}
func main(){
nums := []int{1,2,3,4}
//会返回索引和值
for i,num := range nums {
fmt.Printf("索引是%d,长度是%d\n",i, num)
}
}
//返回方法有哪些参数,不需要使用索引所以用_
func main() {
fmt.Println(len(os.Args))
for _, arg := range os.Args {
fmt.Println(arg)
}
}