这是我参与「第三届青训营 -后端场」笔记创作活动的的第1篇笔记
对比Java学习golang
一、基础语法
1.入门程序HelloWorld
java代码
package test;
public class HelloWorld {
public static void main(String[] args) {
System.out.println("HelloWorld");
}
}
go代码
//标记为程序的入口包,也就是程序的入口文件
package main
import (
//fmt主要向屏幕输入输出字符串,格式化字符串
"fmt"
)
func main() {
//调用fmt中Println来输出HelloWorld
fmt.Println("HelloWorld")
}
由两个入门案例的对比
- java强制要求main方法所在的类名和文件名保持一致,而共语言没有这种要求
- import导包,我觉得还是有相似之处的,由于java将java.lang包默认导入,因此在使用System.out.println没有导包,而go需要显性写出来
2.编译、运行
java使用命令行的方式编译运行
javac main.class
java main
go编译运行
go build main.go
./main
也可以使用
go run main.go
3.变量
go
package main
import (
"fmt"
"math"
)
func main() {
var a string = "initial"
//类型推断,省略变量类型
var b, c = 4, a + "1"
var d = true
var e float64
// := 再次省略var关键字
f := float32(e)
g := a + c
fmt.Println(a, b, c, d, e, f) //initial 4 initial1 true 0 0
fmt.Println(g)
//直接定义常量
const s = "const"
const h = 20000
//3e2 = 3*10^2
const i = 3e2 / h
fmt.Println(s, h, i)
//调用math中的Ceil求出i的向上取整结果值
fmt.Println(math.Ceil(i),math.Sin(i)) //1 0.02999550020249566
}
java
String a = "initial",c = a + "1";
int b = 4;
boolean d = true;
float f = 0;
String g = a + c;
常量在java中的声明为
public static final int s = "const";
4.if-else
go
package main
import "fmt"
func main() {
//与java有所不同的是:1. 条件不需要加括号;2. 强制if/else后面使用{},java不强制但是建议也使用{},便于阅读
if 7%2 == 0 {
fmt.Println("7 is even")
} else {
fmt.Println("7 is odd")
}
//在此处定义的变量,其作用范围也在if中,出了if之后就不能使用
if num := 9; num < 0 {
fmt.Println(num, "is negative")
} else if num < 10 {
fmt.Println(num, "his 1 digit")
}else {
fmt.Println(num)
}
}
java
if (7 % 2 == 0) {
System.out.println("7 is even");
} else {
System.out.println("7 is odd");
}
int num = 0;
if (num < 0) {
System.out.println(num + "is negative");
} else if (num < 10) {
System.out.println(num + "his 1 digit");
}else {
System.out.println(num);
}
5.循环
go没有while,do-while循环,只有for循环
func main() {
i := 1
//不写条件,也就代表死循环
for {
fmt.Printf("%d ", i)
i++
if i == 10 {
break
}
}
fmt.Println()
for j := 7; j < 10; j++ {
fmt.Printf("%d ", j)
}
}
java
int i = 1;
//不写条件,也就代表死循环
for (;;) {
System.out.print(i + " ");
i++;
if (i == 10) {
break;
}
}
System.out.println();
for (int j = 7; j < 10; j++) {
System.out.print(j + " ");
}
6.Switch
go
package main
import (
"fmt"
"time"
)
func main() {
a := 2
switch a {
case 1:
fmt.Println(1)
case 2:
fmt.Println(2)
case 3:
fmt.Println(3)
case 4,5:
fmt.Println(4,"or",5)
default:
fmt.Println("other")
}
t := time.Now()
switch {
case t.Hour() < 12:
fmt.Println("早上")
default:
fmt.Println("下午")
}
fmt.Println(time.Now().Format(time.UnixDate))
}
java
int i = 1;
switch (i){
case 1:
System.out.println(1);
break;
case 2:
System.out.println(2);
break;
case 3:
case 4:
System.out.println(3 + " or " + 4);
break;
default:
System.out.println("other");
}
Date date = new Date();
if (date.getHours() < 12){
System.out.println("早上");
}else {
System.out.println("下午");
}
- 其中,java的switch有以下注意事项
最早时,只支持int、char、byte、short这样的整型的基本类型或对应的包装类型Integer、Character、Byte、Short常量,包装类型最终也会经过拆箱为基本类型,本质上还是只支持基本类型
JDK1.5开始支持enum,原理是给枚举值进行了内部的编号,进行编号和枚举值的映射
JDK1.7开始支持String,但不允许为null,原理是借助 hashcode( ) 来实现。
- 并且在case结束后需要加上break,不然后执行下一个case,而go语言不会有这种担忧
7.数组
go
package main
import (
"fmt"
)
func main() {
var a [5]int
a[4] = 100
fmt.Println(a[4],len(a))
b := [5]int{1,2,3,4,5}
fmt.Println(b)
var c [2][3]int
for i := 0; i < 2; i++ {
for j := 0; j < 3; j++ {
c[i][j] = i*j;
}
}
fmt.Println(c)
}
java
int[] a = new int[5];
a[4] = 100;
System.out.println(a[4] + " " + a.length);
int[] b = {1, 2, 3, 4, 5};
System.out.println(Arrays.toString(b));
int[][] c = new int[2][3];
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 3; j++) {
c[i][j] = i * j;
}
}
for (int i = 0; i < 2; i++) {
System.out.println(Arrays.toString(c[i]));
}
8.切片
go常用切片代替数组
package main
import "fmt"
func main(){
s := make([]string,3)
s[0] = "a"
s[1] = "b"
s[2] = "c"
fmt.Println(s[2])
fmt.Println(len(s))
s = append(s, "d")
s = append(s,"e","f")
fmt.Println(s)
c := make([]string,len(s))
copy(c,s)
//[2,5)
fmt.Println(s[2:5])
fmt.Println(s[:3])
fmt.Println(s[3:])
}
9.Map
go
package main
import "fmt"
func main() {
m := make(map[string] int)
m["one"] = 1
m["two"] = 2
fmt.Println(m)
fmt.Println(len(m))
fmt.Println(m["one"])
fmt.Println(m["unknow"])
r,ok := m["unknow"]
fmt.Println(r,ok)
delete(m,"one")
m2 := map[string]int {"one":1,"two":2}
var m3 = map[string]int {"one":1,"two":2}
fmt.Println(m2,m3)
}
java
Map<String,Integer> map = new HashMap<>();
map.put("one",1);
map.put("two",2);
System.out.println(map);
System.out.println(map.size());
System.out.println(map.get("one"));
System.out.println(map.get("unknow"));
map.remove("one");
10.range
go
package main
import "fmt"
func main() {
nums := []int{1, 2, 3}
sum := 0
for i, num := range nums {
sum += num
if i == 2 {
break
}
}
fmt.Println(sum)
m := map[string]string{"a": "A", "b": "B"}
for k,v := range m{
fmt.Println(k,v)//a A;b B
}
for k := range m {
fmt.Println("key",k)//key a;key b
}
for _,v := range m {
fmt.Println("value",v)//value A;value B
}
}
java
int[] nums = new int[]{1,2,3};
int sum = 0;
for (int num : nums) {
sum += num;
}
Map<String,String> map = new HashMap<>();
map.put("a","A");
map.put("b","B");
System.out.println("map = " + map);
//与forEach类似
map.forEach(
(k,v)->{
System.out.println(k + " : " + v);
}
);
Set<String> keys = map.keySet();
System.out.println("keys = " + keys);
Collection<String> values = map.values();
System.out.println("values = " + values);
11.函数
go
package main
import "fmt"
func main() {
res := add(1, 2)
fmt.Println(res)
m := map[string]string{"a":"A"}
fmt.Println(exists(m,"a"))
}
func add(a int, b int) int {
return a + b
}
func add1(a, b int) int {
return a + b
}
func exists(m map[string]string, k string) (v string, ok bool) {
v, ok = m[k]
return v, ok
}
java只能返回一个值,当然,可以把多个返回值做一个类,或者返回一个容器
private static int add(int a, int b) {
return a + b;
}
private static Map<String, Boolean> exists(Map<String, String> m, String k) {
String s = m.get(k);
if (s == null){
Map map = new HashMap<>();
map.put(null,false);
return map;
}else {
Map map = new HashMap<>();
map.put(map.get(k),true);
return map;
}
}
12.指针
package main
import "fmt"
func main() {
n := 5
add2(n)
fmt.Println(n)
add2ptr(&n)
fmt.Println(n)
}
func add2(n int) {
n += 2
}
func add2ptr(n *int) {
*n += 2
}
13.结构体
package main
import "fmt"
type user struct{
name string
password string
}
func main(){
a := user{name:"zhangsan",password: "111111"}
b := user{"zhangsan","111111"}
c := user{name:"zhangsan"}
c.password = "111111"
var d user
d.name = "zhangsan"
d.password = "111111"
fmt.Println(a,b,c,d)
fmt.Println(checkPassword(a,"111111"))
fmt.Println(checkPassword2(&a,"123456"))
}
func checkPassword(u user,password string) bool{
return u.password == password
}
func checkPassword2(u *user,password string) bool{
return u.password == password
}
14.结构体方法
package main
import "fmt"
type user struct {
name string
password string
}
func main() {
b := user{"zhangsan", "111111"}
//成为了结构体方法,就用“结构体.方法名”调用
b.resetPassword("123456")
fmt.Println(b.checkPassword("111111"))
}
func (u user) checkPassword(password string) bool {
return u.password == password
}
//带指针就可以对这个结构体进行修改
func (u *user) resetPassword(password string) {
u.password = password
}
15.错误处理
package main
import (
"errors"
"fmt"
)
type user struct{
name string
password string
}
func finduser(users []user,name string) (v *user,err error){
for _, u := range users {
if u.name == name {
return &u,nil
}
}
return nil,errors.New("not found")
}
func main(){
u,err := finduser([]user{{"zhangsan", "111111"}},"zhangsan")
if err != nil {
fmt.Println(err)
return
}
fmt.Println(u.name)
if u, err := finduser([]user{{"zhangsan", "123456"}}, "zhang");err ==nil {
fmt.Println(u.name)//not found
}else {
fmt.Println(err)
return
}
}
不同于java的异常处理,go的异常以返回值的形式抛出,需要调用者判断是都发生了异常
16.字符串操作
package main
import (
"fmt"
"strings"
)
func main() {
a := "hello"
//字符串a中是否存在"ll“
fmt.Println(strings.Contains(a,"ll"))
//字符串a中存在几个"l“
fmt.Println(strings.Count(a,"l"))
//字符串a中是否以"He“开头
fmt.Println(strings.HasPrefix(a,"He"))
//字符串a中是否以"lo“结尾
fmt.Println(strings.HasSuffix(a,"lo"))
//字符串a中"o“第一次出现的位置
fmt.Println(strings.Index(a,"o"))
//字符串拼接
fmt.Println(strings.Join([]string{"a","b","a"},"-"))
//重复3次a字符串
fmt.Println(strings.Repeat(a,3))
//替换3次a字符串,当最后一个值小于0,就代表不受次数限制
fmt.Println(strings.Replace(a,"l","n",3))
//根据"-"分割字符串并返回成数组
fmt.Println(strings.Split("a-b-c-d","-"))
//转换为全小写
fmt.Println(strings.ToLower(a))
//转换为全大写
fmt.Println(strings.ToUpper(a))
//内置函数
//字符串长度,一个中文可能对应多个字符
fmt.Println(len(a))
}
17.字符串格式化
package main
import "fmt"
type point struct{
x,y int
}
func main() {
s := "hello"
n := 123
p := point{1,2}
fmt.Println(s,n)
fmt.Println(p)
fmt.Printf("s=%v\n",s)
fmt.Printf("n=%v\n",n)
fmt.Printf("p=%v\n",p)
fmt.Printf("p=%+v\n",p)
fmt.Printf("p=%#v\n",p)
f := 3.1415926
fmt.Println(f)
fmt.Printf("%.2f",f)//3.14
}
18.json处理
package main
import (
"encoding/json"
"fmt"
)
type userInfo struct {
//结构体中的字段以大写开头,就可以使用json序列化
Name string
//字段名改为"age"
Age int `json:"age"`
Hobby []string
}
func main() {
a := userInfo{"zhangsan", 21, []string{"java", "go"}}
//序列化为byte数组
buf, err := json.Marshal(a)
if err != nil {
panic(err)
}
fmt.Println(buf)
//类型转化为字符串
fmt.Println(string(buf))
buf, err = json.MarshalIndent(a, "", "\t")
if err != nil {
panic(err)
}
fmt.Println(string(buf))
var b userInfo
//反序列化到一个空变量里
err = json.Unmarshal(buf,&b)
if err != nil {
panic(err)
}
fmt.Printf("%#v\n",b)
}
19.时间处理
package main
import (
"fmt"
"time"
)
func main() {
now := time.Now()
fmt.Println(now)//2022-05-07 18:33:16.8002889 +0800 CST m=+0.011001801
t := time.Date(2022,5,7,1,18,10,0,time.UTC)
t2 := time.Date(2022,5,7,2,10,30,0,time.UTC)
fmt.Println(t)//2022-05-07 01:18:10 +0000 UTC
fmt.Println(t.Year(),t.Month(),t.Day(),t.Hour(),t.Minute())//2022 May 7 1 18
//格式化时间,特定的时间2006-01-02 15:04:05
fmt.Println(t.Format("2006-01-02 15:04:05"))//2022-05-07 01:18:10
diff := t2.Sub(t)
fmt.Println(diff)//52m20s
fmt.Println(diff.Minutes(),diff.Seconds())//52.333333333333336 3140
t3,err := time.Parse("2006-01-02 15:04:05","2022-05-07 18:47:55")
if err != nil {
panic(err)
}
fmt.Println(t3 == t)//false
fmt.Println(t3)
//获取时间戳
fmt.Println(now.Unix())//1651920422
}
运行结果:
20.数字解析
package main
import (
"fmt"
"strconv"
)
func main() {
f,_ := strconv.ParseFloat("1.2345",64)
fmt.Println(f)//1.2345
//第二位代表传入的是10进制(0代表自动推测),第三位表示返回一个64位整数
n,_ := strconv.ParseInt("111",8,64)
fmt.Println(n)//73
n2,_ := strconv.Atoi("123")
fmt.Println(n2)//123
n2,err := strconv.Atoi("AAA")
fmt.Println(n2,err)//0 strconv.Atoi: parsing "AAA": invalid syntax
}
21.进程信息
package main
import (
"fmt"
"os"
"os/exec"
)
func main() {
//获取命令行参数
fmt.Println(os.Args)
//获取环境变量
fmt.Println("PATH=",os.Getenv("PATH"))
//设置环境变量
fmt.Println(os.Setenv("AA","BB"))
//快速启动子进程,并获取输入输出
buf,err := exec.Command("grep","127.0.0.1","/etc/hosts").CombinedOutput()
if err != nil {
panic(err)
}
fmt.Println(buf)
}
二、实战
1.猜数字游戏
生成0-100之间的一个随机数
每次玩家输入一个数字,程序都会告诉是大于还是小于猜的这个数
直到猜对了,退出游戏
package main
import (
"bufio"
"fmt"
"math/rand"
"os"
"strconv"
"strings"
"time"
)
func main() {
maxNum := 100
//用时间戳初始化随机数种子
rand.Seed(time.Now().Unix())
secretNumber := rand.Intn(maxNum)//81
//fmt.Println("The serect number is ",secretNumber)
fmt.Println("Please input your guess")
reader := bufio.NewReader(os.Stdin)
for {
//读取一行输入
input, err := reader.ReadString('\n')
if err != nil {
fmt.Println("读取错误,请再试一次~")
return
}
//去掉换行符
input = strings.TrimSuffix(input, "\n")
input = strings.TrimSuffix(input, "\r") //不知为何,我这里会多读取一个\r,去除之后就能得到单纯的数字字符串
//转换为数字
guess, err := strconv.Atoi(input)
if err != nil {
fmt.Println("无效的输入,请输入整数值")
}
fmt.Println("You guess is", guess)
//与随机值相比较
if guess > secretNumber {
fmt.Println("猜大了")
} else if guess < secretNumber {
fmt.Println("猜小了")
} else {
fmt.Println("恭喜你,猜对了!!")
break
}
}
}
2.在线词典
命令行调用后添加参数,根据这个参数调用第三方api去查询到这个单词的音标、注释
使用彩云翻译翻译单词
curl 'https://api.interpreter.caiyunai.com/v1/dict' -X POST -H 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:100.0) Gecko/20100101 Firefox/100.0' -H 'Accept: application/json, text/plain, */*' -H 'Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2' -H 'Accept-Encoding: gzip, deflate, br' -H 'Content-Type: application/json;charset=utf-8' -H 'X-Authorization: token:qgemv4jr1y38jyq6vhvi' -H 'app-name: xy' -H 'version: 1.8.0' -H 'os-type: web' -H 'os-version: ' -H 'device-id: ' -H 'Origin: https://fanyi.caiyunapp.com' -H 'Connection: keep-alive' -H 'Referer: https://fanyi.caiyunapp.com/' -H 'Sec-Fetch-Dest: empty' -H 'Sec-Fetch-Mode: cors' -H 'Sec-Fetch-Site: cross-site' -H 'Pragma: no-cache' -H 'Cache-Control: no-cache' --data-raw '{"trans_type":"en2zh","source":"good"}'
不止为何,我生成的代码执行会乱码,所以生成的代码就不贴了
func main() {
client := &http.Client{}
//与视频讲解的不同,此处视频里这个代码需要被注释掉才能使用
//var data = strings.NewReader(`{"trans_type":"en2zh","source":"good"}`)
request := DictRequest{TransType:"en2zh",Source:"good"}
//返回request的 JSON 编码,返回为byte数组
buf,err := json.Marshal(request)
if err != nil {
log.Fatal(err)
}
//将byte数组转化为流
var data = bytes.NewBuffer(buf)
//返回一个Request请求
req,err := http.NewRequest("POST","https://api.interpreter.caiyunai.com/v1/dict",data)
if err != nil {
log.Fatal(err)
}
//粘贴生成的代码
}
根据测试,使用提供代码的设置请求头信息,上述这种乱码的情况也没了
//设置请求头
req.Header.Set("Connection", "keep-alive")
req.Header.Set("DNT", "1")
req.Header.Set("os-version", "")
req.Header.Set("sec-ch-ua-mobile", "?0")
req.Header.Set("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36")
req.Header.Set("app-name", "xy")
req.Header.Set("Content-Type", "application/json;charset=UTF-8")
req.Header.Set("Accept", "application/json, text/plain, */*")
req.Header.Set("device-id", "")
req.Header.Set("os-type", "web")
req.Header.Set("X-Authorization", "token:qgemv4jr1y38jyq6vhvi")
req.Header.Set("Origin", "https://fanyi.caiyunapp.com")
req.Header.Set("Sec-Fetch-Site", "cross-site")
req.Header.Set("Sec-Fetch-Mode", "cors")
req.Header.Set("Sec-Fetch-Dest", "empty")
req.Header.Set("Referer", "https://fanyi.caiyunapp.com/")
req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9")
req.Header.Set("Cookie", "_ym_uid=16456948721020430059; _ym_d=1645694872")
//发送请求
resp, err := client.Do(req)
if err != nil {
log.Fatal(err)
}
//函数执行结束后会执行
defer resp.Body.Close()
//读取请求
bodyText, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s\n", bodyText)
这边贴过去的代码可能浏览器会报错,这可能是因为粘贴过去有换行,根据我自身的操作,将换行删除后可以生成以下代码
type AutoGenerated struct {
Rc int `json:"rc"`
Wiki struct {
KnownInLaguages int `json:"known_in_laguages"`
Description struct {
Source string `json:"source"`
Target interface{} `json:"target"`
} `json:"description"`
ID string `json:"id"`
Item struct {
Source string `json:"source"`
Target string `json:"target"`
} `json:"item"`
ImageURL string `json:"image_url"`
IsSubject string `json:"is_subject"`
Sitelink string `json:"sitelink"`
} `json:"wiki"`
Dictionary struct {
Prons struct {
EnUs string `json:"en-us"`
En string `json:"en"`
} `json:"prons"`
Explanations []string `json:"explanations"`
Synonym []string `json:"synonym"`
Antonym []string `json:"antonym"`
WqxExample [][]string `json:"wqx_example"`
Entry string `json:"entry"`
Type string `json:"type"`
Related []interface{} `json:"related"`
Source string `json:"source"`
} `json:"dictionary"`
}
然后再添加上以下代码,就可以将上面请求返回的byte[]转化成结构体对象
var dictRequest AutoGenerated
//将bodyText中的值写入到dictRequest中
err = json.Unmarshal(bodyText,&dictRequest)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%#v\n",dictRequest)
处理响应,筛选得到想要的
//判断状态码是否为200,也就是这个请求是否正确
if resp.StatusCode != 200 {
//如果错误打印出来状态码和响应报文
log.Fatal("StatusCode : ",resp.StatusCode,"body : ",string(bodyText))
}
var dictResponse AutoGenerated
//将bodyText中的值写入到dictRequest中
err = json.Unmarshal(bodyText,&dictResponse)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%#v\n", dictResponse)
//筛选想得到的内容
fmt.Println("good","UK:", dictResponse.Dictionary.Prons.En,"US:",dictResponse.Dictionary.Prons.EnUs)
for _,item := range dictResponse.Dictionary.Explanations{
fmt.Println(item)
}
修改原先代码,将原本的func main() 变成一个函数 func query(word string)
将request := DictRequest{TransType:"en2zh",Source:"good"} 中固定值good改成函数传入值word
添加一个新的程序入口
func main() {
//对传入参数进行判断
if len(os.Args) != 2{
fmt.Fprintf(os.Stderr,`usage: simpleDict WORD example: simpleDict hello`)
//退出
os.Exit(1)
}
word := os.Args[1]
//调用原先的代码
query(word)
}