继承
继承直观来说:现实生活中父亲很有钱,儿子就可以不用努力,直接拥有父亲的钱,
在java中子类拥有父类中属性和方法(私有的不能被继承)叫做继承.子类继承父类之后,代码就得到了复用,但主要的作用是有了继承才有多态和方法覆盖.
继承的相关特性
1:B类继承A类,则称类为超类(superclass) 、 父类、基类
B类则成为子类(subclass)、派生类、扩展类
2:java中只支持单继承
3:java中的类没有显示的继承任何类,则默认继承Object类,Object类java提供的根类,老祖宗。也就是说,所有的 类都具有Object对象的特征,java庞大的继承系统,最顶点是:Object
4:java中不支持多继承,其实也可以产生间接多继承的效果,例如:
class B extend A{} class C extends B{} ,这时候A被B继承,B被C继承,间接说明A被C继承
但是私有的属性不能再子类中直接使用,可以通过间接的手段来访问
现在有Animal类 Cat类 和Bird类
Animal类有name属性,eat方法,move方法
class Animal{
String name;
public void move(){
System.out.println("动物在移动");
}
}
Cat类有name属性,eat方法,move方法
class Cat{
String name;
public void move(){
System.out.println("动物在移动");
}
//猫类特有的方法
public void catchMouse(){
System.out.println("猫在捉老鼠");
}
}
Bird类也有name属性,eat方法,move方法
class Bird{
String name
public void move(){
System.out.println("动物在移动");
}
//鸟类特有的方法
public void sing(){
System.out.println("鸟儿在唱歌");
}
}
明显看到我们这样写代码看起来很臃肿,Cat类,Bird类 和Animal类有很大类似,有重复的代码块.但是代码并没有的到重复利用,所以希望为了是代码重复利用,把Bird和Cat相同的部分提取出来,放到Animal中,再让Bird 和Cat继承Animal即可
在这里插入代码片
class Animal{
String name;
public void move(){
System.out.println("动物在移动");
}
}
class Cat extends Animal{
//猫类特有的方法
public void catchMouse(){
System.out.println("猫在捉老鼠");
}
}
class Bird extends Ainmal{
//鸟类特有的方法
public void sing(){
System.out.println("鸟儿在唱歌");
}
}
这样重复的代码就得到了利用,代码看起来也美观.
方法覆盖(重写)
上述例子当中有一个很重大的问题,Animal类中的move方法,子类当中继承过来之后,子类调用move方法,执行结果是:
动物在移动
而我们需要子类Cat和Bird的move方法并不是”动物在移动”,这时候就需要对父类的方法进行重写,来满足子类的需求.精确的说:子类继承父类之后,父类的方法不能满足子类的业务需求,子类有这个权利对父类的方法进行重写
class Cat extends Animal{
//重写父类move方法
public void move(){
System.out.println("猫在跑步");
}
//猫类特有的方法
public void catchMouse(){
System.out.println("猫在捉老鼠");
}
}
class Bird extends Ainmal{
//重写父类方法
public void move(){
System.out.println("鸟儿在飞翔");
}
//鸟类特有的方法
public void sing(){
System.out.println("鸟儿在唱歌");
}
}
这时候再进行测试,Cat类和Bird类的move方法不是”动物在跑步”了.已经而满足我们的需求了
public static void main(String[] args) {
Bird bird = new Bird();
bird.move();
Cat cat = new Cat();
cat.move();
}
输出结果:
鸟儿在飞翔
猫在跑步
需要注意的是方法重写是建立在继承关系之上的,并且只针对的是方法,父类型的属性是不能被重写的,重写之后的方法和之前的方法具有相同的返回值类型,相同的方法名,相同的参数列表,其中任意一项不成立说明,不构成方法重写,而是新建立了一个方法.
多态
多态是面向对象三大特征之一,多态是指多种形态,多种状态.是指父类型的引用指向子类型的对象,或者接口中的引用指向实现接口引用类型的变量
父类型引用指向子类型对象:
编译阶段(静态绑定):绑定父类型中的方法
运行阶段(动态绑定):绑定子类型中的方法
假设现在有一个父类Pet(宠物类) 子类Cat(猫类)两者是继承关系,Pet类又一个move()的方法,Cat类继承Pet类
Pet pet = new Cat();
pet.move();
编译阶段(静态绑定)
对于编译器来说,编译器只知道pet的类型是Pet,所以编译器在检查语法的时候,会去Pet.class字节码文件中去找move()方法,找到了绑定move()方法,编译通过了,静态绑定成功.
运行阶段(动态绑定)
运行阶段的时候,实际上在堆内存中创建的java对象是Cat对象,所以在调用move()方法的时候,真正参与move()方法的对象是一只猫(Cat),所以运行阶段执行Cat对象的move()方法,这个过程属于动态绑定
这其中pet就是父类型的引用,new Cat()是一个子类型的对象,这种语法被称为向上转型
不论是向上转型还是向下转型,都必须满足两种类型之间满足继承关系.否则编译器会直接报错
多态的作用
多态在程序中很重要的一点是:提高程序的扩展力,降低耦合度
假设现在有一个主人类(Master类),主人有一个喂养宠物”feed()”方法.还有主人的宠物一个Dog类,
class Dog{
public void eat(){
System.out.println("狗狗正在吃狗粮");
}
}
class Master{
public void feed(Dog dog){
dog.eat();
}
}
public class Test{
public static void main(String[] args) {
Master master = new Master();
Dog dog = new Dog();
master.feed(dog);
}
}
运行结果:
狗狗正在吃狗粮
但是这样写明显有一个问题,如果当主人再养一个宠物猫的时候,就需要对feed方法进行重载,添加一个参数是猫的方法.后期如果还有需求,就需要对主人类不断的进行改进,设置多种方法,来实现每一个类的要求.这时候我们增加一个抽象类或者接口(宠物类)来实现多态机制,这样当以后再来一个新的宠物类,就不需要改变主人类中的方法,就能实现主人喂养宠物的这个业务.这样程序的扩展力就很强.接下来我们对之前的程序进行改进.
新增Pet的抽象类,
public abstract class Pet {
//吃的行为
public abstract void eat();
}
Dog类继承Pet类,
public class Dog extends Pet {
//宠物狗类
public void eat(){
System.out.println("狗狗正在吃狗粮");
}
}
新增Cat类,是主人新增的宠物类,继承Pet
public class Cat extends Pet {
public void eat(){
System.out.println("猫喜欢吃鱼,吃的很香");
}
}
使用多态机制之后,主人类通过抽象类Pet间接来实现宠物的喂养,当以后再新增任何宠物类,主人类不需要在进行改变,新增的宠物类只需要继承Pet抽象类,在测试时,使用多态机制便可实现宠物的喂养
public class Master {
//主人类
public void feed(Pet pet){
pet.eat();
}
}
测试类
public class Test {
public static void main(String[] args) {
Master master = new Master();
Dog dog = new Dog();
Cat cat = new Cat();
master.feed(dog);
master.feed(cat);
}
}
运行结果
狗狗正在吃狗粮
猫喜欢吃鱼,吃的很香