继承是多态的前提,如果没有继承,就没有多态,主要解决问题“共性抽取”。从面向对象的角度来说,其实就是一种“is a”的关系,即子类是父类的扩展。子类可以通过继承(extends)获得父类原有的字段和方法,也可以增加父类所没有的字段和方法,更可以覆写父类中的允许被子类覆盖的字段和方法。类之间的继承关系只允许单继承,即一个类只能有一个直接父类,但是多重继承是被允许的。
定义父类的格式:
public class 父类名称{
}
public class Animal { }
定义子类的格式:
public class 子类名称 extends 父类名称{
}
public class Cat extends Animal{ }
成员变量的访问特点:
1.在父子类的继承关系中,如果成员变量名重名,则创建子类对象时,访问有两种方式:
- 直接通过子类对象访问成员变量:等号左边是谁,就优先用谁,没有则向上找。
- 间接通过成员方法访问成员变量:该方法属于谁,就优先用谁,没有则向上找。
2.访问子类方法中重名的三种变量名
- 局部变量:直接写局部变量名。
- 本类的成员变量:this.成员变量名。
- 父类的成员变量:super.成员变量名。
成员方法的访问特点:
1.在父子类的继承关系中,如果成员方法重名,则访问规则为:
- 创建的对象是谁(=右边的类型决定),就优先使用谁,没有则向上找父类。
注意事项
- 无论是成员变量还是成员方法,如果没有都是向上找父类,绝对不会向下找子类的。
可以继承的情况:
- 父类和子类处于不同包时:子类可以继承父类中使用protected、public修饰的成员和方法。
- 父类和子类处于相同包时:子类可以继承父类中使用(缺省)、protected、public修饰的成员和方法。
不可继承的情况:
- 父类中使用private修饰的成员和方法子类不可继承。
- 父类的构造器(构造方法),子类不能继承。每个类的构造器都必须和类名相同。
在子类中重写父类的方法,重写原则(一同两小一大)
一同:
方法签名必须相同。方法签名 = 方法名 + 方法的参数列表(参数类型、参数个数、参数顺序)。
两小:
子类方法的返回值类型必须和父类的返回类型相同或者更具体。
子类方法声明抛出的异常类型(后续会详细讲异常,这里记住即可)必须小于或者等于父类方法声明抛出异常类型,子类方法可以同时声明抛出多个属于父类方法声明抛出异常的子类。
一大:子类方法的访问权限与父类方法访问权限相同或者更大。
注:@Override注解:修饰子类重写,如果重写语法错误,就会编译报错,能确保重写的正确性。
public Cat(String breed) {
super(breed); //调用父类的构造方法,必须写在第一行
super.breed = breed; //访问父类的成员变量
super.eat(); //访问父类的成员方法
}
一个对象拥有多种形态,就是对象的多态性。例如:小明是一个对象,但小明既有学生形态,也有人类形态。多态实现 = 子类继承父类,并重写父类的方法。
格式:父类的引用指向子类的对象
父类名称 对象名 = new 子类名称();
接口名称 对象名 = new 实现类名称();
多态中成员变量的使用特点:
直接通过对象名称访问:看等号左边是谁,优先用谁,没有则向上找。
间接通过成员方法访问:看该方法属于谁,优先用谁,没有则向上找。
多态中成员方法的使用特点:
看new的是谁,就优先用谁,没有则向上找。
对比一个成员变量和成员方法的熟记口诀:
成员变量:编译看左边,运行还看左边。
成员方法:编译看左边,运行看右边。
其实就是多态的写法
格式:
父类名称 对象名 = new 子类名称();
//父类引用指向子类对象 Animal a = new Cat(); Animal b = new Dog();
说明:
右侧创建一个子类对象,把它当做父类来看待使用。
注意事项:
1.向上转型一定是安全的,从小范围转向了大范围。
2.但是一旦向上转型为父类,就无法调用子类原本特有的方法,只能调用子类和父类公共方法,单独存在于子类中的方法无法调用。
其实就是一个[还原]的动作
格式:
子类名称 对象名 = (子类名称)父类对象名;
Animal a = new Cat(); //向上转型,子类转父类 Cat cat = (Cat)a; //向下转型,父类转子类
含义:
将父类对象,【还原】成为本来的子类对象。
注意事项:
1.必须保证对象本来创建的时候,就是Cat,才能向下转型还原回去。
2.如果对象创建的时候本来不是Cat,强行向下转型为Cat,就会出现类转换异常(java.lang.ClassCastException)。
3.因为父类存在多个子类,所以向下转型不一定是安全,可能会出现问题2,为了保证转型安全,需要用到关键字instanceof。
格式:
对象 instanceof 类名称
Animal a = new Cat(); if(a instanceof Cat){ Cat cat = (Cat)a; }
含义:
用于判断前面的对象能不能当做后面类型的实例,返回结果是一个boolean值。
步骤一:定义一个Animal.java类,包含属性:breed,color;包含方法:eat(),sleep()。
/**
* 定义一个Animal类
* 包含属性:品种,颜色
* 包含方法:吃,睡
*/
public class Animal {
/**
* 品种
*/
String breed;
/**
* 颜色
*/
String color;
/**
* 无参构造方法
*/
public Animal() {
}
/**
* 有参构造方法,重载
* @param breed
*/
public Animal(String breed) {
this.breed = breed;
}
/**
* 构造方法重载
* @param breed
* @param color
*/
public Animal(String breed, String color) {
this(breed); //调用一个参数的构造方法,必须写在第一行
this.color = color; //访问本类的成员变量
this.eat(); //访问本类的成员方法
}
/**
* 吃饭
*/
public void eat(){
System.out.println(breed + "吃饭!");
}
/**
* 睡觉
*/
public void sleep(){
System.out.println("睡觉!");
}
}
步骤二:定义一个Cat.java类,继承Animal类,并重写eat()方法,新增子类特有的方法play()。
/**
* 定义一个Cat类,继承Animal类
*/
public class Cat extends Animal{
public Cat(){
super();//调用父类的无参构造方法
}
/**
* 重写父类的eat方法
*/
@Override
public void eat() {
//breed属性是继承自父类的,可以直接使用
System.out.println(breed + "吃猫粮!");
}
/**
* 子类特有的方法
*/
public void play(){
System.out.println(breed + "在玩耍!");
}
}
步骤三:定义一个Dog.java类,继承Animal类,并重写eat()方法,新增子类特有的方法wagTail()。
/**
* 定义一个Dog类,继承Animal类
*/
public class Dog extends Animal{
/**
* 重写父类的eat方法
*/
@Override
public void eat() {
//breed属性是继承自父类的,可以直接使用
System.out.println(breed + "吃狗粮!");
}
/**
* 子类特有的方法
*/
public void wagTail(){
System.out.println(breed + "在摇尾巴!");
}
}
步骤四:定义一个TestAnimal.java类,测试同一个引用指向不同子类,调用相同方法发生的变化。
/**
* 测试类
*/
public class TestAnimal {
public static void main(String[] args) {
System.out.println("单独调用父类方法:");
//创建父类对象,给属性赋值,调用方法
Animal animal = new Animal();
animal.breed = "动物";
animal.eat();
System.out.println("=====================================");
System.out.println("单独调用子类方法:");
Cat cat = new Cat();
cat.breed = "猫";
cat.eat();
cat.play();
Dog dog = new Dog();
dog.breed = "狗";
dog.eat();
dog.wagTail();
System.out.println("=====================================");
System.out.println("使用多态形式调用子类方法,向上转型:");
Animal a = new Cat();
a.breed = "猫";
a.eat();
a = new Dog();
a.breed = "狗";
a.eat();
System.out.println("=====================================");
//向上转型后,父类引用是无法调用子类特有的方法,如果要调用,只能在转回去
System.out.println("使用向下转型,调用子类特有的方法:");
if(a instanceof Cat){
Cat cat1 = (Cat) a;
cat1.play();
}else if(a instanceof Dog){
Dog dog1 = (Dog) a;
dog1.wagTail();
}
}
}
输出结果:
题外话:
如果文章中存在错误的地方,欢迎大家指正出来,共同学习进步,后续会陆续就JAVA这门课程展开进行分享。