Kotlin基础(五):类和接口

news2024/11/24 15:22:37

前言

本文主要讲解类和接口,主要包括类的声明、构造器、类成员、修饰符、类的继承、接口、抽象类。


Kotlin文章列表

Kotlin文章列表: 点击此处跳转查看


目录

在这里插入图片描述


1.1 类的声明

在 Kotlin 中,类的声明使用关键字 class。下面是一个简单的类声明的示例:

class MyClass {
    // 类的成员变量
    var property1: Int = 0
    var property2: String = ""

    // 类的成员函数
    fun myFunction() {
        // 函数体
    }
}

在上面的示例中,MyClass 是一个简单的类,具有两个成员变量 property1property2,以及一个成员函数 myFunction。可以通过使用点符号来访问类的成员,例如 myClassInstance.property1 来访问 property1 的值,其中 myClassInstanceMyClass 类的一个实例。

除了普通的类声明之外,还可以使用一些修饰符来定义类的属性和函数的可见性。例如,private 关键字可以用于限制成员的可见性,使其只在类内部可访问。还可以使用 open 关键字来声明一个类可被继承,使用 abstract 关键字声明一个抽象类等等。

1.2 构造器

1.2.1 主构造器

在 Kotlin 中,可以使用主构造函数来声明类的主要构造逻辑。主构造函数是类头部的一部分,并跟在类名后面。

以下是一个使用主构造函数的类声明示例:

class MyClass constructor(parameter1: Type1, parameter2: Type2) {
    // 构造函数中的初始化逻辑
    init {
        // 使用参数进行初始化
        // 可以在这里执行其他的构造逻辑
    }
    
    // 类的成员变量
    var property1: Type1 = parameter1
    var property2: Type2 = parameter2
    
    // 类的成员函数
    fun myFunction() {
        // 函数体
    }
}

在上面的示例中,MyClass 使用主构造函数接收两个参数 parameter1parameter2。这些参数可以在类的成员变量中使用。在构造函数中的 init 块中,可以对成员变量进行初始化,也可以执行其他的构造逻辑。

需要注意的是,在 Kotlin 中,如果主构造函数没有任何注解或可见性修饰符,可以省略 constructor 关键字。

你还可以在主构造函数中声明默认参数,例如:

class MyClass(parameter1: Type1, parameter2: Type2 = defaultValue) {
    // ...
}

在这种情况下,如果调用 MyClass 构造函数时省略了 parameter2 参数,它将使用默认值 defaultValue

1.2.2 第二构造器

在 Kotlin 中,除了主构造函数,你还可以使用次要构造函数(secondary constructor)来定义类的其他构造逻辑。次要构造函数提供了额外的构造选项,使你可以使用不同的参数组合来创建对象。

以下是一个使用次要构造函数的类声明示例:

class MyClass {
    var property1: Int = 0
    var property2: String = ""

    constructor(parameter1: Int) {
        // 使用参数进行初始化
        property1 = parameter1
    }

    constructor(parameter1: Int, parameter2: String) {
        // 使用参数进行初始化
        property1 = parameter1
        property2 = parameter2
    }

    // 类的成员函数
    fun myFunction() {
        // 函数体
    }
}

在上面的示例中,MyClass 声明了两个次要构造函数。第一个次要构造函数接收一个 Int 类型的参数,并将其用于初始化 property1。第二个次要构造函数接收一个 Int 类型的参数和一个 String 类型的参数,并将它们用于初始化 property1property2

使用次要构造函数时,需要注意以下几点:

  • 次要构造函数必须直接或间接地调用主构造函数或另一个已存在的次要构造函数。

  • 使用 this 关键字调用同一个类的其他构造函数。

  • 在次要构造函数内部,可以执行其他的构造逻辑。 ​

1.2.3 Kotlin中的Singleton模式

在 Kotlin 中,可以使用对象声明(Object Declaration)来实现 Singleton 模式。Singleton 是一种设计模式,用于确保一个类只有一个实例,并提供全局访问点。

在 Kotlin 中,可以通过以下方式声明一个 Singleton:

object MySingleton {
    // 单例对象的属性和方法
    var property1: Int = 0
    var property2: String = ""

    fun myFunction() {
        // 函数体
    }
}

在上面的示例中,MySingleton 是一个对象声明,它具有属性 property1property2,以及函数 myFunction。这些属性和函数可以直接通过 MySingleton.property1MySingleton.property2MySingleton.myFunction() 来访问,无需创建该类的实例。

对象声明在首次访问时被延迟初始化,且只会初始化一次。因此,MySingleton 对象在应用程序生命周期中只会存在一个实例。

使用 Singleton 可以在需要共享状态或提供全局访问点的情况下非常有用。例如,日志记录器、数据库连接池等场景都可以使用 Singleton 来实现。

1.2.4 Kotlin函数中的默认参数

在 Kotlin 中,可以为函数参数提供默认值,这样在调用函数时可以选择性地省略这些参数。默认参数允许你定义函数的一组默认值,简化了函数调用并提供了更大的灵活性。

以下是一个使用默认参数的函数示例:

fun greet(name: String = "Guest", message: String = "Hello") {
    println("$message, $name!")
}

在上面的示例中,greet 函数有两个参数:namemessage。这两个参数都有默认值,分别为 "Guest""Hello"。如果调用该函数时没有提供这些参数的值,将使用默认值。

可以以多种方式调用带有默认参数的函数:

greet() // 使用所有参数的默认值,输出:Hello, Guest!
greet("John") // 使用默认的 message 参数值,输出:Hello, John!
greet("Alice", "Hi") // 提供所有参数的值,输出:Hi, Alice!

注意,如果需要仅为某些参数提供值而保留其他参数的默认值,可以使用命名参数来显式指定参数的值:

greet(message = "Hey", name = "Bob") // 使用命名参数,输出:Hey, Bob!

通过默认参数,可以在函数定义时提供合理的默认值,从而简化函数调用并为调用者提供更大的灵活性。

1.2.5 创建类的实例

在 Kotlin 中,可以使用 valvar 关键字来创建类的实例。val 用于声明一个不可变的引用,而 var 用于声明一个可变的引用。

以下是创建类实例的示例:

class MyClass {
    var property1: Int = 0
    var property2: String = ""

    fun myFunction() {
        println("Hello from MyClass")
    }
}

// 创建类实例
val myObj = MyClass()

// 访问类的属性和调用方法
myObj.property1 = 42
myObj.property2 = "Hello, World!"
myObj.myFunction()

在上面的示例中,我们首先定义了一个 MyClass 类,该类具有属性 property1property2,以及方法 myFunction。然后,我们使用 val 关键字创建一个名为 myObj 的不可变引用,并通过调用类的构造函数创建了一个 MyClass 的实例。

要访问类的属性和调用方法,我们使用点符号 (.) 来访问实例的成员。例如,myObj.property1 用于访问和设置 property1 的值,myObj.myFunction() 用于调用 myFunction 方法。

如果你希望在类实例化时初始化属性,可以在构造函数中接收参数,并在构造函数中使用它们进行初始化。例如:

class MyClass(val property1: Int, var property2: String) {
    fun myFunction() {
        println("Hello from MyClass")
    }
}

// 创建类实例并传递参数
val myObj = MyClass(42, "Hello, World!")

在上述示例中,我们在 MyClass 构造函数中定义了 property1property2 参数,并将它们用作属性的初始化值。

1.3 类成员

1.3.1 属性的基本用法

在 Kotlin 中,类的属性用于存储对象的状态或数据。属性可以包含数据,并且可以通过 getter 和 setter 方法进行访问和修改。

以下是 Kotlin 类属性的基本用法:

class MyClass {
    // 可变属性
    var mutableProperty: String = "Initial Value"
    
    // 不可变属性
    val immutableProperty: Int = 42
    
    // 自定义 getter 和 setter
    var customProperty: String = ""
        get() = field.toUpperCase()  // 自定义 getter 方法
        set(value) {
            field = value.trim()  // 自定义 setter 方法
        }
}

在上面的示例中,MyClass 类包含了几个属性。具体说明如下:

  • mutableProperty 是一个可变属性,类型为 String,可以被修改。
  • immutableProperty 是一个不可变属性,类型为 Int,只能在对象初始化时赋值,并且不能再修改。
  • customProperty 是一个自定义 getter 和 setter 的属性。在 getter 方法中,我们将属性值转换为大写字母;在 setter 方法中,我们将接收到的值去除首尾空格后再赋给属性。

属性的访问方式如下:

val obj = MyClass()

// 访问可变属性
println(obj.mutableProperty)  // 输出:Initial Value
obj.mutableProperty = "New Value"
println(obj.mutableProperty)  // 输出:New Value

// 访问不可变属性
println(obj.immutableProperty)  // 输出:42

// 访问自定义属性
obj.customProperty = "  Example  "
println(obj.customProperty)  // 输出:EXAMPLE

上述示例展示了如何访问属性以及如何使用自定义 getter 和 setter 方法。

需要注意的是,Kotlin 还提供了简化属性声明的方式,例如 var/var 代替 get/set 方法的定义,以及 field 关键字用于访问属性的幕后字段。这些是更高级的属性概念,可以在更复杂的场景中使用。

1.3.2 属性的getter和setter形式

在 Kotlin 中,类的属性可以具有默认的 getter 和 setter,也可以自定义 getter 和 setter 方法来控制属性的访问和修改行为。

以下是 Kotlin 类属性的 getter 和 setter 形式的示例:

class MyClass {
    var property: String = ""
        get() {
            println("Getting property value")
            return field
        }
        set(value) {
            println("Setting property value")
            field = value
        }
}

在上面的示例中,MyClass 类的 property 属性具有自定义的 getter 和 setter 方法。在 getter 方法中,我们输出一条消息并返回属性的值;在 setter 方法中,我们输出一条消息并将传入的值赋给属性的幕后字段(使用 field 关键字表示)。

属性的访问方式如下:

val obj = MyClass()

obj.property = "New Value"  // 调用自定义 setter
println(obj.property)  // 调用自定义 getter

上述示例中,我们首先创建了一个 MyClass 对象 obj。然后,我们使用 obj.property 来设置属性的值,它将调用自定义的 setter 方法并输出一条消息。接下来,我们使用 obj.property 来获取属性的值,它将调用自定义的 getter 方法并输出一条消息。

需要注意的是,默认情况下,如果不提供自定义的 getter 和 setter 方法,Kotlin 会为属性生成默认的 getter 和 setter。如果属性声明为 val,则只会生成默认的 getter 方法,无法进行修改。

此外,Kotlin 还提供了一种简化的属性声明方式,即通过 val/var 关键字声明属性,而不需要显式地定义 getter 和 setter 方法。

1.3.3 保存属性值的字段

在 Kotlin 中,属性可以使用幕后字段(backing field)来保存其实际值。幕后字段是属性在内部使用的实际存储,它可以被属性的 getter 和 setter 方法访问和修改。

以下是 Kotlin 类属性使用幕后字段的示例:

class MyClass {
    var property: String = ""
        get() {
            println("Getting property value")
            return field
        }
        set(value) {
            println("Setting property value")
            field = value
        }
}

在上面的示例中,MyClass 类的 property 属性使用 field 作为幕后字段。在 getter 方法中,我们使用 field 来访问属性的实际值;在 setter 方法中,我们使用 field 来修改属性的实际值。

属性的访问方式如下:

val obj = MyClass()

obj.property = "New Value"  // 调用自定义 setter
println(obj.property)  // 调用自定义 getter

上述示例中,我们首先创建了一个 MyClass 对象 obj。然后,我们使用 obj.property 来设置属性的值,它将调用自定义的 setter 方法并输出一条消息。接下来,我们使用 obj.property 来获取属性的值,它将调用自定义的 getter 方法并输出一条消息。

field 是 Kotlin 中预留的关键字,用于表示幕后字段。你可以根据需要为属性选择不同的幕后字段名称。如果你没有显式指定幕后字段名称,Kotlin 会根据需要自动为属性生成一个默认的幕后字段。

需要注意的是,幕后字段只在属性的自定义 getter 和 setter 方法内部可见。在类的其他地方,应该使用属性的名称来访问属性。

1.3.4 函数

在 Kotlin 中,类的成员函数用于封装对象的行为和操作。函数定义在类内部,可以访问类的属性和其他成员函数。

以下是 Kotlin 类成员函数的示例:

class MyClass {
    var property: String = ""

    fun myFunction() {
        println("Hello from myFunction")
        println("Property value: $property")
    }

    fun anotherFunction(param: Int): Int {
        return param * 2
    }
}

在上面的示例中,MyClass 类定义了两个成员函数:myFunctionanotherFunctionmyFunction 打印一条消息并访问类的属性值。anotherFunction 接收一个 Int 类型的参数 param,并返回该参数的两倍。

函数的调用方式如下:

val obj = MyClass()

obj.property = "Value"
obj.myFunction()

val result = obj.anotherFunction(5)
println(result)

上述示例中,我们首先创建了一个 MyClass 对象 obj。然后,我们通过 obj.property 设置属性的值。接下来,我们调用 obj.myFunction() 来执行 myFunction 函数,并输出相应的消息和属性值。最后,我们使用 obj.anotherFunction(5) 调用 anotherFunction 函数,并将返回的结果存储在 result 变量中并打印。

需要注意的是,成员函数可以访问类的属性和其他成员函数。在函数内部,可以使用 this 关键字来引用当前对象,从而访问对象的成员。

1.3.5 嵌套类

在 Kotlin 中,嵌套类是一个类被嵌套在另一个类内部的类。嵌套类在外部类的作用域之外是可见的,可以使用外部类的名称进行访问。

以下是 Kotlin 中嵌套类的示例:

class Outer {
    private val outerProperty: String = "Outer Property"

    class Nested {
        fun nestedFunction() {
            println("Nested Function")
            // 无法访问外部类的成员
        }
    }
}

在上面的示例中,Outer 类包含一个嵌套类 Nested。嵌套类可以访问自身的成员,但无法直接访问外部类的成员。

要访问嵌套类,可以使用外部类的名称作为限定符:

val nestedObj = Outer.Nested()
nestedObj.nestedFunction()

上述示例中,我们首先通过 Outer.Nested() 创建了一个 Nested 类的实例 nestedObj。然后,我们调用 nestedObj.nestedFunction() 来执行嵌套类的函数。

需要注意的是,嵌套类和内部类(使用 inner 关键字声明的类)是不同的概念。嵌套类不持有对外部类实例的引用,而内部类可以访问外部类的成员并持有对外部类实例的引用。

1.4 修饰符(Modifiers)

在 Kotlin 中,修饰符(Modifiers)用于修改类、函数、属性和其他声明的行为和访问级别。以下是一些常用的修饰符:

  1. 访问修饰符(Access Modifiers)
    • public:默认的修饰符,对所有地方可见。
    • private:只在同一个文件内可见。
    • protected:对同一个文件和子类可见。
    • internal:对同一个模块(module)内的所有地方可见。
  2. 可见性修饰符(Visibility Modifiers)
    • public:对所有地方可见。
    • private:只在同一个类内可见。
    • protected:对同一个类和子类可见。
    • internal:对同一个模块内的所有地方可见。
  3. 继承修饰符(Inheritance Modifiers)
    • open:允许类被继承。默认情况下,类是不可继承的。
    • final:阻止类被继承,函数被重写。
  4. 重写修饰符(Override Modifiers)
    • override:标记一个函数或属性是重写父类的。必须用在子类中。
  5. 其他修饰符
    • abstract:用于抽象类和抽象函数。
    • final:用于阻止函数被重写,属性被修改。
    • const:用于标记常量,要求在编译时确定其值。

这些修饰符可以根据需要在声明中使用,以达到所需的行为和访问级别。注意,不同的修饰符适用于不同的声明类型(例如,类、函数、属性等)。

需要注意的是,在 Kotlin 中有一些修饰符是默认的(例如,public),因此在大多数情况下不需要显式地使用它们。但是,了解这些修饰符的含义和使用场景对于编写清晰、安全和可维护的代码非常重要。

1.5 类的继承

1.5.1 Kotlin类如何继承

在 Kotlin 中,类的继承通过使用冒号(:)来表示。一个类可以继承自另一个类,称为父类(或超类),并继承父类的属性和函数。

以下是 Kotlin 中类的继承的基本语法:

open class ParentClass {
    // 父类的属性和函数
}

class ChildClass : ParentClass() {
    // 子类的属性和函数
}

在上面的示例中,ChildClass 是继承自 ParentClass 的子类。通过在类名后面使用冒号,后跟父类的名称,可以实现继承关系。

需要注意的是,为了允许继承,父类必须使用 open 关键字进行标记。在默认情况下,类是不可继承的(即,类是 final 的)。通过使用 open 关键字,我们可以将类标记为可继承的。

在子类中,可以访问并重写父类的属性和函数。使用 override 关键字可以重写父类的成员。在重写的函数中,可以使用 super 关键字引用父类的实现。

以下是一个示例,展示了类的继承和重写的用法:

open class Person(val name: String) {
    open fun introduce() {
        println("My name is $name.")
    }
}

class Student(name: String, val grade: Int) : Person(name) {
    override fun introduce() {
        super.introduce()
        println("I'm a student in grade $grade.")
    }
}

在上面的示例中,Person 是一个简单的父类,Student 是继承自 Person 的子类。子类重写了父类的 introduce 函数,并使用 super.introduce() 调用了父类的实现。然后,在子类的 introduce 函数中添加了额外的打印语句。

通过以下方式使用继承和重写:

val person = Person("John")
person.introduce()  // 输出:"My name is John."

val student = Student("Alice", 10)
student.introduce()  // 输出:"My name is Alice.\nI'm a student in grade 10."

在上述示例中,我们首先创建了一个 Person 对象 person,并调用其 introduce 函数。然后,我们创建了一个 Student 对象 student,并调用其 introduce 函数。注意,子类 Student 继承了父类 Person 的属性 name

1.5.2 重写方法

在 Kotlin 中,要重写父类的方法,需要在子类中使用 override 关键字。这样做可以确保子类中的方法与父类中的方法具有相同的签名,并且在运行时调用时会调用子类的方法。

以下是重写方法的基本语法:

open class ParentClass {
    open fun methodName() {
        // 父类方法的实现
    }
}

class ChildClass : ParentClass() {
    override fun methodName() {
        // 子类方法的实现
    }
}

在上面的示例中,ChildClass 是继承自 ParentClass 的子类,并重写了父类中的 methodName 方法。子类中的 methodName 方法使用 override 关键字进行标记,以表示它是对父类方法的重写。

需要注意的是,父类中被重写的方法必须使用 open 关键字进行标记,以允许子类对其进行重写。而子类中的重写方法可以选择使用 override 关键字进行标记,以明确指示该方法是对父类方法的重写(尽管没有使用 override 也能进行重写,但建议使用 override 显式标记,以提高代码的可读性)。

在子类中重写父类的方法时,可以使用 super 关键字来引用父类的实现。例如,可以使用 super.methodName() 调用父类的方法。

以下是一个示例,展示了如何重写父类的方法:

open class Shape {
    open fun draw() {
        println("Drawing a shape.")
    }
}

class Circle : Shape() {
    override fun draw() {
        super.draw()
        println("Drawing a circle.")
    }
}

在上面的示例中,Shape 是一个基类,Circle 是继承自 Shape 的子类。子类重写了父类的 draw 方法,并在子类的方法中使用 super.draw() 调用了父类的实现。然后,在子类的方法中添加了额外的打印语句。

使用重写方法的示例:

val shape: Shape = Circle()
shape.draw()

在上述示例中,我们创建了一个 Shape 类型的变量 shape,但实际上它引用的是 Circle 对象。然后,我们调用 shape.draw() 方法。由于多态性的存在,实际上调用的是 Circle 类中重写的 draw 方法。输出结果将首先打印 “Drawing a shape.”,然后打印 “Drawing a circle.”。

1.5.3 重写属性

在 Kotlin 中,可以通过重写属性来修改从父类继承的属性的行为。属性的重写需要使用 override 关键字,并且可以对父类属性的访问器(getter 和 setter)进行重写。

以下是重写属性的基本语法:

open class ParentClass {
    open val propertyName: Type = initial value
}

class ChildClass : ParentClass() {
    override var propertyName: Type
        get() {
            // 重写 getter
        }
        set(value) {
            // 重写 setter
        }
}

在上面的示例中,ChildClass 是继承自 ParentClass 的子类,并重写了父类的 propertyName 属性。子类中的 propertyName 属性使用 override 关键字进行标记,并重写了父类属性的访问器。

在重写属性的访问器中,可以自定义属性的行为,例如提供不同的值或添加额外的逻辑。

需要注意的是,与重写方法类似,父类中被重写的属性必须使用 open 关键字进行标记,以允许子类对其进行重写。而子类中的重写属性可以选择使用 override 关键字进行标记,以明确指示该属性是对父类属性的重写(尽管没有使用 override 也能进行重写,但建议使用 override 显式标记,以提高代码的可读性)。

以下是一个示例,展示了如何重写父类的属性:

open class Shape {
    open val description: String = "Shape"
}

class Circle : Shape() {
    override val description: String = "Circle"
}

在上面的示例中,Shape 是一个基类,Circle 是继承自 Shape 的子类。子类重写了父类的 description 属性,并提供了不同的值。

使用重写属性的示例:

val shape: Shape = Circle()
println(shape.description)

在上述示例中,我们创建了一个 Shape 类型的变量 shape,但实际上它引用的是 Circle 对象。然后,我们打印 shape.description 属性。由于多态性的存在,实际上访问的是 Circle 类中重写的 description 属性。输出结果将是 “Circle”。

需要注意的是,重写属性的访问器也可以调用父类属性的访问器,使用 super 关键字,例如 super.propertyName

1.6 接口

在 Kotlin 中,接口(Interfaces)是一种定义行为和功能的方式,类可以实现一个或多个接口来继承接口中定义的方法和属性。接口定义了一组要求,而实现接口的类需要提供对应的实现。

以下是定义接口的基本语法:

interface InterfaceName {
    // 接口中的属性和方法
}

接口中可以包含属性和方法声明,但不能包含实际的实现代码。接口的属性可以是抽象的(没有初始值)或具有访问器的属性(可以有自定义的 getter 和 setter)。接口的方法可以是抽象的(没有实现体)或具有默认实现。

接口的实现使用 : 符号,类可以通过关键字 implements 或者直接通过 : 实现接口。

以下是一个简单的接口示例:

interface Printable {
    fun print()
}

class Document : Printable {
    override fun print() {
        println("Printing document...")
    }
}

在上面的示例中,我们定义了一个名为 Printable 的接口,它声明了一个抽象的方法 print()。然后,我们创建了一个类 Document,它实现了 Printable 接口,并提供了对应的实现。

要注意的是,在类中实现接口的方法时,需要使用 override 关键字进行标记,以明确指示这是对接口方法的实现。

使用接口的示例:

val document = Document()
document.print()

在上述示例中,我们创建了一个 Document 类的实例,并调用了实现的 print() 方法。输出结果将是 “Printing document…”。

接口可以用于在不同的类之间建立共同的协议,并实现多态性。类可以实现多个接口,通过逗号分隔。

interface Interface1 {
    // ...
}

interface Interface2 {
    // ...
}

class MyClass : Interface1, Interface2 {
    // ...
}

1.7 抽象类

在 Kotlin 中,抽象类(Abstract Class)是一种不能被实例化的类,它可以包含抽象方法和非抽象方法。抽象类用于提供一个基类的模板,定义通用的属性和方法,但需要子类来实现其中的抽象方法。

以下是定义抽象类的基本语法:

abstract class AbstractClassName {
    // 抽象方法和非抽象方法
}

抽象类使用 abstract 关键字进行标记,其中包含的抽象方法没有实现体,而非抽象方法可以有实现。

子类继承抽象类时,必须提供对抽象方法的具体实现。如果子类不提供对抽象方法的实现,那么子类也必须声明为抽象类。

以下是一个简单的抽象类示例:

abstract class Shape {
    abstract fun calculateArea(): Double

    fun printDescription() {
        println("This is a shape.")
    }
}

class Circle(private val radius: Double) : Shape() {
    override fun calculateArea(): Double {
        return Math.PI * radius * radius
    }
}

在上面的示例中,Shape 是一个抽象类,它声明了一个抽象方法 calculateArea() 和一个非抽象方法 printDescription()。然后,我们创建了一个 Circle 类,它继承自 Shape 抽象类,并提供了对 calculateArea() 方法的具体实现。

需要注意的是,在子类中重写抽象方法时,必须使用 override 关键字进行标记,以明确指示这是对抽象方法的实现。

使用抽象类的示例:

val circle = Circle(5.0)
circle.printDescription()
val area = circle.calculateArea()
println("Area: $area")

在上述示例中,我们创建了一个 Circle 类的实例,并调用了从抽象类 Shape 继承的 printDescription() 方法。然后,我们计算了圆的面积,并将其打印出来。

抽象类是一种有助于提供代码复用和约束子类行为的工具。抽象类可以包含具体的属性和方法,以及抽象的方法,这使得抽象类可以充当一种中间层,实现通用的逻辑,并强制子类提供特定的行为。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/747180.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

感受C++模版的所带来的魅力

一、泛型编程思想 首先我们来看一下下面这三个函数,如果学习过了 C函数重载 和 C引用 的话,就可以知道下面这三个函数是可以共存的,而且传值会很方便 void Swap(int& left, int& right) {int temp left;left right;right temp; }…

三、Exsi安装虚拟机win10系统

Exsi安装虚拟机win10系统 1、新建虚拟机2、选择存储(直接下一步)3、自定义设置3.1 设置cpu3.2 设置内存3.3 设置硬盘3.4 设置网卡3.5 设置驱动3.6 设置BIOS 4、完成安装 1、新建虚拟机 2、选择存储(直接下一步) 3、自定义设置 3.1 设置cpu 3.2 设置内存 3.3 设置硬盘 3.4 设置…

python搭建文件服务

python -m http.server 访问8000端口,可分享或下载命令启动文件夹下文件。

美国访问学者的申请条件

作为一个富有学术活力和创新精神的国家,美国吸引着世界各地的学者前往交流和研究。对于希望成为美国访问学者的人来说,了解申请条件是至关重要的。下面知识人网小编将介绍一些常见的美国访问学者申请条件。 1. 学术背景与研究计划:作为申请者…

安全测试方法介绍(上)静态源代码审查

软件开发完成之后,我们需要进行安全验证,验证我们的软件是不是符合安全要求。软件安全测试主要有以下几个方面:确定软件的安全特性实现是否与预期设计一致的过程;有关验证软件安全等级和识别潜在安全缺陷的过程;查找软…

采集发布到WordPress网址(OneNav主题-WordPress主题)

WordPress系统的一导航主题(OneNav主题)是集网址、资源、资讯于一体的导航主题。 要将采集的数据批量自动发布到一导航主题(OneNav主题)的网址要怎么设置? 普通的文章采集器一般只能发布为wordpress文章类型&#xff…

echarts折线图横向渐变填充

这种情况,需要后端去计算,如何把不同分段的值赋予不同的颜色,然后组合成下面我们需要的格式就可以实现 drawLine1() {if (document.getElementById(s1) ! null) {var that thislet heightNum (this.porosityList.maxDepth-this.porosityLis…

网络安全—综合渗透实验

一、 实验名称 综合实验 二、 实验目的 【实验描述】 随着互联网的普及和快速发展,互联网产品多样化、迭代快的特点为一些企业赢得了机会,同样也给企业带来了众多安全问题。如网络安全、系统安全、web安全、数据安全等。 本实验模拟企业复杂网络、复…

开源代码分享(6)—考虑电动汽车可调度潜力的充电站两阶段市场投标策略(附matlab代码)

[1]詹祥澎,杨军,韩思宁等.考虑电动汽车可调度潜力的充电站两阶段市场投标策略[J].电力系统自动化,2021,45(10):86-96. 摘要:在电力市场环境下,充电站优化投标策略能降低电力成本,甚至通过售电获取收益。文中考 虑了电动汽车成为柔性储荷资源的潜力&#…

使用mongodump和mongorestore备份与恢复Mongodb数据

一、备份与恢复方案 mongodump是MongoDB官方提供的备份工具,它可以从MongoDB数据库读取数据,并生成BSON文件,mongodump适合用于备份和恢复数据量较小的MongoDB数据库, 不适用于大数据量备份。 默认情况下mongodump不获取local数据库里面的内容。mongodump仅备份数据库中的文档…

微信小程序原生button组件使用并修改样式

想使用原生button组件&#xff0c;但是想使用不同样式 <button type"default">登录</button> 注意type使用得type必须和修改时选择器样式&#xff08;button[typedefault]&#xff09;一样&#xff0c;才能对其修改样式&#xff0c;否则样式不会被修改…

ArcGIS提取路网节点

这是我学校周围的路网,数据就不分享给大家了,看教程知道有这个方法就好了 先打开编辑器,把他合并成只有一个字段的要素 然后打开高级编辑,打断相交线 保存退出,然后打开ArcCatalog 10.6, 建立网络数据集 这时候就成了带有节点的网络数据集,但这还不是我们需要的点文件,因为这…

如何创建智能合约游戏系统

区块技术的发展&#xff0c;智能合约成为了一个热门话题。智能合约是一种基于区块技术的自动化合约&#xff0c;它可以自动执行合同中规定的条款&#xff0c;从而实现去中心化的信任和价值传递。在游戏领域&#xff0c;智能合约可以让玩家在游戏中实现各种交易和交互&#xff0…

通信算法之178: 通信信道模型2

上一篇见 通信算法之159: 通信信道模型和循环/线性卷积 一. 衰减 二.多径效应--时延扩展--相干带宽 三. 时变性--多普勒扩展--相干时间 四. 频率选择性衰落&#xff0c;时间选择性衰落 小尺度衰落&#xff0c;小 五.瑞丽和莱斯信道 六循环卷积与线性卷积 线性卷积定义及计算…

1、Linux 文件与目录结构

Linux 文件 Linux 系统中一切皆文件。 Linux 目录结构 VI/VIM 编辑器 是什么 VI 是 Unix 操作系统和类 Unix 操作系统中最通用的文本编辑器。 VIM 编辑器是从 VI 发展出来的一个性能更强大的文本编辑器。可以主动的以字体颜 色辨别语法的正确性&#xff0c;方便程序设计…

网络安全与密码学

1、网络安全威胁 破坏网络安全的一些理论方式&#xff1a; 窃听&#xff1a;窃听信息&#xff0c;在网路通信双方直接进行窃听。 插入&#xff1a;主动在网络连接中插入信息&#xff08;可以在message中插入恶意信息&#xff09; 假冒&#xff1a;伪造&#xff08;spoof&#x…

【从零开始学习CSS | 第一篇】选择器介绍

目录 前言&#xff1a; 选择器介绍&#xff1a; 各类选择器&#xff1a; 总结&#xff1a; 前言&#xff1a; 本文以及后续几篇文章我们将会集中介绍CSS中的常见选择器&#xff0c;选择器的出现可以让我们实现对具体的元素标签进行定制&#xff0c;因此我们要掌握好各类选择…

LiveGBS流媒体平台GB/T28181功能-如何将海康大华宇视华为摄像头视频流集中存储云端录像分布式录像存储

LiveGBS如何将海康大华宇视华为摄像头视频流集中存储云端录像分布式录像存储 1、云端录像存储2、手动配置录像2.1、按需录像2.2、一直录像 3、录像计划3.1、录像计划入口3.2、新增录像计划3.3、编辑录像计划3.4、关联通道 4、查看云端录像4.1、查看录像4.1.1、时间轴模式4.1.2、…

解决Ubuntu 虚拟机没有网络图标(无法联网问题)

问题&#xff1a; 我们打开Ubuntu虚拟机的时候&#xff0c;发现没有网络图标&#xff0c;这就使得联网无从下手&#xff0c;该怎么解决呢&#xff1f; 第一步&#xff1a;点击编辑虚拟机 第二步&#xff1a;点击虚拟网络编辑器 第三步&#xff0c;还原默认设置&#xff1a; 第四…

简单爬虫项目练习

爬虫项目练习 前言任务基本爬虫框架URL管理器Html 下载器HTML 解析器数据存储器爬虫调度器效果分析 前言 自学&#xff0c;参考书籍为 Python爬虫开发与项目实战 &#xff0c;具体参考了该书的第六章。过程中出现两个问题&#xff1a; 在 Pycharm 上实现时发现有些库名更改及…