Golang快速入门
Posted 刘小豆豆豆
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Golang快速入门相关的知识,希望对你有一定的参考价值。
一、Go语言基础:
1、定义包名 ,说明这个文件属于哪个包,main包表示是一个可独立执行的程序,每个Go程序都包含一个名为main的包。
package main
2、引用包,这点与Java等面向对象的语言类似,引用后可直接调用(不过不需要再次声明对象),如下说明该程序需要使用fmt
包中的某些函数或者是元素,那fmt
就是一个非常常用的包,实现了格式化IO的函数,比如fmt.Println
输出到console。
import "fmt"
3、函数声明, main函数是每个可执行的程序必须包含的,一般来说是启动后第一个执行的函数,如果有init
则例外,因为init()
函数会先于main
开始执行。
func main()
当标识符(常量、变量、类型、函数名、结构字段等等)以一个大写字母与开头,比如说Han1,那么这样形式的标识符的对象就可以被外部包的代码所使用(要注意客户端需要先导入其所在的包),有点类似于java中的public,反之如果是小写字母开头,那必然是对包外不可见,但是在整个包的内部是可见可用的,类似于protected。
如下是一个完整的HelloWorld 程序,注意Golang 的代码规范,{ 不要另起一行(强制要求)
package main
import "fmt"
func main() {
fmt.Println("hello world");
}
如果要运行的话,可以通过IDE的方式,或者通过命令:
-
go run filename.go
-
也可以生成二进制文件,每次通过
./filename
的方式执行。- go build filename.go
- ./filename
关于包
Golang的包与java的包有一定的区别:
- 文件名和包名没有直接关系,不一定要将文件名和包名定成同一个;
- 文件夹名和包名也是没直接关系,并非需要一致;
- 同一个文件夹下的文件只能有一个包名否则编译报错
二、Go语言的数据类型
-
布尔:var b bool=true
-
数字类型:整型int、浮点型,Go还支持复数。
-
字符串类型:一串固定长度的字符连接起来的字符序列。
-
派生类型:
-
Pointer
- 数组类型
- struct
- Channel类型
- 函数类型
- 切片类型
- interface类型
- Map类型
go 1.9版本对于数字类型,无需定义int以及float32、float64,系统会自动识别
var isActive bool //全局变量声明
var enabled,disabled=true,false //忽略类型的声明
func test(){
var available bool //一般声明
valid := false //简短声明
available = true //赋值操作
}
三、Go语言变量
Go变量的声明跟js是有点像的,需要用var,或者:= ,但是数据类型要放在变量名后。
变量声明:
-
声明一个变量并初始化:
var a="RUNOOB"
-
声明一个变量,但没有初始化,则默认是零值
几个零值为
nil
的类型:
var a *int
var a []int
var a map[string] int
var a chan int
var a func(string) int
var a error //此处err是个接口
根据值自动判断变量的类型:var var_name = value
省略var,采用:=的方式 v_name := value
,只能提出现在函数体中,相同的代码块中,不能对相同名称的变量使用初始化声明(会提示no new variables on left side of :=
),在定义变量之前使用变量varname,会得到编译错误undefined:varname
,如果声明了一个局部变量但是没有在相同的代码块中使用,也会得到编译错误(会提示a declared and not used
)。注意:这点与Java差别很大,Go不允许声明但不使用的局部变量。
- 下面这种方式常用于声明全局变量,全局变量允许声明但不使用。
var(
vname1 v_type
vname2 v_type
)
其他注意事项
- 简单交换两个变量的值,a,b=b,a,注意变量的类型必须是相同的!
- 空白标识符_可用于抛弃值,比如说_ , b=5,7中,5就是被抛弃的值。 _相当是一个只写变量,得不到它的值,因为Go中必须使用被声明的变量,但有时候不需要从函数中得到返回值。
- 并行赋值也用于当一个函数返回多个返回值的时候(和python有点类似),var,err=Func1(var1)。
附上一个关于空白标识符在函数返回值时的使用case:
package main
import "fmt"
func main(){
_,num,str=getVal()
fmt.Println(numb,strs)
}
func getVal()(int,int,string){
return 1,2,"hi"
}
关于值类型和引用类型
值类型的变量的值放在栈中,int、float、bool、stirng这些基本类型都是值类型,使用这些类型额变量直接指向在内存中的值。
使用 = 将一个变量的值赋给另一个变量的时候,实际上是在内存中将 i 的值进行了拷贝。一个引用类型的变量存储的是这个变量的值所在的内存地址,或者内存地址中第一个字所在的位置,内存地址称之为指针。如果是对引用类型用 = 进行赋值,那么只有引用地址被复制,如两个引用类型,r2 = r1,如果r1的值修改了,那么r2也会受影响。
关于字符串类型
a ="hello"
unsafe.Sizeof(a)
输出结果是16,字符串类型在go中是个结构,包含指向底层数组的指针和长度,二者各是8个字节,所以一共是16。
四、Go语言的常量
常量的定义格式:
const indetifier [type] = value
如:
const b string = "hello"
const b "hello"
const c_name1,c_name2 = val1,val2
常量枚举:
const(
Unknown = 0
Female =1
Male =2
)
iota: 一个可以被编译器修改的常量。
const (
a=iota
b
c
)
第一个iota等于0,每当iota在新的一行被使用的时候,值自动+1,所以b=2,c=3。
package main
import "fmt"
func main(){
const(
a = iota
b
c
d ="ha"
e
f = 100
g
h =iota
i
)
fmt.Println(a,b,c,d,e,f,g,h,i)
}
通过运行结果可以看出来:在定义常量组时,如果不提供初始值,则表示将使用上行的表达式。
运行结果:
0 1 2 ha ha 100 100 7 8
iota 只是在同一个 const 常量组内递增,每当有新的 const 关键字时,iota 计数会重新开始。
五、Go语言运算符
内置的运算符有:算数运算符、关系运算符、逻辑运算符、位运算符、赋值运算符、其他运算符。
说下其他运算符:&a
给出变量的实际地址,*a
表示指针变量。
var a int = 4
var ptr *int
ptr = &a
fmt.Println("%d",a)
fmt.Println("%d",*ptr)
打印出来的值都是4。
另外,Go的自增自减只能作为表达式来使用,不能用于赋值语句。
a = a++ //在定义常量组时,如果不提供初始值,则表示将使用上行的表达式。
六、Go语言条件、循环语句
Go没有三目运算符,不支持 ?:
形式的条件判断。
- if
- if…else
- if嵌套
- switch语句
- select语句
for循环:
for init; condition; post { }
for ; condition; {}
for{}
//for-each range
strings := []string{"google", "runoob"}
for i, s := range strings {
fmt.Println(i, s)
}
numbers := [6]int{1, 2, 3, 5}
for i,x:= range numbers {
fmt.Printf("第 %d 位 x 的值 = %d\\n", i,x)
}
- 循环控制语句:break、continue、goto
七、Go语言中的函数
一个方法就是包含了接受者的函数,接收者可以是命名类型或者结构体类型的一个值或者是一个指针,所有给定类型的方法属于该类型的方法集。
func (variable_name variable_data_type) function_name() [return_type]{
}
Case:
package main
import "fmt"
type Circle struct{
redius float64
}
func main(){
var c Circle
c.radius=10
fmt.Println(c.getArea())
c.changeRadius(20)
fmt.Println(c.radius)
change(&c, 30)
fmt.Println(c.radius)
}
func (c Circle)getArea() float64{
return 3.14*c.radius*c.radius
}
// 注意如果想要更改成功c的值,这里需要传指针
func (c *Circle) changeRadius(radius float64) {
c.radius = radius
}
// 引用类型要想改变值需要传指针
func change(c *Circle, radius float64) {
c.radius = radius
}
闭包:
package main
import "fmt"
func main() {
add_func := add(1,2)
fmt.Println(add_func(1,1))
fmt.Println(add_func(0,0))
fmt.Println(add_func(2,2))
}
// 闭包使用方法
func add(x1, x2 int) func(x3 int,x4 int)(int,int,int) {
i := 0
return func(x3 int,x4 int) (int,int,int){
i++
return i,x1+x2,x3+x4
}
}
八、Go语言数组
声明数组
var variable_name [SIZE] variable_type
//举例说明
var blance [10] float32
初始化数组
var balance=[5]float32{1000.0,2.0,3.4,7.0,50.0}
var balance=[...]float32{1000.2,5.6}
访问数组元素
var salary float32=balance[9]
以上实例读取了数组balance
的第10个元素的值。
多维数组
var threedo, [5][10][4] int
二维数组的定义方式:
var arrayName [x][y] variable_type
初始化二维数组
//Ex1
a=[3][4] int{
{0,1,2,3},
{4,5,6,7},
{8,9,10,11},
}
//Ex2
b=[2][1] int{
{1,2},{3,4}}
访问二维数组
value :=a[2][3]
var val int = a[2][3]
向函数传递数组
通过形参设定数组大小和形参未设定数组大小两种方式来声明函数。
void func1(param [10]int)
void func2(param []int)
与Java不同的是,Go的数组作为函数参数传递的是副本,函数内修改数组并不改变原来的数组。
package main
import "fmt"
func change(nums[3] int){
nums[0]=100
}
func main() {
var nums=[3]int{1,2,3}
change(nums) //nums并未被改变
fmt.Println(nums[0])
return
}
Go的数组是值,长度是其类型的一部分,值传递;Go对数组的处理采用切片的方式,切片包含对底层数组内容的引用,作为函数参数的时候,类似于指针传递。
定义了长度的数组只能传递给限制了相同数组长度的函数,没定义长度的只能传递给不限制数组长度的函数。
b:=[]int{1,2,3,4}
func boo(b []int){
b[0],b[len(b)-1]=b[len(b)-1],b[0]
}
boo(p)
fmt.Println(p) //[4 2 3 1]
p:=[3]int{5,6,7}
func poo(p [3]int){
p[0],p[len(p)-1]=p[len(p)-1],p[0]
}
poo(p)
fmt.Println(p) //[5,6,7]
十一、Go语言指针
一个指针变量指向了一个值的内存地址,在使用指针之前需要声明指针:
var var_name *var-type
例如:
var ip *int
var fp *float32
使用指针
package main
import "fmt"
func main(){
var a int = 20
var ip *int
ip=&a
fmt.Println("a的变量地址是:%x\\n",&a)
fmt.Println("ip变量存储的指针地址是:%x\\n",ip)
fmt.Println("ip变量的值是:%d\\n",*ip)
}
空指针
一个指针被定义后没有分配到任何变量的时候,值为nil,就是空指针,和其他语言的null、NULL是一样的。
十二、Go语言结构体
数组是存储同一类型的数据,但在结构体中可以为不同项定义不同的数据类型。结构体的格式如下:
type struct_variable_type struct{
member defination
member defination
... ...
member defination
}
一旦定义结构体类型,只能用于变量的声明,语法格式如下:
variable_name := structure_variable_type{val1,val2...valn}
variable_name := structure_variable_type{key:val1,key2:val2...,keyn:valn}
访问结构体成员
结构体.成员名。
结构体作为函数参数
package main
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 = "Java 教程"
Book2.author = "www.runoob.com"
Book2.subject = "Java 语言教程"
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)
}
output:
Book title : Go 语言
Book author : www.runoob.com
Book subject : Go 语言教程
Book book_id : 6495407
Book title : Java 教程
Book author : www.runoob.com
Book subject : Java 语言教程
Book book_id : 6495700
结构体指针
结构体是作为参数的值传递,如果想在函数中改变结构体的数据内容,需要传入指针。
package main
import "fmt"
type Books struct{
title string
author string
subject string
book_id int
}
func main(){
var Book1 Books
var 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)
}
struct 类似于 java 中的类,可以在 struct 中定义成员变量。
type Rect struct{ //定义矩形类
x,y float64 //类型只包含属性,并没有方法
width,height float64
}
func (r *Rect) Area() float64{ //为Rect类型绑定Area的方法,*Rect为指针引用可以修改传入参数的值
return r.width*r.height //方法归属于类型,不归属于具体的对象,声明该类型的对象即可调用该类型的方法
}
十三、Go语言切片
切片是对数组的抽象,Go数组的长度不可改变,在特定场景中就不太灵活,所以提供了内置类型切片,可以理解为是动态数组,长度不固定,可以追加元素,再追加的时候可能使切片的容量增大。
定义切片
var identifier []type
slice := make([]type,len)
//len是数组的长度并且也是切片的初始长度,cap是指定容量
var slice1 []type=make([]type,len,[capacity])
切片初始化
//直接初始化切片
s:=[]int {1,2,3}
//初始化切片,是数组arr的引用
s:=arr[:]
//将arr从下标startIndex到endIndex-1 下的元素创建为一个新的切片
s:=arr[startIndex:endIndex]
//类似的有
s := arr[:endIndex]
s := arr[startIndex:]
//通过切片s初始化切片s1
s1 := s[startIndex:endIndex]
len()和cap()函数
切片是可索引的,并且可以由 len() 方法获取长度。切片提供了计算容量的方法 cap() 可以测量切片最长可以达到多少。切片是可索引的,并且可以由 len() 方法获取长度。切片提供了计算容量的方法 cap() 可以测量切片最长可以达到多少。
切片截取
可以通过设置下限及上限来设置截取切片 [lower-bound:upper-bound]
append()和copy()函数如果想增加切片的容量,我们必须创建一个新的更大的切片并把原分片的内容都拷贝过来。
package main
import "fmt"
func main() {
var numbers []int
printSlice(numbers)
/* 允许追加空切片 */
numbers = append(numbers, 0)
printSlice(numbers)
/* 向切片添加一个元素 */
numbers = append(numbers, 1)
printSlice(numbers)
/* 同时添加多个元素 */
numbers = append(numbers, 2,3,4)
printSlice(numbers)
/* 创建切片 numbers1 是之前切片的两倍容量*/
numbers1 := make([]int, len(numbers), (cap<以上是关于Golang快速入门的主要内容,如果未能解决你的问题,请参考以下文章
golang 快速入门让Golang kafka驱动程序发布到“测试”主题,这些主题是从快速入门指南创建的http://kafka.apache.org/docum