Go学习【01】:初步学习需要的知识
理解以下知识,初步写简单go项目就足够了
语言参考(基础篇)
基本语法
基本组成
- 包声明
- 引入包
- 函数
- 变量
- 语句 & 表达式
- 注释
- 其它(可忽略)
- go没有像php、js那样需要在文件开始申明
<?php ...
或者<script>
- 注释:多行
/* *
单行//
- 字符串链接符
+
- 标记:可以是关键字,标识符,常量,字符串,符号。
- 标识符:标识符用来命名变量、类型等程序实体 ;一个标识符实际上就是一个或是多个字母(AZ和az)数字(0~9)、下划线_组成的序列,但是第一个字符必须是字母或下划线而不能是数字。
- go没有像php、js那样需要在文件开始申明
数据类型
- 布尔值:
true
和false
- 数字类型
- 字符串类型
- 派生类型:依据上面是三个基础类型,衍生出的新类型
- 指针类型(Pointer)
- 数组类型(数组)
- 结构化体类型(结构体)
- Channel类型
- 函数类型
- 切片类型
- 接口类型
- Map类型
布尔类型
布尔类型就只有真假,true
和 false
数据类型
会根据系统架构(如32位、64位系统)变化的类型
- int
- uint
- uintptr
明确范围的数字类型
- 整型
- 无符号型
- uint8 范围(0 ~255) 2百
- uint16 范围(0 ~ 65535) 6万
- uint32 范围(0 ~ 4294967295) 42亿
- uint64 范围(0 ~ 18446744073709551615)184京
- 有符号型(范围最大值小减半)
- int8
- int16
- int32
- int64
- 无符号型
- 浮点型
- 其它数字类型
- byte =》 uint8
- rune =》 int32
- uint
- int
- uintptr 无符号型
变量
定义
var identifier type
var identifier1, identifier2 type
说明
变量生命关键字 var
变量名称:identifier
变量类型:type
常用的几种声明方式
第一种:标准定义
指定变量类型,如果没有初始化,则变量默认为零值。
//var identifier type
var a int
//a值为0
- 数值类型(包括complex64/128)为 0
- 布尔类型为 false
- 字符串为 ""(空字符串)
- 以下几种类型为 nil:
第二种:自适应
根据值自行判定变量类型。
//var identifier = value
var a = 100
//a值为100 类型 int
第三种: :=
不使用 var 声明,用 :=
声明变量
//value_name := value
a := 100
//等同于
// var a int
// a = 100
//注意:所以这里的a如果被声明过,这里再次var声明就会报错
派生类型声明定义方式不同,后面会讲到
多个变量声明
//法一
var a,b,c int
//-----环境分割线---------------------
//法二
var a,b,c = 100,200,300
//-----环境分割线---------------------
//法三
a,b,c := 100,200,300
//法四
//下面这种因式分解关键字的写法一般用于声明全局变量
var (
vname1 v_type1
vname2 v_type2
)
常量
定义
const identifier [type] = value
//如果不定义类型,会根据值自动确定类型
特殊常量 iota ,每次出现自增,但是每次CONST关键字出现时,将会被重置为0
表达式
变量使用
作用域
Go 语言中变量可以在三个地方声明:
- 函数内定义的变量称为局部变量
- 函数外定义的变量称为全局变量
- 函数定义中的变量称为形式参数
定义
字符串、数字、布尔
//字符串
var a = "abc"
//数字
var b = 1
//布尔
var c = true
总结:字面量的类型定义使用常规定义就行var identifier = value
,变种不过多阐述。
//注意: 字符串使用双引号;单引号一般使用单个字符,并且还不是字符串类型,而是数据类型(type untyped rune,值为ASCII 码对应值)。
数组
数组是具有相同唯一类型的一组已编号且长度固定的数据项序列,这种类型可以是任意的原始类型例如整型、字符串或者自定义类型
//声明: var variable_name [SIZE] variable_type
var calss_member [60] string
//赋值: variable_name[0] = "Tom"
//初始化
var class_member_1 =[5]string{"Jim","Tom","Neo","Lucy","Mei"}
class_member_2 :=[5]string{"Jim","Tom","Neo","Lucy","Mei"}
//不确定长度
class_member_3 =[...]string{"Jim","Tom","Neo","Lucy","Mei"}
总结:
- {}中元素的个数,不能大于[]中指定的长度
- 不知道长度时,可以使用
...
代替 - 访问数据使用下标索引,从0开始
//多维数组
var variable_name [SIZE1][SIZE2]...[SIZEN] variable_type
//初始化 例子
a := [3][4]int{
{0, 1, 2, 3} , /* 第一行索引为 0 */
{4, 5, 6, 7} , /* 第二行索引为 1 */
{8, 9, 10, 11}, /* 第三行索引为 2 */
}
//那么[2][2][2][2][2]是什么样子呢?
a := [2][2][2][2][2] int {
{
{
{
{1,2},
{3,4},
},
{
{5,6},
{7,8},
},
},
{
{
{9,10},
{11,12},
},
{
{13,14},
{15,16},
},
},
},
{
{
{
{17,18},
{19,20},
},
{
{21,22},
{23,24},
},
},
{
{
{25,26},
{27,28},
},
{
{29,30},
{31,32},
},
},
},
}
指针
一个指针变量指向了一个值的内存地址。
所以指针变量储存的是地址,同时操作的也是地址(上的值),因为地址无法修改。当一个指针被定义后没有分配到任何变量时,它的值为 nil。
//举例:jack的钱包里有100元
var jack int
var point_jack *int
jack = 100
point_jack = &jack //拿到钱包
*point_jack = 50 //操作钱包中的钱,放50元
fmt.Println(jack) //jack只有五十了
// 50
使用
//定义
// var var_name *var-type
var point_jack *int
// & 取地址
var num = 100
point_jack = &num
// * 使用
fmt.Println(*point_jack)
// 操作(不能掉了符号*)
*point_jack = 50
结构体
结构体是由一系列具有相同类型或不同类型的数据构成的数据集合。
/* 定义 */
type struct_variable_type struct {
member definition
member definition
...
member definition
}
// 例子,如一个学生有ID、名字、学号、自我介绍
type student struct {
id int
name string
code string
dis string
}
/* 变量声明 */
variable_name := structure_variable_type {value1, value2...valuen}
或
variable_name := structure_variable_type { key1: value1, key2: value2..., keyn: valuen}
//例子
Jack := student{1,"Jack","10001","一个叫jack的孩子"}
Neo := student{id:2,naem:"Neo",code:"10002",dis:"一个叫Neo的孩子"}
/* 使用 */
variable_name.key
//例子
Jack.dis
结构体在函数中的使用
func main() {
Jack := student{1,"Jack","10001","一个叫jack的孩子"}
fmt.Println(Jack.dis)
var jk *student
var num *int
code := 1231231;
num = &code
jk = &Jack
show(jk,num)
}
func show(stu *student,id *int) {
fmt.Println(stu.dis)
fmt.Println(*id)
}
/* 函数运行结果 */
//一个叫jack的孩子
//一个叫jack的孩子
//1231231
总结:
- 结构体是一个指定的数据结构,访问时使用符号
.
访问 - 指针结构体访问和普通结构体访问没区别,但是基本类型指针访问值需要加前缀符号
*
切片(slice)
Go 语言切片是对数组的抽象。数组的长度是固定的,切片的长度是不固定的。一个切片在未初始化之前默认为 nil,长度为 0
/* 定义 */
var identifier []type
// 其它定义方式
var slice1 []type = make([]type, len)
slice1 := make([]type, len)
make([]T, length, capacity)
// 初始化(变量声明)
variable_name := [] variable_type {value1, value2...valuen}
variable_name := 数组[start_index:end_index]
//例子
name := []string{"Tom","Jim","Neo"}
arr := [5]int{1,2,3,4,5}
slice_arr :=arr[2:4]
/* 使用 */
variable_name[index]
//例子
fmt.Println(name);
fmt.Println(slice_arr);
fmt.Println(slice_arr[0]);
-------
[Tom Jim Neo]
[3 4]
3
从上面可以看到切片常用的操作
/*
截取 [start_index:end_index] 明显索引范围是半包围,即 start_index 到 end_index -1
// 常见的截取如下:
[start_index:end_index]
[start_index:]
[:end_index]
// len() 和 cap() 函数
var numbers = make([]int,3,5)
len(numbers) => 3
cap(numbers) => 5
fmt.Println(numbers) => [0,0,0]
//空(nil)切片 //nil 空值,未赋值前变量的值
var numbers []int
if(numbers == nil){
fmt.Printf("切片是空的")
}
*/
//append() 和 copy() 函数
slice_int1 := [] int //空切片, []
slice_int1 = append(slice_int1,1) //[1]
slice_int2 = []int{10,11,12}
copy(slice_int1,slice_int2) // slice_int1 => [1,10,11,12]
范围(rang)
Go 语言中 range 关键字用于 for 循环中迭代数组(array)、切片(slice)、通道(channel)或集合(map)的元素。在数组和切片中它返回元素的索引和索引对应的值,在集合中返回 key-value 对。
//有点类似 PHP语言的 as ,使用方法如下
nums := []int{2, 3, 4}
sum := 0
for _, num := range nums {
fmt.Println(num)
}
集合(map)
Map 是一种无序的键值对的集合。
/* 定义 */
// 声明变量;默认 map 是 nil 和切片,指针一样
var variable map[key_type]value_type
// make 函数
variable := make(map[key_type]value_type)
/* 初始化(变量声明) */
var name_CN map[string]string
name_CN['Jim'] = "张三"
name_CN['Lilei'] = "李雷"
name_CN['Hanmeimei'] = "韩梅梅"
num_EN := map[string]string{"one":1,"two":2}
/* 删除元素: delete() 函数 */
delete(map,index)
delete(name_CN,"Jim")
类型转换
类型转换用于将一种数据类型的变量转换为另外一种类型的变量。
type_name(expression)
/* 例如 */
var money = 199.99
var pay int
pay = int(money)
fmt.Println(pay)
//199
运算符
算术运算符:(假定有变量 a 和 b ,a=100, b=200)
+
a+b
-
a-b*
a*b/
a/b%
a%b++
a++--
a--
关系运算符
==
!=
>
<
>=
<=
逻辑运算符
&&
||
!
位运算符
&
|
^
<<
>>
赋值运算符
=
前缀=
a+=b ;a*=b 等
其他运算符
&
取地址*
地址指向值
流程控制
if
if 布尔表达式 {
/* 在布尔表达式为 true 时执行 */
}
// 例如,a=100
if a > 50 {
fmt.Printf("> 50")
}
if...else
if 布尔表达式 {
/* 在布尔表达式为 true 时执行 */
} else {
/* 在布尔表达式为 false 时执行 */
}
// 例如,a=100
if a > 50 {
fmt.Printf("> 50")
}else{
fmt.Printf("< 50")
}
if 嵌套
if 布尔表达式 1 {
/* 在布尔表达式 1 为 true 时执行 */
if 布尔表达式 2 {
/* 在布尔表达式 2 为 true 时执行 */
}
}
// 例如,a=100
if a > 50 {
fmt.Printf("> 50")
if a > 80{
fmt.Printf("> 80")
}
}
switch
switch var1 {
case val1:
...
case val2:
...
default:
...
}
// 例如,成绩有 S,A,B,C,D zhangsan ='B'
switch zhangsan {
case 'S':
fmt.Printf("优")
case 'A':
fmt.Printf("良")
case 'B':
fmt.Printf("一般")
case 'C','D':
fmt.Printf("不及格")
default:
fmt.Printf("没有成绩")
}
//fallthrough 强制执行,继续执行后面的选项,并且下一个还是强制执行
package main
import "fmt"
func main() {
var a = 'B'
switch a {
case 'S':
fmt.Println("S")
fallthrough
case 'A':
fmt.Println("A")
case 'B':
fmt.Println("B")
fallthrough
case 'C':
fmt.Println("C")
case 'D':
fmt.Println("D")
fallthrough
default:
fmt.Println("other")
}
}
//
B
C
select
后期体会
for循环
//for
for init; condition; post { }
for condition { }
for { }
//搭配使用 break continue goto
//例子:
package main
import "fmt"
func main() {
var i int
for i = 0; i<10;i++ {
fmt.Println(i)
}
for ; i<20;{
i++
fmt.Println(i)
}
for i<30 {
i++
if(i%2 == 0){
continue
}
fmt.Println(i)
}
for {
i++
fmt.Println(i)
if i>40 {
break
}
}
goto LOOP
fmt.Println("你可能出不来")
LOOP:fmt.Println("果然出不来")
}
函数
函数是基本的代码功能块,用来实现一个基本功能。
/* 定义 */
func function_name(param type [,param type,param type])[return_type]{
}
/* 例子 */
func double(x int)int {
return x*2
}
func changePosition(x,y int)(int,int) {
return y,x
}
接口
Go 语言提供了另外一种数据类型即接口,它把所有的具有共性的方法
定义在一起,任何其他类型
只要实现
了这些方法
就是实现了这个接口
。
/* 定义 */
type interface_name interface {
method_name1 [return_type]
method_name2 [return_type]
method_name3 [return_type]
}
type struct_name struct {
}
/* 实现 */
func (struct_varibale_name struct_name) method_name()[return_type]{
}
/* 示例 */
异常处理
内置的go错误处理接口
type error interface {
Error() string
}
举例
package main
import "fmt"
import "errors"
func Sqrt(f float64) (float64, error) {
if f < 0 {
return 0, errors.New("math: error")
}
return f*f , errors.New("math: ok")
}
func main(){
result, err:= Sqrt(22)
if err != nil {
fmt.Println(err)
}
fmt.Println(result)
}
//math: ok
//484
并发
Go 语言支持并发,我们只需要通过 go 关键字来开启 goroutine 即可。
goroutine
goroutine 是轻量级线程,goroutine 的调度是由 Golang 运行时进行管理的。
goroutine 语法格式:
/* 定义 */
go 函数名( 参数列表 )
/* 例子 */
package main
import "fmt"
import "time"
func message(message string){
for i := 0; i < 5; i++ {
time.Sleep(1 * time.Second)
fmt.Println(message)
}
}
func main() {
message("000")
go message("111")
message("222")
}
// 输出
000
000
000
000
000
111
222
222
111
111
222
111
222
222
111
//结论:1.主流程不受影响,还是顺序执行;2.新启动的流程和主流程同时在执行
channel
通道(channel)是用来传递数据的一个数据结构。
通道可用于两个 goroutine 之间通过传递一个指定类型的值来同步运行和通讯。
操作符 <-
用于指定通道的方向,发送或接收。如果未指定方向,则为双向通道。
package main
import "fmt"
import "time"
func tmpCache(a int,b int ,cache chan int){
num := 0
fmt.Println("node 2:")
fmt.Println(time.Now())
time.Sleep(3 * time.Second)
num = (a+b)*(a-b)/2
cache <- num
}
func main() {
message :=make(chan int)
count := 0
fmt.Println("node 1:")
fmt.Println(time.Now())
go tmpCache(1,100,message)
fmt.Println("node 3:")
fmt.Println(time.Now())
// time.Sleep(1 * time.Second)
count = <- message
fmt.Println("node 4:")
fmt.Println(time.Now())
fmt.Println(count)
}
//结果
node 1:
2021-09-15 16:48:35.746247 +0800 CST m=+0.000101265
node 3:
2021-09-15 16:48:35.746418 +0800 CST m=+0.000271887
node 2:
2021-09-15 16:48:35.746425 +0800 CST m=+0.000278833
node 4:
2021-09-15 16:48:38.748364 +0800 CST m=+3.002355026
-4999
// 注意点:node 2 到 node 4 有3秒;说明阻塞了3秒
package main
import "fmt"
func tmpCache(a int,b int ,cache chan int){
num := 0
// time.Sleep(3 * time.Second)
num = (a+b)*(a-b)/2
cache <- num
close(cache)
}
func main() {
message :=make(chan int)
go tmpCache(0,5,message)
go tmpCache(5,10,message)
go tmpCache(10,15,message)
go tmpCache(15,20,message)
go tmpCache(20,25,message)
//close(message)
fmt.Println("===================")
for i := range message{
fmt.Println(i)
}
}
//结果1
===================
-12
-62
-37
-87
-112
//结果2
===================
-12
panic: send on closed channel
goroutine 22 [running]:
main.tmpCache(0x0, 0x0, 0x0)
/Users/liupeng/go/test/src/heelo/func_channel1.go:8 +0x45
created by main.main
/Users/liupeng/go/test/src/heelo/func_channel1.go:19 +0x1cf
exit status 2
//结果3
===================
-12
-37
-62
总结:
- 通道消息是按队列进出的
- rang使用到通道时,会有异常关闭情况,如
结果3
明显是没有跑完,进程就关闭了;所以开启的进程数是1时可以(在异步函数中)关闭进程
Go学习【01】:初步学习需要的知识的更多相关文章
- json2.js的初步学习与了解
json2.js的初步学习与了解,想要学习json的朋友可以参考下. json2.js的初步学习与了解 1.)该js的下载地址是:http://www.json.org/json2.js 2.)在页面 ...
- JVM学习01:内存结构
JVM学习01:内存结构 写在前面:本系列分享主要参考资料是 周志明老师的<深入理解Java虚拟机>第二版. 内存结构知识要点Xmind梳理 案例分析 分析1 package com.h ...
- Git的初步学习
前言 感谢! 承蒙关照~ Git的初步学习 为什么要用Git和Github呢?它们的出现是为了用于提交项目和存储项目的,是一种很方便的项目管理软件和网址地址. 接下来看看,一家公司的基本流程图: 集中 ...
- 语法分析器初步学习——LISP语法分析
语法分析器初步学习——LISP语法分析 本文参考自vczh的<如何手写语法分析器>. LISP的表达式是按照前缀的形式写的,比如(1+2)*(3+4)在LISP中会写成(*(+ 1 2)( ...
- VirtualBox上LInux命令初步学习
大二的寒假已经接近了尾声,寒假期间我初步使用了VirtualBox虚拟机软件,并安装了ubuntu的操作系统进行了Linux语言的学习.然而寒假期间的学习没有太多的计划,纯粹是为了完成作业而应付性的学 ...
- 20145219 《Java程序设计》第01周学习总结
20145219 <Java程序设计>第01周学习总结 教材学习内容总结 软件分类:系统软件(DOS.Windows.Linux等).应用软件(扫雷.QQ等) 人机交互方式:图形化界面.命 ...
- 以太坊web3开发初步学习
以太坊web3开发初步学习 此文是对https://learnblockchain.cn/2018/04/15/web3-html/的学习再理解. 以太坊智能合约通过使用web3.js前端和智能合约交 ...
- Python学习--01入门
Python学习--01入门 Python是一种解释型.面向对象.动态数据类型的高级程序设计语言.和PHP一样,它是后端开发语言. 如果有C语言.PHP语言.JAVA语言等其中一种语言的基础,学习Py ...
- 学习 shell脚本之前的基础知识
转载自:http://www.92csz.com/study/linux/12.htm 学习 shell脚本之前的基础知识 日常的linux系统管理工作中必不可少的就是shell脚本,如果不会写sh ...
- Java虚拟机JVM学习01 流程概述
Java虚拟机JVM学习01 流程概述 Java虚拟机与程序的生命周期 一个运行时的Java虚拟机(JVM)负责运行一个Java程序. 当启动一个Java程序时,一个虚拟机实例诞生:当程序关闭退出,这 ...
随机推荐
- 012 基于FPGA的网口通信实例设计【转载】
一.网口通信设计分类 通过上面其他章节的介绍,网口千兆通信,可以使用TCP或者UDP协议,可以外挂PHY片或者不挂PHY片,总结下来就有下面几种方式完成通信: 图8‑17基于FPGA的网口通信实例设计 ...
- Git在IDEA中的日常使用
1.Git介绍Git对于做开发的小伙伴并不陌生,Git是现在比较流行的版本控制工具.Git的仓库分为本地仓库和远程仓库,当代码开发完成后,先提交(commit)到本地仓库,再推送(push)到远程仓库 ...
- docker 安装 sonarQube
sonarQube 是一款开源代码检测工具.本篇介绍通过 docker 来安装.大概的一个运作流程是这样的,先通过 sonar-scanner 插件扫描代码,把数据存储到数据库,sonarQube 读 ...
- javascript,html,正则表达式,邮箱密码验证
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> ...
- DotNetCore深入了解:HTTPClientFactory类
一.HttpClient使用 在C#中,如果我们需要向某特定的URL地址发送Http请求的时候,通常会用到HttpClient类.会将HttpClient包裹在using内部进行声明和初始化,如下面的 ...
- 【ArcEngine】AE连接SDE_For_SQLServer参数设置
SDE for sqlserver直连的ArcEngine访问 Ae中的数据的连接实质还是采用服务连接的方式.连接代码如下: 1 public IWorkspace Getworkspace() 2 ...
- plsql developer中各个window的作用【转】
转载自,原文链接: -程序窗口(program window) :可以执行 sql,sqlplus 相关的语句,例如存储过程,方法,一般用来开发程序用的. -测试窗口(test window):一般是 ...
- mysql基础操作(二):简单查询DQL
-- 1.查询所有字段 select * from student; -- 2.查询指定的字段 select id from student; select id, name from student ...
- pgsql学习
--求所有人的薪水的总和,平均值,最大值,最小值 select sum(sal) , avg(sal), max(sal) , min(sal) from emp; --求总的行数 select co ...
- 在C++中使用openmp进行多线程编程
在C++中使用openmp进行多线程编程 一.前言 多线程在实际的编程中的重要性不言而喻.对于C++而言,当我们需要使用多线程时,可以使用boost::thread库或者自从C++ 11开始支持的st ...