当前位置: 首页 > news >正文

政务网站建设工作总结/揭阳新站seo方案

政务网站建设工作总结,揭阳新站seo方案,政府网站建设整改落实情况报告,陕西宁德建设工程有限公司网站5 map、结构体、方法、面向对象【Go语言教程】 1 map 1.1 概念及声明 ①概念 基本语法:var map 变量名 map[keytype]valuetype key 可以是什么类型? golang 中的 map,的 key 可以是很多种类型,比如 bool, 数字,stri…

5 map、结构体、方法、面向对象【Go语言教程】

1 map

1.1 概念及声明

①概念

基本语法:var map 变量名 map[keytype]valuetype

  • key 可以是什么类型?
    golang 中的 map,的 key 可以是很多种类型,比如 bool, 数字,string, 指针, channel , 还可以是只包含前面几个类型的 接口, 结构体, 数组
    通常 key 为 int 、string
    注意: slice, map 还有 function 不可以,因为这几个没法用 == 来判断
  • valuetype 可以是什么类型?
    valuetype 的类型和 key 基本一样,这里我就不再赘述了通常为: 数字(整数,浮点数),string,map,struct

②声明方式

  1. var a map[string]string
  2. var a map[string]int
  3. var a map[int]string
  4. var a map[string]map[string]string
    注意:声明是不会分配内存的,初始化需要 make ,分配内存后才能赋值和使用
func main(){//map的声明和注意事项var a map[string]string//在使用map之前,需要先make(给map分配内存空间)a = make(map[string]string, 10)a["no1"] = "curry"a["no2"] = "徐杰"a["no3"] = "John"a["no4"] = "欧文"a["no3"] = "Bob"fmt.Println(a)
}

在这里插入图片描述

对上面的代码进行说明:

  1. map 在使用前一定要 make
  2. map 的 key 是不能重复,如果重复了,则以最后这个 key-value 为准
  3. map 的 value 是可以相同的.
  4. map 的 key-value 是无序
  5. make 内置函数数目

在这里插入图片描述
map的三种初始化方式:

package main
import ("fmt"
)func main(){//map的初始化方式//1. 先声明后makevar a map[string]string//在使用map前,需要先make(给map分配数据空间)a = make(map[string]string, 10)a["no1"] = "天暗星"a["no2"] = "天捷星"a["no3"] = "天速星"fmt.Println(a)//2. 声明时直接makecities := make(map[string]string)cities["no1"] = "newYork"cities["no2"] = "taibei"cities["no3"] = "beijing"fmt.Println(cities)//3. 声明时,直接初始化并赋值heros := map[string]string{"hero1": "宋江","hero2": "赵云","hero3": "岳飞",}heros["hero3"] = "林冲"fmt.Println("heros=", heros)// map[no1:天暗星 no2:天捷星 no3:天速星]// map[no1:newYork no2:taibei no3:beijing]     // heros= map[hero1:宋江 hero2:赵云 hero3:林冲]	
}

1.2 map的crud

①map的增加和更新

map[“key”] = value //如果 key 还没有,就是增加,如果 key 存在就是修改。

func main(){a := make(map[string]string)//map中没有存在key1,因此直接新增a["key1"] = "jack"//map中已经存在了key1,因此直接更新a["key1"] = "jackson"//map[key1:jackson]fmt.Println(a)
}

②map的删除

delete(map,“key”) ,delete 是一个内置函数,如果 key 存在,就删除该 key-value,如果 key 不存在, 不操作,但是也不会报错
在这里插入图片描述
在这里插入图片描述

func main(){a := make(map[string]string)a["key1"] = "jack"//删除key1delete(a, "key1")//map[]fmt.Println(a)
}
  • 注意:如果我们要删除 map 的所有 key ,没有一个专门的方法一次删除,可以遍历一下 key, 逐个删除或者 map = make(…),make 一个新的,让原来的成为垃圾,被 gc 回收
    在这里插入图片描述
    ③map的查找
    在这里插入图片描述

对上面代码的说明:
说明:如果 heroes 这个 map 中存在 “no1” , 那么 findRes 就会返回 true,否则返回 false

func main(){a := make(map[string]string)a["key1"] = "jack"a["key2"] = "tom"val, ok := a["key3"]if ok {fmt.Println("值为:", val)}else {fmt.Println("没找到")}
}

1.3 map的遍历与切片

①map的遍历

  • 案例演示相对复杂的 map 遍历:该 map 的 value 又是一个 map
    说明:map 的遍历使用 for-range 的结构遍历
package main
import ("fmt"
)func main(){//map通过for-range方式来进行遍历//1. 遍历普通mapcities := make(map[string]string)cities["no1"] = "北京"cities["no2"] = "天津"cities["no3"] = "上海"for k, v := range cities {fmt.Printf("k=%v v=%v\n", k, v)}//2. 遍历双重map【map的value又是一个map】studentMap := make(map[string]map[string]string)//map使用之前需要make make(map[string]string, 3)studentMap["stu01"] = make(map[string]string, 3)studentMap["stu01"]["name"] = "jack"studentMap["stu01"]["sex"] = "男"studentMap["stu01"]["address"] = "北京胡同"studentMap["stu02"] = make(map[string]string)studentMap["stu02"]["name"] = "lucy"studentMap["stu02"]["sex"] = "女"studentMap["stu02"]["address"] = "上海外滩"for k1, v1 := range studentMap {fmt.Println("k1=", k1)for k2, v2 := range v1 {fmt.Printf("\t k2=%v v2=%v\n", k2, v2)}fmt.Println()}
}

在这里插入图片描述
在这里插入图片描述
②map切片

切片的数据类型如果是 map,则我们称为 slice of map,map 切片,这样使用则 map 个数就可以动态变化了。

案例:

要求:使用一个 map 来记录 monster 的信息 name 和 age, 也就是说一个 monster 对应一个 map,并且妖怪的个数可以动态的增加=>map 切片

package main
import ("fmt"
)func main(){//1. 声明一个map切片var monsters []map[string]stringmonsters = make([]map[string]string, 2) //准备放入2个妖怪monsters[0] = make(map[string]string, 2)monsters[0]["name"] = "牛魔王"monsters[0]["age"] = "500"monsters[1] = make(map[string]string, 2)monsters[1]["name"] = "狐狸精"monsters[1]["age"] = "300"//下面写法越界,因为只开辟了两块空间//panic: runtime error: index out of range [2] with length 2 // monsters[2] = make(map[string]string, 2)//这里我们可以使用切片的append函数,动态的增加monster//①定义monster信息newMonster := map[string]string{"name" : "新妖怪~火云邪神","age" : "200",}monsters = append(monsters, newMonster)//[map[age:500 name:牛魔王] map[age:300 name:狐狸精] map[age:200 name:新妖怪~火云邪神]]fmt.Println(monsters)
}

1.3 map的排序及使用细节

①map的排序

  1. golang 中没有一个专门的方法针对 map 的 key 进行排序
  2. golang 中的 map 默认是无序的,注意也不是按照添加的顺序存放的,你每次遍历,得到的输出可能不一样.
  3. golang 中 map 的排序,是先将 key 进行排序,然后根据 key 值遍历输出即可
package main
import ("fmt""sort"
)func main(){//map的排序map1 := make(map[int]int, 10)map1[10] = 100map1[1] = 13map1[4] = 56map1[0] = 90fmt.Println(map1)//map的排序://1. 先将map的key放入到切片中//2. 对切片进行排序//3. 遍历切片,然后按照key来输出map的值var keys []intfor k, _ := range map1 {keys = append(keys, k)}//排序sort.Ints(keys)//根据排序后的切片输出map中key对应的valuefor _, k := range keys {fmt.Printf("map1[%v]=%v \n", k, map1[k])}
}

在这里插入图片描述
②使用细节

  1. map 是引用类型,遵守引用类型传递的机制,在一个函数接收 map,修改后,会直接修改原来的 map
    在这里插入图片描述
  1. map 的容量达到后,再想 map 增加元素,会自动扩容,并不会发生 panic,也就是说 map 能动态的增长 键值对(key-value)
  2. map 的 value 也经常使用 struct 类型,更适合管理复杂的数据(比前面 value 是一个 map 更好),比如 value 为 Student 结构体
    在这里插入图片描述

2 结构体(面向对象特性)

2.1 概念

Golang中面向对象的特点:

  1. Golang 也支持面向对象编程(OOP),但是和传统的面向对象编程有区别,并不是纯粹的面向对象语言。所以我们说 Golang 支持面向对象编程特性是比较准确的。

  2. Golang 没有类(class),Go 语言的结构体(struct)和其它编程语言的类(class)有同等的地位,你可以理解 Golang 是基于 struct 来实现 OOP 特性的。

  3. Golang 面向对象编程非常简洁,去掉了传统 OOP 语言的继承、方法重载、构造函数和析构函数、隐藏的 this 指针等等

  4. Golang 仍然有面向对象编程的继承,封装和多态的特性,只是实现的方式和其它 OOP 语言不一样,比如继承 :Golang 没有 extends 关键字,继承是通过匿名字段来实现。

  5. Golang 面向对象(OOP)很优雅,OOP 本身就是语言类型系统(type system)的一部分,通过接口(interface)关联,耦合性低,也非常灵活。后面同学们会充分体会到这个特点。也就是说在Golang 中面向接口编程是非常重要的特性。

结构体变量(实例)在内存的布局(重要!)
在这里插入图片描述

2.2 声明、使用及注意细节

①声明

type 结构体名称 struct { field1 typefield2 type}
  1. 从概念或叫法上看: 结构体字段 = 属性 = field (即授课中,统一叫字段)
  2. 字段是结构体的一个组成部分,一般是基本数据类型、数组,也可是引用类型。比如我们前面定义猫结构体 的 Name string 就是属性

②使用

package main
import ("fmt"_ "sort"
)//首字母大写表明公有
type Person struct {Name stringAge intslice []int //切片map1 map[string]string //map
}func main(){//1. 直接声明var person Person person.slice = make([]int, 5)person.map1 = make(map[string]string)fmt.Println("person=", person)//2. 直接声明并初始化 {}var person2 Person = Person{"jack", 20, make([]int, 5), make(map[string]string)}fmt.Println("person2=", person2)// person3 := Person{"jucy", 0, make([]int, 5), make(map[string]string)}// fmt.Println("person3=", person3)//3. &var p3 *Person = new(Person)//因为p3是一个指针, 因此://(*)p3.Name = "smith" 也可以这样写 p3.Name = "smith"//go的设计者为了程序员使用方便,所以在底层帮我们做了封装,底层会对 p3.Name = "smith"进行处理,会变为(*p3).Name(*p3).Name = "smith"p3.Name = "john"fmt.Println("person3=", p3)//4. {}var p4 *Person = &Person{}(*p4).Name = "scott"p4.Name = "scott~~"fmt.Println("person4=",*p4)
}

在这里插入图片描述

③注意细节

  1. 字段声明语法同变量,示例:字段名 字段类型
  2. 字段的类型可以为:基本类型、数组或引用类型
  3. 在创建一个结构体变量后,如果没有给字段赋值,都对应一个零值(默认值),规则同前面讲的一样:
    布尔类型是 false ,数值是 0 ,字符串是 “”。
    数组类型的默认值和它的元素类型相关,比如 score [3]int 则为[0, 0, 0]
    指针,slice,和 map 的零值都是 nil ,即还没有分配空间。
    在这里插入图片描述
  1. 不同结构体变量的字段是独立,互不影响,一个结构体变量字段的更改,不影响另外一个, 结构体是值类型。

注意:

  1. 结构体的所有字段在内存中是连续的
  2. 结构体是用户单独定义的类型,和其它类型进行转换时需要有完全相同的字段(名字、个数和类型)
  3. 结构体进行 type 重新定义(相当于取别名),Golang 认为是新的数据类型,但是相互间可以强转
  4. struct 的每个字段上,可以写上一个 tag, 该 tag 可以通过反射机制获取,常见的使用场景就是序列化和反序列化。

序列化使用场景:
在这里插入图片描述

package main
import ("fmt""encoding/json"
)//前后端传输过程中,前端更希望字段名为小写,但是go语言如果为了让其他包能访问到字段只能
//首字母大写,因此就需要struct的tag来完成
type Monster struct {Name string `json:"name"` //`json:"name"`就是strcut tagAge int `json:"age"`Skill string `json:"skill"`
}func main(){monster := Monster{"牛魔王", 500, "芭蕉扇~"}//将monster变量序列化为json格式字符串//json.Marshal 函数中使用反射jsonStr, err := json.Marshal(monster)if err != nil {fmt.Println("json处理错误 ", err)} //jsonStr {"name":"牛魔王","age":500,"skill":"芭蕉扇~"}fmt.Println("jsonStr", string(jsonStr))
}

3 方法

3.1 介绍

在某些情况下,我们要需要声明(定义)方法。比如 Person 结构体:除了有一些字段外( 年龄,姓名…),Person 结构体还有一些行为比如:可以说话、跑步…,通过学习,还可以做算术题。这时就要用方法才能完成。

  • Golang 中的方法是作用在指定的数据类型上的(即:和指定的数据类型绑定),因此自定义类型, 都可以有方法,而不仅仅是 struct。

3.2 方法的声明和调用

package main
import ("fmt"
)type Person struct {Name string
}//给Person绑定一个方法[绑定之后只有Person才能使用]
func (p Person) test(){fmt.Println("test() name=", p.Name)
}func main(){var p Personp.Name = "tom"p.test()//调用方法
}
  1. test 方法和 Person 类型绑定
  2. test 方法只能通过 Person 类型的变量来调用,而不能直接调用,也不能使用其它类型变量来调
  3. func (p Person) test() {}… p 表示哪个 Person 变量调用,这个 p 就是它的副本, 这点和函数传参非常相似。
  4. p 这个名字,有程序员指定,不是固定, 比如修改成 person 也是可以

3.3 方法的调用和传参机制(重要)

方法的调用和传参机制和函数基本一样,不一样的地方是方法调用时,会将调用方法的变量,当做实参也传递给方法。下面我们举例说明。

例如:getSum的方法过程
在这里插入图片描述

说明:

  1. 在通过一个变量去调用方法时,其调用机制和函数一样
  2. 不一样的地方时,变量调用方法时,该变量本身也会作为一个参数传递到方法(如果变量是值类型,则进行值拷贝,如果变量是引用类型,则进行地址拷贝)

3.4 注意事项

①声明和定义

func (recevier type) methodName(参数列表) (返回值列表){
方法体
return 返回值
}

  1. 参数列表:表示方法输入
  2. recevier type : 表示这个方法和 type 这个类型进行绑定,或者说该方法作用于 type 类型
  3. receiver type : type 可以是结构体,也可以其它的自定义类型
  4. receiver : 就是 type 类型的一个变量(实例),比如 :Person 结构体 的一个变量(实例)
  5. 返回值列表:表示返回的值,可以多个
  6. 方法主体:表示为了实现某一功能代码块
  7. return 语句不是必须的。

②注意事项和细节

  1. 结构体类型是值类型,在方法调用中,遵守值类型的传递机制,是值拷贝传递方式
  2. 如程序员希望在方法中,修改结构体变量的值,可以通过结构体指针的方式来处理
  3. Golang 中的方法作用在指定的数据类型上的(即:和指定的数据类型绑定),因此自定义类型, 都可以有方法,而不仅仅是 struct, 比如 int , float32 等都可以有方法
  4. 方法的访问范围控制的规则,和函数一样。方法名首字母小写,只能在本包访问,方法首字母大写,可以在本包和其它包访问。
  5. 如果一个类型实现了 String()这个方法,那么 fmt.Println 默认会调用这个变量的 String()进行输出【类比java中重写了toString方法】

3.5 方法和函数的区别

  1. 调用方式不一样
    函数的调用方式: 函数名(实参列表)
    方法的调用方式: 变量.方法名(实参列表)
  2. 对于普通函数,接收者为值类型时,不能将指针类型的数据直接传递,反之亦然
  3. 对于方法(如 struct 的方法),接收者为值类型时,可以直接用指针类型的变量调用方法,反过来同样也可以
package mainimport ("fmt"
)type Person struct {Name string
}//对于方法(如:struct的方法)
//接收者为值类型时,可以直接使用指针类型的变量调用方法,反过来也同样可以//1. 和结构体类型绑定
func (p Person) test03(){p.Name = "jack"fmt.Println("test03()=", p.Name)
}//2. 和指针类型绑定
func (p *Person) test04(){p.Name = "mary"fmt.Println("test04()=", p.Name)
}func main(){var p Personp.Name = "tom"p.test03()fmt.Println("main() p.Name=", p.Name) //tom(&p).test03() //从形式上看是传入地址,但是本质上仍然是值拷贝【因为test03方法是和结构体绑定的】fmt.Println("main() p.Name=", p.Name) //tom(&p).test04()fmt.Println("main() p.Name=", p.Name) //maryp.test04() //等价(&p).test04(),从形式上是传入值类型,但是本质仍然是地址拷贝//【调用方不决定,决定权在调用的方法和什么绑定】}

总结:

  1. 不管调用形式如何,真正决定是值拷贝还是地址拷贝,看这个方法是和哪个类型绑定.
  2. 如果是和值类型,比如 (p Person) , 则是值拷贝, 如果和指针类型,比如是 (p *Person) 则是地址拷贝。

3.6 工厂模式(类比get、set)

在java中为了访问私有的属性,通常会提供对应的get、set方法,但是Go中并没有;因此我们可以使用工厂模式来达到类似的效果

  • Golang 的结构体没有构造函数,通常可以使用工厂模式来解决这个问题。
一个结构体的声明是这样的: 
package model
type Student struct { Name string...
}

因为这里的Student 的首字母S 是大写的,如果我们想在其它包创建 Student 的实例(比如main 包), 引入 model 包后,就可以直接创建 Student 结构体的变量(实例)。但是问题来了,如果首字母是小写的, 比如 是 type student struct {…} 就不不行了,怎么办—> 工厂模式来解决.

  1. model包中定义一个结构体student

main.go:

package model//定义一个私有的结构体
//Name字段是共有的,age字段是私有的
type student struct {Name stringage int
}//工厂模式:类比构造函数
//返回Student指针类型
func NewStudent(name string, age int) *student{return &student{Name: name,age: age,}
}//工厂模式定义student对应的方法:类比get、set方法
func (stu *student) GetAge() int{return (*stu).age
}func (stu *student) SetAge (age int) {(*stu).age = age
}
  1. 在main包下使用student

hello.go

package mainimport ("fmt""go_code/project01/main/model"
)func main(){//创建一个studentvar stu = model.NewStudent("tom", 20)stu.SetAge(17)//因为Name在model中是首字母大写表明是公有的,因此可以直接访问;而age需要通过GetAge函数实现//tom 17fmt.Println(stu.Name, stu.GetAge())
}

在这里插入图片描述

4 面向对象

4.1 面向对象练习题(封装)

  1. 创建程序,在 model 包中定义 Account 结构体:在 main 函数中体会 Golang 的封装性。
  2. Account 结构体要求具有字段:账号(长度在 6-10 之间)、余额(必须>20)、密码(必须是六
  3. 通过 SetXxx 的方法给 Account 的字段赋值。(同学们自己完成
  4. 在 main 函数中测试
  1. 定义account结构体
package modelimport ("fmt"
)type account struct {accountNo string //账户Balance float64	//余额Password string
}//使用工厂方法完成构造
func NewAccount(accountNo string, Balance float64, Password string) *account {if len(accountNo) < 6 || len(accountNo) > 10 {fmt.Println("账号必须在6-10之间...")return nil}if Balance < 20 {fmt.Println("余额不能少于20...")return nil}if len(Password) != 6 {fmt.Println("密码必须是6位..")return nil}account := account{accountNo: accountNo,Balance: Balance,Password: Password,}fmt.Println("创建账户成功~~~")return &account
}//定义get方法[与结构体绑定]
func (a account) GetAccountNo() string{return a.accountNo
}//set方法[与指针绑定]
func (a *account) SetAccountNo(accountNo string){(*a).accountNo = accountNo
}
  1. 使用
package mainimport ("fmt""go_code/project01/main/model"
)func main(){//创建一个账户var account = model.NewAccount("4131341324", 35.5, "348291")fmt.Println("账户No=", account.GetAccountNo())account.SetAccountNo("6666666666")fmt.Println("新的账户No=", account.GetAccountNo())
}

结果:
在这里插入图片描述

4.2 面向对象(继承)

①概念

在这里插入图片描述

继承的优点:

  1. 代码的复用性提高了
  2. 代码的扩展性和维护性提高了
type Goods struct { Name string Price int
}
type Book struct {
//这里就是嵌套匿名结构体 Goods Writer stringGoods	
}

②使用及注意点

继承的深入讨论:

  1. 结构体可以使用嵌套匿名结构体所有的字段和方法,即:首字母大写或者小写的字段、方法, 都可以使用。【举例说明】
package mainimport ("fmt"_ "go_code/project01/main/model"
)type A struct {Name stringage int
}//公有
func (a *A) SayOk(){fmt.Println("A SayOk ", a.Name)
}//私有
func (a *A) hello(){fmt.Println("A hello", a.Name)
}type B struct {//B继承了AA
}func main(){var b Bb.A.Name = "tom"//b.Name = "jack" //两种写法均可b.age = 12b.SayOk()b.hello()
}
  1. 匿名结构体字段访问可以简化
	b.A.Name = "tom"//b.Name = "jack" //两种写法均可

对上面的代码小结
(1) 当我们直接通过 b 访问字段或方法时,其执行流程如下比如 b.Name
(2) 编译器会先看 b 对应的类型有没有 Name, 如果有,则直接调用 B 类型的 Name 字段
(3) 如果没有就去看 B 中嵌入的匿名结构体 A 有没有声明 Name 字段,如果有就调用,如果没有继续查找…如果都找不到就报错.

  1. 当结构体和匿名结构体有相同的字段或者方法时,编译器采用就近访问原则访问,如希望访问匿名结构体的字段和方法,可以通过匿名结构体名来区分【举例说明】
    在这里插入图片描述
  2. 结构体嵌入两个(或多个)匿名结构体,如两个匿名结构体有相同的字段和方法(同时结构体本身没有同名的字段和方法),在访问时,就必须明确指定匿名结构体名字,否则编译报错。【举例说明】
    在这里插入图片描述
  3. 如果一个 struct 嵌套了一个有名结构体,这种模式就是组合,如果是组合关系,那么在访问组合的结构体的字段或方法时,必须带上结构体的名字
    在这里插入图片描述
    在这里插入图片描述
  4. 嵌套匿名结构体后,也可以在创建结构体变量(实例)时,直接指定各个匿名结构体字段的值
package mainimport ("fmt"_ "go_code/project01/main/model"
)type Goods struct {Name stringPrice float64
}type Brand struct {Name stringAddress string
}type TV struct {GoodsBrand
}type TV2 struct {*Goods*Brand
}func main(){//嵌套匿名结构体后,也可以在创建结构体变量(实例)时,直接指定各个匿名结构体字段的值tv := TV{Goods{"电视机", 5000.99}, Brand{"海尔", "山东"}}tv2 := TV{Goods{Price: 6999.99,Name: "电视机002",},Brand{Name: "夏普",Address: "北京",},}fmt.Println("tv=", tv)fmt.Println("tv2=", tv2)tv3 := TV2{&Goods{Name: "电视机003",Price: 79999.99,},&Brand{Name: "长虹",Address: "四川",},}fmt.Println("tv3=", *(tv3.Goods), *(tv3.Brand))
}

在这里插入图片描述7. 结构体的匿名字段是基本数据类型,如果需要有多个 int 的字段,则必须给 int 字段指定名字

package mainimport ("fmt"_ "go_code/project01/main/model"
)type Monster struct {Name stringAge int
}type E struct {Monsterint //匿名字段的基本数据类型n int
}func main(){//匿名字段是基本数据类型的使用var e Ee.Name = "狐狸精"e.Age = 300e.int = 20e.n = -50//e= {{狐狸精 300} 20 -50}fmt.Println("e=",e)}

③多重继承

如一个 struct 嵌套了多个匿名结构体,那么该结构体可以直接访问嵌套的匿名结构体的字段和方法,从而实现了多重继承。

在这里插入图片描述

多重继承细节说明

  1. 如嵌入的匿名结构体有相同的字段名或者方法名,则在访问时,需要通过匿名结构体类型名来区分。【案例演示】
    在这里插入图片描述

  2. 为了保证代码的简洁性,建议大家尽量不使用多重继承

http://www.jmfq.cn/news/5054149.html

相关文章:

  • 网站改版的前端流程/sem代运营托管公司
  • 珠海外贸网站建设/合肥seo关键词排名
  • 做网站 哪些公司/站长工具查询网站信息
  • 局域网网站域名怎么做/百度地图人工客服电话
  • 做淘宝客网站 首选霍常亮/武汉seo和网络推广
  • 网站上传小马后怎么做/nba排名最新
  • pc网站建设怎么样/谷歌优化方法
  • 江苏省网站备案查询/站长工具seo下载
  • 漳州正规网站建设哪家便宜/你就知道
  • 设计网站 问题/seo优化技术
  • 网站内页banner一般做多高/指数基金
  • wordpress 判断文章页/谷歌seo教程
  • 枸橼酸西地那非片是什么/天津百度seo排名优化
  • 天津建设工程注册中心网站/实时新闻热点
  • 网站建设案例模板/杭州网站优化效果
  • 深圳网站科技有限公司是真是假/seo网站制作优化
  • 贵阳网站建设q479185700棒/拓客团队怎么联系
  • 北京信息网站建设/seo网站监测
  • 自己的电脑做服务器搭建网站/新闻热点大事件
  • 沈阳网页设计制作/搜索引擎排名优化方案
  • 做网站需要花费那方面的钱/seo网站推广专员
  • 安阳专业做网站公司/做销售有什么技巧和方法
  • 数据统计网站有哪些/做网站价格
  • 邢台网站建设地方/免费站长工具
  • 大连餐饮网站建设/网络媒体推广报价
  • asp网站建设mdb文件/发新闻稿平台
  • 网站降权查询/自己怎么做游戏推广赚钱
  • 做色情网站牟利200万判刑/百度风云排行榜
  • 网站建设项目策划/分销平台
  • 国家允许哪几个网站做顺风车/精准防恶意点击软件