Loading

Java基础--day16

继承与多态

父类与子类

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

不同的类也可能会有一些共同的特征和行为 , 这些共同的特征和行为都统一放在一个类中 , 它是可以被其他类所共享的

如圆形和三角形可以继承图形的特征:

Snipaste_2020-04-07_11-08-35

在 Java 术语中 , 如果类 C1 扩展自另一个类 C2 , 那么就将 C1 称为次类 ( subclass ) , 将C2 称为超类 ( superclass ) 。 超类也称为父类 ( parent class ) 或基类 ( base class ) , 次类又称为子类 ( child class ) 、 扩展类 ( extended class ) 或派生类 ( derived class ) 。 子类从它的父类中继承可访问的数据域和方法 , 还可以添加新数据域和新方法

上面例子的代码

// 父类
public class SimpleGeometricObject {
    private String color = "white";
    private boolean filled;
    private java.util.Date dataCreated;
    
    public SimpleGeometricObject(){
        dataCreated = new java.util.Date();
    }
    
    public SimpleGeometricObject(String color, boolean filled){
        this.color = color;
        this.filled = filled;
        this.dataCreated = new java.util.Date();
    }
    
    public String getColor(){
        return color;
    }
    
    public void setColor(String color){
        this.color = color;
    }
    
    public boolean isFilled(){
        return filled;
    }
    
    public void setFilled(boolean filled){
        this.filled = filled;
    }
    
    public java.util.Date getDataCreated(){
        return dataCreated;
    }
    
    public String toString(){
        return "created on " + dataCreated + "\ncolor: " + color +
                " and filled: " + filled;
    }
}
// 圆形子类
public class CircleFromSimpleGeometricObject extends SimpleGeometricObject {
    private double radius;

    public CircleFromSimpleGeometricObject(){

    }

    public CircleFromSimpleGeometricObject(double radius){
        this.radius = radius;
    }

    public CircleFromSimpleGeometricObject(double radius, String color, boolean filled){
        this.radius = radius;
        setColor(color);
        setFilled(filled);
    }

    public double getRadius(){
        return radius;
    }

    public void setRadius(double radius){
        this.radius = radius;
    }

    public double getArea(){
        return radius * radius * Math.PI;
    }

    public double getPerimeter(){
        return 2 * radius * Math.PI;
    }

    public double getDiameter(){
        return 2 * radius;
    }

    public void printCircle(){
        System.out.println("The circle is created " + getDataCreated() +
                " and the radius is " + radius);
    }
    
}
  1. 子类 extends 父类,即Circle类扩展自GeometricObject类,这样子,它就继承了getColor、setColor、isFilled、setFilled和toString方法

  2. 重载构造方法CircleFromSimpleGeometricObject(double radius, String color, boolean filled)是通过调用setColorsetFilled方法设置colorfilled属性来执行的。这两个公共方法是在基类 GeometricObject 中定义的 , 并在 Circle 中继承 , 因此可以在 Circle 类中使用它们

  3. 唯一读取和改变父类私有数据域的方法就是通过他们的getset方法,如下面是错误

public CircleFromSimpleGeometricObject(double radius, String color, boolean filled){
    this.radius = radius;
    this.color = color;			// 非法操作
    this.filled = filled;		// 非法操作
}

理解继承的关键点

  • 1️⃣和传统的理解不同 , 子类并不是父类的一个子集 。 实际上 , 一个子类通常比它的父类包含更多的信息和方法
  • 2️⃣父类中的私有数据域在该类之外是不可访问的。 因此 , 不能在子类中直接使用 。 但是 , 如果父类中定义了公共的访问器get / 修改器set , 那么可以通过这些公共的访问器 / 修改器来访问和修改它们
  • 3️⃣不是所有的是一种 is - a关系都该用继承来建模
  • 4️⃣继承是用来为是一种关系is - a建模的。不要仅仅为了重用方法这个原因而盲目地扩展一个类 。
  • 5️⃣ Java 中是不允许多重继承(几个类派生出一个子类)的,只允许单一继承( 一个 Java 类只可能直接继承自一个父类)
    • 如果使用 extends 关键字来定义一个子类 , 它只允许有一个父类
    • 多重继承是可以通过接口来实现

使用super关键字

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

super();		// 调用父类的无参构造方法
super(参数);	   // 调用与参数匹配的父类的构造方法

语句 super()super ( arguments )必须出现在子类构造方法的第一行 ,这是显式调用父类构造方法的唯一方式

public CircleFromSimpleGeometricObject(double radius, String color, boolean filled){
    super(color, filled);
    this.radius = radius;
}

构造方法链

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

public ClassName(){
    // 语句
}
/*等价于*/
public ClassName(){
    super();
    // 语句
}
public ClassName(double b){
    // 语句
}
/*等价于*/
public ClassName(double b){
    super();
    // 语句
}

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

public class Person {
    public static void main(String[] args) {
        new Faculty();
    }
    public Person(){
        System.out.println("1 performs Person's tasks");
    }
}

class Employee extends Person{
    public Employee(){
        this("2 Performs Employee's overload constructor");
        System.out.println("3 Performs Employee's tasks");
    }
    public Employee(String s){
        System.out.println(s);
    }
}

class Faculty extends Employee{
    public Faculty(){
        System.out.println("4 Performs Faculty's tasks");
    }
}
1 performs Person's tasks
2 Performs Employee's overload constructor
3 Performs Employee's tasks
4 Performs Faculty's tasks

上面例子的调用过程如下

Snipaste_2020-04-07_14-47-23

new Faculty()调用 Faculty 的无参构造方法 。 由于 Faculty 是 Employee 的子类 , 所以 , 在Faculty 构造方法中的所有语句执行之前 , 先调用 Employee 的无参构造方法 。 Employee 的
无参构造方法调用 Employee 的第二个构造方法 。 由于 Employee 是 Person 的子类 , 所以 , 在 Employee 的第二个构造方法中所有语句执行之前 , 先调用 Person 的无参构造方法

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

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 显式地定义了构造方法 。 因此 , 程序不能被成功编译

调用父类的方法

关键字 super 不仅可以引用父类的构造方法 , 也可以引用父类的方法

super.方法名(参数);
public void printCircle(){
    System.out.println("The Circle is created " + super.getDataCreated +
                      " and the radius is " + radius);
}

方法重写

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

如,在GeometricObject类中的toString方法的重写

public class CircleFromSimpleGeometricObject extends SimpleGeometricObject{
    // 其他的方法和数据
    // 重写toString方法
    public String toString(){
        return super.toString() + "\n radius is " + radius;
    }
}
  1. toString()方法在GeometricObject类中定义 , 在 CircleFromSimpleGeometricObject 类中修改 。 在这两个类中定义的方法都可以在 CircleFromSimpleGeometricObject 类中使用 。 要在 CircleFromSimpleGeometricObject 类中调用定义在 GeometricObject 中的
    toString 方法 , 使用 super.toString()

  2. CircleFromSimpleGeometricObject 的子类不能用语法 super.super.toString() 访问定义在 GeometricObject 中的toString 方法

注意

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

方法重写和重载

  • 重载意味着使用同样的名字但是不同的签名来定义多个方法 。

  • 重写意味着在子类中提供一个对方法的新的实现

// 重写
import javax.swing.plaf.synth.SynthUI;

public class Test{
    public static void main(String[] args) {
        A a = new A();
        a.p(10);
        a.p(10.0);
    }
}

class B {
    public void p(double i) {
        System.out.println(i * 2);
    }
}

class A extends B{
    public void p(double i){        // 重写方法
        System.out.println(i);
    }
}
10.0		// 调用类A中的p(double i)
10.0		// 调用类A中的p(double i)
// 重载
public class Test{
    public static void main(String[] args) {
        A a = new A();
        a.p(10);
        a.p(10.0);
    }
}

class B {
    public void p(double i) {
        System.out.println(i * 2);
    }
}

class A extends B{
    public void p(int i){        // 重载方法
        System.out.println(i);
    }
}
10			// 调用类A中的p(double i)
20.0		// 调用类B中的p(double i)

注意

  • 🅰方法重写发生在通过继承而相关的不同类中 ; 方法重载可以发生在同一个类中(如无参构造函数与含参构造函数), 也可以发生在由于继承而相关的不同类中

  • 🅱方法重写具有同样的签名和返回值类型 ; 方法重载具有同样的名字 , 但是不同的参数列表

  • 🅾重写标注:在子类的方法前放一个@override来表示重写函数

public class CircleFromSimpleGeometricObject extends SimpleGeometricObject{
    // 其他的方法和数据
    
    @Override
    public String toString(){
        return super.toString() + "\n radius is " + radius;
    }
}

Object类及toString()方法

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

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

public class ClassName{...}
//等价于
public class ClassName extends Object{...}

⭕补充方法的签名:即函数名 + 参数类型

toString()方法的签名是public String toString()

调用一个对象的 toString() 会返回一个描述该对象的字符串,一般 包括:

  1. 该对象所属类名
  2. at符号@
  3. 该对象十六进制形式的内存地址
    public static void main(String[] args) {
        A a = new A();
        System.out.println(a.toString());
    }
A@3feba861

这个信息不是很有用 , 或者说没有什么信息量 。 通常 , 应该重写这个toString方法 , 这样 , 它可以返回一个代表该对象的描述性字符串,如GeometricObject类中重写的那样

其实,使用System.out.println(object)等价于System.out.println(object.toString())

多态

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

子类型:子类定义的类型

父类型:父类定义的类型

如,CircleFromSimpleGeometricObjectSimplGeometricObject的子类型,而后者是前者的父类型

public class PolymorphismDemo {
    public static void main(String[] args) {
        DisplayObject(new SimpleGeometricObject("red", true));
        DisplayObject(new CircleFromSimpleGeometricObject(1.0, "black", true));
    }

    public static void DisplayObject(SimpleGeometricObject object){
        System.out.println("Created on " + object.getDataCreated() +
                ". Color is " + object.getColor());
    }
}
Created on Tue Apr 07 17:04:36 CST 2020. Color is red
Created on Tue Apr 07 17:04:36 CST 2020. Color is black

观察调用函数的形参与传入值使用父类对象的地方都可以使用子类的对象,这就是多态

多态意味着父类型的变量可以引用子类型的对象

动态绑定

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

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

Object o = new GeometricObject();
System.out.println(o.toString());

这里的o调用哪个 toString() 呢 ?

  • 声明类型:一个变量必须被声明为某种类型,所以o的声明类型是Object
  • 实际类型:被变量引用的对象的实际类,所以o的实际类型是GeometricObject

o 调用哪个toString() 方法由 o 的实际类型决定 。 这称为动态绑定

动态绑定工作机制

假设对象o是类C1,C2,...,Cn-1,Cn的实例,其中C1是C2的子类,C2是C3的子类,...,Cn-1是Cn的子类。也就是说,Cn是最通用的类,C1是最特殊的类。在Java中,Cn是Object类。如果对象o调用一个方法p,那么JVM会依次在类C1,C2,...,Cn-1,Cn中查找方法p的实现,直到找到为止。一旦找到一个实现,就停止查找,然后调用这个首先找到的实现。

Snipaste_2020-04-08_10-36-24

举例

public class DymaticBindingDemo {
    public static void main(String[] args){
        System.out.println(new GraduateStudent());
        System.out.println(new Student());
        System.out.println(new Person());
        System.out.println(new Object());
    }
}

class GraduateStudent extends Student{

}

class Student extends Person{
    @Override
    public String toString() {
        return "Student";
    }
}

class Person extends Object{
    @Override
    public String toString() {
        return "Person";
    }
}
Student
Student
Person
java.lang.Object@7ef20235

匹配方法的签名绑定方法的实现是两个不同的问题 。 引用变量的声明类型决定了编译时匹配哪个方法 。 在编译时 , 编译器会根据参数类型 、 参数个数和参数顺序找到匹配的方法 。 一个方法可能在沿着继承链的多个类中实现 。Java虚拟机在运行时动态绑定方法的实现 ,这是由变量的实际类型决定的

对象转换和instanceof运算符

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

m(new Student());

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

Object o = new Student();
m(o);

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

但是,当运行Student b = o;将会发生编译错误,因为Student是Object的实例,但是Object对象不一定是Student的实例,为了能够正常编译,我们要使用显示转换

Student b = (Student)o;

向上转换:因为子类的实例永远是它的父类的实例,所以总是可以将一个子类的实例转换为一个父类的变量

向下转换:一个父类的实例转换为它的子类变量,必须进行显示转换

如果父类对象不是子类的一个实例 , 就会出现一个运行异常 ClassCastException ,所以,可以利用运算符instanceof来确保一个对象是另一个对象的实例

instanceof测试它左边的对象是否是它右边的类的实例,返回 boolean 的数据类型。

Object myObject = new Circle();

if(myObject instanceof Circle){
    System.out.println("The circle diameter is " + 
                       ((Circle)myObject).getDiameter());
}

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

为了能够进行通用程序设计 , 一个好的经验是把变量定义为父类型 , 这样 , 它就可以接收任何子类型的值

举例

public class CastingDemo {
    public static void main(String[] args) {
        Object object1 = new CircleFromSimpleGeometricObject(1);
        Object object2 = new RectangleFromSimpleGeometriObject(1, 1);
        displayObject(object1);
        displayObject(object2);
    }
    
    public static void displayObject(Object object){
        if(object instanceof CircleFromSimpleGeometricObject){
            System.out.println("The circle area is " +
                    ((CircleFromSimpleGeometricObject)object).getArea());
            System.out.println("The circle diameter is" +
                    ((CircleFromSimpleGeometricObject)object).getDiameter());
        }else if(object instanceof RectangleFromSimpleGeometriObject){
            System.out.println("The rectangle area is " +
                    ((RectangleFromSimpleGeometriObject)object).getArea());
        }
        
    }
}
The circle area is 3.141592653589793
The circle diameter is 2.0
The rectangle area is 1.0

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

((Circle)object).getArea();

Object类中的equals方法

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

测试两个对象是否相等

public boolean equals(Object obj){
    return (this == obj);	// 使用 == 运算符检测两个引用变量是否指向同一个对象
}

可以重写 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 )

ArrayList

ArrayList对象可以用于存储一个对象列表,ArrayList类存储的对象个数可以不限定

Snipaste_2020-04-11_10-47-15

ArrayList 是一种泛型类 , 具有一个泛型类型 E 。 创建一个 ArrayList 时 , 可以指定一个具体的类型来替换 E ,如:

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

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

ArrayList<String> cities = new ArrayList();

举例

import java.util.ArrayList;			// 导入ArrayList的包
public class TestArrayList {
    public static void main(String[] args) {
        ArrayList<String> cities = new ArrayList<>();   // 创建数组列表

        // 添加城市名到列表中
        cities.add("FuJian");       // [FuJian]
        cities.add("ZheJiang");     // [FuJian, ZheJiang]
        cities.add("GuangDong");    // [FuJian, ZheJiang, GuangDong]
        cities.add("JiangXi");      // [FuJian, ZheJiang, GuangDong, JiangXi]
        cities.add("JiangSu");      // [FuJian, ZheJiang, GuangDong, JiangXi, JiangSu]
        cities.add("BeiJing");       // [FuJian, ZheJiang, GuangDong, JiangXi, JiangSu, BeiJing]

        System.out.println("数组列表大小 : " + cities.size());
        System.out.println("FuJian 在列表中吗 : " + cities.contains("FuJian"));
        System.out.println("JiangXi 在列表中的位置 : " + cities.indexOf("JiangXi"));
        System.out.println("数组列表是否为空 :" + cities.isEmpty());

        // 在指定位置插入
        cities.add(2, "TianJing");
        // [FuJian, ZheJiang, TianJing, GuangDong, JiangXi, JiangSu, BeiJing]
        // 按照名字移除
        cities.remove("JiangXi"); // [FuJian, ZheJiang, TianJing, GuangDong, JiangSu, BeiJing]
        // 按照位置移除
        cities.remove(2);     // [FuJian, ZheJiang, GuangDong, JiangSu, BeiJing]
        // 输出列表
        System.out.println(cities.toString());
        // 反向输出
        for (int i = cities.size() - 1; i >= 0; i--){
            System.out.print(cities.get(i) + " ");
        }
        System.out.println();
        // 创建一个圆形对象的数组列表
        ArrayList<CircleFromSimpleGeometricObject> list = new ArrayList<>();
        // 添加两个圆
        list.add(new CircleFromSimpleGeometricObject(2));
        list.add(new CircleFromSimpleGeometricObject(3));
        // 输出第一个圆的面积
        System.out.println("第一个圆形的面积为 : " + list.get(0).getArea());
    }
}
数组列表大小 : 6
FuJian 在列表中吗 : true
JiangXi 在列表中的位置 : 3
数组列表是否为空 :false
[FuJian, ZheJiang, GuangDong, JiangSu, BeiJing]
BeiJing JiangSu GuangDong ZheJiang FuJian 
第一个圆形的面积为 : 12.566370614359172

数组和ArrayList的异同

操作 数组 ArrayList
创建数组 / 数组列表 String[] a = new String[10] ArrayList<String> list = new ArrayList<>()
引用元素 a[index] list.get[index]
更新元素 a[index] = "FuJian" list.set(index, "FuJian")
返回大小 a.length list.size()
添加一个新元素 list.add("FuJian")
插入一个新元索 list.add(2, "FuJian")
删除一个元索 list.remove("FuJian")
删除一个元素 list.remove(1)
删除所有元索 list.clear()
  • 一旦创建了数组,它的大小就是确定的
  • ArrayList创建后,其大小为0,可以添加任意个元素
  • 排序
    • 数组:java.util.Arrays.sort(array)
    • 数组列表:java.util.Collections.sort(arrayList)
  • 不能使用基本数据类型创建数组列表,数组列表中必须存放对象,ArrayList<int> list = new ArrayList<>()是错误的用法,所以转换为Integer对象的ArrayList,如ArrayList<Integer> list = new ArrayList<>()

使用ArrayList的好处

  1. 大小灵活,无须前提给定大小
  2. 包含许多方法

可以使用foreach来遍历数组和数组列表

for(int num : list)
    System.out.print(num + " ");

对于列表有用的方法

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

  1. 数组创建列表
String array = {"red", "green", "blue"};
ArrayList<String> list = new ArrayList<>(Arrays.asList(array));

Arrays 类中的静态方法 asList 返回一个列表 , 该列表传递给 ArrayList 的构造方法用于创建一个 ArrayList

  1. 数组列表创建对象数组
String[] array1 = new String[list.size()];
list.toArray(array1);

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

  1. 排序
Integer[] array = {3, 5, 7, 2, 1};
ArrayList<Integer> list = new ArrayList<>(Arrays.asList(array));
java.util.Collections.sort(list);
System.out.println(list);
  1. 最大值和最小值
Integer[] array = {3, 5, 7, 2, 1};
ArrayList<Integer> list = new ArrayList<>(Arrays.asList(array));
System.out.println(java.util.Collections.max(list));
System.out.println(java.util.Collections.min(list));
  1. 随机打乱
Integer[] array = {3, 5, 7, 2, 1};
ArrayList<Integer> list = new ArrayList<>(Arrays.asList(array));
java.util.Collections.shuffle(list);
System.out.println(list);

自定义栈类

设计一个栈类,用于存放对象

Snipaste_2020-04-11_11-40-38

import java.util.ArrayList;

public class MyStack {
    private ArrayList<Object> list = new ArrayList<>();
    
    public boolean isEmpty(){
        return list.isEmpty();
    }
    
    public int getSize(){
        return list.size();
    }
    
    public Object peek(){
        return list.get(getSize() - 1);
    }
    
    public Object pop(){
        Object o = list.get(getSize() - 1);
        list.remove(getSize() - 1);
        return o;
    }
    
    public void push(Object o){
        list.add(o);
    }

    @Override
    public String toString() {
        return "stack: " + list.toString();	// 返回表示一个数组线性表中所有元素的字符串表示
    }
}

protected数据和方法

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

protected的功能:

  • 允许子类访问定义在父类中的数据域或方法
  • 不允许非子类访问这些数据域和方法

可见性修饰符

  • private:可以完全隐藏类的成员,不能从类外直接访问它们
  • protected:允许任何包中的子类或同一包中的类访问类的成员
  • public:修饰符允许任意类访问类的成员
  • default:允许同一个包里的任何类直接访问类的成员,但其他包中的类不可以访问

可见性顺序如下

Snipaste_2020-04-11_11-49-48

数据和方法的可见性

Snipaste_2020-04-11_11-55-46

使用可见性修饰符控制如何访问数据和方法

Snipaste_2020-04-11_11-56-22

类可以以两种方式使用

  1. 用于创建该类的实例
  2. 通过扩展该类创建它的子类

类与类的成员对可见性修饰符使用区分

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

注意

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

防止扩展与重写

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

为了防止类的扩展,使用final来将类声明为最终类,是不能作为父类的,如:

Math类、String类、StringBuilder类、StringBuffer类

类和方法都能定义为final

public final class A{	// 定义为final的类不能被继承
    
}
public class B{
    public final void m(){	// 定义为final的方法不能被重写
        // Do something
    }
}

Write by Gqq

posted @ 2020-04-11 12:33  ZHGQCN  阅读(150)  评论(0)    收藏  举报