爆肝一个月学习的go基础的 整理一下
前言
为什么我要学习go语言,原因大致也就是下面几个。|´・ω・)ノ
- 早在2年前就想学习,奈何自己挤不出时间,因为go语言是最萌的语言
- 个人看好go在未来的发展,执行效率和并发上面都是有天然的优势
- 想学习其他的编程语言,通过不同编程思想来更好的理解java
其实还是想学就学,没有什么过多纠结的。那我就开始来梳理一下最近学习的go语言和并且与java进行对比 梳理 【可能是java学习go的福音文章 】
简单介绍一下基础信息
go是一门强类型的编程语言(比java弱),在编写go的时候 也是存在oop的思想,但也会看到js的func影子,也会看到Python的类类似语法。go 代码规范要求高,连方法大小写,括号,缩进都规定了 变得是非常的简洁【<u>不会产生没有用到的代码</u>】。还有有天然的协程并发优势,多线程开发或许会爱不释手。
helloWorld
开局helloWorld不解释
import fmt //相当于java的System包
func main(){ //主入口 main函数
fmt.Print("helloWorld")
}
基本数据类型 = 结构体+数组+基本字段
基本字段解释和一些关键词
go的基本字段类型
//go
int
uint
float
bool
string
你没有看错,基本数据类型 就只有五种类型,没有像java那样字符集的字段分为 byte short long int,以int64表示为long
字段的声明方式
var param_name type
var param_name = value
var param_anme := value
如果你熟悉js看见这个var[关键词] 想必不陌生,go可以定义值的对象 或者进行类型推导定义。
var number int;//声明一个int 的number变量 普通声明
var number2 = 20 //推断声明一个number2 为20的变量
number3 := 30 //简单声明一个类型对象 就是把:代替了var
number = 10
var a,b,c int;
a=1
b=2
c=3
var a1,b2,c3 = 4,5,"eat";
//声明列表
var (
z = 2
x = "go"
)
const 【关键字】
简意:常量 在运行的时候不能修改 (相当于java 的final 在顶部修饰的不可变常量)
字母都要大写下划线分开
const var NORMAL_PARAM = 10 //声明方式不能用简单声明的方式。a:=10这种
iota 【关键字】
一个程序计数器,搭配const使用,相当于一个const里面的下标指针,当下一个const出现就会被重置
const(){
a = iota iota 的 值 = 0,
b = 1, iota 的 值 =1
}
关系运算符
》 > < >= <= != 和java一样
位运算关系符
增加一个位清空的概念
&^:位清空 a&^b
针对于b来说。如果b上值0则取a对应的值。b上位1则取0
条件语句
if
if 条件
逻辑
else
逻辑
//在go语法。if可以增加一个条件
if num1:=2 ; num1 >= 3 {
f.Print("这个 num1的 作用范围就是在 if语句里面")
}
注意点就是 if了可以增加一个 当前的 暂时条件 存在于if判断结束 相当于在java的for 里面 int i = 1
switch 【关键字】
switch 用起来来和java差不多 最大一点就是不用加break
跟if一样 可以额外增加方法内的条件 switch 条件;type{}
还有一点很重要 case支持多条件 case a,b,c
如果swtich不写 type 那就是默认bool的值,可以早case里面写条件判断了
falltrough 【关键字】
可以穿透到下一个case里面去 不需要判断就行执行了,大多数语言都是默认穿透的
num:= 2
switch num {
case 2:
f.Print("这是一个2")
case 3:
f.Print("这是一个3")
fallthrough //穿透
case 4://上面有穿透这个 4就不需要判断就可以进来了
f.print("这是一个4“)
default:
f.Print("着啥也不是")
}
swtich word := "A" ; word{
case "A", "E","i","O","u":
fmt.println("这是一个元音字母")
case "B","s"
fmt.println("这是一个其他字母")
}
for【关键字】
表达式:for 变量;条件;变量修改 {
}
//写法一
for i:=1;i<= 4;i++{
}
//写法二 支持这么写
i:=1
for i<=4 {
i++
}
// 写法三 相当于 while(true)
for {
//死循环
}
break和continue
和java一样
数组
数组是基本数据类型,在方法值传递的时候 拷贝了一份数据过去,不会因为在其他方法体中改变了数据 而改变主体方法的数据,切片才是对于java的数组
声明方式
- var name [num] type
- var name = [num] type {value,value}
- name:=[...] type{value,value}
cap(array) //获取数组的容器的大小
len(array) //获取数据存储变量长度
数组一种特殊的循环
这个在循环的时候 可以取到index 和java只能依靠下标或者遍历循环 更加;灵活的一点
for index,value := range array_name{
//index 下标
//value 值
}
//不想接受index值就写成for _,value ····
结构体 struct
【是值类型的传递,*struct才是有对象的概念】
表达式:type struc_name struct
type Person struct {
name string
age int
canEat bool
}
func strucuse() {
var person1 Person
person1.name = "小埋"
person1.age = 15
person1.canEat = true
f.Println(person1)
var person2 = Person{"jb",2,true}
f.Println(person2)
var person3 = Person{name:"我的", age:12,canEat: true}
f.Println(person3)
}
oop思想
结构的体的oop思想
在struct里面加入的了其他的结构体 相当于继承了,可以进行简写
type person strcut{
name string
age int
}
type man strcut{
name string
person
}
func main() {
man := man{}
man.age = 12
man.name = "22"
man.person.name = ”出现相同字段 要点出那个对象 在赋值“
}
复合数据类型 = 切片+map+指针
切片
切片大致就是java的list,是一个引用类型的数据
声明一个切片,跟声明一个数组差不多,但是不要在[]中加入局的数字,不加为切片 加为数组,go语言在这个设计上真的是很简约,以manke内置函数创建make(type,存储的大小,容器的大小),超过容器的长度会自动扩容,增加一个
- var name [] type
- make(type,储存的大小,容器的大小)
增加一个值的内容(扩容)
name := apppend([]type,value ...) //value可以是多个 也可以是一个切片
var slice [] int
var slice2 [] int
temp := append(slice, 1,2)
temp2 := append(slice, slice2...)
go 没有改变原始slice对象的内存引用地址,所有经过拷贝后增加,然后创建了一个新对象去接收。
map
map是一个键值对,这还是和java差不多的
声明方式:var map_name map[key_type]value_type
- var map1 map[int]string
- make(map[int]string)
- var map1 = map[int]string{1:"22"}
第一种创建没有复制这个map是一个nil(null)相当于null,make函数就是把这个对象初始化了但是没有赋值!!!
增加map的keyvalue
这个map必须是被实例化过的 就是make过的或者 初始化赋值过的
map[key] = value
删除map的value
delete(map_name,key)
获取值
value,ok=map[key]
ok 用来时判断是否获取到这个值,因为go 通过key获取值 如果没有该键也会返回该map默认的value类型的值
map循环
表达式 for key,value := range map_name{
}
指针
指针是一个储存一个对象的内存地址的复合类型
表达式:T (T代表任何类型 如果是想存指针那就是*pointer)
var a = 1
s := &a //就是指针
ss := &a
fmt.Print(*s)
fmt.Print(**ss)
其他一些基础关键知识
defer
延迟执行函数
可以让某一行 代码在这个方法体结束的时候结束,如果有多defer修饰
那么是以先进后出的方式执行
func main(){
var s = "a"
var num = 1
defer fmt.Print(s)
defer fmt.Print("这个比上面那个先执行")
defer Print(num)
num++
fmt.Print("这是结束的")
}
func Print(a int){
fmt.Print(a)//这里打印出来时1 不会因为num++变成2 ,可以 理解成法法已经装载好了,但是还没有执行Q!。如果是复合数据类型就会改变
}
defer像finnaly一样处理 关闭流程 或者处理错误之类的
回调函数
func main(){
aa := B( 1,2, func(){2,1})
}
func B(a, b int, method_name func(c,d int))int{
return a-b-c-d
}
闭包结构
大概:内层函数调用了外层函数的局部变量,导致外层函数的那个变量生命周期改变,变量内存的逃逸。
每一个方法被调用 就会有一个地址内存地址,。方法就是对象!!!
func main(){
res := method()
ftm.Print(res()) //1 res这个方法中i的内存地址指向的是method被调用的那个地址,
ftm.Print(res()) //2
ftm.Print(res()) //3
}
f unc method() func()int{
i := 0
return func () int{
i++
return i
}
}
error 是一个错误类型
在errors包下面可以去 新建一个错误类型做为函数判断返回
errors.New("这是一个错误的类型"),
或者说是fmt.Error("这是一个错误类型")
panic recover
panic 抛出异常 -------- java try
recover 恢复异常 ----- java catch
painc(a intterface{}) ..可以放入参数 让recover接收
painc的代码都不会去执行 先当于当前return了,之前的defer的代码也是要被执行的额
recover 恢复恐慌 可以接收painc传过来的参数
接口
go以面定义的一个接口的话,只要方法实现了该接口的所有方法,那么这个方法就是实现了这个接口,不需要去指派哪struct实现了哪个接口
非侵入式的
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方法,通过接口模拟实现了多肽,可以把struct 的值赋值给接口 ,然后在调用phone Pohone = new (Iphone),Phone等于父类
空接口
任何东西都可以可以多肽化,相当于java object对象
接口继承
在接口里面写其他接口,如果要实现这个接口,就要实现这个接口及其父接口的所有方法