上一篇地址:赶紧收藏!2024 年最常见 100道 Java 基础面试题(六)-CSDN博客
十三、抽象类能使用final
修饰吗?
在Java中,final
关键字有几种不同的用途,但是当它用于类时,它表示该类不能被继承。因此,抽象类不能被声明为final
,原因如下:
-
继承的目的:
- 抽象类的设计意图是作为其他类的基类,允许其他类通过继承来扩展其功能。如果一个抽象类被声明为
final
,那么它就失去了作为基类的主要目的,因为它阻止了继承。
- 抽象类的设计意图是作为其他类的基类,允许其他类通过继承来扩展其功能。如果一个抽象类被声明为
-
抽象方法的实现:
- 抽象类通常包含一个或多个抽象方法,这些方法没有具体的实现,而是期望继承的子类提供实现。如果抽象类是
final
的,那么它就不能被继承,因此无法由子类提供这些抽象方法的实现。
- 抽象类通常包含一个或多个抽象方法,这些方法没有具体的实现,而是期望继承的子类提供实现。如果抽象类是
-
设计模式和原则:
- 在面向对象编程中,抽象类通常用于实现里氏替换原则(Liskov Substitution Principle),这是SOLID原则之一。声明类为
final
会违背这一原则,因为它阻止了其他类通过继承来重用和扩展基类的行为。
- 在面向对象编程中,抽象类通常用于实现里氏替换原则(Liskov Substitution Principle),这是SOLID原则之一。声明类为
-
语法限制:
- 在Java语言规范中,不允许将含有抽象方法的类声明为
final
。即使抽象类中没有抽象方法,从设计的角度来看,将其声明为final
也是不合理的,因为它失去了作为抽象基类的意义。
- 在Java语言规范中,不允许将含有抽象方法的类声明为
示例代码:
// 尝试将抽象类声明为final将导致编译错误
abstract final class InvalidDesign {
// 这样的声明是不合法的,因为final和abstract是矛盾的
public abstract void someAbstractMethod();
}
// 正确的抽象类声明
abstract class ValidDesign {
public abstract void someAbstractMethod();
// 可以有具体的方法实现
public void someConcreteMethod() {
// 方法实现
}
}
// 继承ValidDesign的子类
class SubClass extends ValidDesign {
@Override
public void someAbstractMethod() {
// 提供抽象方法的具体实现
}
}
总结:
- 抽象类不能被声明为
final
,因为这样做违背了抽象类作为基类被继承和扩展的目的。 - 在实际编程中,如果一个类被声明为
final
,那么它通常是具体类,提供了所有方法的具体实现,并且不希望被继承。
十四、接口和抽象类有什么区别?
在Java中,接口(interface
)和抽象类(abstract class
)都是实现面向对象编程中的抽象化概念的工具,但它们之间存在一些关键的区别:
-
定义:
- 抽象类:使用
abstract
关键字声明,可以包含抽象方法和具体方法。抽象方法没有方法体,而具体方法有完整的实现。 - 接口:使用
interface
关键字声明,在Java 8之前,接口中的方法都是隐式地被声明为public abstract
的。从Java 8开始,接口也可以拥有默认方法(带有具体实现的方法)和静态方法。
- 抽象类:使用
-
实现:
- 一个类可以通过
extends
关键字继承一个抽象类,并且必须提供抽象类中所有抽象方法的实现。 - 一个类可以通过
implements
关键字实现多个接口,并且必须提供所有方法的具体实现(除非接口方法被声明为默认方法)。
- 一个类可以通过
-
构造方法:
- 抽象类可以有构造方法,而接口不能有构造方法。
-
访问修饰符:
- 抽象类中的方法可以有多种访问修饰符(如
public
,protected
,private
)。 - 接口中的方法在Java 9之前默认是
public
的,且不能有private
或protected
修饰。从Java 9开始,接口方法可以有private
修饰,但仅用于实现默认方法。
- 抽象类中的方法可以有多种访问修饰符(如
-
属性:
- 抽象类可以有各种属性,包括实例变量和静态变量。
- 接口在Java 8之前只能有静态常量。从Java 8开始,接口可以有静态方法和默认方法。
-
多继承:
- Java中的类只能继承自一个抽象类,因为多继承会导致“菱形问题”(Diamond Problem)。
- 一个类可以实现多个接口,没有数量限制,这允许更灵活的设计。
-
使用场景:
- 抽象类更适合用来表达一个概念的部分实现,当你想在一个类中提供一些共享的代码时,可以使用抽象类。
- 接口更适合用来表达一个角色或一个特定的行为,当你想定义一个可以被多个类实现的契约时,可以使用接口。
-
实例化:
- 抽象类不能被直接实例化,必须通过其子类实例化。
- 接口不能被实例化,但可以声明接口类型的引用,并通过该引用来使用实现接口的类的实例。
示例代码:
// 抽象类示例
abstract class Animal {
public abstract void makeSound();
public void eat() {
System.out.println("Animal is eating.");
}
}
// 接口示例
interface Drivable {
void drive();
}
interface Flyable {
void fly();
}
// 实现接口的类
class Car implements Drivable {
public void drive() {
System.out.println("Car is driving.");
}
}
// 一个类可以实现多个接口
class Plane implements Drivable, Flyable {
public void drive() {
System.out.println("Plane cannot drive, but it can taxi.");
}
public void fly() {
System.out.println("Plane is flying.");
}
}
总结:
- 接口和抽象类都用于定义抽象概念,但接口更侧重于行为的约定,而抽象类可以提供一些默认的行为和实现细节。
- 一个类可以实现多个接口,但只能继承一个抽象类。
- 接口中的方法默认是
public
的,而抽象类中的方法可以有多种访问修饰符。 - 抽象类可以有构造方法和属性,而接口(在Java 8之前)除了静态常量外,不能有属性和构造方法。Java 8及以后的版本允许接口有静态方法和默认方法。