子类和继承
子类和父类
子类,父类
当我们准备编写一个类时发现某个类已经有我们所需的成员变量和成员方法,我们想复用这个类中的成员变量和方法,即在所编写的类中不用声明成员变量就相当于有了这个成员变量,不用定义方法就相当于有了这个方法,那么我们可以将编写的类声明为这个类的子类。
格式:
class 子类名 extends 父类名{
...
}
如果一个类的声明中没有使用extends关键字,那么这个类被系统默认为Object的子类,Object是java.lang包中的类
访问权限
访问权限修饰符不仅限制了对象对自己的成员变量和方法的访问,还限制了继承性。
1. 子类和父类在同一个包中时。子类继承父类中的除private访问权限外的其他成员变量作为自己的成员变量;同样的,子类继承父类中除private访问权限外的其他方法作为自己的方法
2. 子类和方法不在同一个包时。子类只继承父类中protected和public访问权限的成员变量和方法,不继承private和友好(包)访问权限的成员变量和方法
【子类不仅可以从父类继承成员变量和方法,而且可以根据需要声明自己的新成员、定义新的方法】
protected进一步说明
一个类A中的protected成员变量和方法可以被它的直接子类和间接子类所继承。eg B是A的子类,C是B的子类,那么B、C都可以继承A中的protected成员变量和方法。
从表中我们知道,类C中创建C类的对象可以用“.”访问C类中的protected成员变量和方法,包括继承的和自己新声明的成员变量和新定义的方法。
在其他类M(不是C的子类)中创建C类的对象,那么想通过“.”访问C中的protected成员变量和方法,必须在同一个包中。
需要注意:
对于C自己新声明的成员变量和定义的方法,只要将C类和M类在同一个包即可;
对于C从父类继承的protected成员变量和方法,那么就要一直追溯到该protected成员变量或方法的“祖先”类,M类和祖先类放在同一个包中 eg A类
子类对象
当用子类的构造函数创建一个子类的对象时, 不仅子类中声明的成员变量会被分配内存,且父类的成员变量都会被分配内存空间, 但只将其中一部分(子类继承的那部分)作为分配给子类对象的变量。
(这样看感觉似乎浪费了一些内存,因为没继承的成员变量也分配了空间,而子类对象不能直接通过”.“来操作这些没继承的成员变量,但是,子类继承了父类中的一些方法,而这些方法可能可以操作未继承的成员变量)
instanceof运算符
双目运算符,左边的操作元是对象,右边的操作元是类,当左边的对象是右边的类或子类创建的对象时,instanceof运算的结果是true,否则是false。
成员变量的隐藏和方法重写
成员变量的隐藏
在编写子类时,我们可以重新声明成员变量,一种特殊情况是:如果声明的成员变量的名字和从父类继承的成员变量的名字相同(声明的类型可以不相同),这种情况下,子类就会隐藏掉所继承的成员变量,即子类对象以及自己定义的方法中所操作的与父类同名的成员变量指的是子类自己重新声明的成员变量。
需要注意 :子类对象可以调用从父类继承的方法来操作所隐藏的成员变量,父类方法中操作的成员变量是指父类中的成员变量。
方法重写(Override)
方法重写是指子类中定义一个方法。该方法的类型和父类的方法的类型一致或者是父类方法的类型的子类型,且方法的名字,参数个数,参数类型和父类方法完全相同。
子类通过重写可以隐藏已继承的方法(方法重写也称方法覆盖),把父类的状态和行为改变为自己的状态和行为。
如果子类重写了从父类继承的方法,这个方法既可以操作所继承的成员变量和方法(未被隐藏),也可以操作子类新声明的成员变量和调用新定义的方法,但是无法操作被子类隐藏的成员变量和方法(子类想使用的话需要用到super关键字,后面会介绍)。
重写时注意:
1. 重写方法时,不可以降低方法的访问权限(但可以提高),访问权限范围:public>protected>无修饰符(友好权限,包权限)>private
2. 子类不可以将父类的static方法重写为实例方法,也不可以把实例方法重写为static方法。
super关键字
super.
用super操作被隐藏的成员变量和方法
子类一旦隐藏了继承的成员变量,那么这些被隐藏的成员变量就不会分配给子类创建的对象,这些变量将归关键词super所有。
同样一旦隐藏了继承的方法,子类对象就不能调用这些方法,这些方法的调用由super负责。因此,想使用这些变量和方法就需要通过”super.成员变量“或者”super.方法“
super与构造函数
当用子类的构造函数创建一个对象时,子类的构造函数总是先调用父类的某个构造函数。
如果子类的构造函数中没有明显地指明使用父类的哪个构造方法,子类就会调用父类的不带参数的构造方法,即在子类的构造方法中默认地有:super();
注意:
当父类中没定义构造函数时,系统会提供一个默认无参构造方法,但是当父类定义了一个或多个构造方法时,系统不会提供默认无参构造函数,那么此时如果子类的构造函数没有明显地写出用super关键字来调用父类的某个构造函数时,且父类定义的多个构造方法中不包括无参构造方法,就会出现错误。因此,当我们在父类中定义构造方法时,应当包括一个不带参数的构造方法。
final关键字
final关键字可以修饰类、成员变量、方法和方法中的局部变量
final类
用final将类声明为final类,final类不能被继承,即不能有子类
final class 类名{
...
}
有时出于安全考虑将一些类声明为final类。eg Java提供的String类就是final类
final方法
如果用final修饰父类中的一个方法,那么这个方法不允许被子类重写 (老老实实继承,不许做任何篡改)
常量
成员变量或者局部变量被修饰为final,那么它就是常量。常量在声明时没有默认值,因此声明时必须指定初值,且之后不能再发生更改。
对象的上转型对象
上转型对象
“猫是动物”,“兔子是动物“,这在强调动物的属性和功能而忽略猫,兔子的独有的属性和功能。这种思维方式属于上溯思维方式,类似于Java中对象的上转型对象
假设,A类是B类的父类,当用子类B创建一个对象,并把这个对象的引用放到父类的对象中时(即把子类创建的对象实体在内存中的地址赋给父类的引用变量,父类的引用变量指向子类的对象实体)
A a=new B();
或者
A a;
B b=new B();
a=b; //这时称对象a是对象b的上转型对象
上转型对象相当于子类对象的“简化“对象
上转型对象的特点
1. 上转型对象不能操作子类新增的成员变量,不能调用子类新增的方法
2. 上转型对象可以访问子类继承或隐藏的成员变量,也可以调用子类继承的方法或子类重写的实例方法
注意:
1. 对于子类中新声明的和从父类中继承的相同名字的成员变量,上转型对象操作的是被隐藏的变量,而不是新声明的;
2. 对于子类中重写的方法和从父类中继承的实例方法,上转型对象调用的是子类重写的实例方法,而不是父类的实例方法。但是如果子类重写了父类的类方法,那么子类对象的上转型对象不能调用子类重写的类方法,只能调用父类的静态方法。
3. 注意不要将父类用new运算符创建对象时声明的对象和子类对象的上转型对象混淆
4. 可以将子类对象的上转型对象再强制转换为一个子类对象,这时,得到的子类对象又具备子类所有的属性和行为。
5. 不可以将父类创建的对象的引用赋值给子类声明的对象(不能说“动物是猫”,“动物是兔子”)
继承和多态
当一个类有很多子类时,并且这些子类都重写了父类的某个实例方法,那么这些子类对象的上转型对象的这个实例方法不同,则一个父类声明的对象作为上转型对象在调用这个实例方法时可能具有多种形态。
多态性指的是父类的某个实例方法被子类重写可以各自产生自己的行为,有不同的功能。
abstract类和abstract方法
用关键字abstract修饰的类称为abstract类(抽象类)
abstract class A{
...
}
用关键字abstract修饰的方法称为abstract方法(抽象方法)。对于抽象方法,只允许声明,不允许实现,不允许使用final和abstract修饰同一个方法
注意:
1. 普通类中不能有abstract方法,abstract类可以有abstract方法,当然也可以有非abstract方法,这样abstract类中可以没有abstract方法。
2. 对于abstract类,我们不能使用new运算符创建该类的对象
3. 如果一个非abstract类是某个abstract类的子类,那么它必须重写 (注意重写就是针对于可继承的方法) 父类的abstract方法,给出方法体 这就是为什么不可以同时用abstract和final修饰同一个方法的原因。
如果不想重写,就必须把这个子类修饰为abstract类,即一个abstract类是abstract类的子类,那么它可以重写父类的abstract方法,也可以继承(不重写,不给出方法体)这个abstract方法。
面向抽象编程
在设计程序时,经常会使用abstract类,其原因在于,abstract类只关心操作,而不关心这些操作的具体实现细节,可以使程序设计者把主要精力放在程序的设计上,而不必拘泥细节的实现(这些细节,具体的算法留给子类的设计者)
在设计一个程序时,可以通过abstract类中声明若干个abstract方法,表明这些方法在整个系统设计中的重要性,方法体的内容细节由它的非abstract子类去完成。
使用多态进行程序设计的核心技术之一是使用上转型对象。将abstract类声明的对象作为其子类的上转型对象,这个上转型对象就可以调用子类重写的方法(重写父类中的那些abstract方法)
所谓面向抽象编程是指当设计一个类时,不该让这个类面向具体的类(非abstract类),而是面向抽象类(abstract类),即所设计的类的重要数据(成员变量)是抽象类声明的对象,而不是具体类声明的变量。
开—闭原则
开-闭原则(Open-Closed Principle)就是让设计的系统应当对扩展开放,对修改关闭,即当系统增加新的模块时,不需要修改现有的模块。
在设计系统是,应当首先考虑到用户需求的变化,将应对用户变化的部分设计为扩展开放,而设计的核心部分是经过精心考虑后确定下来的基本结构,这部分应当对修改关闭,即不能因为用户的需求变化而再发生变化,因为这部分不是用来应对需求变化的。
如果系统上的设计遵守了“开-闭原则”,那么系统一定是易维护的,因为在系统中增加新的模块时,不必去修改系统的核心模块。