栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Go语言基础语法(二)

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Go语言基础语法(二)

目录

1.数组

2.切片

2.1切片定义

2.2Reslice

2.3切片的扩展

 2.4向切片添加元素

2.5slice ops

3.Map

3.1map的定义

3.2map的操作

3.3map的一个例题

4.字符和字符串的处理

5.结构体和方法

6.封装

7.扩展已有类型

8.go语言的依赖管理

8.1 GOPATH

8.2GOVENDOR

8.3go mod


1.数组

定义数组的几种方法:

var arr1 [5]int                 //定义一个元素为5个的数组
arr2 := [3]int{1, 3, 5}         //用:=定义一个元素为3个的数组,这里要将元素写出来
arr3 := [...]int{2, 3, 4, 5, 3} //[...]表示这个数组的长度是不固定的
var grid [4][5]int

fmt.Println(arr1, arr2, arr3)
fmt.Println(grid)

遍历数组:

    //遍历数组
	for i, v := range arr3 { //i是数组下标,v是数组的值
		fmt.Println(i, v)
	}

	//若不想要下标,只想取值
	for _, v := range arr3 { //i是数组下标,v是数组的值
		fmt.Println(v)
	}

在go语言中,数组是值类型。

这就意味着在传入函数的时候,会被拷贝一遍,函数内的变动不会改变外面的值。

[10]int 和[20]int是不同的类型

func printArrary(arr [5]int) {
	arr[0] = 100
	for i, v := range arr { //i是数组下标,v是数组的值
		fmt.Println(i, v)
	}
}

fmt.Println("printArrary(arr1)")
printArrary(arr1)//这里第一个元素会变成100
fmt.Println("printArrary(arr3)")
printArrary(arr3)//这里第一个元素会变成100
fmt.Println("printArrary(arr1,arr3)")
fmt.Println(arr1, arr3)//这里两个数组的元素不会改变

那么要是想让传入的数组能够被函数改变,如何做呢?

其中一个办法是传入数组指针:
 

func printArraryptr(arr *[5]int) {
	arr[0] = 100
	for i, v := range arr { //i是数组下标,v是数组的值
		fmt.Println(i, v)
	}
}



	fmt.Println("printArraryptr(&arr1)")
	printArraryptr(&arr1)//第一个元素会编程100
	fmt.Println("printArraryptr(&arr3)")
	printArraryptr(&arr3)//第一个元素会变成100
	fmt.Println("fmt.Println(arr1, arr3)")
	fmt.Println(arr1, arr3)//两个数组的第一个元素都会变成100

但是go语言一般不用数组和数组指针当传入参数,因为太麻烦,一般是用切片。

到目前为止,以上第一部分的所有代码如下:

package main

import "fmt"

func printArrary(arr [5]int) {
	arr[0] = 100
	for i, v := range arr { //i是数组下标,v是数组的值
		fmt.Println(i, v)
	}
}

func printArraryptr(arr *[5]int) {
	arr[0] = 100
	for i, v := range arr { //i是数组下标,v是数组的值
		fmt.Println(i, v)
	}
}

func main() {
	var arr1 [5]int                 //定义一个元素为5个的数组
	arr2 := [3]int{1, 3, 5}         //用:=定义一个元素为3个的数组,这里要将元素写出来
	arr3 := [...]int{2, 3, 4, 5, 3} //[...]表示这个数组的长度是不固定的
	var grid [4][5]int

	fmt.Println(arr1, arr2, arr3)
	fmt.Println(grid)

	//遍历数组
	for i, v := range arr3 { //i是数组下标,v是数组的值
		fmt.Println(i, v)
	}

	//若不想要下标,只想取值
	for _, v := range arr3 { //i是数组下标,v是数组的值
		fmt.Println(v)
	}
	fmt.Println("printArrary(arr1)")
	printArrary(arr1)
	fmt.Println("printArrary(arr3)")
	printArrary(arr3)
	fmt.Println("printArrary(arr1,arr3)")
	fmt.Println(arr1, arr3)

	fmt.Println("printArraryptr(&arr1)")
	printArraryptr(&arr1)
	fmt.Println("printArraryptr(&arr3)")
	printArraryptr(&arr3)
	fmt.Println("fmt.Println(arr1, arr3)")
	fmt.Println(arr1, arr3)

}

2.切片

2.1切片定义
arr:=[...]int{0,1,2,3,4,5,6,7}
s:=arr[2:6]

//s就是切片,s的值是[2 3 4 5]
//在计算机里面[2:6]一般是包前不包后,也就是包含下标为2的但不包含为6的。

fmt.Println("arr[:6]", arr[:6])
fmt.Println("arr[2:]", arr[2:])
fmt.Println("arr[:]", arr[:])


//arr[2:6]: [2 3 4 5]
//arr[:6]: [0 1 2 3 4 5]
//arr[2:]: [2 3 4 5 6 7]
//arr[:]:[0 1 2 3 4 5 6 7]

切片不是值类型,所以传入函数的时候不是拷贝:

func updateSlice(s []int) {
	s[0] = 100
}


s1 := arr[2:]   //[2 3 4 5 6 7]
updateSlice(s1)
fmt.Println(s1)//[100 3 4 5 6 7]
fmt.Println(arr)//[0 1 100 3 4 5 6 7]

2.2Reslice

切片是可以反复取的(Reslice):

	fmt.Println("Reslice")//Reslic
	s2 := arr[:]
	fmt.Println(s2)//[0 1 100 3 4 5 6 7]
	s2 = s2[:5]
	fmt.Println(s2)//[0 1 100 3 4]
	s2 = s2[2:]
	fmt.Println(s2)//[100 3 4]

2.3切片的扩展

切片还可以扩展:

fmt.Println("Extending slice")
arr[0], arr[2] = 0, 2
s1 = arr[2:6]
s2 = s1[3:5]
fmt.Println("s1", s1)
fmt.Println("s2", s2)


//s1: [2 3 4 5],这没什么好说的
//s2,[5 6],这里s1明明没有s[4]这个元素如何取出来的呢?

s2如何能取出s[4]呢?

原因是切片是数组的一个视图,s1取数组下标为2-5的元素。

所以s1的下标为s[0]-s[3],但这时候s1是能够知道有s[4](为6),s[5](为7)的

只不过要是硬取是取不出来的,但是用s2对其取切片是能够取到的。

这就是切片扩展:

那么slice是如何实现的呢:

 首先这个slice是有一个指针指向array的其中一段的开始,然后len是本切片可以直接访问的长度,cap是记录了指针到结尾的长度,所以可以实现切片扩展,只要不超过cap的长度即可。

 slice是可以向后扩展的,但是不可以向前扩展。

s[i]不可以超过len(s),向后扩展不可以超越底层数组cap(s)

fmt.Println("Extending slice")
arr[0], arr[2] = 0, 2
fmt.Println("arr=", arr)
s1 = arr[2:6]
s2 = s1[3:5]
fmt.Printf("s1=%v,len(s1)=%d,cap(s1)=%dn", s1, len(s1), cap(s1))
fmt.Printf("s2=%v,len(s2)=%d,cap(s2)=%dn", s2, len(s2), cap(s2))

结果:

 2.4向切片添加元素
s3 := append(s2, 10)
s4 := append(s3, 11)
s5 := append(s4, 12)
fmt.Println("s3,s4,s5=", s3, s4, s5)
//s2的cap是3,[5 6]后面隐藏了一个7,这时候s3执行的时候,将7换成了10
//s4,s5后面再添加就超过了cap,这时候go语言会在内部新建一个arr,也就是说s4和s5不再view当前arr了
fmt.Println("arr=", arr)

结果:

添加元素时如果超越了cap,系统会重新分配更大的底层数组,以便于能够执行添加元素的操作,这个更大的底层数组会将原来的数组拷贝过去,那么原来的数组如果程序还用到了就保留,如果程序没用到了,那么go语言的垃圾回收机制会将原来的数组回收掉。

由于值传递的髋膝,必须接收append的返回值,因为添加元素后,slice的len可能改变,cap也可能改变(创建了更大的底层数组后),所以必须用一个新的切片来接收这个append的返回值。

2.5slice ops

 定义一个切片:

	var s []int //声明一个切片,这时候s==nil
	for i := 0; i < 100; i++ {
		s = append(s, 2*i)
	}
	fmt.Println(s)

那么这样定义的slice,它的len和cap是多少呢:

func printSlice(s []int) {
	fmt.Printf("len=%d,cap=%dn", len(s), cap(s))
}
func main() {
	var s []int //声明一个切片,这时候s==nil
	for i := 0; i < 100; i++ {
		printSlice(s)
		s = append(s, 2*i)
	}
	fmt.Println(s)
}

结果为:

可以看到,len是一个一个增加,cap每次装不下了会成倍增加。

还可以这样创建slice:

	s1 := []int{2, 4, 6, 8}
	printSlice(s1)
	s2 := make([]int, 16) //创建len为16,cap也是16的slice
	printSlice(s2)
	s3 := make([]int, 16, 32) //创建len为16,cap为32的slice
	printSlice(s3)

 复制slice:

	fmt.Println("Copying slice")
	copy(s2, s1) //把s1copy给s2
	printSlice(s2)

删除slice中间的元素:

	fmt.Println("Deleting elements from slice")
	s2 = append(s2[:3], s2[4:]...)
	printSlice(s2)

删除头元素:

	fmt.Println("Popping from front")
	front := s2[0]
	s2 = s2[1:]
	fmt.Println(front)

删除尾元素:

	fmt.Println("Poping from back")
	tail := s2[len(s2)-1]
	s2 = s2[:len(s2)-1]
	fmt.Println(tail)

到目前为止第二部分的全部代码如下:

package main

import "fmt"

func updateSlice(s []int) {
	s[0] = 100
}

func main() {
	arr := [...]int{0, 1, 2, 3, 4, 5, 6, 7}
	s := arr[2:6]
	fmt.Println("arr[2:6]", s)

	//s就是切片,s的值是[2 3 4 5]
	//在计算机里面[2:6]一般是包前不包后,也就是包含下标为2的但不包含为6的。
	fmt.Println("arr[:6]", arr[:6])
	fmt.Println("arr[2:]", arr[2:])
	fmt.Println("arr[:]", arr[:])

	s1 := arr[2:]
	//s2 := arr[:]
	updateSlice(s1)
	fmt.Println(s1)
	fmt.Println(arr)
	fmt.Println("Reslice")
	s2 := arr[:]
	fmt.Println(s2)
	s2 = s2[:5]
	fmt.Println(s2)
	s2 = s2[2:]
	fmt.Println(s2)

	fmt.Println("Extending slice")
	arr[0], arr[2] = 0, 2
	fmt.Println("arr=", arr)
	s1 = arr[2:6]
	s2 = s1[3:5]
	fmt.Printf("s1=%v,len(s1)=%d,cap(s1)=%dn", s1, len(s1), cap(s1))
	fmt.Printf("s2=%v,len(s2)=%d,cap(s2)=%dn", s2, len(s2), cap(s2))
	fmt.Println("---------------------------")
	s3 := append(s2, 10)
	s4 := append(s3, 11)
	s5 := append(s4, 12)
	fmt.Println("s3,s4,s5=", s3, s4, s5)
	//s2的cap是3,[5 6]后面隐藏了一个7,这时候s3执行的时候,将7换成了10
	//s4,s5后面再添加就超过了cap,这时候go语言会在内部新建一个arr,也就是说s4和s5不再view当前arr了
	fmt.Println("arr=", arr)
}


//--------------------------------------------------------------------

package main

import "fmt"

func printSlice(s []int) {
	fmt.Printf("%v,len=%d,cap=%dn", s, len(s), cap(s))
}
func main() {

	fmt.Println("Creating slice")
	var s []int //声明一个切片,这时候s==nil
	for i := 0; i < 100; i++ {
		printSlice(s)
		s = append(s, 2*i)
	}
	fmt.Println(s)

	s1 := []int{2, 4, 6, 8}
	printSlice(s1)
	s2 := make([]int, 16) //创建len为16,cap也是16的slice
	printSlice(s2)
	s3 := make([]int, 16, 32) //创建len为16,cap为32的slice
	printSlice(s3)

	fmt.Println("Copying slice")
	copy(s2, s1) //把s1copy给s2
	printSlice(s2)

	fmt.Println("Deleting elements from slice")
	s2 = append(s2[:3], s2[4:]...)
	printSlice(s2)

	fmt.Println("Popping from front")
	front := s2[0]
	s2 = s2[1:]
	fmt.Println(front)

	fmt.Println("Poping from back")
	tail := s2[len(s2)-1]
	s2 = s2[:len(s2)-1]
	fmt.Println(tail)

	fmt.Println(s2)
}



3.Map

3.1map的定义

map的定义:

map[key的类型]值的类型

复合map的定义:

map[key1的类型]map[key2的类型]值的类型

map的定义:

	m1 := map[string]string{ //第一种定义方式
		"name":   "maoweiyang",
		"age":    "18",
		"yanzhi": "good",
	}
	m2 := make(map[string]int) //第二种定义方式,m2==empty map

	var m3 map[string]int //第三种定义方式,m3==nil

	fmt.Println(m1)
	fmt.Println(m2)
	fmt.Println(m3)

3.2map的操作

map的遍历:

	fmt.Println("Traversing map")
	for k, v := range m1 {
		fmt.Println(k, v)
	}

注意:map里面的元素是无序的。

map取值:

	fmt.Println("Getting values")
	myname := m1["name"]
	fmt.Println(myname)

注意:当key填写错误的时候,go并不会报错,而是会取出一个空值。

那么如何判断所取关键字对应的元素在不在:

	fmt.Println("Getting values")
	myname, ok := m1["name"]
	fmt.Println(myname, ok)
	myneme, ok := m1["neme"]
	fmt.Println(myneme, ok)

结果为:

所以一般这样操作:

	if myname, ok := m1["neme"]; ok {
		fmt.Println(myname, ok)
	} else {
		fmt.Println("key does not exist")
	}

 删除元素:

	fmt.Println("Deleting values")
	age, ok := m1["age"]
	fmt.Println(age, ok)
	delete(m1, "age")
	age, ok = m1["age"]
	fmt.Println(age, ok)

关于map的key:

  1. map使用哈希表,必须可以比较相等。
  2. 除了slice,map,function的内建类型都可以作为key。
  3. Struct类型不包含上述字段,也可以作为key。

到目前为止第三部分的所有代码如下:

package main

import "fmt"

func main() {
	m1 := map[string]string{ //第一种定义方式
		"name":   "maoweiyang",
		"age":    "18",
		"yanzhi": "good",
	}
	m2 := make(map[string]int) //第二种定义方式,m2==empty map

	var m3 map[string]int //第三种定义方式,m3==nil

	fmt.Println(m1)
	fmt.Println(m2)
	fmt.Println(m3)

	fmt.Println("Traversing map")
	for k, v := range m1 {
		fmt.Println(k, v)
	}

	fmt.Println("Getting values")

	myneme, ok := m1["neme"]
	fmt.Println(myneme, ok)

	if myname, ok := m1["neme"]; ok {
		fmt.Println(myname, ok)
	} else {
		fmt.Println("key does not exist")
	}

	fmt.Println("Deleting values")
	age, ok := m1["age"]
	fmt.Println(age, ok)
	delete(m1, "age")
	age, ok = m1["age"]
	fmt.Println(age, ok)
}

3.3map的一个例题

题目:给定一个字符串s,请你找出其中不含有重复字符的 最长子串的长度 。

例如:

abcabcbb   :   3

bbbbbbb:1

pwwkew:3

思路:

start:当前检测的无重复子串的开始位置。

x:需要检查其是否需要加入当前无重复子串的元素。

lastOccurred[X]:x最后出现的位置。

lastOccurred[X]不存在,或者小于start的位置,那么无需操作

lastOccurred[X]大于等于start的位置,那么更新start到lastOccurred[X]+1

package main

import "fmt"

func lengthofNonRepeatingSubStr(s string) int {
	lastOccurred := make(map[byte]int)
	start := 0 //当前检测的无重复子串的开始位置。
	maxLength := 0
	for i, ch := range []byte(s) {
		//ch最后出现的位置存在且在start后。
		if lastI, ok := lastOccurred[ch]; ok && lastI >= start {
			start = lastI + 1 //start移动到ch最后出现的位置的后面一位。
		}

		if i-start+1 > maxLength {
			//如果当前无重复字符串比记录的长度要长
			maxLength = i - start + 1
		}
		lastOccurred[ch] = i //更新ch最后出现的位置
	}

	return maxLength
}
func main() {
	fmt.Println(lengthofNonRepeatingSubStr("abcabcbb"))
	fmt.Println(lengthofNonRepeatingSubStr("bbbbb"))
	fmt.Println(lengthofNonRepeatingSubStr("pwwkew"))
	fmt.Println(lengthofNonRepeatingSubStr(""))
	fmt.Println(lengthofNonRepeatingSubStr("b"))
	fmt.Println(lengthofNonRepeatingSubStr("abcdef"))
}

4.字符和字符串的处理

rune就是go的char类型

func main() {
	s := "Yes我爱我的祖国!" //UTF-8编码
	for _, b := range []byte(s) {
		fmt.Printf("%X ", b)

	}
	fmt.Println()
}

结果:

 可以看到,UTF-8的编码方式中,英文字符一个占1个字节(8位),中文字符一个占3个字节。

	for i, ch := range s { // ch is a rune
		fmt.Printf("(%d %X)", i, ch)
	}
	fmt.Println()

可以看到,ch是一个rune类型,它将string进行了UTF-8的解码,然后进行unicode编码,然后放在了rune类型里面。

统计rune的数目:

fmt.Println("Rune count", utf8.RuneCountInString(s)) //结果为10

 遍历s:

	bytes := []byte(s)
	for len(bytes) > 0 {
		ch, size := utf8.DecodeRune(bytes) //ch是字符,size是位置,英文隔1,中文隔3
		bytes = bytes[size:]
		fmt.Printf("%c ", ch)
	}
	fmt.Println()

其他的一些字符串的操作:

Fields, Split, Join
Contains, Index
ToLower,ToUpper
Trim, TrimRight,TrimLeft

到这里为止,第四部分的全部代码如下:

package main

import (
	"fmt"
	"unicode/utf8"
)

func main() {
	s := "Yes我爱我的祖国!" //UTF-8编码
	for _, b := range []byte(s) {
		fmt.Printf("%X ", b)
	}
	fmt.Println()

	for i, ch := range s { // ch is a rune
		fmt.Printf("(%d %X)", i, ch)
	}
	fmt.Println()
	fmt.Println("Rune count", utf8.RuneCountInString(s)) //结果为10

	bytes := []byte(s)
	for len(bytes) > 0 {
		ch, size := utf8.DecodeRune(bytes) //ch是字符,size是位置,英文隔1,中文隔3
		bytes = bytes[size:]
		fmt.Printf("%c ", ch)
	}
	fmt.Println()

	for i, ch := range []rune(s) {
		fmt.Printf("(%d %c)", i, ch)
	}
	fmt.Println()

}

5.结构体和方法

go语言仅支持封装,不支持继承和多态。

结构体定义以及声明:

package main

import "fmt"

type treeNode struct {
	value       int
	left, right *treeNode
}

func createNode(value int) *treeNode { //工厂函数
	return &treeNode{value: value}
}

func main() {
	var root treeNode
	root = treeNode{value: 3}
	root.left = &treeNode{}
	root.right = &treeNode{5, nil, nil}
	root.right.left = new(treeNode)
	root.left.right = createNode(2)
	nodes := []treeNode{
		{value: 3},
		{},
		{6, nil, &root},
	}
	fmt.Println(nodes)
}

注意:上面用了自定义工厂函数来创建结构体,且返回了局部变量的地址(go语言是允许这样的),用这种方法来代替构造函数的功能,go语言中是没有构造函数这种说法的。

在go语言的结构体中,函数如何实现呢:

func (node treeNode) print() {
	fmt.Println(node.value)
}

函数有个接收者,这个接收者是在函数名字前面价格括号,里面协商接收的结构体类型。

使用时候:

	root.print()

这里实际上也相当于一个参数传递,也是值传递:

func (node treeNode) setValue(value int) {
	node.value = value
}


root.right.left.setValue(4)
root.right.left.print()
fmt.Println()

上述代码打印结果还是0,是改不掉的,即这里也是值传递。

那么如果希望改变:

func (node *treeNode) setValue(value int) {
	node.value = value
}

root.right.left.setValue(4)

把接收者改为指针,但是调用的时候还是照常调用。

在go语言中,参数是指针的时候可以用地址调用也可以用值调用,当参数是值的时候,可以用地址调用也可以用值调用,编译器可以帮我们处理这些不对应的关系。

只有指针才可以改变结构内容。

nil指针也可以调用方法。

实现一个遍历节点的函数:

func (node *treeNode) traverse() {
	if node == nil {
		return
	}
	node.left.traverse()
	node.print()
	node.right.traverse()

}

要改变内容的话必须使用指针接收者。

结构过大也需要考虑使用指针接收者,因为拷贝会影响性能。

一致性:有指针接收者,最好都是指针接收者。

6.封装

在go语言中名字一般使用CamelCase

首字母大写代表:public

首字母小写代表:private

这个public或者private是针对包来说的。

每个目录一个包

main包包含可执行入口

为结构定义的方法必须放在同一个包内

但可以是不同的文件

tree目录下的包为tree(当然包名可以和目录名不一样),ebtry目录下的包名为main,那么main包下的代码要用tree包里面的结构和方法,就必须将tree给import进去,“包名.结构体”使用,注意tree包里面的名字都要首字母大写。

7.扩展已有类型

 如何扩充系统类型或者别人的类型:定义别名或者使用组合

例子一:

定义别名:最简单

type myTreeNode struct {
	node *tree.Node
}

func (myNode *myTreeNode) postOrder() {
	if myNode == nil || myNode.node == nil {
		return
	}

	left := myTreeNode{myNode.node.Left}
	left.postOrder()
	right := myTreeNode{myNode.node.Right}
	right.postOrder()
	myNode.node.Print()

}

例子二:

使用组合:最常用

package queue

type Queue []int

func (q *Queue) Push(v int) {
	*q = append(*q, v)
}

func (q *Queue) Pop() int {
	head := (*q)[0]
	*q = (*q)[1:]
	return head
}

func (q *Queue) IsEmpty() bool {
	return len(*q) == 0
}
package main

import (
	"Basic_grammar_go/queue"
	"fmt"
)

func main() {
	q := queue.Queue{1}
	q.Push(2)
	q.Push(3)
	fmt.Println(q.Pop())
	fmt.Println(q.Pop())
	fmt.Println(q.IsEmpty())
	fmt.Println(q.Pop())
	fmt.Println(q.IsEmpty())
}

例子三:

使用内嵌的方式来扩展已有的类型:需要省下很多代码

package main

import (
	"Basic_grammar_go/tree"
	"fmt"
)

type myTreeNode struct {
	*tree.Node //Ebedding 内嵌,不要名字,那么取值的时候直接忽略前面的,直到Node
}

func (myNode *myTreeNode) postOrder() {
	if myNode == nil || myNode.Node == nil {
		return
	}

	left := myTreeNode{myNode.Left}
	left.postOrder()
	right := myTreeNode{myNode.Right}
	right.postOrder()
	myNode.Node.Print()

}

func main() {
	root := myTreeNode{&tree.Node{Value: 3}}
	root.Left = &tree.Node{}
	root.Right = &tree.Node{5, nil, nil}
	root.Right.Left = new(tree.Node)
	root.Left.Right = tree.CreateNode(2)
	nodes := []tree.Node{
		{Value: 3},
		{},
		{6, nil, nil},
	}
	fmt.Println(nodes)

	root.Print()
	root.Right.Left.SetValue(4)
	root.Right.Left.Print()
	fmt.Println()
	root.Traverse()
	root.postOrder()
}

8.go语言的依赖管理

依赖管理的三个阶段 GOPATH,GOVENDOR,go mod

8.1 GOPATH

你告诉我一个GOPATH,你要的所有依赖包,我都去这个GOPATH里面找。

8.2GOVENDOR

每个项目都有自己的vendor目录,存放第三方库

大量第三方依赖管理工具:glide,dep,go dep,...

8.3go mod

例如:

go get -u go.uber.org/zap

会安装zap1.12

那么改为1.11怎么改

//安装1.11版本
go get -u go.uber.org/zap@v1.11

//清除1.12
go mod tidy

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/489100.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号