Java抽象类的访问修饰符使用
2021-04-042.5k 阅读
Java抽象类访问修饰符概述
在Java编程中,抽象类是一种特殊的类,它不能被实例化,主要用于为其他类提供一个通用的框架。访问修饰符则用于控制对类、方法和变量的访问权限。理解如何在抽象类中正确使用访问修饰符,对于编写健壮、安全且可维护的Java代码至关重要。
Java中有四种访问修饰符:public
、protected
、private
以及默认(不写任何修饰符)。这些修饰符在抽象类中的应用有着独特的规则和场景。
public
访问修饰符在抽象类中的应用
- 抽象类使用
public
修饰- 当一个抽象类被声明为
public
时,意味着它可以被任何其他类访问,只要这些类在同一个项目或者可以通过合适的类路径引用到该抽象类。这在开发一些通用的框架或者库时非常有用,其他开发者可以基于这个public
的抽象类进行扩展和定制。 - 代码示例:
- 当一个抽象类被声明为
public abstract class PublicAbstractClass {
public abstract void publicAbstractMethod();
}
class ImplementingClass extends PublicAbstractClass {
@Override
public void publicAbstractMethod() {
System.out.println("Implementing public abstract method.");
}
}
- 在上述示例中,
PublicAbstractClass
是一个public
的抽象类。ImplementingClass
可以继承自它并实现其抽象方法,任何其他类只要在合适的作用域内都可以使用ImplementingClass
,因为它继承自一个public
的抽象类。
- 抽象类中的方法使用
public
修饰- 如果抽象类中的抽象方法被声明为
public
,这表明所有继承该抽象类的子类都必须以public
的方式实现这个方法。这是因为public
方法具有最广泛的访问权限,子类重写方法时不能降低其访问权限。 - 代码示例:
- 如果抽象类中的抽象方法被声明为
public abstract class PublicAbstractMethodClass {
public abstract void publicAbstractMethod();
}
class SubClass extends PublicAbstractMethodClass {
@Override
public void publicAbstractMethod() {
System.out.println("Sub - class implementing public abstract method.");
}
}
- 这里
PublicAbstractMethodClass
中的publicAbstractMethod
是public
的抽象方法,SubClass
在实现它时必须保持public
修饰符,否则会导致编译错误。
protected
访问修饰符在抽象类中的应用
- 抽象类使用
protected
修饰- 当抽象类被声明为
protected
时,它的访问权限相对较窄。protected
的抽象类可以被同一包内的所有类以及不同包内的子类访问。这种修饰符适用于那些作为内部框架组成部分,但又希望在一定范围内可扩展的抽象类。 - 代码示例:
- 当抽象类被声明为
// 包com.example.protectedabstract
package com.example.protectedabstract;
protected abstract class ProtectedAbstractClass {
protected abstract void protectedAbstractMethod();
}
class SamePackageClass extends ProtectedAbstractClass {
@Override
protected void protectedAbstractMethod() {
System.out.println("Same package class implementing protected abstract method.");
}
}
-
在上述代码中,
ProtectedAbstractClass
是protected
的抽象类,SamePackageClass
在同一包内,可以继承并实现其抽象方法。 -
如果在不同包内的子类想要访问这个
protected
的抽象类:
// 包com.example.otherpackage
package com.example.otherpackage;
import com.example.protectedabstract.ProtectedAbstractClass;
class DifferentPackageSubClass extends ProtectedAbstractClass {
@Override
protected void protectedAbstractMethod() {
System.out.println("Different package subclass implementing protected abstract method.");
}
}
DifferentPackageSubClass
通过继承可以访问ProtectedAbstractClass
,因为它是子类,尽管不在同一包内。
- 抽象类中的方法使用
protected
修饰- 抽象类中
protected
的抽象方法,子类必须以protected
或更宽松(即public
)的访问权限来实现。这是因为子类重写方法时不能降低访问权限。 - 代码示例:
- 抽象类中
abstract class ProtectedAbstractMethodHolder {
protected abstract void protectedAbstractMethod();
}
class SubClassForProtectedMethod extends ProtectedAbstractMethodHolder {
@Override
protected void protectedAbstractMethod() {
System.out.println("Sub - class implementing protected abstract method.");
}
}
class AnotherSubClass extends ProtectedAbstractMethodHolder {
@Override
public void protectedAbstractMethod() {
System.out.println("Another sub - class implementing protected abstract method with public access.");
}
}
SubClassForProtectedMethod
以protected
方式实现,AnotherSubClass
以public
方式实现,这两种方式都是符合规则的。
默认访问修饰符(包访问权限)在抽象类中的应用
- 抽象类使用默认访问修饰符
- 当抽象类没有显式的访问修饰符时,它具有包访问权限。这意味着该抽象类只能被同一包内的其他类访问和继承。这种访问权限适用于一些内部使用的抽象类,不希望在包外被直接访问。
- 代码示例:
// 包com.example.defaultabstract
package com.example.defaultabstract;
abstract class DefaultAbstractClass {
abstract void defaultAbstractMethod();
}
class SamePackageSubClass extends DefaultAbstractClass {
@Override
void defaultAbstractMethod() {
System.out.println("Same package subclass implementing default abstract method.");
}
}
- 在上述代码中,
DefaultAbstractClass
具有包访问权限,SamePackageSubClass
在同一包内可以继承并实现其抽象方法。如果在其他包内尝试访问或继承DefaultAbstractClass
,会导致编译错误。
- 抽象类中的方法使用默认访问修饰符
- 抽象类中默认访问权限的抽象方法,子类在同一包内继承时,可以使用默认访问权限或更宽松(
protected
、public
)的访问权限来实现。 - 代码示例:
- 抽象类中默认访问权限的抽象方法,子类在同一包内继承时,可以使用默认访问权限或更宽松(
abstract class DefaultAbstractMethodClass {
abstract void defaultAbstractMethod();
}
class SubClassInSamePackage extends DefaultAbstractMethodClass {
@Override
void defaultAbstractMethod() {
System.out.println("Sub - class in same package implementing default abstract method.");
}
}
class AnotherSubClassInSamePackage extends DefaultAbstractMethodClass {
@Override
public void defaultAbstractMethod() {
System.out.println("Another sub - class in same package implementing default abstract method with public access.");
}
}
SubClassInSamePackage
使用默认访问权限实现,AnotherSubClassInSamePackage
使用public
访问权限实现,都在同一包内是合法的。
private
访问修饰符在抽象类中的应用
- 抽象类使用
private
修饰(不常见但理论上可行)- 从理论上来说,抽象类可以被声明为
private
,但这种情况非常罕见。private
的抽象类只能在其所在的类内部被访问和继承。这通常用于一些高度封装的内部类结构,在外部几乎没有任何意义,因为外部类无法直接访问或继承private
的抽象类。 - 代码示例:
- 从理论上来说,抽象类可以被声明为
class OuterClass {
private abstract class PrivateAbstractClass {
abstract void privateAbstractMethod();
}
class InnerSubClass extends PrivateAbstractClass {
@Override
void privateAbstractMethod() {
System.out.println("Inner sub - class implementing private abstract method.");
}
}
}
- 在上述代码中,
PrivateAbstractClass
是OuterClass
内部的private
抽象类,只有OuterClass
内部的InnerSubClass
可以继承并实现其抽象方法。
- 抽象类中的方法使用
private
修饰- 抽象类中
private
的抽象方法是一个相对特殊的概念。因为private
方法不能被继承,所以在常规意义上,它不能像普通抽象方法那样被子类实现。然而,private
抽象方法可以在抽象类内部被使用,例如用于内部逻辑的封装。 - 代码示例:
- 抽象类中
abstract class PrivateAbstractMethodClass {
private abstract void privateAbstractMethod();
public void publicMethod() {
privateAbstractMethod();
}
}
class SubClassForPrivateAbstractMethod extends PrivateAbstractMethodClass {
// 这里不能直接实现privateAbstractMethod,因为它是private的
// 但是可以调用publicMethod间接触发privateAbstractMethod的执行逻辑
@Override
public void publicMethod() {
System.out.println("Before calling private abstract method logic.");
super.publicMethod();
System.out.println("After calling private abstract method logic.");
}
}
- 在上述代码中,
PrivateAbstractMethodClass
中的privateAbstractMethod
是private
的抽象方法,虽然SubClassForPrivateAbstractMethod
不能直接实现它,但可以通过调用publicMethod
间接触发privateAbstractMethod
的执行逻辑,前提是privateAbstractMethod
在publicMethod
中有具体的调用实现。
不同访问修饰符在抽象类继承中的规则
- 访问权限一致性原则
- 当子类继承抽象类并实现其抽象方法时,重写的方法访问权限不能低于抽象方法的访问权限。例如,如果抽象类中的抽象方法是
protected
,子类可以用protected
或public
来实现,但不能用private
或默认访问权限(如果子类与抽象类不在同一包内)。 - 代码示例:
- 当子类继承抽象类并实现其抽象方法时,重写的方法访问权限不能低于抽象方法的访问权限。例如,如果抽象类中的抽象方法是
abstract class ParentAbstractClass {
protected abstract void protectedAbstractMethod();
}
class ChildClass extends ParentAbstractClass {
// 以下实现是正确的
@Override
protected void protectedAbstractMethod() {
System.out.println("Child class implementing protected abstract method.");
}
// 以下实现也是正确的
@Override
public void protectedAbstractMethod() {
System.out.println("Child class implementing protected abstract method with public access.");
}
// 以下实现是错误的(编译错误)
// private void protectedAbstractMethod() {
// System.out.println("This is an incorrect implementation.");
// }
}
- 包访问权限与继承关系
- 如果抽象类具有包访问权限,其子类在同一包内可以正常继承并实现其抽象方法,访问权限可以是默认、
protected
或public
。但如果子类在不同包内,只有当抽象类或抽象方法的访问权限为protected
或public
时,子类才能继承并实现。 - 代码示例:
- 如果抽象类具有包访问权限,其子类在同一包内可以正常继承并实现其抽象方法,访问权限可以是默认、
// 包com.example.packageaccess
package com.example.packageaccess;
abstract class PackageAccessAbstractClass {
abstract void packageAccessAbstractMethod();
}
// 同一包内的子类
class SamePackageSubClass extends PackageAccessAbstractClass {
@Override
void packageAccessAbstractMethod() {
System.out.println("Same package subclass implementing package access abstract method.");
}
}
// 不同包内的情况(假设在com.example.otherpackage包内)
// package com.example.otherpackage;
// import com.example.packageaccess.PackageAccessAbstractClass;
// class DifferentPackageSubClass extends PackageAccessAbstractClass {
// // 编译错误,因为PackageAccessAbstractClass具有包访问权限,在不同包内无法访问
// @Override
// void packageAccessAbstractMethod() {
// System.out.println("This would be an incorrect implementation in a different package.");
// }
// }
- 如果将
PackageAccessAbstractClass
的访问权限改为protected
:
// 包com.example.packageaccess
package com.example.packageaccess;
protected abstract class ProtectedPackageAccessAbstractClass {
protected abstract void protectedPackageAccessAbstractMethod();
}
// 不同包内的子类
package com.example.otherpackage;
import com.example.packageaccess.ProtectedPackageAccessAbstractClass;
class DifferentPackageSubClass extends ProtectedPackageAccessAbstractClass {
@Override
protected void protectedPackageAccessAbstractMethod() {
System.out.println("Different package subclass implementing protected package access abstract method.");
}
}
- 此时
DifferentPackageSubClass
可以在不同包内继承并实现抽象方法。
访问修饰符对抽象类多态性的影响
- 多态性与访问权限
- 抽象类作为多态性的重要组成部分,访问修饰符会影响其多态特性的表现。例如,
public
和protected
修饰的抽象类及其方法在不同的继承和调用场景下,能够更好地展现多态性,因为它们具有更广泛的访问范围。 - 代码示例:
- 抽象类作为多态性的重要组成部分,访问修饰符会影响其多态特性的表现。例如,
public abstract class Shape {
public abstract double calculateArea();
}
class Circle extends Shape {
private double radius;
public Circle(double radius) {
this.radius = radius;
}
@Override
public double calculateArea() {
return Math.PI * radius * radius;
}
}
class Rectangle extends Shape {
private double width;
private double height;
public Rectangle(double width, double height) {
this.width = width;
this.height = height;
}
@Override
public double calculateArea() {
return width * height;
}
}
class ShapeProcessor {
public void processShape(Shape shape) {
double area = shape.calculateArea();
System.out.println("The area of the shape is: " + area);
}
}
- 在上述代码中,
Shape
是一个public
的抽象类,Circle
和Rectangle
继承自它并实现了calculateArea
方法。ShapeProcessor
类通过接收Shape
类型的参数来处理不同形状,体现了多态性。由于Shape
和calculateArea
方法都是public
的,这种多态性在不同的类之间能够顺利实现。
- 限制访问权限对多态性的限制
- 如果抽象类或其抽象方法的访问权限较低,例如
private
或包访问权限,多态性的应用范围会受到限制。以private
抽象类为例,由于外部类无法继承,它很难在外部展现多态性,只能在其所在的内部类结构中有限地体现。 - 代码示例:
- 如果抽象类或其抽象方法的访问权限较低,例如
class Outer {
private abstract class PrivateShape {
abstract double calculateArea();
}
class InnerCircle extends PrivateShape {
private double radius;
public InnerCircle(double radius) {
this.radius = radius;
}
@Override
double calculateArea() {
return Math.PI * radius * radius;
}
}
class InnerRectangle extends PrivateShape {
private double width;
private double height;
public InnerRectangle(double width, double height) {
this.width = width;
this.height = height;
}
@Override
double calculateArea() {
return width * height;
}
}
public void processInnerShape(PrivateShape shape) {
double area = shape.calculateArea();
System.out.println("The area of the inner shape is: " + area);
}
}
- 在这个例子中,
PrivateShape
是private
的抽象类,多态性只能在Outer
类内部通过InnerCircle
和InnerRectangle
来体现,外部类无法利用这种多态性。
访问修饰符在抽象类与接口对比中的体现
- 访问修饰符的差异
- 接口中的方法默认是
public
和abstract
的,不能使用其他访问修饰符(除了在Java 9及之后可以给接口方法添加private
修饰符用于内部逻辑封装)。而抽象类中的方法可以使用各种访问修饰符,这是两者的一个重要区别。 - 代码示例:
- 接口中的方法默认是
interface MyInterface {
void myMethod(); // 隐式public和abstract
}
abstract class MyAbstractClass {
public abstract void publicAbstractMethod();
protected abstract void protectedAbstractMethod();
abstract void defaultAbstractMethod();
private abstract void privateAbstractMethod();
}
- 这里
MyInterface
中的myMethod
默认是public
和abstract
,而MyAbstractClass
中的方法可以有不同的访问修饰符。
- 访问权限对实现和继承的影响
- 类实现接口时,必须以
public
方式实现接口中的方法,因为接口方法默认是public
的。而类继承抽象类时,根据抽象类中抽象方法的访问权限,按照相应规则实现。 - 代码示例:
- 类实现接口时,必须以
interface AnotherInterface {
void anotherMethod();
}
class InterfaceImplementingClass implements AnotherInterface {
@Override
public void anotherMethod() {
System.out.println("Implementing interface method.");
}
}
abstract class ParentAbstract {
protected abstract void protectedAbstractMethod();
}
class AbstractSubClass extends ParentAbstract {
@Override
protected void protectedAbstractMethod() {
System.out.println("Implementing protected abstract method from abstract class.");
}
}
InterfaceImplementingClass
以public
方式实现AnotherInterface
的方法,AbstractSubClass
按照protected
权限实现ParentAbstract
的抽象方法。
实际应用场景中访问修饰符的选择
- 通用框架开发
- 在开发通用框架时,通常会使用
public
修饰抽象类及其抽象方法。这样其他开发者可以方便地继承和扩展框架,实现自己的业务逻辑。例如,在一些开源的图形绘制框架中,可能会有一个public
的抽象Shape
类,包含public
的抽象draw
方法,开发者可以继承Shape
类并实现draw
方法来绘制自定义的图形。 - 代码示例:
- 在开发通用框架时,通常会使用
public abstract class FrameworkShape {
public abstract void draw();
}
class CustomTriangle extends FrameworkShape {
@Override
public void draw() {
System.out.println("Drawing a custom triangle.");
}
}
- 内部模块封装
- 对于内部模块的封装,
protected
或默认访问权限的抽象类比较合适。如果这些抽象类只希望在同一模块(包)内被使用或扩展,可以使用默认访问权限。如果希望在不同包内的子类也能访问和扩展,则使用protected
修饰符。例如,在一个大型项目的内部数据处理模块中,可能有一个protected
的抽象DataProcessor
类,用于定义一些通用的数据处理逻辑,不同包内的子类可以继承它并根据具体需求实现特定的数据处理方法。 - 代码示例:
- 对于内部模块的封装,
// 包com.example.internalpackage
package com.example.internalpackage;
protected abstract class DataProcessor {
protected abstract void processData();
}
// 同一包内的子类
class SamePackageDataProcessor extends DataProcessor {
@Override
protected void processData() {
System.out.println("Same package data processor.");
}
}
// 不同包内的子类
package com.example.otherpackage;
import com.example.internalpackage.DataProcessor;
class DifferentPackageDataProcessor extends DataProcessor {
@Override
protected void processData() {
System.out.println("Different package data processor.");
}
}
- 高度封装的内部逻辑
- 当需要高度封装内部逻辑,不希望外部类直接访问和继承时,可以使用
private
修饰抽象类或抽象方法。例如,在一个加密算法的内部实现中,可能有一个private
的抽象类,用于封装一些基础的加密步骤,只有内部的具体实现类可以继承和使用这些抽象方法,外部无法直接访问,从而保证了加密算法的安全性和内部逻辑的完整性。 - 代码示例:
- 当需要高度封装内部逻辑,不希望外部类直接访问和继承时,可以使用
class EncryptionModule {
private abstract class PrivateEncryptionStep {
abstract void performStep();
}
class ConcreteEncryptionStep extends PrivateEncryptionStep {
@Override
void performStep() {
System.out.println("Performing a concrete encryption step.");
}
}
}
通过合理选择访问修饰符,能够在抽象类的设计中实现良好的封装性、扩展性和安全性,使Java代码更加健壮和易于维护。在实际编程中,需要根据具体的需求和场景,仔细权衡不同访问修饰符的使用,以达到最佳的设计效果。