面向对象基础9(继承)

封装、继承、多态是面向对象的三大特征

继承的理解

苹果(类)继承了水果(类)

老虎(类)继承了动物(类)

  • Java的继承是类与类之间的关系
  • 是一种由一般到特殊的关系(与现实中的辈分没有关系,所以子类和父类名字并不准确)

——小类(子类)是一种特殊的父类(大类)

子类的实例完全可当做父类的实例来使用

别称

父类(超类、基类、大类);子类(派生类、小类)

继承的语法

修饰符 class 类名 extends 父类

{代码块}

说明:

Java是单继承的,所以,一个子类只能继承一个父类

若不显示继承父类,Java会默认继承Object

——一切皆Object!

举例:

  1. public class A {
  2. }
  3. class B extends A{
  4. }
  5. class C extends B{
  6. }
  7. //C的直接父类是B;Object、A是C的间接父类(我附庸的附庸不是我的附庸...)
  8. //A的直接子类是B,间接子类是C

继承的作用:

子类继承父类,就可以直接使用父类的方法成员变量

举例1:

  1. public class Bird {
  2. public void fly(){
  3. System.out.println("Birds Flying");
  4. }
  5. }
  6. class Sparrow extends Bird{
  7. }
  8. //此处Sparrow类继承Bird类,Sparrow类并没有写任何方法但继承了Bird类的方法
  1. public class SparrowTest {
  2. public static void main(String[] args) {
  3. Sparrow sp=new Sparrow();
  4. sp.fly();
  5. }
  6. }
  7. /*
  8. Birds Flying
  9. */

继承的优点是可以实现代码的复用

方法重写

存在意义

当子类发现父类的方法不适合自己的时候,就需要重写方法

规则

两同两小一大:

两同:方法名相同、形参列表相同;

两小:返回值类型相同或者更小,声明抛出的异常相同或者更小;

一大:访问权限相同或者更大

举例2:

  1. public class Ostrich extends Bird{
  2. //重写fly方法
  3. @Override
  4. public void fly(){
  5. System.out.println("can not fly");
  6. }
  7. }
  1. public class OstrichTest {
  2. public static void main(String[] args) {
  3. Ostrich A=new Ostrich();
  4. A.fly();
  5. }
  6. }
  7. /*
  8. can not fly
  9. */

此时原有的fly方法被重写

注解@Override——用于报错,要求被修饰的方法必须重写父类方法,否则就报错(方法拼写错误);

Super关键字

this引用很相像,super可以用于限定访问父类的实例变量和实例方法。

super.父类的实例变量

super.父类的实例方法(参数)

例3:

  1. class Base {
  2. int age=2;
  3. }
  4. class Sub extends Base{
  5. int age =20;
  6. public void test(){
  7. int age=200;
  8. System.out.println(age);
  9. System.out.println(this.age);//调用该类中的成员变量
  10. System.out.println(super.age);//调用父类中的成员变量
  11. public void names(String name)
  12. {
  13. System.out.println("执行父类"+name);
  14. }
  15. }
  16. }
  17. public class SubTest {
  18. public static void main(String[] args) {
  19. Sub A=new Sub();
  20. A.test();
  21. }
  22. }
  23. /*
  24. 200
  25. 20
  26. 2
  27. */

例3改:

  1. class Base {
  2. int age=2;
  3. }
  4. class Sub extends Base{
  5. public void test(){
  6. System.out.println(age);
  7. System.out.println(this.age);
  8. System.out.println(super.age);
  9. }//此处删除了原有的局部变量与子类中的成员变量
  10. }
  11. public class SubTest {
  12. public static void main(String[] args) {
  13. Sub A=new Sub();
  14. A.test();
  15. }
  16. }
  17. /*
  18. 2
  19. 2
  20. 2
  21. */

从上面两个例子可以看出:

变量的调用遵循就近原则,输出的age变量优先调用所属方法的局部变量,若局部变量不存在,其次考虑所属类的实例变量(此处是子类),最后考虑其父类的实例变量。

thissuper关键字再次就是为了打破就近原则,调用所需要的变量。

  1. class Base {
  2. public void names(String name)
  3. {
  4. System.out.println("执行父类"+name);
  5. }
  6. }
  7. class Sub extends Base{
  8. public void names(String name)
  9. {
  10. System.out.println("执行子类"+name);
  11. }
  12. public void test(){
  13. names("Java");
  14. /*
  15. 此处等同于this.names("Java");
  16. 等同于Base.names("Java");
  17. 类方法需要类来调用,默认添加类名
  18. */
  19. super.names("Java");
  20. }
  21. }
  22. public class SubTest {
  23. public static void main(String[] args) {
  24. Sub A=new Sub();
  25. A.test();
  26. }
  27. }
  28. /*
  29. 执行子类Java
  30. 执行父类Java
  31. */

此时带有super关键子的names方法会打破就近原则,执行父类的原有方法。

子类调用父类的构造器

子类的构造器一定要调用父类的构造器一次(有且仅有一次)

  1. 如果子类构造器没有显示调用父类构造器,系统会在子类构造器的第一行自动调用父类无参数的构造器;
  2. 子类构造器的第一行显示使用super调用父类构造器:super()
    super和this关键字不顾能出现在同一语句

例5:

  1. package Super关键字;
  2. class Fruit{
  3. private double weight;
  4. public Fruit(double weight)
  5. {
  6. this.weight=weight;
  7. }
  8. }
  9. public class Apple extends Fruit {
  10. public Apple() {
  11. super(2.2);//常识调用父类Fruit的double构造器
  12. }
  13. }

备注:如果父类没有无参构造器——则子类构造器必须显示调用(super调用,如上例)父类的构造器