Java编程思想6~8章
当我们写了一个工具类以后,我们可以给它编写一个私有构造器,这样可以防止别人创建它的实例再使用,减少重复创建实例的时间和空间,只需要通过类名.方法名调用即可。
组合是通过将一个类的对象嵌入到另一个类中来实现复用。这种方式允许一个类使用另一个类的功能,但不会继承其实现。
class Engine {
// 引擎的实现
}
class Car {
private Engine engine;
public Car(Engine engine) {
this.engine = engine;
}
public void start() {
engine.start();
}
}
继承是通过创建一个新类,使用已有类的属性和方法,然后在新类中添加或修改功能来实现复用。
class Animal {
public void eat() {
// 动物吃东西的行为
}
}
class Dog extends Animal {
public void bark() {
// 狗叫的行为
}
}
在继承中,基类的初始化是通过调用基类的构造方法来实现的。子类在创建实例时,需要首先初始化基类的状态,然后再添加自己的额外状态。
在Java中,使用 super() 调用基类的构造方法,这必须是子类构造方法的第一条语句。如果子类的构造方法没有显式调用 super(),则默认会调用基类的无参构造方法。如果基类没有提供无参构造方法,并且子类没有显式调用基类的其他构造方法,那么编译器将报错。
class Animal {
private String name;
public Animal(String name) {
this.name = name;
System.out.println("Animal constructor");
}
public void eat() {
System.out.println(name + " is eating");
}
}
class Dog extends Animal {
private String breed;
public Dog(String name, String breed) {
super(name); // 调用基类构造方法
this.breed = breed;
System.out.println("Dog constructor");
}
public void bark() {
System.out.println(breed + " dog is barking");
}
}
public class Main {
public static void main(String[] args) {
Dog myDog = new Dog("Buddy", "Golden Retriever");
myDog.eat(); // 调用基类方法
myDog.bark(); // 调用子类方法
}
}
在上述例子中,Dog 类继承自 Animal 类。在 Dog 的构造方法中,通过 super(name) 调用了基类 Animal 的构造方法。这样,当创建 Dog 的实例时,首先会执行 Animal 的构造方法,然后再执行 Dog 的构造方法。
松散的耦合(Loose Coupling):
当对象之间的关系需要在运行时动态确定,或者需要更灵活的对象替换时,组合是一个较好的选择。对象之间通过接口进行通信,而不是通过继承关系。
动态配置对象行为:
当需要在运行时动态配置或更改对象的行为时,组合可以提供更灵活的方式。通过替换或添加组件,可以修改对象的行为。
代码复用:
当想要实现代码复用而不引入继承的复杂性时,组合是一种更灵活的选择。可以通过组合来构建包含所需功能的对象,而不必考虑继承层次结构。
接口和多态性需求:
当希望通过接口实现多态性,使得对象能够以相似的方式被使用时,组合通常是更好的选择。通过接口定义,对象可以替换为具有相同接口的其他对象。
是一个关系(Is-a Relationship):
当子类对象是父类对象的一种类型时,适合使用继承。例如,狗是动物的一种类型。
共享基本行为:
当多个类具有共享的基本行为,并且这些行为在整个继承层次结构中都是一致的时,使用继承可以减少代码的重复。
抽象基类:
当希望创建一个通用的抽象基类,而具体的实现由子类提供时,可以使用继承。这对于框架和库的设计很常见。
扩展已有类:
当需要扩展已有类的功能,并且新类可以被视为原始类的扩展时,继承是一种自然的选择。这符合“是一个”关系。
final 是一个关键字,它可以用于修饰类、方法和变量。final 的主要作用是表示不可改变的、不可继承的、或者不可覆盖的。
当一个类被声明为 final 时,它表示这个类不能被其他类继承。这通常用于防止对类的进一步修改和扩展。
当一个方法被声明为 final 时,它表示这个方法不能被子类覆盖(即不能被重写)。
当一个变量(字段)被声明为 final 时,它表示这个变量的值只能被赋值一次,之后不能再修改(成员变量只能在定义时或者构造器中赋值)。对于基本数据类型,这意味着其数值不可改变;对于引用类型,这意味着引用不可变,但对象本身的状态可能是可变的。
class Example {
// final 常量,只能被赋值一次
final int CONSTANT_VALUE = 10;
// final 引用,引用不可变,但对象本身的状态可能是可变的
final StringBuilder stringBuilder = new StringBuilder("Hello");
void modifyValues() {
// 编译错误,不能修改 final 常量的值
// CONSTANT_VALUE = 20;
// 可以修改 final 引用所指向的对象的状态
stringBuilder.append(", World!");
}
}
多态(Polymorphism): 多态是面向对象编程的一个核心概念,它允许不同的子类对象被视为相同的父类对象。
方法重写(Method Overriding): 子类可以提供与父类中相同签名的方法,并重新定义其实现。
向上转型(Upcasting): 将子类对象赋给父类引用。例如:Animal myDog = new Dog();
向下转型(Downcasting): 将父类引用转换为子类引用。需要使用强制类型转换,但要注意转型的安全性。
动态绑定(Dynamic Binding): 在运行时确定对象的类型,并调用相应的方法。
运行时类型信息(RTTI): 使用 instanceof 操作符检查对象的实际类型。
抽象类(Abstract Class): 抽象类可以包含抽象方法,子类必须提供实现。可以使用多态性创建指向子类对象的引用。
接口(Interface): 接口定义规范,实现类提供具体实现。通过接口可以实现多态性。
灵活性和可扩展性: 多态性提高了代码的灵活性,使得程序更容易适应变化和扩展。
代码复用: 通过多态性,可以将同一接口的不同实现交替使用,实现代码的复用。
方法私有化: 私有方法不会被继承,因此不具备多态性。
静态方法和字段: 静态方法和字段属于类,而不是实例,因此不具备多态性。
构造器和多态: 构造器不具备多态性,因为在对象构造之前,它的类型已经确定。
遵循多态的原则: 利用多态性设计灵活且易于扩展的代码。
理解继承层次结构: 多态性依赖于继承关系,理解类的层次结构对正确使用多态性非常重要。
RTTI 的谨慎使用: 使用 instanceof 操作符时需小心,过度使用可能暗示设计上的问题。