似乎抽象类和构造函数可能不兼容的。构造函数方法被称为实例化一个类时,和一个抽象类不能实例化。
在本文中,我们将看到为什么抽象类可以有构造函数和如何使用它们在子类实例化提供了好处。
当一个类不申报任何构造函数,编译器会创建一个默认的构造函数。这也适用于抽象类。即使没有显式的构造函数,抽象类有一个默认的构造函数。
在一个抽象类,它的后代可以使用超级调用抽象默认构造函数():
public abstract class AbstractClass {
// compiler creates a default constructor
}
public class ConcreteClass extends AbstractClass {
public ConcreteClass() {
super();
}
}
我们可以声明一个构造函数没有参数在一个抽象类。它会覆盖默认的构造函数,以及任何子类创建将称之为第一建设链。
让我们验证这一行为有两个抽象类的子类:
public abstract class AbstractClass {
public AbstractClass() {
System.out.println("Initializing AbstractClass");
}
}
public class ConcreteClassA extends AbstractClass {
}
public class ConcreteClassB extends AbstractClass {
public ConcreteClassB() {
System.out.println("Initializing ConcreteClassB");
}
}
让我们看看输出得到当调用新的ConcreateClassA ():
初始化AbstractClass
而输出调用新的ConcreteClassB()将会是:
初始化AbstractClass
初始化ConcreteClassB
(1)安全初始化
声明一个抽象的不带参数的构造函数可以有利于安全初始化。
以下计数器类计数自然数的超类。我们需要它的值从0开始。
让我们来看看我们可以使用一个参数构造函数来确保安全的初始化:
public abstract class Counter {
int value;
public Counter() {
this.value = 0;
}
abstract int increment();
}
我们SimpleCounter子类实现增量和+ +运算符()方法。它由一个在每个增量的价值调用:
public class SimpleCounter extends Counter {
@Override
int increment() {
return ++value;
}
}
请注意,SimpleCounter不申报任何构造函数。成立依赖柜台默认的无参数构造函数被调用。
下面的单元测试演示了安全属性被初始化的值构造函数:
@Test
void givenNoArgAbstractConstructor_whenSubclassCreation_thenCalled() {
Counter counter = new SimpleCounter();
assertNotNull(counter);
assertEquals(0, counter.value);
}
(2)阻止访问
计数器的初始化工作正常,但是让我们想象我们不想让子类覆盖这个安全初始化。
首先,我们需要阻止子类的构造函数私有访问:
private Counter() {
this.value = 0;
System.out.println("Counter No-Arguments constructor");
}
其次,让我们创建另一个调用子类的构造函数:
public Counter(int value) {
this.value = value;
System.out.println("Parametrized Counter constructor");
}
最后,我们需要SimpleCounter覆盖参数化构造函数,否则,它不会编译:
public class SimpleCounter extends Counter {
public SimpleCounter(int value) {
super(value);
}
// concrete methods
}
注意编译器预计我们称之为超级构造函数(值),限制访问我们的私人参数构造函数。
最常见的一种使用抽象类的构造函数是为了避免冗余。让我们创建一个示例使用汽车,看看我们可以利用参数化构造函数。
我们从一个抽象的汽车类来代表所有类型的汽车。我们还需要知道多少旅行距离属性:
public abstract class Car {
int distance;
public Car(int distance) {
this.distance = distance;
}
}
我们的超类看起来很好,但我们不希望距离属性被初始化一个非零值。我们也要防止子类改变财产或覆盖的距离参数化构造函数。
让我们看一下如何限制距离和安全地使用构造函数来初始化:
public abstract class Car {
private int distance;
private Car(int distance) {
this.distance = distance;
}
public Car() {
this(0);
System.out.println("Car default constructor");
}
// getters
}
现在,我们距离属性和参数化构造函数是私有的。有公共汽车默认构造函数()代表私有构造函数来初始化的距离。
财产使用我们的距离,让我们添加一些行为来获取和显示汽车的基本信息:
abstract String getInformation();
protected void display() {
String info = new StringBuilder(getInformation())
.append("\nDistance: " + getDistance())
.toString();
System.out.println(info);
}
所有子类都需要提供一个实现getInformation(),和显示()方法将使用它来打印所有的细节。
现在让我们创建ElectricCar和FuelCar子类:
public class ElectricCar extends Car {
int chargingTime;
public ElectricCar(int chargingTime) {
this.chargingTime = chargingTime;
}
@Override
String getInformation() {
return new StringBuilder("Electric Car")
.append("\nCharging Time: " + chargingTime)
.toString();
}
}
public class FuelCar extends Car {
String fuel;
public FuelCar(String fuel) {
this.fuel = fuel;
}
@Override
String getInformation() {
return new StringBuilder("Fuel Car")
.append("\nFuel type: " + fuel)
.toString();
}
}
让我们看看这些子类行动:
ElectricCar electricCar = new ElectricCar(8);
electricCar.display();
FuelCar fuelCar = new FuelCar("Gasoline");
fuelCar.display();
产生的输出的样子:
Car default constructor
Electric Car
Charging Time: 8
Distance: 0
Car default constructor
Fuel Car
Fuel type: Gasoline
Distance: 0
像任何其他类在Java中,抽象类可以有构造函数,即使他们只是从具体的子类。如果大家想了解更多相关知识,不妨来关注一下本站的Java基础教程,里面还有更丰富的知识等着大家去学习,希望对大家能够有所帮助哦。
你适合学Java吗?4大专业测评方法
代码逻辑 吸收能力 技术学习能力 综合素质
先测评确定适合在学习