继承
概述
多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那个类即可。
格式:
class 子类名 extends 父类名{}
//例:
/*
猫和狗都有进食、奔跑的能力,也都有名称和年龄
对于大部分动物来说,进食和奔跑是他们共有的功能,名称和年龄是他们共有的属性
对于这些共有内容我们可以将其向上抽取形成一个新的类(Animal类),让Dog类和Cat类继承Animal类
这些类就获取了Animal类的成员,不用在每个类中都写一遍,这样做提高了代码的复用性。
在创建Dog类和Cat类对象时,可以使用Animal中的成员
具体代码如下:
*/
class Animal {
String name;
int age;
//进食方法
public void eat()
{
System.out.println("eat...");
}
//奔跑方法
public void run()
{
System.out.println("run...");
}
}
class Dog extends Animal{
//看门方法
public void guardEntrance()
{
System.out.println("guard entrance...");
}
}
class Cat extends Animal{
//抓老鼠方法
public void catchMouse()
{
System.out.println("catch mouse...");
}
}
/*
需要注意的是,Dog类和Cat类也有其特有的成员
比如:狗会看门,猫会抓老鼠
对于这些特有成员我们不能将其向上抽取,更不能为了继承部分功能而继承
*/
继承的优点:提高了代码的复用性和维护性,让类与类之间产生了关系,是多态的前提
继承的弊端:类的耦合性增强了
开发的原则:高内聚,低耦合
耦合:类与类的关系
内聚:一个类独立完成某件事的能力
即要尽量加强一个类独立完成功能的能力,降低与其他类的关系
继承体系
Java只支持单继承,不支持多继承。即一个类的父类只有一个,但一个类可以有多个子类
多继承存在安全隐患:当多个父类中定义了相同功能,但功能内容不同时,子类无法确定运行哪一个
但Java保留了这种机制,用多实现这种表现形式来完成。
Java支持多层继承,这是Java的继承体系
class B
{
}
class A extends B
{
}
class C extends A
{
}
在具体调用时,要创建最子类的对象
- 有可能父类不能创建对象。
- 创建子类对象可以使用更多的功能,包括父类的也包括自己特有的。
继承的注意事项
- 子类只能继承父类所有非私有的成员
- 子类不能继承父类的构造方法,但是可以通过super关键字去访问父类的构造方法
- 不要为了部分功能而去继承
继承思想:将共性内容向上抽取封装成一个新的类,将这个类作为父类
继承是一个“is a”的关系,即子类是父类中的一员,比如猫和狗是动物
继承中成员变量的关系
当子类中的成员变量和父类中的成员变量名称一样时,访问顺序采取就近原则
例:
class Cat extends Animal{
int age=20;
public void showAge(int age)
{
System.out.println(age);
}
}
public class Test {
public static void main(String[] args) {
Cat cat=new Cat();
cat.showAge(50);
}
}
/*
运行结果:
50
访问变量的查找顺序为:
showAge方法-->Cat类中-->Animal类中
会先在子类的的方法中找,找到就使用
没有的话,在子类的成员变量中寻找,找到就使用
还是没有,在父类的成员变量中寻找,找到就使用
如果父类中没有,就会报错
*/
Object类
Object类是层次结构的根类,每个类都使⽤Object类作为超类
该类中定义的是所有对象都具备的功能
所有类如果没有表明继承什么类,那么会默认在类名后加上extends Object
class A extends Object //extends Object是不显示出来的
{
}
子父类出现后,类成员的特点
super关键字
super:用于子类局部范围访问父类成员变量和方法
super和this类似
this代表的是本类对象的引用
super代表的是父类对象的引用
子父类变量的特点
内存中的加载方式
class Fu{
int num;
public void test(){}
}
class Zi{
int num;
public void method(){}
}
class Demo
{
public static void main(String[] args) {
Zi z=new Zi();
}
}
- 当执行new Zi()时,会先加载Fu.class文件,然后再加载Zi.class文件。
- 在方法区中的一个空间存储父类的方法及静态变量,另一个空间存储子类的方法及静态变量
- 在堆内存中开辟空间存储该对象,并给这个空间分配一个地址值。
- 在空间中分配两个属性空间,一个是父类的空间,一个是子类的空间,两个空间里都有num变量,初始化值为0
- 将这个地址值赋值给栈内存中的变量z,z指向这个对象。
子父类中变量的特点
/*
我们知道我们可以用this来区分同名的成员变量和局部变量
那我们同样可以用super来区分同名的父类成员变量、子类成员变量和局部变量
我们再回到上一个问题
如何在showAge方法中输出父类中的age和子类中的age
如下所示:
*/
class Animal {
int age=10;
}
class Cat extends Animal{
int age=20;
public void showAge(int age)
{
System.out.println(age); //50
System.out.println(this.age); //20
System.out.println(super.age); //10
}
}
public class Test {
public static void main(String[] args) {
Cat cat=new Cat();
cat.showAge(50);
}
}
子父类中成员函数的特点
用super调用父类方法
class Animal {
//奔跑方法
public void run()
{
System.out.println("run...");
}
}
class Cat extends Animal{
//抓老鼠方法
public void catchMouse()
{
//在抓老鼠方法中调用父类中的奔跑方法,代表猫在抓老鼠的过程中会奔跑
super.run();
System.out.println("catch mouse...");
}
}
重写(覆盖)
class Fu
{
void show()
{
System.out.println(“fu show”);
}
}
class Zi extends Fu
{
@override
void show()
{
System.out.println(“zi show”);
}
}
class ExtendsDemo
{
public static void main(String[] args)
{
Zi z=new Zi();
z.show();
}
}
//我们可以用一个标签@override来检测该方法是否是重写,如果不是重写会报错
/*
运行结果:
zi show
*/
子类和父类中有同名方法时,当子类对象调用该方法时,会执行子类中的方法,这种情况叫做重写(覆盖)
作用:当⼦类继承⽗类,沿袭⽗类的功能到⼦类中,但是⼦类虽具备该功能,当功能的内容与⽗类不⼀致,这时没有必 要定义新的功能,⽽是使⽤覆盖特性,保留⽗类的功能定义,并重写功能内容。
即当对⼀个功能进⾏更新升级时,不⽤去改动源代码,直接⽤继承去覆盖之前的功能
class Phone {
public void show()
{
System.out.println("号码");
}
}
class Phone2 extends Phone{
public void show()
{
super.show();
System.out.println("姓名");
System.out.println("头像");
}
}
/*
我们开始有一个Phone类,该类中的show方法可以显示号码
过了一段时间我们对手机进行升级,出现一个类Phone2继承Phone类,代表该手机的2.0版本
子类中的show方法中,我们不仅可以显示号码,还可以显示姓名和头像
我们无需将父类中show方法中显示号码的代码再写一遍,直接用super.show();调用父类中的show方法
*/
注意事项:
- 子类覆盖父类,必须保证子类权限大于或等于父类权限,才可以覆盖,否则编译失败。
- 静态只能覆盖静态(其实静态方法不能算作重写,因为静态属于类)
- 当父类的方法被private修饰时,子类无法重写。
因为私有方法只在本类中使用,子类都没有继承私有方法,如何去重写
我们要区别方法的重载和重写(覆盖)
重载:发生在一个类中的同名方法间,方法的方法名相同但是参数列表不同,与返回值类型无关
重写:发生在子父类中的同名方法间,子父类方法要一样,把包括返回值类型,子父类中不允许存在返回值类型不同的同名方法
子父类中构造方法的特点
class Animal {
String name;
int age;
public Animal() {
System.out.println("父类无参构造方法");
}
}
class Cat extends Animal{
public Cat(){
System.out.println("子类无参构造方法");
}
}
/*
运行结果:
父类无参构造方法
子类无参构造方法
子类中所有的构造方法中第一行都有一条隐式语句:super();
即访问父类中空参数的构造方法
因为子类会继承父类中的数据,可能还会使用父类的数据。所以,子类初始化之前,要先完成父类数据的初始化。
*/
//父类没有无参构造方法,要手动让子类通过super去调用父类其他的带参的构造方法,否则会报错
class Animal {
String name;
int age;
public Animal(int age) {
System.out.println("父类无参构造方法");
}
}
class Cat extends Animal{
public Cat(){
super(age);
System.out.println("子类无参构造方法");
}
}
通过this调用本类其他构造方法时
class Animal {
String name;
int age;
public Animal(int age) {
System.out.println("父类有参构造方法");
}
}
class Cat extends Animal{
public Cat(){
super(5);
System.out.println("子类无参构造方法");
}
public Cat(int age){
this();
System.out.println("子类有参构造方法");
}
}
/*
当子类构造方法中出现this();即通过this调用本类其他构造方法时,该构造方法第一行没有super();
因为super(…)或者this(….)必须出现在第一条语句上,不会同时存在
注意:必须有一个子类构造方法调用父类构造方法!
*/
final关键字
final(最终)关键字,是一个修饰符
可以修饰类、函数、变量
- 被final修饰的类不能被继承
继承打破了封装,因为被继承的类可以被访问被覆盖,用final修饰可以让一些类不被继承 - 被final修饰的函数不能被覆盖
一个类中,一部分方法可能涉及到底层虚拟机,用final修饰可以防止被覆盖,且该类还可被继承 - 被final修饰的变量是一个常量,只能赋值⼀次,既可以修饰成员变量,也可以修饰局部变量