JAVA基础11继承和多态

本文详细介绍了Java中的继承和多态概念,包括父类和子类的关系、super关键字的使用、构造方法链、方法重写与重载的区别、Object类的toString方法以及多态的实现。强调了方法重写和多态在面向对象编程中的重要性,同时也讨论了对象转换、instanceof运算符的用途以及equals方法的重写。文章还探讨了ArrayList类在存储对象列表中的应用及其常用方法,以及protected访问修饰符的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

11. 继承和多态

面向对象的编程允许你从已经存在的类中定义新的类这称为继承

11.1 父类和子类

继承使得你可以定义一个通用的类即父类)之后扩充该类为一个更加特定的类(即子类

使用类来对同一类型的对象建模不同的类也可能会有一些共同的特征和行为这些共同的特征和行为都统一放在一个类中,它是可以被其他类所共享的你可以定义特定的类继承自通用类。这些特定的类继承通用类中的特征和方法

考虑一下几何对象假设要设计类建模像圆和矩形这样的几何对象几何对象有许多共同的属性和行为。它们可以是用某种颜色画出来的填充的或者不填充的这样,一个通用类 CeometricObject 可以用来建模所有的几何对象这个类包括属性 color 和 filled , 以及适用于这些属性的 get set 方法假设该类还包括 dateCreated 属性以及 getDateCreated( ) toString( )  方法toString( )  方法返回代表该对象的字符串。由于圆是一个特殊类型的几何对象,所以它和其他几何对象共享共同的属性和方法。因此通过继承自 CeometricObject 类来定义 Circle 类是合理的同理Rectangle 也可以定义为 GeometricObject 的子类。下图显示了这些类之间的关系指向父类的三角箭头用来表相关的两个类之间的继承关系。

Java 术语中如果类 C1 扩展自另一个类 C2, 那么就将 C1 称为次类subclass ), C2 称为超类superclass )超类也称为父类 ( parent class) 或基类 ( base class), 次类又称为子类 ( child class)扩展类 ( extended class) 或派生类derived class )。子类从它的父类中继承可访问的数据域和方法,还可以添加新数据域和新方法Circle 类继承了 CeometricObject 类所有可以访问的数据域和方法除此之外它还有一个新的数据域 radius, 以 及 与 radius 相关的 get set 方法它还包括 getArea( ) getPerimeter( ) getDiameter( ) 方法以返回圆的面积周长和直径Rectangle 类从 CeometricObject 类继承所有可访问的数据域和方法此外它还有 width height 数据域以及和它们相关的 get set 方法它还包括 getAreagetPerimeter( ) 方法返回矩形的面积和周长

下面是关于继承应该注意的几个关键点

  • 和传统的理解不同子类并不是父类的一个子集实际上一个子类通常比它的父类包含更多的信息和方法。
  • 父类中的私有数据域在该类之外是不可访问的因此不能在子类中直接使用但是,如果父类中定义了公共的访问器 / 修改器那么可以通过这些公共的访问器 / 修改器来访问和修改它们。
  • 不是所有的 “是一种is a) 关系都该用继承来建模例如正方形是一种矩形但是不应该定义一个 Square 类来扩展 Rectangle 类,因为 width height 属性并不适合于正方形。应该定义一个继承自 CeometricObject 类的 Square 并为正方形的边定义一个 side 属性
  • 继承是用来为 是一种关系is a) 建模的不要仅仅为了重用方法这个原因而盲目地扩展一个类。例如尽管 Person 类和 Tree 类可以共享类似高度和重量这样的通用特性,但是从 Person 类扩展出 Tree 类是毫无意义的一个父类和它的子类之间必须存在 是一种is a) 关系
  • 某些程序设计语言是允许从几个类派生出一个子类的这种能力称为多重继承 ( multiple inheritance)但是在 Java 中是不允许多重继承的一个 Java 类只可能直接继承自一个父类。这种限制称为单一继承single inheritance)如果使用 extends 关键字来定义一个子类,它只允许有一个父类然而多重继承是可以通过接口来实现的。

 

 

11.2 使用 super 关键字

关键字 super 指代父类可以用于调用父类中的普通方法和构造方法

关键字 super 可以用于两种途径

1 ) 调用父类的构造方法

2 ) 调用父类的方法

11.2.1 调用父类的构造方法

构造方法用于构建一个类的实例不同于属性和普通方法父类的构造方法不会被子类继承。它们只能使用关键字 super 从子类的构造方法中调用

调用父类构造方法的语法是

super()或者super(parameters);

语句 super( ) 调用父类的无参构造方法而语句 super(arguments) 调用与参数匹配的父类的构造方法。语句 super( ) super (arguments) 必须出现在子类构造方法的第一行,这是显式调用父类构造方法的唯一方式。

警告要调用父类构造方法就必须使用关键字 super, 而且这个调用必须是构造方法的第一条语句。在子类中调用父类构造方法的名字会引起一个语法错误

 

11.2.2 构造方法链

构造方法可以调用重载的构造方法或父类的构造方法如果它们都没有被显式地调用,编译器就会自动地将 super( ) 作为构造方法的第一条语句例如

在任何情况下构造一个类的实例时将会调用沿着继承链的所有父类的构造方法当构造一个子类的对象时,子类构造方法会在完成自己的任务之前首先调用它的父类的构造方法。如果父类继承自其他类那么父类构造方法又会在完成自己的任务之前调用它自己的父类的构造方法。这个过程持续到沿着这个继承体系结构的最后一个构造方法被调用为止。 这就是构造方法链constructor chaining)

警告如果要设计一个可以被继承的类最好提供一个无参构造方法以避免程序设计错误。

思考下面的代码

public class Apple extends Fruit { 
    } 

class Fruit { 
    public Fruit(String name) { 
        System.out.println("Fruit' s constructor is invoked"); 
    } 
}

由于在 Apple 中没有显式定义的构造方法因此Apple 的默认无参构造方法被隐式调用。因为 Apple Fruit 的子类所以 Apple 的默认构造方法会自动调用 Fruit 的无参构造方法。然而Fruit 没有无参构造方法因为 Fruit 显式地定义了构造方法因此程序不能被成功编译。

设计指南一般情况下最好能为每个类提供一个无参构造方法以便于对该类进行扩展,同时避免错误

 

11.2.3 调用父类的方法

关键字 super 不仅可以引用父类的构造方法也可以引用父类的方法所用语法如下

super.方法名(参数);

可以如下改写 Circle 类中的 printCircle)方法

public void printCircle() {
    System.out.println("The circle is created " + super.getDateCreated() + " and the radius is " + radius); 
}

在这种情况下没有必要在 getDateCreated ( ) 前放置 super, 因为 getDateCreated 是 GeometHcObject 类中的一个方法并被 Circle 类继承

 

 

11.3 方法重写

要重写一个方法需要在子类中使用和父类一样的签名以及一样的返回值类型来对该方法进行定义。

子类从父类中继承方法有时子类需要修改父类中定义的方法的实现这称作方法重写(method overriding)。

以下两点值得注意

  • 仅当实例方法是可访问时它才能被覆盖因为私有方法在它的类本身以外是不能访问的,所以它不能被覆盖如果子类中定义的方法在父类中是私有的,那么这两个方法完全没有关系
  • 与实例方法一样静态方法也能被继承但是静态方法不能被覆盖如果父类中定义的静态方法在子类中被重新定义,那么在父类中定义的静态方法将被隐藏可以使用语法:父类名 .静态方法名SuperClassName.staticMethodName) 调用隐藏的静态方法。

 

 

11.4 方法重写与重载

重载意味着使用同样的名字但是不同的签名来定义多个方法。重写意味着在子类中提供一个对方法的新的实现。

方法重写是指该方法必须使用相同的签名和相同的返回值类型在子类中定义。

让我们用一个例子来显示重写和重载的不同在图 a A 中的方法 p(double 1)重写了在类 B 中定义的相同方法但是在图 bA 中有两个重载的方法 p(double i)和 P(int i)e 方法 p(double i)继承自类 B

运行 a 中的 Test 类时a.p(10)a.p(10.0)调用的都是定义在类 A中的 p(double i) 方法. 所以程序都显示;10.0运行 b中的 Test 类时a.p(10)调用类 A中定义的 p(int i) 方法,显示输出为 10, a.p(10.0)调用定义在类 B 中的 P(double i)方法显示输出为 20.0

注意以下问题

  • 方法重写发生在通过继承而相关的不同类中方法重载可以发生在同一个类中也可以发生在由于继承而相关的不同类中。
  • 方法重写具有同样的签名和返回值类型方法重载具有同样的名字但是不同的参数列表。

为了避免错误可以使用一个特殊的 Java 语法称为重写标注 (override annotation, 在子类的方法前面放一个 @Override例如

public class CircleFromSimpleGeometricObject
    extends SimpleCeometricObject { 
    // Other methods are omitted

    ©Override
    public String toString() { 
        return super.toString() + "\nradius is " + radius; 
    }
}

该标注表示被标注的方法必须重写父类的一个方法如果具有该标注的方法没有重写父类的方法,编译器将报告一个错误例如如果 toString 被错误地输人为 tostring, 将报告一个编译错误。如果没有使用重写标注编译器不会报告错误使用标注可以避免错误

 

 

11.5 Object 类及其 toString( )方法

Java 中的所有类都继承自 java.lang.Object

如果在定义一个类时没有指定继承性那么这个类的父类就被默认为是 Object例如, 下面两个类的定义是一样的:

诸如 StringStringBuilderLoan GeometricObject 这样的类都是 Object的隐含子类熟悉 Object 类提供的方法是非常重要的因为这样就可以在自己的类中使用它们。本节将介绍 Object类中的 toString( ) 方法

toString( ) 方法的签名是

public String toString()

调用一个对象的 toStringO 会返回一个描述该对象的字符串默认情况下它返回一个由该对象所属的类名、at 符号@)以及该对象十六进制形式的内存地址组成的字符串。例如,考虑下面定义 Loan 类的代码

Loan loan = new Loan();
System.out.println(loan.toString());

这些代码会显示像 LOan®15037e5 的字符串这个信息不是很有用或者说没有什么信息量。通常应该重写这个 toString 方法这样它可以返回一个代表该对象的描述性字符串。例如Object类中的 toString 方法在 GeometricObject 类中重写。

public String toString() {
    return "created on " + dateCreated + "\ncolor: " + color + " and filled: " + filled; 
}

注意也 可 以 传 递 一 个 对 象 来 调 用 System.out.println(object)或 者 System.out.print(object)这 等价于调用 System.out.println(object.toString( ))或 者 System.out.print (object - toString( ))因此可以使用 System.out.println(loan)来替换 System.out.println(loan.toString( ))

 

 

11.6 多态

多态意味着父类的变量可以指向子类对象

面向对象程序设计的三大支柱是封装继承和多态

首先定义两个有用的术语子类型和父类型一个类实际上定义了一种类型子类定义的类型称为子类型(subtype), 而父类定义的类型称为父类型supertype)因此可以说 Circle 是 GeometricObject的子类型GeometricObject Circle 的父类型

继承关系使一个子类继承父类的特征并且附加一些新特征子类是它的父类的特殊化,每个子类的实例都是其父类的实例但是反过来就不成立例如每个圆都是一个几何对象,但并非每个几何对象都是圆。因此,总可以将子类的实例传给需要父类型的参数。考虑下列程序清单的代码。

    PolymorphismDemo.java
public class PolymorphismDemo { 
    /** Main method */ 
    public static void main(String[] args){ 
        // Display circle and rectangle properties
        displayObject(new CircleFromSimpleCeometricObject(1, "red",false)); 
        displayObject(new RectangleFromSimpleCeometricObject(1, 1,"black", true)); 
    }

    /** Display geometric object properties */
    public static void displayObject(SimpleCeometricObject object){
        System out.println("Created on + object.getDateCreatedO + Color is " + object.getColor());
    }
}

显示

Created on Mon Mar 09 19:25:20 EDT 2011.Color is red
Created on Mon Mar 09 19:25:20 EDT 2011.Color is black

方法 displayObject (12 具有 GeometricObject 类型的参数可以通过传递任何一个 GeometricObject 的实例例如在第 5~8 行的 new Ci rcleFromSimpleCeometricObject(1"red".false)new RectangleFromSimpleGeometricObject(1,1,"black",false))来调用 displayObject使用父类对象的地方都可以使用子类的对象这就是通常所说的多态 ( polymorphism, 它源于希腊文字意思是 多种形式简单来说多态意味着父类型的变量可以引用子类型的对象。

 

 

11.7 动态绑定

方法可以在沿着继承链的多个类中实现JVM 决定运行时调用哪个方法

方法可以在父类中定义而在子类中重写例如toString( )方法是在 Object 类中定义的,而在 GeometricObject 类中重写。思考下面的代码

Object o = new CeometricObject();
System.out.println(o.toStHng());

这里的 0 调用哪个 tostringo 为了回答这个问题我们首先介绍两个术语声明类型和实际类型一个变量必须被声明为某种类型变量的这个类型称为它的声明类型(declared type)。这里o 的声明类型是 Object一个引用类型变量可以是一个 null 值或者是一个对声明类型实例的引用。实例可以使用声明类型或它的子类型的构造方法创建。变量的实际类型actual type) 是被变量引用的对象的实际类这里o 的实际类型是 GeometricObject, 因为 o 指向使用 new GeometricObject( ) 创建的对象D o 调用哪个 toString( ) 方法由 o 的实际类型决定。这称为动态绑定(dynamic binding)。

动态绑定工作机制如下:假设对象 o 是类 C1, C2, Cn - 1, Cn 的实例其中 C1C2 的子类,C2 C3 的子类Cn - 1是 Cn 的子类如下图所示也就是说Cn 是最通用的类,C1是最特殊的类Java Cn Object 如果对象 o 调用一个方法 p, 那么 JVM 会依次在类 C1C2, Cn - 1,Cn中查找方法 p 的实现直到找到为止一旦找到一个实现,就停止査找,然后调用这个首先找到的实现

 

 

11.8 对象转换和 instanceof 运算符

对象的引用可以类型转换为对另外一种对象的引用这称为对象转换

在上一节中语句

m(new Student());

将对象 new Student( ) 赋值给一个 Object 类型的参数这条语句等价于

Object o = new Student(); // Implicit casting
m(o);

由于 Student 的实例也是 Object 的实例所以语句 Object o = new StudentO 是合法的,它称为隐式转换(implicit casting)

假设想使用下面的语句把对象引用 o 赋值给 Student 类型的变量

Student b = o;

在这种情况下将会发生编译错误为什么语句 Object o = new Student( ) 可以运行,而语句 Student b = o 不行呢原因是 Student 对象总是 Object 的实例但是Object 对象不一定是 Student 的实例即使可以看到 0实际上是一个 Student 的对象但是编译器还没有聪明到知道这一点。为了告诉编译器 0就是一个 Student 对象就要使用显式转换 ( explicit casting)o 它的语法与基本类型转换的语法很类似用圆括号把目标对象的类型括住,然后放到要转换的对象前面如下所示

Student b = (Student)o;// Explicit casting

总是可以将一个子类的实例转换为一个父类的变量称为向上转换upcasting),因为子类的实例永远是它的父类的实例。当把一个父类的实例转换为它的子类变量称为向下转换 (downcasting)) 必须使用转换记号 (子类名)进行显式转换向编译器表明你的意图。为使转换成功,必须确保要转换的对象是子类的一个实例如果父类对象不是子类的一个实例,就会出现一个运行异常 ClassCastException例如如果一个对象不是 Student的实例,它就不能转换成 Student 类型的变量因此一个好的经验是在尝试转换之前确保该对象是另一个对象的实例。这是可以利用运算符 hstanceof 来实现的考虑下面的代码

Object myObject = new Circle(); 
... // Some lines of code
/** Perform casting if myObject is an instance of Circle */
if (myObject instanceof Circle){
    System.out.println("The circle diameter is + ((Circle)myObject).getDiameter());
    ...
}

你可能会奇怪为什么必须进行类型转换变量 myObject 被声明为 Object声明类型决定了在编译时匹配哪个方法。使用 myObject.getDUmeterO 会引起一个编译错误因为 Object 类没有 getDiameter 方法编译器无法找到和 myObject.getDiameter( )匹配的方法。所以,有必要将 myObject 转换成 Circle 类型来告诉编译器 myObject 也是 Circle 的一个实例。

为什么没有在一开始就把 myObject定义为 Circle 类型呢为了能够进行通用程序设计,一个好的经验是把变童定义为父类型这样它就可以接收任何子类型的值

注意instanceof Java 的关键字Java 关键字中的每个字母都是小写的

提示为了更好地理解类型转换可以认为它们类似于水果苹果檷子之间的关系其中水果类 Fruit 是苹果类 Apple 和橘子类 Orange 的父类苹果是水果所以总是可以将 Apple 的实例安全地賦值给 Fruit 变量但是水果不一定是苹果所以必须进行显式转换才能将 Fruit 的实例赋值给 Apple 的变量

警告对象成员访问运算符.优先于类型转换运算符使用圆括号保证在点运算符)之前进行转换,例如

((Circl e)object).getArea();

对基本类型值进行转换不同于对对象引用进行转换转换基本类型值返回一个新的值

例如

int age = 45;
byte newAge = (byte)age; // A new value is assigned to newAge

而转换一个对象引用不会创建一个新的对象例如

Object o = new Circle();
Circle c = (Circle)o; // No new object is created

现在引用变量 和 c 指向同一个对象

 

 

11.9 Object 类的 equals 方法

如同 toString( ) 方法equals(Object) 方法是定义在 Object 类中的另外一个有用的方法。

Object 类中定义的另外一个经常使用的方法是 equals 方法它的签名是

public boolean equals(Object o)

这个方法测试两个对象是否相等调用它的语法是

objectl.equals(object2);

Object 类中 equals 方法的默认实现是

public boolean equals(Object obj) {
    return (this obj); 
}

这个实现使用 == 运算符检测两个引用变量是否指向同一个对象因此应该在自己的客户类中重写这个方法,以测试两个不同的对象是否具有相同的内容

equals 方法在 Java API的许多类中被重写比 如 java.lang.String java.util .Date, 用于比较两个对象的内容是否相等String类中的 equals 方法继承自 Object 然后在 String类中被重写,使之能够检验两个字符串的内容是否相等

可以重写 Circle 类中的 equals 方法基于圆的半径比较两个圆是否相等如下所示

public boolean equals(Object o) {
    if (o instanceof Circle)
        return radius == ((Circle)o) .radius;
    else
        return this == o; 
}

注意比较运算符一用来比较两个基本数据类型的值是否相等或者判断两个对象是否具有相同的引用。如果想让 equals 方法能够判断两个对象是否具有相同的内容可以在定义这些对象的类时,重写 Circle 类中的 equals 方法运算符 == 要比 equals 方法的功能强大因为 == 运算符可以检测两个引用变量是否指向同一个对象

警告在子类中使用签名 equals(SomeClassName obj) (例如equals(Circle c)) 重写 equals 方法是一个常见错误应该使用 equals(Object obj)。

 

 

11.10 ArrayList

ArrayList 对象可以用于存储一个对象列表。

现在我们介绍一个很有用的用于存储对象的类了可以创建一个数组存储对象但是这个数组一旦创建,它的大小就固定了Java 提供 ArrayList 类来存储不限定个数的对象。下图给出了 ArrayList中的一些方法

ArrayList 是一种泛型类,具有一个泛型类型 E创建一个 ArrayList可以指定一个具体的类型来替换 E例如下面语句创建一个 ArrayList, 并且将其引用赋值给变量 citiesArrayList 对象可以用于存储字符串

ArrayList<String> cities = new ArrayList<String>();

下面语句创建一个 ArrayList 并且将其引用赋值给变量 datesArrayList 对象可以用于存储日期。

ArrayList< java.util .Date> dates = new ArrayList< java.util .Date> ();

注意JDK1.7 开始语句

ArrayList<AConcreteType> list = new ArrayList<AConcreteType>();

可以简化为

ArrayList<AConcreteType> list = new ArrayList<>();

由于使用了称为类型推导的特征构造方法中不再要求给出具体类型编译器可以从变量的声明中推导出类型。

可以像使用数组一样使用 ArrayList 对象但是两者还是有很多不同之处。下表列出了它们的异同点。

旦创建了一个数组它的大小就确定下来了可以使用方括号访问数组元素例如: a[index])0 当 创 建 ArrayList 它的大小为 0如果元索不在数组列表中就不能使用 get(index)set(index .element)方法向数组列表中添加插人和删除元素是比较容易的,而向数组中添加插人和删除元素是比较复杂的为了实现这些操作必须编写代码操纵这个数组。注意可以使用;java.util.Arrays.sortUrray)方法来对一个数组排序如果要对一个数组列表排序,使用 java.util.Collections.sort(arrayList)方法

假设想创建一个用于存储整数的 ArrayList, 可以使用下面代码来创建一个列表吗

ArrayList<int> list = new ArrayList<>();

答案是不行这样行不通因为存储在 ArrayList 中的元素必须是一种对象不能使用诸如 int 的基本数据类型来代替一个泛型类型然而你可以创建一个存储 Integer 对象的 ArrayList, 如下所示

ArrayList<Integer> list = new ArrayList<>();

使用 ArrayList 来实现程序将更简单,有以下两个原因

  • ArrayList 的大小是灵活的所以无须提前给定它的大小而当创建一个数组时它的大小必须给定。
  • ArrayList 包含许多有用的方法比如可以使用 contains 方法来测试某个元素是否在列表中。如果使用数组则需要编写额外代码来实现该方法

可以在数组里使用 foreach 循环来遍历元素数组列表中的元素也可以使用 foreach 循环来进行遍历,语法如下

for (elementType element: arrayList){ 
    // Process the element
}

 

 

11.11 对于列表有用的方法

Java 提供了方法用于从数组创建列表对列表排序找到列表中的最大和最小元素,以及打乱一个列表

我们经常需要从一个对象数组中创建一个数组列表或者相反可以使用循环来实现,但是更容易的方法是使用 Java API中的方法这里是一个从数组中创建一个数组列表的例子

String[] array = {"red","green","blue"};
ArrayList<String> list = new ArrayListo(Arrays.asList(array));

Arrays 类中的静态方法 asList 返回一个列表该列表传递给 ArrayList 的构造方法用于创建一个 ArrayList反过来可以使用下面代码从一个数组列表来创建一个对象数组

String[] arrayl = new String[list.size()];
list.toArray(array1);

调用 list.toArray(arrayl)list中的内容复制到 array1

如果列表中的元素是可比较的比如整数双精度浮点数或者字符串则可以使用 java.util .Collections 类中的静态的 sort 方法来对元素进行排序这里是一些例子

Intege「[] array = {3, 5, 95, 4, 15, 34, 3, 6, 5};
ArrayList<Integer> list = new ArrayListo(Arrays.asList(array));
java.util .Collections.sort(1ist);
System.out.println(list);

可以使用 java.util .Collections 类中的静态的 max min 方法来返回列表中的最大和最小元素。这里是一些例子

Integer[] array = {3, 5, 95, 4, 15, 34, 3, 6, 5};
ArrayList<Integer> list = new ArrayListo(Arrays.asList(array));
System.out.println(java.util.Collections.max(list));
System.out.println(java.util.Collections.min(list));

可以使用 java.util .Collections 类中的静态的 shuffle 方法来随机打乱列表的元素。这里是一些例子:

Integer[] array = {3, 5, 95, 4, 15, 34, 3, 6, 5};
ArrayList<Integer> list = new ArrayListo(Arrays.asList(array));
java.util .Col1ections.shuff1e(list);
System.out.println(list);

 

 

11.12 protected 数据和方法

一个类中的受保护成员可以从子类中访问

至今为止我们已经使用过关键字 private public 来指定是否可以从类的外部访问数据域和方法。私有成员只能在类内访问而公共成员可以被任意的其他类访问

经常需要允许子类访问定义在父类中的数据域或方法但不允许非子类访问这些数据域和方法。可以使用关键字 Protected 完成该功能父类中被保护的数据域或方法可以在它的子类中访问。

修饰符 privateprotected public 都称为可见性修饰符visibility modifier) 或可访问性修饰符(accessibility modifier), 因为它们指定如何访问类和类的成员这些修饰符的可见性按下面的顺序递增:

下表总结了类中成员的可访问性

描述了C1类中的 publicprotected默认的和 private 数据或方法是如何被 C2C3C4 C5 类访问的其中C2 类与 C1类在同— 个包中C3 类是 C1类在同一个包中的子类C4 类是 C1类在不同包中的子类C5 类与 C1 类在不同包中。

使用 private 修饰符可以完全隐藏类的成员这样就不能从类外直接访问它们不使用修饰符就表示允许同一个包里的任何类直接访问类的成员,但是其他包中的类不可以访问。使用 Protected 修饰符允许任何包中的子类或同一包中的类访问类的成员使用 public 修饰符允许任意类访问类的成员。

类可以以两种方式使用一种是用于创建该类的实例另一种是通过扩展该类创建它的子类。如果不想从类的外部使用类的成员就把成员声明成 private如果想让该类的用户都能使用类的成员,就把成员声明成 public如果想让该类的扩展者使用数据和方法而不想让该类的用户使用,则把成员声明成 Protected

修饰符 private protected 只能用于类的成员public 修饰符和默认修饰符也就是没有修饰符)既可以用于类的成员也可以用于类一个没有修饰符的类即非公共类)是不能被其他包中的类访问的

注意子类可以重写它的父类的 protected 方法并把它的可见性改为 public但是子类不能削弱父类中定义的方法的可访问性。例如如果一个方法在父类中定义为 public,在子类中也必须定义为 public

 

 

11.13 防止扩展和重写

一个被 final 修饰的类和方法都不能被扩展final 修饰的数据域是一个常数。

有时候可能希望防止类扩展在这种情况下使用 final 修饰符表明一个类是最终的,是不能作为父类的Math 类就是一个最终类StringStringBuilder StringBuffer 类也可以是最终类。例如下面的类 A 就是最终类是不能被继承的

public final class A { 
    // Data fields, constructors, and methods omitted
}

也可以定义一个方法为最终的最终方法不能被它的子类重写

例如下面的方法是最终的是不能重写的

public class Test { 
    // Data fields, constructors, and methods omitted
    public final void m(){ 
        // Do something
    } 
}

注意修饰符 publicprotectedprivate、static、 abstract 以及 final 可以用在类和类的成员(数据和方法只有 final 修饰符还可以用在方法中的局部变量上方法内的最终局部变量就是常量。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值