黑马GO语言 ---- 学习笔记

Go语言

  • 文件入口main
  • 左括号必须同行
  • 每个文件必须先声明包(必须得有main包)
  • 没有分号
  • 导包必须得用

数据类型

变量的声明 ==> var a int

c := 10 自动推导类型

多重赋值 a,b := 10, 20 互换 a, b := b, a

匿名变量 _, b, _ = func() 配合函数返回值使用

var (

​ a int = 1

​ b float64 = 1.0

)

**常量 **

const a int = 10 const a = 10

const (

​ a = 1

​ b = 1.1

)

iota枚举 常量自动生成器 但是遇到const会变成0

package main

import "fmt"

func main() {
	const (
		a = iota
		b = iota
		c = iota
	)
	fmt.Printf("a = %d, b = %d, c = %d", a, b, c)

	const d = iota
	fmt.Printf("d = %d", d)

	const (
		i    = iota
		j, k = iota, iota  // 同一行值一样
		h    = iota
	)
	fmt.Printf("i = %d, j = %d, h = %d", i, j, h)
}

switch

package main

import "fmt"

func main() {
	fmt.Println("请数入:")
	var a int
	fmt.Scan(&a)
	
    switch a := 1;a {
	case 1:
		fmt.Println("111")
		fallthrough   //不加默认跳出
	case 2:
		fmt.Println("222")
		fallthrough
	case 3:
		fmt.Println("333")
	default
		fmt.Println("444")
	}
}


    switch {
	case a > 10:
		fmt.Println("111")
		fallthrough   //不加默认跳出
	case a < 10:
		fmt.Println("222")
		fallthrough
	case a == 10:
		fmt.Println("333")
	default
		fmt.Println("444")
	}

循环

package main

import "fmt"

func main() {
	sum := 0
	for i := 1; i <= 100; i++ {
		sum = sum + i
	}
    fmt.Println("sum = ", sum)
}


package main

import "fmt"

func main() {
	str := "abc"
	for i, data := range str {
		fmt.Printf("str[%d] = %c\n", i, data)
	}
	for i := range str {
		fmt.Printf("str[%d] = %c\n", i, str[i])
	}
}

跳转

goto 不能跨函数
package main

import "fmt"

func main() {
	fmt.Println("111111")
	goto End
	fmt.Println("222")
End:
	fmt.Println("333333")
}

函数

package main

import "fmt"

// 无参无返回值
func MyFunc() {
	a := 666
	fmt.Println("a = ", a)
}

// 有参无返回值
func MyFunc01(a int) {
	fmt.Println("a = ", a)
}

// 不定参数类型   必须放在最后
func MyFunc02(a int, args ...int) {
	fmt.Println("len", len(args))
	MyFunc03(args[1:]...)
}

func MyFunc03(args ...int) {
	for _, data := range args {
		fmt.Println(data)
	}
}

// 有返回值
func MyFunc04() (result int) {
	result = 777
	return
}

func MyFunc05() (a int, b int, c int) {
	a, b, c = 11, 22, 33
	return
}

func main() {
	a, b, c := MyFunc05()
	fmt.Println("c = ", a, b, c)
}

有参有返回值

递归函数

pakeage main
import "fmt"

func test01() (sum int) {
    if (i == 1) {
        return 1 
    }
    return i + test01(i - 1)
}
func main() {
	var sum int 
    sum = test01(100)
    fmt.Println("sun = ", sum)
}

函数类型

pakeage main
import "fmt"

func add(a, b, int) int {
    return a + b
}

func bdd(a, b, int) int {
    return a - b
}

type FuncType func(int, int) int

func main() {
	var ftest FuncType
    ftest = add
    result := ftest(10, 20)
}

回调函数(函数参数有函数类型) (多态)

pakeage main
import "fmt"

// 实现加法
func add(a, b, int) int {
    return a + b
}

// 实现减法
func bdd(a, b, int) int {
    return a - b
}

type FuncType func(int, int) int
func calc(a, b, int, ftest FuncType) (result int) {
    Println("calc")
    result = ftest(a, b)
    return 
}


func main() {
    a := calc(1, 1, add)
    fmt.Println("a = ", a)
    b := calc(1, 1, bdd)
    fmt.Println("b = ", b)
}

闭包 (匿名函数)

func main() {
    a := 10
    str := "mike"
    
    f1 := func() {
        fmt.Println("a = ", a)
        fmt.Printlb("str =", str)
    }
    f1()
    
    type FuncType func()
    
    var f2 func()
 	f2 = f1
    
    func () {
        fmt.Println("111")
    } ()
    func (i, j int) (max int) {
        if i > j {
            max = i
		}
        return 
    } (10, 20)
}

闭包引用方式捕获外部变量 (闭包是自己独立的空间,里面变量不会释放)

defer(析构) 延迟调用

func main() {
    defer fmt.Println("bbbb")
    fmt.Println("aaaaaa")
}

调用顺序 (后进先出) 函数发生错误也会执行

和闭包一起使用

func main() {
    a := 10
    b := 20
    
    defer func(a, b, int) {
        fmt.Printf("a = %d, b = %d\n", a, b)
    }(a, b)   //已经先传参了
    a = 111
    b = 222
    fmt.Println("a ,b", a, b)
}

获取命名行参数

package main

import "fmt"
import "os"

func main() {
	list := os.Args
	n := len(list)
	fmt.Println("n = ", n)
}

工程管理

src源文件, gopath gobin设置

package main

import print "fmt" //必须使用
import _ "os"      //忽略   需要使用 init函数

func main() {
	n := 6
	print.Println("n = ", n)
}

init函数 到包先执行

// 为了执行init函数
import (
_ "fmt"
)

复合类型

指针

package main

import "fmt"

func main() {
	var a int = 10
	var p *int = &a

	fmt.Println("%v", p)
}

---------------------
package main

import "fmt"

func swap(p1, p2 *int) {
	*p1, *p2 = *p2, *p1
}

func main() {
	a, b := 10, 20
	swap(&a, &b)

	fmt.Println("a b", a, b)
}

new函数 自动回收

package main

import "fmt"

func main() {

	var p *int
	p = new(int)
	*p = 666
	fmt.Println("p = ", *p)
}

数组

package main

import "fmt"

func main() {
	var id [50]int //元素个数是常量
	c := [5]int{1,2,3}
	b := [5]int{2:10, 4:20}
	
	// 比较(== OR !=)和赋值
	
	fmt.Println("a b", id[0], id[1])
}

-----------
//随机数
package main

import "fmt"
import "math/rand"
import "time"

func main() {
	rand.Seed(time.Now().UnixNano())

	for i := 0; i < 5; i++ {
		fmt.Println("rand = ", rand.Intn(100))
	}

}

切片(动态数组)

slice

package main

import "fmt"

func main() {
	a := []int{1, 2, 3, 0, 0}
	s := a[1:3:5]
	fmt.Println("s=", s)
	fmt.Println("s=", len(s))
	fmt.Println("s=", cap(s))
}
--------------
// 和数组的区别,容量和长度不固定   切片出来的数据会改变原始数据
package main

import "fmt"

func main() {
	s := []int{}
    // s2 := make([]int, 5, 10)
    // s3 := make([]int, 5) 长度和容量一样为5
	fmt.Println("len(s),cap(s)", len(s), cap(s))
	s = append(s, 11)
	fmt.Println("len(s),cap(s)", len(s), cap(s))
}

---------------
// 切片操作
package main

import "fmt"

func main() {
	array := []int{0, 1, 2, 3}
	s1 := array[:]
	data := array[1]
	s2 := array[2:3:4]
	s3 := array[:4]
	s3 := array[2:]
    // copy
}

------------
//引用传递

map

package main

import "fmt"

func main() {
	var m1 map[int]string
	m2 := make(map[int]string, 10)
	m2[1] = "make"
    for key,value := m1 {
        
    }
    delete(m2, 1)
    test(m1)  //引用传递
}

结构体

使用其他包的函数,结构体,首字母必须大写

是有的话就小写

package main

import "fmt"

type Student struct {
	id   int
	name string
	sex  byte
	age  int
	addr string
}

func main() {
	// 顺序初始化必须全部
	var s1 Student = Student{1, "make", 'm', 18, "bj"}
	fmt.Println("s1 = ", s1)
	s2 := Student{name: "make"}
	s2.name = "mike"

	p2 := &Student{name: "mike"}
	fmt.Println("p2 = ", *p2)
	p2.name = "mike" // 没有->

}

面向对象编程

继承

匿名字段

package main

import "fmt"

type Student struct {
	name string
	age  int
}

type People struct {
	foot int
	head int
    name string   //同名复写
	Student
    *Student  // 初始化多加 &
}

func main() {
	var p1 People = People{2, 1, Student{"mike", 10}}
	fmt.Println("p1.name", p1.name, p1.age)
    p1.Student.name = "mike"
	p2 := People{Student: Student{name: "mike"}}
	fmt.Println("p1.name", p2)
}
-------------------------
// 继承
package main

import "fmt"

type Person struct {
	name string
	sex  byte
	age  int
}

func (tmp *Person) PrintInfo() {
	fmt.Println("tmp = ", *tmp)
}

type Student struct {
	Person
	id   int
	addr string
}

func (tmp *Student) PrintInfo() {
	fmt.Println("tmp = ", *tmp)
}

func main() {
	s := Student{Person{"mike", 'm', 18}, 1, "bj"}
	s.PrintInfo()
    sfunc := s.PrintInfo  //方法值
    sfunc()
    f := (*person).PrintInfo()
    f(&s)
}

封装

方法

package main

import "fmt"

func Add(a, b int) int {
	return a + b
}

type long int

// tmp 是接收者   接收者本身不能是指针   接收者类型不同函数名可以一样
func (tmp long) Add01(other long) long {
	return tmp + other
}
type long32 long
func (tmp long32) Add01(other long) long {
	return tmp + other
}

func main() {
	var result int
	result = Add(1, 1)
	fmt.Println("result = ", result)
	var a long = 2 // 接收者就是对象
	r := a.Add01(3)
	fmt.Println("result = ", r)
}

------------------------
package main

import "fmt"

type Person struct {
	name string
	sex  byte
	age  int
}

func (tmp Person) testprint() {
	fmt.Println("tmp = ", tmp)
}

func (p *Person) setinfo(n string, s byte, a int) {
	p.name = n
	p.sex = s
	p.age = a
}

func main() {
	p := Person{"mike", 'm', 18}
	p.testprint()

	var p2 Person   //对象
	(&p2).setinfo("yoyo", 'f', 22)  // 引用传递
	p2.testprint()
}

----------------
// 指针变量的方法集
func main() {
	p := &Person{"mike", 'm', 18}
	p.setinfo("yoyo", 'f', 22)  //内部会自动转换
    (*p).set("yoyo", 'f', 22)  
	p2.testprint()
}

接口

定义接口

package main

import "fmt"

//定义接口类型
type Humaner interface {
	sayhi()
}
type Student struct {
	name string
	id   int
}

func (tmp *Student) sayhi() {
	fmt.Println("学生")
}

type Teacher struct {
	addr  string
	group string
}

func (tmp *Teacher) sayhi() {
	fmt.Println("老师")
}

func WhoSayhi(i Humaner) {
	i.sayhi()
}

func main() {
	/*var i Humaner
	s := &Student{"mike", 19}
	i = s
	i.sayhi()

	t := &Teacher{"mike", "go"}
	i = t
	t.sayhi()
	*/
	s := &Student{"mike", 19}
	t := &Teacher{"mike", "go"}
	WhoSayhi(s)
	WhoSayhi(t)
}

----------------
//接口的继承
package main

import "fmt"

//定义接口类型
type Humaner interface {
	sayhi()
}

type Personer interface {
	Humaner
	sing(lrc string)
}

type Student struct {
	name string
	id   int
}

func (tmp *Student) sayhi() {
	fmt.Println("学生")
}
func (tmp *Student) sing(lrc string) {
	fmt.Println("学生", lrc)
}

func main() {
	var i Personer
	s := &Student{"mike", 55}
	i = s
	i.sayhi()
	i.sing("111")

	//接口转换  父类可以接收子类
	var ipro Personer
	ipro = &Student{"mike", 77}
	var base Humaner
	base = ipro
	base.sayhi()
}
---------------------------
// 空接口
package main

import "fmt"

type Student struct {
	name string
	id   int
}

func main() {
	//万能类型   void *
	//var i1 interface{} = 1
	//fmt.Println("i = ", i1)
	//类型断言
	i := make([]interface{}, 3)
	i[0] = 1
	i[1] = "go"
	i[2] = Student{"mike", 666}
	for _, data := range i {
		if _, ok := data.(int); ok == true {
			fmt.Println("int")
		} else if _, ok := data.(string); ok == true {
			fmt.Println("string")
		} else {
			fmt.Println("struct")
		}
	}
}

异常处理

常用于返回错误

package main

import "fmt"
import "errors"

func MyDiv(a, b int) (result int, err error) {
	err = nil
	if b == 0 {
		err = errors.New("分母不能为零")
	} else {
		result = a / b
	}
	return
}

func main() {
	//err1 := fmt.Errorf("%s", "thisis normol err1")
	//fmt.Println("err1 = ", err1)
	//err2 := errors.New("this is normal err2")
	//fmt.Println("err2", err2)
	result, err := MyDiv(10, 0)
	if err != nil {
		fmt.Println("err = ", err)
	} else {
		fmt.Println("result =", result)
	}
}
--------------------  
// 致命错误   
package main

import "fmt"

func testa() {
	fmt.Println("aaaaaaaa")
}

func testb(x int) {
	defer func() {
		if err := recover(); err != nil {
			fmt.Println(err)
		}
	}()
	var a [10]int
	a[x] = 111 //默认会调用
	//panic("this is a panic test")
}

func testc() {
	fmt.Println("ccccccccccc")
}

func main() {
	testa()
	testb(20)
	testc()
}

字符串操作

package main

import (
	"fmt"
	"strings"
)

func main() {
	// "hellogo" 是否有”hello"
	fmt.Println(strings.Contains("hellogo", "hello"))

	// joins 组合
	s := []string{"abc", "hello", "mike"}
	buf := strings.Join(s, "x")
	fmt.Println("buf = ", buf)

	// index
	fmt.Println(strings.Index("abc", "cd"))

	buf = strings.Repeat("go", 3)
	fmt.Println("buf = ", buf)

	// Split
	buf = "hello@go@mike"
	s2 := strings.Split(buf, "@")

	//trim
	strings.Trim("       are y ok   ", " ")

	//Fields
	s3 := strings.Fields("       are y ok   ")
}
----------------------
//字符串转换
package main

import (
	"fmt"
	"strconv"
)

func main() {
	slice := make([]byte, 0, 1024)
	slice = strconv.AppendBool(slice, true)
	slice = strconv.AppendInt(slice, 123, 10)
	slice = strconv.AppendQuote(slice, "abc")
	fmt.Println("slice = ", string(slice))

	var str string
	str = strconv.FormatBool(false)
	str = strconv.FormatFloat(3.14, 'f', -1, 64)
	fmt.Println("str = ", str)

	str = strconv.Itoa(6666)
	fmt.Println("str = ", str)

    a, _ := strconv.Atoi("56654")
}

正则表达式

package main

import (
	"fmt"
    "regexp"
)

func main() {
    buf := "abc azc a7c aac 888";
    
    // 解释规则
    reg1 := regexp.MustComplile(`a.c`)//a[0-9]c
    if reg1 == nil {
        fmt.Println("err")
        return
    }
    // 提取信息
    result := reg1.FindAllStringSubmatch(buf, -1)// -1表示全部
    fmt.Println("result = ", result)
}

---------------------

package main

import (
	"fmt"
    "regexp"
)

func main() {
    buf := "3.14 567 asd 1.23 7. 8.99 llk1 6.66"
    reg := regexp.MustCompile('\d+\.\d+')
    if ret == nil {
        fmt.Println("err")
        return 
    }
    //result := reg.FindAllString(buf, -1)
    result := reg.FindAllStringSubmatch(buf, -1)//分组
    fmt.Println("result =", result)
    
}

------------------------
// 爬虫
package main

import (
	"fmt"
    "regexp"
)

func main() {
    buf := `网页信息` //原生字符串
    
    reg := regexp.MustCompile('<div>(?s:(.*?))</div>')
    if ret == nil {
        fmt.Println("err")
        return 
    }
    //result := reg.FindAllString(buf, -1)
    result := reg.FindAllStringSubmatch(buf, -1)//分组
    fmt.Println("result =", result)
	// 过滤
    for _, text := range result {
        fmt.Println("text[0] = ", text[0])
        fmt.Println("text[1] = ", text[1])
    }
}

json

编码和解码

package main

import (
	"fmt"
	"encoding/json"
)
//成员变量名必须大写
type IT struct {
    Company string
    Subject []string
    IsOk bool
    Price float64
}
/* 二次编码
type IT struct {
    Company string `json:"-"`
    Subject []string `json:"subjects"`
    IsOk bool `json:",string"`
    Price float64
}
*/

func main() {
    s := IT{"IT", []string{"go", "c", "c++"}, true, 666.666}
    //buf, err := json.Marshal(s) 
    //格式化编码
    buf, err := json.MarshalIndent(s, "", " ")
    if err != nil {
        fmt.Println("err = ", err)
      	return 
    }
    fmt.Println("buf = ", string(buf))
    
    //解析
    jsonBuf := `json字符串`
    var tmp IT
    // 第二个参数地址传递
    err := json.Unmarshal([]byte(jsonBuf), &tmp)
    if err != nil {
        fmt.Println("err = ", err)
        return 
    }
    fmt.Println("tmp = ", tmp)
    
    type IT1 struct {
        Subject []string `json:"subjects"`
    }
    var Tmp2 IT1
    // 第二个参数地址传递
    err := json.Unmarshal([]byte(jsonBuf), &tmp2)
    if err != nil {
        fmt.Println("err = ", err)
        return 
    }
}
------------------
//通过map生成json
package main

import (
	"fmt"
	"encoding/json"
)

func main() {
	// 创建一个map
    m := make(map[string]interface{}, 4)
    m["company"] = "itcast"
    m["subjects"] = []string{"go", "c++"}
    m["isok"] = true
    m["price"] = 666.666
    
    result, err := json.Marshal(m)
    if err != nil {
        fmt.Println("err = ", err)
        return 
    }
    fmt.Println("result = ", string(result))
    
    jsonBuf := `json字符串`
    m := make(map[string]interface{}, 4)
    // 第二个参数地址传递
    err := json.Unmarshal([]byte(jsonBuf), &m)
    if err != nil {
        fmt.Println("err = ", err)
        return 
    }
    fmt.Println("m = ", m)
    // 不能直接赋值
    //var str string
    //str = m['conmpany']
    
    // 需要反推
    var str string
 	   
    for key, value := range m {
        switch data := value.(type) {
            case string:
            	str = data
            case bool:
            case float64:
        }
    }
}

文件操作

package main

import (
	"fmt"
	"OS"
    "io"
    "bufio"
)
func WriteFile(path string) {
    //打开文件
    f, err := os.Create(path)
    if err != nil {
        return 
    }
    
    //关闭
    defer f.close()
    
    var buf string
    for i := 0 i <10; i++ {
        buf = fmt.Sprintf("i = $d\n", i)
        f.WriteString(buf)        
    }
}

func ReadFile(path string) {
    f, err := os.Open(path) 
    if err != nil {
        return 
    }
    defer f,close
    r := bufio.NewReader(f)
    //读取一行
    r.ReadBytes('\n')
   
}

// 每次读取一行

func ReadFileLine(path string) {
    f, err := os.Open(path) 
    if err != nil {
        return 
    }
    defer f,close
    buf := make([]byte, 1024 * 2)
    n, err := Read(buf)
    if err != nil && err != io.EOF { //结尾
		return 
    }
    fmt.Println("buf = ", string(buf[:n]))
}



func main() {
    //os.Stdout.Close()
    //os.Stdout.WriteString("are y ok")
    path = "./demo.txt"
    WriteFile(path)
    ReadFile(path)
}

拷贝文件案例

package main

import (
	"fmt"
	"OS"
    "io"
)

func main() {
    list := os.Args
    if len(list) != 3 {
        fmt.Println("err")
        return 
    }
    srcFileName := list[1]
    dstFileName := list[2]
    if srcFileName == dstFileName {
        fmt.Println("err")
        return
    }
    sf, err1 := os.Open(srcFileName)
    if err1 != nil {
        fmt.Println("err")
        return
    }
    df, err 2 := os.Open(dstFileName)
    if err2 != nil {
        fmt.Println("err")
        return
    }
    
    defer sf.Close()
    defer df.Close()
    
    buf := make([]byte, 4 * 1024)
    for {
        n, err := sf.Read(buf)
        if err != nil {
			if err == io.EOF {
                break
            }
            fmt.Println("err")
        }
        df.Write(buf[:n])
    }
        
}

go优势

并发(时间片轮转交替使用)和并行

语音层面上的并发,支持GC自动垃圾回收,通过通信的方法支持同步

goroutine

go协程,十几个goroutine可能底层就是5,6个线程,go语音实现了goroutine之间的共享内存

package main

import (
	"fmt"
	"time"
)

func newTask() {
    for {
        fmt.Println("this is new")
        time.Sleep(time.Second)
    }
}
// 主协程退出,子协程也退出
func main() {
    go newTask()    //新建一个协程
    for {
        fmt.Println("this is main")
        time.Sleep(time.Second)
    }
}

----------------------
// runtime 包   设置协程
package main

import (
	"fmt"
	"runtime"
)


func main() {
    go func() {
        for i:= 0; i <5; i++
        {
            // 终止协程,协程中的程序都结束了
            runtime.Goexit()
            // 设置cpu数量
            n := runtime.GOMAXPEOCS(2)
            fmt.Println("go")   
        }    
    } ()
    for i:= 0; i < 2; i++ {
        // 让出时间片
        runtime.Goshed()
        fmt.Println("this is main")
    }
}

channel

通过管道实现通信,是一个数据类型

package main

import (
	"fmt"
	"runtime"
)
// 全局变量,创建channel
var ch = make(chan int)

func Printer(str string) {
    for _, data := range str {
        fmt.Printf("%c", data)
    }
    fmt.Printf("\n")
}

func person1() {
    Printer("hello")
    ch <- 666 
}

func person2() {
    <- ch //从管道取数据,接受,没数据会阻塞
    Printer("world")
}

func main() {
    go person1()
    go person2()
    for {
        
    }
}

--------------------
// 数据交换
package main

import (
	"fmt"
	"runtime"
)

func main() {
    ch := make(chan string)
    go func () {
        defer fmt.Println("son");
        
        for i := 0; i < 2; i++ {
            fmt.Pritln("i",i)
        }
        ch <- "我是子协程"
    }()
    str := <- ch
    fmt.Println('str',str)
}
--------------
// 无缓冲 => 接受和发送没准备好会阻塞  make(chan type, 0)
// 有缓存  make(chan type, capacity)  满了会阻塞
// 关闭channel
close(ch);
for {
    if num, ok := <-ch; ok == true {
        
    } else {
        
    }
}
// 遍历ch
for num := range ch {
    fmt
}
// 单向
pakeage main()

func main() {
    ch := make(chan int)
    var writenCh chan<- int = ch
    var readCh <-chan int = ch
    
    writeCh <- 666
    <- readCh
}

定时器

pakeage main()

import (
	"fmt"
    "time"
)

func main() {
    timer := time.NewTimer(1 * time.Second)
    // 一次
    for {
        <-time.C
        fmt.Println("时间到")
    }
}
--------------------
pakeage main()

import (
	"fmt"
    "time"
)

func main() {
    timer := time.NewTimer(3 * time.Second)
    
    go func() {
        <-timer.C
        fmt.Println("子协程可以打印了")
    }
    //重新设置
    timer.Reset(1 * time.Second)
    //停止
    timer.Stop()
    
    for {
        
    }
}
-----------------------------
// ticker  周期
pakeage main()

import (
	"fmt"
    "time"
)

func main() {
    ticker := time.NewTicker(1 * time.Second)
    i := 0
    for {
        <-ticker.C
        i++
        fmt.Println("i = ",i)
    }
}

select

监听channel

pakeage main

import (
	"fmt"
) 

func f1(ch chan<- int, quit <-chan bool) {
    x, y := 1, 1
    for {
        select {
            case ch <- x:
            x, y := y, x +y
            case flag := <- quit:
            	fmt.Println("exit")
            	return
            case <-time.After(3 * time.Second)
        }
    }
}

func main() {
    ch := make(chan int)
    quit := make(chan bool)
    go func() {
        for i := 0; i < 8; i++{
            num := <-ch
            fmt.Println("num =", num)
        }
     	quit<-true      
    }()
    
    f1(ch, quit)
    
}


go语言学习笔记

这个几个类型得用make创建

语法歧义

函数的特点

posted @   小润_c++  阅读(69)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
点击右上角即可分享
微信分享提示