编译运行
在安装了go环境的机器上,通过go run 源代码文件名可以直接运行,如
go run main.go
同时还可以通过go build命令生成二进制文件
go build main.go
.\main.exe
则在没有安装go环境的机器上可以直接运行二进制文件
go的基本数据类型
- 布尔型
bool 布尔型的值只可以是常量 true 或者 false,默认值为 false。 - 字符串类型
string 编码统一为 UTF-8 编码标识 Unicode 文本,默认值为空字符串。 - 整型(默认值为 0)
uint8: 无符号 8 位整型(0 ~ 255);
uint16:无符号 16 位整型(0 ~ 65535);
uint32:无符号 32 位整型(0 ~ 4294967295);
uint64:无符号 64 位整型(0 ~ 18446744073709551615);
int8:有符号 8 位整型(-128 ~ 127);
int16:有符号 16 位整型(-32768 ~ 32767);
int32:有符号 32 位整型(-2147483648 ~ 2147483647);
int64:有符号 64 位整型(-9223372036854775808 ~ 9223372036854775807) - 浮点型(默认值为 0)
float32:IEEE-754 32 位浮点数;
float64:IEEE-754 64 位浮点数;
complex64:32 位实数和虚数;
complex128:64 位实数和虚数; - 其他数值类型
byte:类似 uint8;
rune:类似 int32;
uint:32 或 64 位;
int:与 uint 一样大小;
uintptr:无符号整型,用于存放一个指针;
包管理
golang中语句结尾可以不加分号。
定义包名在程序代码第一行使用
package 包名
指定包名,使用
import (
alias "p1"
)
方式导入包,其中alias参数是包的别名,不指定别名时,可以省略括号,使用’.'代表以当前路径作为别名,所以使用包中成员时不需要加包前缀。当导入包的源文件包含init函数时,会在主函数运行前执行其init函数,然后再执行当前源程序的init函数,最终再执行当前程序的主函数。
自动补齐分号
golang在一些情况下,会在代码中每行末尾自动补齐分号:
1.当输入被断开为标记时,如果行末标记为:
- 一个标识符
- 一个整数、浮点数、虚数、字符或字符串文字
- 关键字break、continue、fallthrough或return中的一个
- 运算符和分隔符++、–、)、]或}中的一个
则分号将被自动插入到标记流中非空白行的末尾。
2.如果让复合语句占用单独一行,则在“)”或“"}"”后面的分号可以被省略
类型别名
golang支持使用type关键字为类型起别名,如
type duration int
则为int起了个别名duration,接下来duration就可以用来指代int
变量定义
golang具备编译时的类型推断机制,可以使用var关键字定义变量,变量类型会根据初始值自动推断类型,此种方式必须在定义时指定变量初始值。比如如下定义一个整型变量
var a = 5
此种定义方式还可以简写为
a := 5
如果不想指定初始值或者指定变量类型,可以通过
var variable typename
使用指定类型定义变量,或者同时初始化
var variable typename = value
比如
var a int
var b int = 9
使用fmt输出
package main
import (
"fmt"
)
func main() {
var a = 10 //自动推导为int
var b int = 5 //主动定义为int
c := 9 //相当于 var c = 9
fmt.Printf("Hello word %d ,%d ,%d", a, b, c)
}
golang还支持同时初始化多个变量,使用逗号分隔变量和初始值,形式如
var A,B = 100,200
则A的值为100,B的值为200
函数定义
golang中的函数定义如下
func name() (typename...){}
golang中的函数可以一次返回多个数据,这与他可以同时初始化多个变量值的特性相关,当返回数据就一个时,可以省略返回值的括号,如
func re2val() (int,string){
return 100,"aaa"
}
var num,str = re2val()
当函数没有返回值时,可以不写返回类型
所以完整的主类如下,fmt是输入输出包
package main
import "fmt"
func main() {
fmt.Printf("Hello word")
}
与其他语言不同的是,go中函数传参数组名,与数组赋值,执行的是数组复制而不是引用赋值
匿名变量
当我们需要使用一个复用一个表达式的结果,而不想为其开辟内存空间时,会面临类似c++的右值引用问题,golang中提供了匿名变量,通过下划线,表示定义一个匿名变量,形式如下
_, b := 100,200
此处代表,b接收200,匿名变量指向100的临时变量空间
字符串
golang提供类型string代表字符串类型,字符串中,ascii字符会占据一个字节,其他字符根据需要自动计算为2~4给字节。
字符串的字符可以直接使用索引获取,如
var s string = "abc"
println(s[0])//字符a
使用len内置函数获取字符串长度
len(s)
字符串可以直接使用+拼接,返回一个新字符串
s1,s2 := "a","bc"
s3 := s1+s2
struct
golang中支持使用struct定义复合类型,如下定义一个包含一个整型成员和一个字符串成员的复合类型,并起名为Stu
package main
import (
"fmt"
)
type Stu struct{
num int
name string
}
func main() {
var stu1 = Stu {
num : 1,
name : "yyt", //如果不加逗号,则右括号必须在最后一行成员末尾,不能换行
}
var stu2 Stu //使用自动初始化
var test = struct {
txt string
}{
txt: "aaa",
} //定义匿名结构体,并进行初始化
fmt.Printf("Hello word %d ,%d,%s", stu1.num, stu2.num, test.txt) // 1,0,aaa
}
指针类型
通过在类型前面加上 *代表是改类型的指针类型,比如
var a int = 1
var p * int = &a
与c++不同,golang中的结构体指针和普通结构体变量,都是通过.运算符获得成员,所以指针变量使用起来与普通变量差别看起来不是很大,只是因为其代表的是指针的变量,不一定是栈中变量。比如
type Stu struct{
name string
}
var stu1 Stu;
var stu2 * Stu = &stu1
println(stu1.name,stu2.name)
println是golang默认提供的输出函数,不需要导入包,通过逗号运算符自动拼接多个输入
结构体函数
golang支持为结构体定义未实现的函数成员,并且可以通过赋值提供实现,这也意味着,我们可能实例化一个结构体对象,而没有提供其方法成员的实现,这时如果我们调用该成员方法,则会抛出invalid memory address or nil pointer dereference的错误,比如
type Test struct{
test func()
}
func main(){
t := Test{}
t.test = func(){}
t.test()
}
golang支持在结构体外部为结构体提供函数成员,形式如下
func (variable typename) funName(){}
variable代表指定调用当前函数的结构体变量对象的别名,则函数中可以通过这个别名访问改变量,typeName代表对应的结构体类型,比如
type Stu struct{
name string
}
func (stu Stu) call() string {
return stu.name
}
func (stu *Stu) pCall() string {
return "*" + stu.name
}
如上定义,则 Stu类型和* Stu类型变量都具备了call和pCall方法,这是因为golang会完成自动的取地址运算和指针解引用运算,在使用Stu类型变量调用Stu指针类型的方法时,golang会自动将代码执行为(&variable).方法名;对应的Stu指针类型变量调用Stu类型方法时,会被执行为(*variable).方法名
函数变量
go语言真的没有oop,其方法就是函数,而非行为,只是通过调用结构体方法时,会传递该结构体对象自身,所以还可以通过函数变量,来接受结构体函数,比如
type Stu struct{
name string
}
func (stu Stu) call(age int) string {
return stu.name
}
var stu Stu
f := stu.call
f(1) //=> stu.call(1)
const与iota
go中存在const关键字,用于创建常变量,比如
const a = 5
iota表示一个计数器,在一个const表达式中,多个变量定义的iota会逐行递增,递增变量为1,默认iota初始值为0,如
const (
A = iota
B = iota
)
最终结果,A变量值为0,B变量值为1
在const表达式中,不指定变量初始值,会自动沿用上一变量的初始值表达式,所以上述代码可以简写为
const (
A = iota
B
)
再利用匿名变量,如
const (
A = iota
_
B
)
则此时B的值会是2
使用iota执行计算,如下
const (
A,B = iota+1 , iota+2
C = iota+4
D
)
则结果A的值为1,B的值为2,C的值为5,D的值为6(沿用iota+4)
接口
golang支持定义接口类型,作为限制类型规范,要求变量的值类型必须是实现对应方法的结构体类型或对应结构体指针类型,比如
type Reader interface {
read(b []byte) ()
}
type File struct{
}
func(file File) read(b []byte){}
var fileReader Reader = File{}
接口类型不自动转化指针类型与普通类型,也就是说,如果只为struct的指针类型实现了接口方法,则只能传递指针变量,虽然他的普通变量也可以调用该方法,但那是因为他会被执行取地址后再执行该方法,不算实现了该接口,也就是说,如
type Man struct{
name string
}
func (man * Man ) call() string {
return man .name
}
type Human interface{
call()
}
//var human Human = Man{} 无法编译,因为没有实现call方法
var human Human = &Man{}
嵌入类型与方法重写
golang支持在结构体中组合其他的结构体,其中如下为普通的成员组合,没有嵌入的情况
type Man struct{
}
func (man Man ) call() {
}
type Postman struct{
man Man
}
var postman Postman = Postman {
man : Man{},
}
postman .man.call()
而一种独特的形式是嵌入类型,嵌入类型使用类型名作为字段名,然后则该嵌入类型的所有成员及方法的作用域会扩展到外部类型(实际调用时自动解析为里面的嵌入类型),如
type Man struct{
}
func (man Man ) call() {
}
type Postman struct{
Man
}
var postman Postman = Postman {
Man: Man{},
}
postman .man.call()
postman .call() //自动解析为postman .Man.call()
因为嵌入类型使得可以通过外部类型访问内部类型的成员。所以内部类型实现的接口,相当于外部类型也间接实现了,此时可以通过外部类型变量传递给内部类型实现的接口类型变量,比如
type Caller interface {
call()
}
type Man struct {
}
func (man Man) call() {
println("call ...")
}
type Postman struct {
Man
}
var caller Caller = Postman {
Man: Man{},
}
caller .call() //call...
同时,此时因为外部类型和嵌入类型是不同struct,则如果为外部类型创建于嵌入类型相同的方法,则在通过外部类型调用时,优先匹配外部类型方法,形成类似继承对方法的重写,比如刚刚的例子,在PostMan中重写call方法
func (postman Postman) call() {
println("postman call ...")
}
最终输出就成为了重写方法的结果,类似的接口也存在嵌套效果,其表现行为与结构体一样,嵌套以后,外部接口则具备了内部接口的所有成员
type Reader interface {
read(b []byte) ()
}
type MyReader interface{
Reader
}
类型转化
通过 typeName(variable)进行安全类型转化,如
var a int = 5
var b float32 = float32(a)
通过variable.(typeName)进行非安全类型转化
使用该方法的对象必须是interface{}类型,而这是个空接口,意味着我们只需要通过一次变量赋值,即可得到(空接口类型可以存储任意值)
其第一个返回值为,将该对象转化为指定类型后的值,第二个返回值为是否满足该类型定义。
package main;
type A interface{
a()
}
type B struct{}
func main(){
var var1 interface{} = B{}
//不能使用val1 := B{},因为这样类型是B{}
val,ok := var1.(A)
println(ok) //false
println(val == nil) //true
var a interface{} = 5
var b int = a.(int)//ok
var d interface{} = 5
e,_ := d.(B)
e.a = 9 //ok
}
- 转化类型是基本类型,如果对象不满足类型定义,则抛出panic
- 转化类型是结构体类型,则即便该对象不是该结构体类型,也会使用默认构造方法构造转化对象
- 转化类型是接口类型,如果对象不满足类型定义,则返回nil
同时,因为golang的switch的case表达式可以是任意类型(包括类型名称),所以通过关键字type结合switch可以进行类型判断,如
switch t := areaIntf.(type) {
case *Square:
fmt.Printf("Type Square %T with value %v\n", t, t)
case *Circle:
fmt.Printf("Type Circle %T with value %v\n", t, t)
case nil:
fmt.Printf("nil value: nothing to check?\n")
default:
fmt.Printf("Unexpected type %T\n", t)
}
更多文章,请搜索公众号歪歪梯Club