在我们编写程序的过程当中,会遇到这种情况:
比如现在有一个狗,他的功能有跑,有跳,有吃,有叫,属性有雌雄,大小,颜色等等,同时现在我们也有一个猫,上述功能她也有。这个时候我们写代码的时候,就得分别把上述的功能给猫写一遍,也要给狗写一遍,这样合理么?这就违背了面向对象复用性的原则,为了解决这类问题,我们这里引入继承这个概念,那么什么是继承?
继承的概念:继承就是把某几个类具有相同属性或者功能的代码单独抽象出来放到一个类当中,当编写这些类的时候继承抽象出来的类,可以提高复用性,这个就是继承。比如说,这里我们把猫和狗的相同属性和功能抽象出来放到动物这个类当中,这个时候猫和狗就被称为子类,而动物这个类就是父类。代码示例:
class Animal { String color; int age; void eat() { System.out.println("Eat Something"); } void my() { System.out.println(color+"dddd"+age); } } class Dog extends Animal { } class Cat extends Animal { }
我们这个时候,并没有在Dog还有Cat类当中定义方法,我们只是让他们继承自Animal,我们从继承的概念可以知道,某个子类继承了父类,自然而然也就继承了父类当中的方法还有属性。此时我们来测试一下我们上述代码继承是什么样子的?
class Extend { public static void main(String[] args) { Dog dog = new Dog(); Cat cat = new Cat(); dog.eat(); cat.eat(); dog.my(); cat.my(); } }
这个时候编译输出,结果如下:。
那么在日常生活当中如何利用继承,如何更好的发挥继承的作用呢,继承就是把事物的共性不断的往上层剥离相同的部分,抽取共性。
继承也分为单继承还有多继承:
单继承是一个子类只能有一个父类,这个就是java所支持的。
多继承就是一个子类可能有多个父类,这个java直接不支持,但是间接通过”实现“的方法可以实现多继承。
继承当中变量的特点,当我们继承一个父类,同时子类当中又有与父类当中相同的成员变量的时候,会怎样呢?
class Animal { String color="Yellow"; int age; void eat() { System.out.println("Eat Something"); } void my() { System.out.println(color+"dddd"+age); } } class Dog extends Animal { String color="Black"; }
这个时候我们测试代码写成这个样子:
class Extend { public static void main(String[] args) { Dog dog = new Dog(); System.out.println(dog.color); } }
这个时候输出的是"Black",这个时候我们往往会认为我们覆盖了父类当中的这个成员变量,这样子理解其实是错误的。父类当中的成员变量并没有别覆盖,这个时候只是因为没有被调用而已,这个成员变量还是存在的,那么此时如何调用这个已经存在的父类当中的成员变量呢?java给我们提供了一个super关键字,这个关键词的作用就是在子类当中的成员变量和父类当中的成员变量相同的时候,可以利用这个关键字来调用父类当中的成员方法或者成员变量。此时如果我们想调用父类当中的成员变量,我们可以这样做:
class Dog extends Animal { String color="Black"; String fcolor = super.color; } class Extend { public static void main(String[] args) { Dog dog = new Dog(); System.out.println(dog.color); System.out.println(dog.fcolor); } }
那么此时的这两者在内存当中的表现是怎样的呢?
还记得用this关键字的时候,是为了区别类内同名变量和成员方法当中的临时变量,这里用super关键字是为了父类和子类的区别。
那么当父类和子类当中的方法冲突的时候会怎样呢?这个时候才是所说的覆盖。子类当中的同名方法会覆盖掉父类当中的成员方法。
class Animal { String color="Yellow"; int age; void eat() { System.out.println("Eat Something"); } void my() { System.out.println(color+"dddd"+age); } } class Dog extends Animal { String color="Black"; String fcolor = super.color; void eat() { System.out.println("I'm a dog ,I'm eating"); } } class Extend { public static void main(String[] args) { Dog dog = new Dog(); dog.eat(); } }
这里的eat()方法就没有调用父类当中的成员方法而是直接调用的本地的成员方法。那我们也画一个内存图来深入了解下:
这种类型的成员方法覆盖我们会经常用来做扩展,比如父类当中定义了吃这个动作,但是动物之间的吃的动作是不确定性的,有的吃草,有的吃肉。此时我们要尽可能的避免去修改源代码来实现功能,这个时候我们就用到了继承当中这个覆盖的方法。
这里需要明确的是继承和重载是两个不同的概念:
继承是类与类之间的关系,一个类是所属另外一个类,叫做这个类继承自另外一个类。
重载是针对一个类中同名函数,参数个数,或者类型同而叫做重载。
举个例子来说,我们以前的手机,只能显示来电号码,这个时候我们来定义一个类:
class Phone { void display() { System.out.println("I can display the calling Number"); } } class NewPhone extends Phone { void display() { System.out.println("I can show the calling user"); System.out.println("I can show the calling user pic"); System.out.println("I can display the calling Number"); } } class PhoneDemo { public static void main(String[] args) { NewPhone np = new NewPhone(); np.display(); } }
我们现在知道,我们在旧手机的功能基础上增添了许多功能,这个时候为了避免去修改源代码,我们直接去继承了这个类,然后覆盖了父类当中的display()这个方法,这里为了更好的提高代码的复用性,此时我们可以把子类再调用父类当中的方法:
class NewPhone extends Phone { void display() { System.out.println("I can show the calling user"); System.out.println("I can show the calling user pic"); //System.out.println("I can display the calling Number"); super.display();//直接调用父类当中已经定义好了的代码。 } }
当覆盖父类当红的方法时,我们应该注意些什么东西呢?
1、覆盖父类当中的静态方法的时候,子类当红也必须是静态方法。
2、子类当中覆盖父类的方法的时候,访问权限要高于或者等于父类当红被覆盖方法的权限。
待续....