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

做自己的网站不是免费的华为手机网络推广方案

做自己的网站不是免费的,华为手机网络推广方案,工农区网站建设,上海市建设工程监理咨询有限公司可空类型 在Kotlin中#xff0c;我们可以在任何类型后面加上“?”#xff0c;比如“Int?”#xff0c;实际上等同于“Int? Int or null”。 通过合理的使用#xff0c;不仅能够简化很多判空代码#xff0c;还能够有效避免空指针异常。 注意#xff1a;由于null只能…可空类型 在Kotlin中我们可以在任何类型后面加上“?”比如“Int?”实际上等同于“Int? Int or null”。 通过合理的使用不仅能够简化很多判空代码还能够有效避免空指针异常。 注意由于null只能被存储在 Java 的引用类型的变量中所以在 Kotlin 中基本数据的可空版本都会使用该类型的包装形式。同样如果你用基本数据类型作为泛型类的类型参数Kotlin同样会使用该类型的包装形式。(即可空类型会自动装箱) Java中对于null的一些解决方案 函数内对于无效值可以抛异常处理。采用NotNull/Nullable标注。使用专门的Optional对象对可能为null的变量进行装箱。 可空类型相关的安全操作符 安全的调用 ?. s.student?.glasses?.degreeOfMyopiaElvis操作符 ?: val result student.glasses?.degreeOfMyopia ?: -1又称合并运算符 非空断言 !! val result student!!.glasses类型检查 在Kotlin中我们可以用“is”来判断。 if (obj is String) {print(obj.length) }if (obj !is String) { // 等同于 !(obj is String)print(Not a String) } else {print(obj.length) }when (obj) {is String - print(obj.length)!is String - print(Not a String) }类型智能转换 Smart Casts 可以将一个变量的类型转变为另一种类型它是隐式完成的。 val stu: Any Student(Glasses(189.00)) if(stu is Student) println(stu.glasses) 对于可空类型我们可以使用 Smart Casts val stu: Student Student(Glasses(189.00)) if (stu.glasses ! null) println(stu.glasses.degreeOfMyopia) 我们将这个例子反编译成Java核心代码如下 ... Intrinsics.checkParameterlsNotNull(args, args); Student stu new Student(new Glasses(189.0D)); if (stu instanceof Student) {Glasses var2 ((Student)stu).getGlasses(); System.out.println(var2); } ...我们可以看到这与我们写的Java版本一致这其实是Kotlin的编译器帮我们做出了转换。 根据官方文档介绍当且仅当Kotlin的编译器确定在类型检查后该变量不会再改变才会产生SmartCasts。 利用这点我们能确保多线程的应用足够安全。举个例子 class Kot {var stu: Student? getStu()fun dealStu() {if (stu ! null) {print(stu.glasses)}} }上述代码中我们将stu声明为引用可空类型变量这意味着在判断 stu ! null 之后stu在其他线程中还是会被修改的所以被编译器无情地拒绝了。 将var改为val就不会存在这样的问题引用不可变能够确保程序运行不产生额外的副作用。你也许会觉得这样写不够优雅我们可以用let函数来简化⼀下 class Kot {var stu: Student? getStu()fun dealStu() {stu?.let { print(it.glasses) }} }这样就会满足 Smart Casts 的条件就不会被拒绝编译。 在实际开发中我们并不总能满足 Smart Casts 的条件。并且 Smart Casts 有时会缺乏语义并不适用于所有场景。 当类型需要强制转换时我们可以利用“ as” 操作符来实现。 class Kot {val stu: Student? getStu() as Student?fun dealStu() {if (stu ! null) {print(stu.classes)}} }由于val只允许赋值一次这样我们在外部已经确定了stu的类型当stu不为空时在dealStu方法里就可以成功调用stu的参数。 注意 这里stu变量是在dealStu()方法的外面必须使用val生声明否则如果是var仍然可能有线程安全问题所以也会被拒绝编译。如果stu变量是放在dealStu()方法的里面那么可以使用var, 因为是方法本地局部变量会被线程独占此时也满足 Smart Casts 的条件。 因为getStu可能为空如果我们将其转换类型改为 Student val stu: Student? getStu() as Student则会抛出类型转换失败的异常因为它不是可空的。所以我们通常称之为“ 不安全” 的类型转换。 那是否有安全版本的转换呢除了上述写法外Kotlin还提供了操作符“ as?”我们可以这样改写 val stu: Student? getStu() as? Student这时如果stu为空将不会抛出异常而是返回转换结果null。 Any非空类型的根类型 与 Object 作为 Java 类层级结构的顶层类似Any 类型是 Kotlin 中所有非空类型如String、Int的超类 与 Java 不同的是Kotlin 不区分“原始类型”primitivetype和其他的类型它们都是同一类型层级结构的一部分。 如果定义了一个没有指定父类型的类型则该类型将是Any的直接子类型。如 class Animal(val weight: Double) 如果你为定义的类型指定了父类型则该父类型将是新类型的直接父类型但是新类型的最终根类型为Any。 另外Kotlin 把 Java 方法参数和返回类型中用到的 Object 类型看作 Any更确切地说是当作“平台类型”。当在 Kotlin 函数中使用 Any 时它会被编译成 Java 字节码中的 Object。 什么是平台类型 平台类型本质上就是 Kotlin 不知道可空性信息的类型所有 Java 引用类型在 Kotlin 中都表现为平台类型。当在 Kotlin 中处理平台类型的值的时候它既可以被当作可空类型来处理也可以被当作非空类型来操作。 平台类型的引入是 Kotlin 兼容 Java 时的一种权衡设计。试想下如果所有来自 Java 的值都被看成非空那么就容易写出比较危险的代码。反之如果 Java 中的值都强制当作可空则会导致大量的 null 检查。综合考量平台类型是一种折中的设计方案。 Any所有类型的根类型 如果说Any是所有非空类型的根类型那么 Any? 才是所有类型可空和非空类型的根类型。 Any 与 Any? 看起来没有继承关系然而在我们需要用 Any? 类型值的地方显然可以传入一个类型为Any的值这在编译上不会产生问题。比如在 Kotlin 中 Int 是 Number 的子类 fun printNum(num : Number){println(num) }val n : Int 1 printNum(n)反之却不然比如一个参数类型为Any的函数我们传入符合 Any? 类型的null值就会出现如下的错误 error: null can not be a value of a non-null type Any Nothing 与 Nothing 在 Kotlin 类型层级结构的最底层是 Nothing 类型。 Nothing是没有实例的类型。Nothing类型的表达式不会产生任何值。需要注意的是任何返回值为Nothing的表达式之后的语句都是无法执行的。你是不是感觉这有点像return或者break的作用没错Kotlin 中 return、throw 等流程控制中与跳转相关的表达式返回值都为Nothing。 Nothing? 是 Nothing 的父类型其实它只能包含一个值null本质上与null没有区别。所以我们可以使用null作为任何可空类型的值。 自动装箱与拆箱 我们发现Kotlin 中并没有 int、float、double、long这样的原始类型取而代之的是它们对应的引用类型包装类Int、Float、Double、Long。 除了以上代表数值的类型还有布尔Boolean、字符Char、字符串String及数组Array。这让 Kotlin 比起 Java 来更加接近纯面向对象的设计——一切皆对象。 但这么说其实也是不够严谨的。以Int为例虽然它可以像Integer一样提供额外的操作函数但这两个类型在底层实现上存在差异。Kotlin 中的 Int 在 JVM 中实际以 int 存储对应字节码类型为I但是作为一个“ 包装类型”编译后应该装箱才对难道Kotlin 不会自动装箱 我们可以简单地认为 Kotlin中的 Int 类型等同于 intKotlin中的 Int? 等同于 Integer。 Int作为一种小技巧让Int看起来是引用类型这在语法上让 Kotlin 更接近纯面向对象语言。 数组类型 val funList arrayOf() // 声明长度为0的数组 val funList arrayOf(n1, n2, n3, ..., nt) // 声明并初始化长度为t的数组 Kotlin中 Array 并不是一种原生的数据结构而是一种Array类甚至我们可以将 Kotlin 中的Array视作集合类的一部分。 由于 Smart Casts编译器能够隐式推断出funList元素类型。当然我们也可以手动指定类型 val funList arrayOfT(n1, n2, n3..., nt)在 Kotlin 中还为原始类型额外引入了一些实用的类IntArray、CharArray、ShortArray等分别对应 Java 中的int[]、char[]、short[]等。 val x intArrayOf(1,2,3) 注意IntArray等并不是Array的子类所以用两者创建的相同值的对象并不是相同对象。 由于 Kotlin 对原始类型有特殊的优化主要体现在避免了自动装箱带来的开销所以我们建议优先使用原始类型数组。 泛型 class SmartListT : ArrayListT() {fun find(t: T): T? {val index super.indexOf(t)return if (index 0) super.get(index) else null}fun main(args: ArrayString) {val smartList SmartListString()smartList.add(one)println(smartList.find(one)) // 输出: oneprintln(smartList.find(two).isNullOrEmpty()) // 输出: true} }由于扩展函数支持泛型可以利用扩展函数实现上面的功能 fun T ArrayListT.find(t: T): T? {val index this.indexOf(t)return if (index 0) this.get(index) else null }fun main(args: ArrayString) {val arrayList ArrayListString()arrayList.add(one)println(arrayList.find(one)) // 输出: oneprintln(arrayList.find(two).isNullOrEmpty()) // 输出: true }类型约束设定类型上界 class FruitPlateT : Fruit(val t: T) class Noodles(weight: Double) // 面条类 val applePlate FruitPlateApple(Apple(100.0)) // 允许 val applePlate FruitPlate(Apple(100.0)) // 允许, 同上简化写法 val noodlesPlate FruitPlateNoodles(Noodles(200.0)) // 不允许支持可空T类型 class FruitPlateT: Fruit?(val t:T) val fruitPlate FruitPlate(null) 多个泛型条件约束 interface Ground {} class Watermelon(weight: Double) : Fruit(weight), Groundfun T cut(t: T) where T : Fruit, T : Ground {print(You can cut me.) }cut(Watermelon(3.0)) // 允许 cut(Apple(2.0)) // 不允许我们可以通过where关键字来实现这种需求它可以实现对泛型参数类型添加多个约束条件比如这个例子中要求被切的东西是一种水果而且必须是长在地上的水果。 Java 为什么无法声明一个泛型数组 我们先来看一个简单的例子Apple是Fruit的子类思考下Apple[]和Fruit[]以及ListApple和ListFruit是什么关系呢 Apple[] appleArray new Apple[10]; Fruit[] fruitArray appleArray; // 允许 fruitArray[0] new Banana(0.5); // 编译通过运行报 ArrayStoreExceptionListApple appleList new ArrayListApple(); ListFruit fruitList appleList; // 不允许我们发现一个奇怪的现象Apple[]类型的值可以赋值给Fruit[]类型的值而且还可以将一个Banana对象添加到fruitArray编译器能通过。作为对比ListFriut类型的值则在一开始就禁止被赋值为ListApple类型的值这其中到底有什么不同呢 其实这里涉及一个关键点数组是协变的而List是不变的。简单来说就是 Object[] 是所有对象数组的父类而 ListObject 却不是 ListT 的父类。 Java 中的泛型是类型擦除的可以看作伪泛型简单来说就是你无法在程序运行时获取到一个对象的具体类型。 我们可以用以下代码来对比一下ListT和数组 System.out.println(appleArray.getClass()); System.out.println(appleList.getClass()); 运行结果 class [Ljavat.Apple; class java.util.ArrayList 数组在运行时是可以获取自身的类型而ListApple在运行时只知道自己是一个List而无法获取泛型参数的类型。 而 Java 数组是协变的也就是说任意的类 A 和 B若 A 是 B 的父类则 A[] 也是 B[] 的父类。但是假如给数组加入泛型后将无法满足数组协变的原则因为在运行时无法知道数组的类型。 Kotlin 中的泛型机制与 Java 中是一样的所以上面的特性在 Kotlin 中同样存在。 比如通过下面的方式同样无法获取列表的类型 val appleList ArrayListApple() println(appleList.javaClass)但不同的是Kotlin 中的数组是支持泛型的当然也不再协变也就是说你不能将任意一个对象数组赋值给 ArrayAny 或者 ArrayAny?。 val appleArray arrayOfNullsApple(3) val anyArray: ArrayAny? appleArray // 不允许获取泛型参数类型 如何在运行时获取泛型类型的参数信息可以利用匿名内部类。 val list1 ArrayListString() val list2 object : ArrayListString() {} // 匿名内部类 println(list1.javaClass.genericSuperclass) println(list2.javaClass.genericSuperclass)结果 java.util.AbstractListE java.util.ArrayListjava.lang.String 那么为什么使用匿名内部类的这种方式能够在运行时获取泛型参数的类型呢 其实泛型类型擦除并不是真的将全部的类型信息都擦除还是会将类型信息放在对应class的常量池中的。 import java.lang.reflect.ParameterizedType import java.lang.reflect.Typeopen class GenericsTokenT {var type: Type Any::class.javainit {val superClass this.javaClass.genericSuperclasstype (superClass as ParameterizedType).actualTypeArguments[0]} fun main() {val gt object : GenericsTokenMapString, String() {} // 使用 object 创建println(gt.type)} }结果 java.util.Mapjava.lang.String, ? extends java.lang.String 匿名内部类在初始化的时候就会绑定父类或父接口的相应信息这样就能通过获取父类或父接口的泛型类型信息来实现我们的需求。 你可以利用这样一个类来获取任何泛型的类型我们常用的Gson也是使用了相同的设计。TypeToken private Type getTypeTokenTypeArgument() {Type superclass getClass().getGenericSuperclass();if (superclass instanceof ParameterizedType) {ParameterizedType parameterized (ParameterizedType) superclass;if (parameterized.getRawType() TypeToken.class) {return $Gson$Types.canonicalize(parameterized.getActualTypeArguments()[0]);}}... }比如我们在 Kotlin 中可以这样使用Gson来进行泛型类的反序列化 val json ... val rType object: TypeTokenListString(){}.type val stringList Gson().fromJsonListString(json, rType)使用内联函数获取泛型 在 Kotlin 中除了用这种方式来获取泛型参数类型以外还有另外一种方式那就是内联函数。 Kotlin中的内联函数在编译的时候编译器便会将相应函数的字节码插入调用的地方也就是说参数类型也会被插入字节码中我们就可以获取参数的类型了。 inline fun reified T getType() { return T::class.java }使用内联函数获取泛型的参数类型非常简单只需加上reified关键词即可。这里的意思相当于在编译的会将具体的类型插入相应的字节码中那么我们就能在运行时获取到对应参数的类型了。所以我们可以在 Kotlin 中改进 Gson 的使用方式 inline fun reified T: Any Gson.fromJson(json: String): T { // 对 Gson 进行扩展return Gson().fromJson(json, T::class.java) }// 使用 fun main() {val json ...val stringList Gson().fromJsonListString(json) } 这里利用了 Kotlin 的扩展特性对 Gson 进行了功能扩展在不改变原有类结构的情况下新增方法很多场景用 Kotlin 来实现便会变得更加优雅。 另外需要注意的一点是Java 并不支持主动指定一个函数是否是内联函数所以在 Kotlin 中声明的普通内联函数可以在Java中调用因为它会被当作一个常规函数而用reified来实例化的参数类型的内联函数则不能在 Java 中调用因为它永远是需要内联的。 泛型中的协变 在 Java 中不支持将ListString赋值给ListObject如果支持这种行为的话那么它将会和数组支持泛型一样不再保证类型安全。 ListString stringList new ArrayListString(); ListObject objList stringList; // 假设可以编译报错 objList.add(Integer.valueOf(1)); String str stringList.get(0); // 将会出错 但是在 Kotlin 中却支持这样做 val stringList: ListString ArrayListString() val anyList: ListAny stringList // 编译成功关键在于这两个List并不是同一种类型。我们分别来看一下两种List的定义 public interface ListE extends CollectionE { // java 的 List... }public interface Listout E : CollectionE { // kotlin 的 List... }虽然都叫List也同样支持泛型但是 Kotlin 的 List 定义的泛型参数前面多了一个out关键词。普通方式定义的泛型是不变的简单来说就是不管类型 A 和类型 B 是什么关系GenericA与GenericB其中Generic代表泛型类都没有任何关系。比如在 Java 中 String 是 Oject 的子类型但 ListString 并不是 ListObject 的子类型在 Kotlin 中泛型的原理也是一样的。 如果在定义的泛型类和泛型方法的泛型参数前面加上out关键词说明这个泛型类及泛型方法是协变简单来说如果类型 A 是类型 B 的子类型那么 GenericA 也是 GenericB 的子类型比如在 Kotlin 中 String 是 Any 的子类型那么ListString也是ListAny的子类型所以ListString可以赋值给ListAny。 但是我们上面说过如果允许这种行为将会出现类型不安全的问题。那么Kotlin是如何解决这个问题的我们来看一个例子 val stringList: MutableListString ArrayListString() stringList.add(kotlin) // 编译报错不允许 这又是什么情况往一个List中插入一个对象竟然不允许难道这个List只能看看确实是这样的因为这个List支持协变那么它将无法添加元素只能从里面读取内容。(即只读的List) 这点我们查阅List的源码也可以发现List中本来就没有定义add方法也没有remove及replace等方法也就是说这个List一旦创建就不能再被修改这便是将泛型声明为协变需要付出的代价。 那么为什么泛型协变会有这个限制呢同样我们用反证法来看这个问题如果允许向这个List插入新对象会发生什么我们来看一个例子 val stringList: ListString ArrayListString() val anyList: ListAny stringList anyList.add(1) val str: String anyList[0] // Int 无法转换为 String 从上面的例子可以看出假如支持协变的List允许插入新对象那么它就不再是类型安全的了也就违背了泛型的初衷。 所以我们可以得出结论支持协变的List只可以读取而不可以添加。其实从out这个关键词也可以看出out就是出的意思可以理解为List是一个只读列表。 在 Java 中也可以声明泛型协变用通配符及泛型上界来实现协变? extends T其中T可以是任意类。比如在 Java 中声明一个协变的List List? extends Animal list new ArrayListDog();但泛型协变实现起来非常别扭这也是 Java 泛型一直被诟病的原因。很庆幸Kotlin 改进了它使我们能用简洁的方式来对泛型进行不同的声明。 另外需要注意的一点的是通常情况下若一个泛型类Genericout T支持协变那么它里面的方法的参数类型不能使用 T 类型因为一个方法的参数不允许传入参数父类型的对象因为那样可能导致错误。 class Genericout T {fun funs1(a: T) { // 编译报错}fun E fun2(a: E) { // 但是普通的泛型方法可以支持这样写}fun E fun3() : T { // 可以作为返回值} }什么是逆变 你说协变我还好理解毕竟原来是父子支持泛型协变后的泛型类也还是父子关系。但是反过来又是一个什么情 况 比如 Double是 Number 的子类型反过来GenericDouble却是GenericNumber的父类型那么到底有没有这种场景呢 一个支持逆变的 Comparator 我们来思考一个问题假设现在需要对一个MutableListDouble进行排序利用其sortWith方法我们需要传入一个比较器所以可以这么做 val doubleComparator ComparatorDouble { d1, d2 - d1.compareTo(d2) }val doubleList mutableListOf(2.0, 3.0) doubleList.sortWith(doubleComparator)暂时来看 没有什么问题。 但是现在我们又需要对MutableListInt、MutableListLong等进行排序那么我们是不是又需要定义intComparator、longComparator等呢现在看来这并不是一种好的解决方法。那么试想一下可不可以定义一个比较器给这些列表使用。 我们知道这些数字类有一个共同的父类Number那么Number类型的比较器是否代替它的子类比较器比如 val numberComparator ComparatorNumber { n1, n2 - n1.toDouble().compareTo(n2.toDouble()) }val doubleList mutableListOf(2.0, 3.0) doubleList.sortWith(numberComparator)val intList mutableListOf(1,2) intList.sortWith(numberComparator)编译通过验证了我们的猜想。 那么为什么numberComparator可以代替 doubleComparator、 intComparator 呢 我们来看一下sortWith方法的定义 public fun T MutableListT.sortWith(comparator: Comparatorin T): Unitif (size 1) java.util.Collections.sort(this, comparator) }这里我们又发现了一个关键词 in跟out一样它也使泛型有了另一个特性那就是逆变。简单来说假如类型 A 是类型 B 的子类型那么 GenericB 反过来是 GenericA 的子类型所以我们就可以将一个numberComparator作为doubleComparator传入。那么将泛型参数声明为逆变会不会有什么限制呢 前面我们说过用out关键字声明的泛型参数类型将不能作为方法的参数类型但可以作为方法的返回值类型而in刚好相反。比如声明以下一个列表 class WriteableListin T {fun add(a: T): Int { // 允许}fun get(index: T): T { // 不允许返回T类型 // Type parameter T is declared as in but occurs in out position in type T}fun get(index: T): Any { // 允许} }我们不能将泛型参数类型当作方法返回值的类型但是作为方法的输入参数类型没有任何限制其实从in这个关键词也可以看出in就是入的意思可以理解为消费内容所以我们可以将这个列表看作一个可写、但可读功能受限的列表获取的值只能为Any类型。在Java中使用? super T可以达到相同效果。 如何使用 in 和 out in和out是一个对立面其中in代表泛型参数类型逆变out代表泛型参数类型协变。从字面意思上也可以理解in代表着输入而out代表着输出。但同时它们又与泛型不变相对立统称为型变而且它们可以用不同方式使用。 比如 public interface Listout E : CollectionE {} 这种方式是在声明处型变另外还可以在使用处型变比如前面例子中sortWith方法。 假设现在有个需求需要将数据从一个Double数组拷贝到另一个Double数组我们该怎么实现呢 一开始我们可能会这么做 fun copy(dest: ArrayDouble, src: ArrayDouble) {if (dest.size src.size) {throw IndexOutOfBoundsException()} else {src.forEachIndexed{index,value - dest[index] src[index]}} } var dest arrayOfNullsDouble(3) val src arrayOfDouble(1.0,2.0,3.0) copy(dest, src)但是学过泛型后的你一定不会这么做了因为假如替换成Int类型的列表是不是又得写一个copy方法所以我们可以对其进一步抽象 fun T copy(dest: ArrayT, src: ArrayT) {if (dest.size src.size) {throw IndexOutOfBoundsException()} else {src.forEachIndexed{index,value - dest[index] src[index]}} } var destDouble arrayOfNullsDouble(3) val srcDouble arrayOfDouble(1.0,2.0,3.0) copy(destDouble, srcDouble) var destInt arrayOfNullsInt(3) val srcInt arrayOfInt(1,2,3) copy(destInt, srcInt)那么这种方式有没有什么局限呢我们发现使用copy方法必须是同一种类型那么假如我们想把ArrayDouble拷贝到ArrayNumber中将不允许。 这时候我们就可以利用泛型型变了。 fun T copy(dest: Arrayin T, src: Arrayout T) {if (dest.size src.size) {throw IndexOutOfBoundsException()} else {src.forEachIndexed{index,value - dest[index] src[index]}} } fun test() {val dest arrayOfNullsNumber(3)val src arrayOfDouble(1.0,2.0,3.0)copy(dest, src) }in是声明在dest数组上而out是声明在src数组上所以dest可以接收T类型的父类型的Arraysrc可以接收T类型的子类型的Array。当然这里的T要到编译的时候才能确定。 Kotlin 与 Java 的型变比较 型变类型Kotlin 实现方式Java 实现方式含义协变out T? extends T消费者只能读取不能添加逆变in T? super T生产者只能添加读取受限不变TT既可以添加也可以读取 如果你对泛型参数的类型不感兴趣那么你可以使用类型通配符来代替泛型参数。前面已经接触过 Java 中的泛型类型通配符“?”而在 Kotlin 中则用“*”来表示类型通配符。比如 val list: MutableList* mutableListOf(1, kotlin) list.add(2.0) // 出错这个列表竟然不能添加不是说好是通配吗按道理应该可以添加任意元素。 其实不然MutableList*与MutableListAny?不是同一种列表后者可以添加任意元素而前者只是通配某一种类型但是编译器却不知道这是一种什么类型所以它不允许向这个列表中添加元素因为这样会导致类型不安全。 前面所说的协变也是不能添加元素那么它们两者之间有什么关系呢其实通配符只是一种语法糖背后上也是用协变来实现的。所以MutableList*本质上就是MutableListout Any?使用通配符与协变有着一样的特性。
http://www.pierceye.com/news/576513/

相关文章:

  • 帝国cms做的网站私人定制女装店
  • 网站建设南沙wordpress video
  • 网站建设开票应该开哪个行业什么网站可以免费做视频的软件
  • 百度seo查询收录查询网站推广策划案seo教程
  • 如何免费建立网站中贤建设集团网站
  • 如何做转运网站黄聪 wordpress
  • 临海市住房与城乡建设规划局网站宁波网络推广培训
  • go 网站开发自己在线制作logo
  • 重庆市网站建设公司企业服务账号
  • 网站建设的市场情况网站系统里不能打印
  • 网站如何适应屏幕做网站时无法上传图片
  • 网站的橱窗怎么做嘉兴住房和城乡建设厅网站
  • 吉林省城乡建设官方网站163企业邮箱登录入口官网
  • 做网站参考文献某企业网站建设方案2000字
  • 网站托管哪家好织梦购物网站整站源码
  • 怎么做网站的优化排名wordpress的目录结构(一)
  • 个人可以做公益网站吗美食杰网站的建设目的
  • 宿迁公司企业网站建设《网站基础建设-首保》
  • 做全屏式网站尺寸是多大国外虚拟主机 两个网站
  • 黑龙江建设网站招聘广西住房和城乡建设厅培训中心官方网站
  • 做网站客户最关心的是什么制作网页原型的目的
  • 电子商务网站建设工具河南安阳吧
  • 南通网站建设公司哪个好肯德基的网站建设
  • 高端大气网站源码wordpress做双语网站
  • 360网站推广东莞凤岗
  • 公司网站高端网站建设赣州做网站多少钱
  • dw做网站怎么发布建设银行官方网站登录入口
  • 怎样查看网站建设时间免费外贸自建网站
  • 网站备案注销原因网站建设入账
  • 番禺做网站哪家好wordpress 样式引用