2019独角兽企业重金招聘Python工程师标准>>>

第3章 类型系统与可空类型

跟Java、C和C ++ 一样, Kotlin也是“静态类型编程语言”。

通常,编程语言中的类型系统中定义了

1.      如何将数值和表达式归为不同的类型

2.     如何操作这些类型

3.     这些类型之间如何互相作用

我们在编程语言中使用类型的目的是为了让编译器能够确定类型所关联的对象需要分配多少空间。

在每一个编程语言中,都有一个特定的类型系统。静态类型在编译时期时,就能可靠地发现类型错误。因此通常能增进最终程序的可靠性。

类型系统在各种语言之间有非常大的不同,主要的差异存在于编译时期的语法,以及运行时期的操作实现方式。

本章我们简单介绍一下Kotlin的类型系统。

3.1 类型系统

我们知道在计算机中中,信息都是以01比特存储的,硬件无法区分存储器地址、脚本、字符、整数、以及浮点数。为了赋予01比特意义,于是有了类型。

3.1.1 类型系统的作用

使用类型系统,编译器可以检查无意义的、无效的、类型不匹配等错误代码。这也正是强类型语言能够提供更多的代码安全性保障的原因之一。

另外,静态类型检查还可以提供有用的信息给编译器。跟动态类型语言相比,由于有了类型的显式声明,静态类型的语言更加易读好懂。

有了类型我们还可以更好地做抽象化、模块化的工作。这使得我们可以在较高抽象层次思考并解决问题。例如,Java中的字符数组 char[] s = {'a', 'b', 'c'} 和字符串类型 String str = "abc" 就是最简单最典型的抽象封装实例。

字符数组

jshell> char[] s = {'a','b','c'}  // 声明并初始化一个字符数组

s ==> char[3] { 'a', 'b', 'c' }

jshell> s[0] $3 ==> 'a'

jshell> s[1] $4 ==> 'b'

jshell> s[2] $5 ==> 'c'

字符串

jshell> String str = "abc" // 直接声明一个字符串类型的变量

str ==> "abc"

jshell> str.toCharArray() // 字符串可以通过这个方法转为字符数组

$7 ==> char[3] { 'a', 'b', 'c' }

3.1.2 Java的类型系统

Java的类型系统可以简单用下面的图来表示:

图3-1 Java的类型系统

关于Java中的null,有很多比较坑的地方。例如

int i = null; //类型不匹配,不能把null 转换成int

short s = null; //类型不匹配,不能把null 转换成 short

byte b = null: //类型不匹配,不能把null 转换成 byte

double d = null; //类型不匹配,不能把null 转换成double

Integer io = null; // 这个可以编译通过

int j = io; //这个也可以编译通过, 但是在运行的时候会抛 NullPointerException ,这是Java的null比较坑的地方,在Kotlin中不会发生这样的情况,下文我们将会看到关于Kotlin的可空类型的介绍

基本数据类型与引用数据型在创建时,内存存储方式区别如下:

·基本数据类型在被创建时,在栈上给其划分一块内存,将数值直接存储在栈上(性能高)。

·引用数据型在被创建时,首先在栈上给其引用(句柄)分配一块内存,而对象的具体信息存储在堆内存上,然后由栈上面的引用指向堆中对象的地址。

3.1.3 Kotlin的类型系统

Java是一个近乎纯洁的面向对象编程语言,但是为了编程的方便还是引入了基本数据类型,为了能够将这些基本数据类型当成对象操作,Java为每一个基本数据类型都引入了对应的包装类型(wrapper class),例如int的包装类就是Integer。从Java 5开始引入了自动装箱/拆箱机制,使得二者可以相互转换。Java 为每个原始类型提供了包装类型:

·原始类型: boolean,char,byte,short,int,long,float,double

·包装类型:Boolean,Character,Byte,Short,Integer,Long,Float,Double

Kotlin中去掉了原始类型,只有“包装类型”, 编译器在编译代码的时候,会自动优化性能,把对应的包装类型拆箱为原始类型。

Kotlin系统类型分为可空类型和不可空类型。Kotlin中引入了可空类型,把有可能为null的值单独用可空类型来表示。这样就在可空引用与不可空引用之间划分出来一条明确的显式的“界线”。

Kotlin类型层次结构如下图所示:

图3-2 Kotlin类型层次结构

通过这样显式地使用可空类型,并在编译器作类型检查,大大降低了出现空指针异常的概率。

对于Kotlin的数字类型而言,不可空类型与Java中的原始的数字类型对应。如下表所示

表3-1 Kotlin的不可空数字类型与Java的数字类型映射

Kotlin

Java

Int

int

Long

long

Float

float

Double

double

Kotlin中对应的可空数字类型就相当于Java中的装箱数字类型。如下表所示

表3-2 Kotlin的可空数字类型与Java的数字类型映射

Kotlin

Java

Int?

Integer

Long?

Long

Float?

Float

Double?

Double

在Java中,从基本数字类型到引用数字类型的转换就是典型的装箱操作,例如int转为Integer。同理,在Kotlin中,非空数字类型到可空数组类型需要进行装箱操作。

>>> val a: Int = 1000 // 不可空Int

>>> val b:Int = 1000

>>> a===b

true

>>> a==b

true

上面返回的都是true,因为a,b它们都是以原始类型存储的,类似于Java中的基本数字类型。

>>> val a:Int? = 1000 // 可空Int

>>> val b:Int? = 1000

>>> a==b

true

>>> a===b

false

我们可以看出,当 a, b 都为可空类型时, a 跟 b 的引用是不等的。

这里的“等于”号简单说明如下:

表3-3 Kotlin中的“等于”号

等于符号

功能说明

=

赋值,在逻辑运算时也有效

==

等于运算,比较的是值,而不是引用

===

完全等于运算,不仅比较值,而且还比较引用,只有两者一致才为真

另外,Java中的数组也是一个较为特殊的类型。这个类型是 T[] , 这个方括号让我们觉得不大优雅。Kotlin中摒弃了这个数组类型声明的语法。Kotlin简单直接地使用Array类型代表数组类型。这个Array中定义了get, set 算子函数, 同时有一个size 属性代表数组的长度,还有一个返回数组元素的迭代子 Iterator<T>的函数iterator()。

Kotlin数组Array完整的定义如下:

public class Array<T> {

public inline constructor(size: Int, init: (Int) -> T) // 构造函数

public operator fun get(index: Int): T // get操作符,等价于Array[index]

public operator fun set(index: Int, value: T): Unit // set操作符,等价于Array[index]=value

public val size: Int // 数组长度,不可变Int

public operator fun iterator(): Iterator<T> // 数组元素迭代子

}

其中,构造函数我们可以这么用

>>> val square = Array(5, { i -> i * i }) // 创建一个长度是5的数组,i对应数组的下标,值是i * i

>>> square.forEach(::println)

0 1 4 9 16

我们在编程过程中常用的boolean[], char[],byte[],short[],int[],long[],float[],double[] ,Kotlin直接使用了8个新的类型来对应这样的编程场景:

BooleanArray

ByteArray

CharArray

DoubleArray

FloatArray

IntArray

LongArray

ShortArray

3.2 可空类型T?

我想Java和Android开发者肯定早已厌倦了空指针异常(Null Pointer Exception)。这个讨厌的空指针异常在运行时总会在某个你意想不到的地方忽然出现,让我们感到措手不及。

自然而然地,人们会想到为何不能在编译时就提前发现这类空指针异常,并大量修复这些问题? 现代编程语言正是这么做的。

Kotlin自然也不例外。

在 Java 8中,我们可以使用 Optional 类型来表达可空的类型。

package com.easy.kotlin;

import java.util.Optional;

import static java.lang.System.out;

public class Java8OptionalDemo {

public static void main(String[]args){

out.println(strLength(Optional.of("abc")));                        out.println(strLength(Optional.ofNullable(null)));

}

static Integer strLength(Optional<String> s) {

return s.orElse("").length();

}

}

运行输出:

3 0

但是,这样的代码,依然不是那么地优雅。

在Groovy中提供了一种安全的属性/方法访问操作符 (?.)

user?.getUsername()?.toUpperCase();

Swift 也有类似的语法, 只作用在 Optional 的类型上。

Kotlin中使用了Groovy里面的安全调用符,并简化了 Optional 类型的使用,直接通过在类型T后面加个?, 这个类型 T? 就表达了Optional<T>的意义。

上面 Java 8的例子,用 Kotlin 来写就显得更加简单优雅了:

package com.easy.kotlin

fun main(args: Array<String>) {

println(strLength(null))

println(strLength("abc"))

}

fun strLength(s: String?): Int {

return s?.length ?: 0

}

其中,我们使用 String? 同样表达了 Optional<String>的意思。相比之下,哪个更简单呢?想必是一目了然啦。

3.3 安全操作符

让我们赶紧扔掉Java中的一堆null的防御式样板代码吧!

当我们使用Java开发的时候,我们的代码大多是防御性的。如果我们不想遇到NullPointerException,我们就需要在使用它之前不停地去判断它是否为null。

有了安全调用符 (?.) , Elvis 操作符 (?:) 和 let 函数,我们就可以完全抛弃掉 Java 中冗长的 if-null 和 if-not-null 语句了。

本节我们来学习一下在Kotlin中如何优雅正确地使用安全操作符。

可空类型与null

Kotlin正如很多现代编程语言一样——是空安全的。因为我们需要通过一个可空类型符号T?来明确地指定一个对象类型T是否能为空。

我们可以像这样去写:

>>> val str: String = null // null不能赋值给不可空的String类型

error: null can not be a value of a non-null type String val str: String = null                   ^ 

我们可以看到,这里不能通过编译, String 类型不能是null 。

一个可以赋值为null的String类型的正确姿势是:String? , 代码如下所示

>>> var nullableStr: String? = null // null可以赋值给可空的字符串类型String?

>>> nullableStr

null

我们再来看一下Kotlin中关于null的一些有趣的运算。

null跟null是相等的:

>>> null==null

true

>>> null!=null

false

null这个值比较特殊,null 不是Any类型

>>> null is Any

false

但是,null是Any?类型:

>>> null is Any?

true

我们来看看null对应的类型是什么:

>>> var a=null // 隐式声明变量a,并初始化值为null

>>> a is Nothing? // a 是Nothing?类型

true

>>> a is Nothing // a 不是Nothing类型

false

>>> a

null

>>> a=1 // 1 不能赋值给a , Nothing? 类型只有一个值 null

error: the integer literal does not conform to the expected type Nothing? a=1   ^

从报错信息我们可以看出,null的类型是Nothing?。关于Nothing?我们将会在下面的小节中介绍。

安全调用符 ?.

我们不能直接使用可空的nullableStr来调用其属性或者方法

>>> var nullableStr: String? = null

>>> nullableStr.length

error: only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type String? nullableStr.length            ^

上面的代码无法编译, nullableStr可能是null。我们需要使用安全调用符(?.) 来调用

>>> nullableStr?.length //如果 nullableStr为空,则返回 null。这个表达式的类型是 Int?

null

>>> nullableStr = "abc"

>>> nullableStr?.length //如果 nullableStr非空,就返回 nullableStr.length。这个表达式的类型是 Int?

3

只有在 nullableStr != null 时才会去调用其length属性。

非空断言 !!

如果我们想只有在确保 nullableStr 不是null的情况下才能这么调用,否则抛出异常,我们可以使用断言操作符( !! )

>>> nullableStr = null

>>> nullableStr!!.length

kotlin.KotlinNullPointerException

Elvis运算符 ?:

Java 8 中Optional 提供了orElse方法,我们可以如下使用

s.orElse("").length();

这个东东,在 Kotlin 是最最常见不过的 Elvis 运算符 ( :? )了:

s?.length ?: 0

相比之下,还有什么理由继续用 Java 8 的 Optional 呢?

我们也可以使用Elvis操作符来给定一个在是null的情况下的替代值

>>> nullableStr

null

>>> var s= nullableStr?:"NULL" // 如果nullableStr是null,表达式的值返回“NULL”

>>> s

NULL

let 函数

let函数是一个inline函数,它的定义如下

@kotlin.internal.InlineOnly

public inline fun <T, R> T.let(block: (T) -> R): R = block(this) //默认以当前这个对象T作为闭包的block参数

如果一个List中有空null元素和非空String元素,而只要对非空值执行某个操作,我们就可以使用let操作符

>>> val listWithNulls= listOf("A", "B","C",null,null,"D")

>>> listWithNulls

[A, B, C, null, null, D]

>>> listWithNulls.forEach{ it?.let{ println(it) }  }

A

B

C

D

我们再举个简单的例子。例如,我们想要在TextView和text都不为null时,在消息框(toast)中显示text消息,代码如下

textView?.text?.let { toast(it) }

是不是相当简洁、易懂?

3.4 特殊类型

本节我们介绍Kotlin中的特殊类型:Unit,Nothing,Any以及其对应的可空类型Unit? , Nothing? , Any? 。

3.4.1 Unit类型

Kotlin也是面向表达式的语言。在Kotlin中所有控制流语句都是表达式(除了变量赋值、异常等)。

Kotlin中的Unit类型实现了与Java中的void一样的功能。

总的来说,这个Unit类型并没有什么特别之处。它的定义是:

package kotlin

public object Unit {

override fun toString() = "kotlin.Unit"

}

不同的是,当一个函数没有返回值的时候,我们用Unit来表示这个特征,而不是null。

大多数时候,我们并不需要显式地返回Unit,或者声明一个函数的返回类型为Unit。编译器会推断出它。

代码示例:

>>> fun unitExample(){println("Hello,Unit")}

>>> val helloUnit = unitExample()

Hello,Unit

>>> helloUnit

kotlin.Unit

>>> println(helloUnit)

kotlin.Unit

>>> helloUnit is Unit

true

我们可以看出,变量helloUnit的类型是 kotlin.Unit 。

下面几种写法是等价的:

@RunWith(JUnit4::class)

class UnitDemoTest {

@Test fun testUnitDemo() {

val ur1 = unitReturn1()

println(ur1) // kotlin.Unit

val ur2 = unitReturn2()

println(ur2) // kotlin.Unit

val ur3 = unitReturn3()

println(ur3) // kotlin.Unit

}

fun unitReturn1() {}

fun unitReturn2() {

return Unit

}

fun unitReturn3(): Unit {}

}

跟任何其他类型一样,它的父类型是Any。如果是一个可空的Unit?,它的父类型是Any?。

图3-3 Unit类型结构

3.4.2 Nothing类型

在Java中,void不能是变量的类型。也不能被当做值打印输出。但是,在Java中有个包装类Void是 void 的自动装箱类型。如果你想让一个方法返回类型 永远是 null 的话, 可以把返回类型置为这个大写的V的Void类型。

代码示例:

public Void voidDemo() {

System.out.println("Hello,Void");

return null;

}

测试代码:

@RunWith(JUnit4.class)

public class VoidDemoTest {

@Test

public void testVoid() {

VoidDemo voidDemo = new VoidDemo();

Void v = voidDemo.voidDemo(); // Hello,Void

System.out.println(v); // null

}

}

这个Void对应Kotlin中的Nothing?。它的唯一可被访问到的返回值也是null。

如上面小节的Kotlin类型层次结构图所示,在Kotlin类型层次结构的最底层就是类型Nothing。

图3-4 Nothing的类型层次结构

它的定义如下:

public class Nothing private constructor()

这个Nothing不能被实例化

>>> Nothing() is Any

error: cannot access '<init>': it is private in 'Nothing' Nothing() is Any ^

从上面代码示例,我们可以看出 Nothing() 不可被访问。

如果一个函数的返回值是Nothing,这也就意味着这个函数永远不会有返回值。

但是,我们可以使用Nothing来表达一个从来不存在的返回值。例如EmptyList中的 get 函数

internal object EmptyList : List<Nothing>, Serializable, RandomAccess {

override fun get(index: Int): Nothing = throw IndexOutOfBoundsException("Empty list doesn't contain element at index $index.")

}

}

一个空的List调用get函数, 直接是抛出了IndexOutOfBoundsException ,这个时候我们就可以使用Nothing 作为这个get函数的返回类型,因为它永远不会返回某个值,而是直接抛出了异常。

再例如, Kotlin的标准库里面的exitProcess函数:

@file:kotlin.jvm.JvmName("ProcessKt")

@file:kotlin.jvm.JvmVersion

package kotlin.system

@kotlin.internal.InlineOnly

public inline fun exitProcess(status: Int): Nothing {

System.exit(status)

throw RuntimeException("System.exit returned normally, while it was supposed to halt JVM.")

}

注意:Unit与Nothing之间的区别: Unit类型表达式计算结果的返回类型是Unit。Nothing类型的表达式计算结果是永远不会返回的(跟Java中的void相同)。

Nothing?可以只包含一个值:null。代码示例:

>>> var nul:Nothing?=null

>>> nul = 1

error: the integer literal does not conform to the expected type Nothing? nul = 1       ^

>>> nul = true

error: the boolean literal does not conform to the expected type Nothing? nul = true       ^

>>> nul = null

>>> nul

null

从上面的代码示例,我们可以看出:Nothing?它唯一允许的值是null,被用作任何可空类型的空引用。

3.4.3 Any与Any?类型

就像Any是在非空类型层次结构的根,Any?是可空类型层次的根。

Any?是Any的超集,Any?是Kotlin的类型层次结构的最顶端。

图3-5 Any与Any?类型

代码示例:

>>> 1 is Any

true

>>> 1 is Any?

true

>>> null is Any

false

>>> null is Any?

true

>>> Any() is Any?

true

3.5 类型检测与类型转换

3.5.1 is运算符

is运算符可以检查对象是否与特定的类型兼容(此对象是该类型,或者派生于该类型)。

is运算符用来检查一个对象(变量)是否属于某数据类型(如Int、String、Boolean等)。C#里面也有这个运算符。

is运算符类似Java的instanceof:

jshell> "abc" instanceof String

$10 ==> true

在Kotlin中,我们可以在运行时通过使用 is 操作符或其否定形式 !is 来检查对象是否符合给定类型:

>>> "abc" is String true

>>> "abc" !is String

false

>>> null is Any

false

>>> null is Any?

true

代码示例:

@RunWith(JUnit4::class)

class ISTest {

@Test fun testIS() {

val foo = Foo()

val goo = Goo()

println(foo is Foo) //true 自己

println(goo is Foo)// 子类 is 父类 = true

println(foo is Goo)//父类 is 子类 = false

println(goo is Goo)//true 自己

}

}

open class Foo

class Goo : Foo()

3.5.2 类型自动转换

在Java代码中,当我们使用str instanceof String来判断其值为true的时候,我们想使用str变量,还需要显式的强制转换类型:

@RunWith(JUnit4.class)

public class TypeSystemDemo {

@org.junit.Test

public void testVoid() {

Object str = "abc";

if (str instanceof String) {

int len = ((String)str).length();  // 显式的强制转换类型为String

println(str + " is instanceof String");

println("Length: " + len);

} else {

println(str + " is not instanceof String");

}

boolean is = "1" instanceof String;

println(is);

}

void println(Object obj) {

System.out.println(obj);

}

}

 

而大多数情况下,我们不需要在 Kotlin 中使用显式转换操作符,因为编译器跟踪不可变值的 is检查,并在需要时自动插入(安全的)转换:

    @Test

fun testIS() {

val len = strlen("abc")

println(len) // 3

val lens = strlen(1)

println(lens) // 1

}

fun strlen(ani: Any): Int {

if (ani is String) {

return ani.length

} else if (ani is Number) {

return ani.toString().length

} else if (ani is Char) {

return 1

} else if (ani is Boolean) {

return 1

}

print("Not A String")

return -1

}

3.5.3 as运算符

as运算符用于执行引用类型的显式类型转换。如果要转换的类型与指定的类型兼容,转换就会成功进行;如果类型不兼容,使用as?运算符就会返回值null。

代码示例:

>>> open class Foo

>>> class Goo:Foo()

>>> val foo = Foo()

>>> val goo = Goo()

>>> foo as Goo

java.lang.ClassCastException: Line69$Foo cannot be cast to Line71$Goo

>>> foo as? Goo

null

>>> goo as Foo

Line71$Goo@73dce0e6    

我们可以看出,在Kotlin中,子类是禁止转换为父类型的。

本章小结

Kotlin通过引入可空类型,在编译时就大量“清扫了”空指针异常。同时,Kotlin中还引入了安全调用符(?.) 以及Elvis操作符( ?: ) , 使得我们的代码写起来更加简洁。Kotlin的类型系统比Java更加简单一致,Java中的原始类型与数组类型在Kotlin中都统一表现为引用类型。Kotlin中还引入了Unit,Nothing等特殊类型,使得没有返回值的函数与永远不会返回的函数有了更加规范一致的签名。我们还可以使用is 操作符来判断对象实例的类型,以及使用as 操作符进行类型的转换。

KotlinChina编程社区 微博

《Kotlin极简教程》正式上架:

点击这里 > 去京东商城购买阅读

点击这里 > 去天猫商城购买阅读

非常感谢您亲爱的读者,大家请多支持!!!有任何问题,欢迎随时与我交流~

转载于:https://my.oschina.net/universsky/blog/1545171

《Kotlin项目实战开发》 第3章 类型系统与可空类型相关推荐

  1. 《Kotlin项目实战开发》第1章 Kotlin是什么

    第1章 Kotlin是什么 当下互联网大数据云计算时代,数以百万计的应用程序在服务器.移动手机端上运行,其中的开发语言有很大一部分是用流行软件界20多年的.强大稳定的主力的编程语言Java编写. 如果 ...

  2. 《Kotlin项目实战开发》第5章 函数与函数式编程

    第5章 函数与函数式编程 凡此变数中函彼变数者,则此为彼之函数. ( 李善兰<代数学>) 函数式编程语言最重要的基础是λ演算(lambda calculus),而且λ演算的函数可以传入函数 ...

  3. 第8章 泛型 《Kotlin 项目实战开发》

    第8章 泛型 通常情况的类和函数,我们只需要使用具体的类型即可:要么是基本类型,要么是自定义的类.但是在集合类的场景下,我们通常需要编写可以应用于多种类型的代码,我们最简单原始的做法是,针对每一种类型 ...

  4. Node项目实战开发-博客系统

    Nodejs项目实战开发-博客系统(已完结) 个人博客系统 欢迎访问我的博客~ MaXiaoYu's Bolg 前言: 开发技术 技术 版本 Node ^14.3.0 ejs ^3.1.3 expre ...

  5. jsp项目开发案例_Laravel 中使用 swoole 项目实战开发案例一 (建立 swoole 和前端通信)life...

    1 开发需要环境 工欲善其事,必先利其器.在正式开发之前我们检查好需要安装的拓展,不要开发中发现这些问题,打断思路影响我们的开发效率. 安装 swoole 拓展包 安装 redis 拓展包 安装 la ...

  6. jsp项目开发案例_Laravel中使用swoole项目实战开发案例一 (建立swoole和前端通信)

    Laravel中使用swoole项目实战开发案例二(后端主动分场景给界面推送消息) 工欲善其事,必先利其器.在正式开发之前我们检查好需要安装的拓展,不要开发中发现这些问题,打断思路影响我们的开发效率. ...

  7. 零基础学习嵌入式入门以及项目实战开发【手把手教+国内独家+原创】

    零基础学习嵌入式入门以及项目实战开发[手把手教+国内独家+原创] 独家拥有,绝对经典                            创 科 之 龙 嵌入式开发经典系列教程 [第一期] 主讲人: ...

  8. 【创科之龙】零基础学习嵌入式开发以及项目实战开发【第二期视频】

    [创科之龙]零基础学习嵌入式开发以及项目实战开发[学习交流零基础火热进行ing] 大家好,我是aiku,上期的项目学习资料在电子发烧友论坛上分享,大家觉得都很好. 在这里我首先要感谢电子发烧友给我们的 ...

  9. aiku大神归来!送给初学者嵌入式项目实战开发

    aiku大神归来!送给初学者嵌入式项目实战开发 aiku嵌入式视频教程 地址:[创科之龙]基于安卓(android)Linux3.x平台下设备树专题 https://pan.baidu.com/s/1 ...

最新文章

  1. linux下syscall函数,SYS_gettid,SYS_tgkill
  2. HDU 1564 简单博弈 水
  3. OpenCASCADE:Direct3D CSharp 示例
  4. 使用commons httpclient请求https协议的webservice
  5. C++ 高级数据类型(五)—— 数据结构
  6. 内核电源管理器已启动关机转换_电气器件-菲尼克斯UPS(不间断电源)使用
  7. html有4个li怎么选择第二个,如何在html中使用两个具有不同属性的Li?
  8. codesys raspberry pi_【Pi讯早餐】2020.11.02 星期一gt;gt;
  9. Python使用scrapy框架编写自动爬虫爬取京东商品信息并写入数据库
  10. ElasticSearch中minimum_should_match详细介绍
  11. oracle热备是什么意思,Oracle冷备和热备脚本
  12. 【FinE】资本市场理论(1) CAPM模型
  13. abaqus三点弯曲有限元模拟
  14. 保密局计算机网络的安全检查与防护,自治区网络安全检查组莅临图书馆检查指导工作...
  15. php退款系统设计思路,关于退款的10篇文章推荐
  16. 数理统计学类毕业论文文献都有哪些?
  17. 大数据项目离线数仓(全 )二(数仓系统)
  18. project子项目之间任务关联_任务关联的类型(Project)
  19. Idea中使用maven打包出现Cleaning up unclosed ZipFile for archive?
  20. 【QZSS L6E 增强服务改正数支持的 PPP 性能评估】

热门文章

  1. C#实现实时监控文件目录下的变化
  2. Flex 加载pdf
  3. Java多线程之可见性之volatile
  4. 关闭防火墙linux 16.04,如何在Ubuntu 16.04上配置和设置防火墙
  5. TemplateSyntaxError: 'crispy_forms_tags' is not a registered tag library.报错的解决办法
  6. MSP借助五招让中小企业签约云计算服务
  7. ABP理论之CSRF
  8. 【IOS】ios8推送消息注册
  9. Chrome渲染Transition时页面闪动Bug
  10. DOM 提供了一些滚动页面设置指定可见