当前位置:   article > 正文

Java探索之旅(8)——继承与多态_基类是父母,子类是儿子的构造函数

基类是父母,子类是儿子的构造函数

1父类和子类

    ❶父类又称基类和超类(super class)子类又称次类和扩展类。同一个package的子类可以直接(不通过对象)访问父类中的(public,缺省,protected)数据和方法。

    ❷扩展关键字:extends。倘若子类Circles,父类GeometricObject。定义:

public class Circles extends GeometricObject

    ❸Java只容许单一继承,即一个类只能继承自1个父类。多重继承使用接口实现。


2.子类使用父类的构造函数

    ❶super关键字:调用父类的构造方法----super()和super(parameters)分别可在子类构造函数中显式地调用父类的构造方法。注意必须出现在子类构造方法的第1行

    ❷构造方法链:构造一个类,会继承链中各层次基类的所有构造方法。构造子类对象时,系统会首先调用父类的构造方法,以此嵌套调用至最前一层父类。这里的构造方法隐式为无参构造方法。

    ❸应该为各类提供无参数构造方法。倘若某个仅有带参构造法的派生类作为基类派生,由于系统不再默认提供无参构造法,故构造方法链在此会出现编译错误。


3.覆盖与重载

    ❶方法覆盖:即子类修改父类的实例方法。可覆盖的方法要求是可访问的,且子类的覆盖不能削弱父类方法的访问特性(即父类的protected方法,子类覆盖可以public和protected;父类的public方法,子类只能是public)。
    为了覆盖父类方法,必须具有相同的签名,方法名,返回类型。倘若签名不同,则变为重载。
    ❷静态方法不能被覆盖。如果父类的静态方法在子类中重定义(仍旧要求同名同签名同返回类型),则父类的静态方法将会被隐藏,但是可以使用【父类名+'.'+静态方法名】显式调用。
    父类和子类同名的静态方法,若返回类型相异,直接导致出错,若不同的签名则是重载而非覆盖。

  1. public class StudyInheritance{
  2. public static void main(String[] args)
  3. {A I=new A();//先调用C的无参构造函数
  4. A.p();//调用的为A的静态方法,基类C的被隐藏
  5. C.p();//使用类名调用C的静态方法}}
  6. class C
  7. {protected int data;
  8. C(){System.out.println("Call C Constructor1!");}
  9. C(int d){data=d;System.out.println("Call C Constructor!2");}
  10. protected static void p(){System.out.println("static C method p");}}
  11. class A extends C
  12. {A(){System.out.println("Call A Constructor!");}
  13. public static void p(){
  14. System.out.println("static A method p");}}

   输出:

           Call C Constructor1!
           Call A Constructor!
           static A method p
           static C method p

4.多态与动态绑定

    ❶面向对象的3个特点:封装、继承和多态。

    ❷多态性:使用父类型的地方(比如父类为函数参数)都可以使用子类型,即父类型的变量可以引用子类的变量。

    ❸动态绑定:如前描叙,一个方法可以在父类定义但是在子类覆盖。但是子类对象优先使用子类的(覆盖父类的)方法,类似C++的虚函数。
    ❹Java中未指定继承性的类,默认其父类是java.lang.Object。即所有的类均是Object类的子类,即他们都继承了Object类的toString()函数,该方法返回“对象所属派生类+@+16进制地址”。
    例如:对于Object中方法toString()。倘若有一个类A派生自Object,中间有若干层派生,则基类Object中的public函数toString()被A的toString()覆盖。那么究竟使用基类还是派生类的toString呢?
                               String str=O.toString();
                               Object Obj=new A();
    注意到对象Obj的声明类型为Object,实际类型是A。实际上Obj使用的toString(),由实际使用类型(即 A)决定的,即动态绑定。Java虚拟机将会从类A开始,依次向每一层基类回溯,找到第一个toString()执行。

5.对象转换和instanceof运算符

    ❶一般而言。将变量定义为父类型引用,这就可以指向任意子类型的对象。若要使用派生类的函数,必须显式将【声明为基类引用变量,但是实际指向派生类】的引用,即显式转换。如下:

                          class A extends Obeject
                              {int data;
                               function(){.........}; }
                          Obeject myObject= new A();//隐式转换,基类引用变量指向派生类对象
                          if(myObject instanceof A)
                               ((A)myObject).function();//为了使用派生类的函数,必须显式说明
    ❷是声明类型决定在编译时候匹配哪个方法,因此必须显式地告诉编译器,基类型引用变量myObeject具体指向哪一类型的对象,然后才能调用A区别于基类Obeject的成员函数(即不是覆盖函数,而是基类没有的数据和方法,即所谓子类的特性)。
      关键字 instanceof 判断声明的基类类型引用变量,是否实际指向某个派生类的对象。

    ❸动态绑定决定调用最靠近子类对象的覆盖函数。对象转换则用来调用子类中的特性数据域和方法域


6.Object中的equals方法

    ❶作用:判断两个引用变量是否指向同一个对象(等同“==”)。返回true,否则返回false。并没有实际检查是否内容相同。默认实现为:

              public boolean equals(Object obj)
                           {return (this==obj);}

  1. public class Animal{........}
  2. Animal animal1=new Dog();
  3. Animal animal2=new Cat();
  4. Animal animal3=animal1;
  5. animal1==animal2 (False)
  6. animal1.equals(animal2) (False)
  7. animal1==animal3 (True)
  8. animal1.equals(animal3) (True)

    ❷JDK类中有一些类覆盖了Object类的equals()方法。比较规则为:如果两个对象的类型一致且内容一致,则返回true。这些类有:java.io.file,java.util.Date,java.lang.string,包装类(Integer,Double)。
   相反,“==”此时不具备内容比较功能。

  1. Integer int1=new Integer(1);
  2. Integer int2=new Integer(1);
  3. String str1=new String("hello");
  4. String str2=new String("hello");
  5. int1==int2;//false,不同引用对象
  6. int1.equals(int2);//TRUE,相同内容
  7. str1==str2;//False,不同引用对象
  8. str1.equals(str2);//True,相同内容

   ❸可自定义覆盖object类的equals()方法,重新定义比较规则。

下面Person类的equals()比较规则为:只要两个对象都是Person类,并且他们的属性name都相同,则比较结果为true,否则返回false

  1. public class Person{
  2. private String name;
  3. Person(String name)
  4. {this.name=name;}
  5. public boolean equals(Object o){
  6. if (this==null) return true;
  7. else if (!o instanceof Person) return false;
  8. final Person other=(Person)o;
  9. if (this.name().equals(other.name()))
  10. return true;
  11. else
  12. return false;
  13. }
  14. }

 注意:在重写equals方法时,要注意满足离散数学上的特性

   ①自反性:对任意引用值X,x.equals(x)的返回值一定为true.
   ②对称性:当且仅当y.equals(x)返回值为true时,x.equals(y)的返回值一定为true;
   ③传递性:如果x.equals(y)=true, y.equals(z)=true,则x.equals(z)=true
   ④一致性:如果参与比较的对象没任何改变,则对象比较的结果也不应该有任何改变
   ⑤非空性:任何非空的引用值X,x.equals(null)的返回值一定为false

7.防止扩展和覆盖

   ❶为了防止类的扩散,使用final修饰符表明一个类是终极类。如Math类就是终极类。

                         final public class{........};

   ❷final关键字也可以用来定义终极方法,其不能被子类方法覆盖

                         public final void function{......}

   ❸所有的修饰符中,只有final还可以修饰方法的局部变量,所谓的终极局部变量即常量。


8.实例

   ❶定义Person类。数据域为姓名、地址、电话、邮箱
   ❷扩展Student。数据域还包括班级
   ❸扩展Employ。数据域还包括办公室、工资、受聘日期。
   ❹使用super关键字调用父类构造函数和父类被覆盖的方法toString()。
   ❺使用instanceof关键字判断基类引用变量的具体指向

  1. package test11_2;
  2. public class Person {
  3. protected String name;
  4. protected String address;
  5. protected String phone;
  6. protected String email;
  7. Person(){this(null,null,null,null);}//无参调用有参
  8. Person(String a,String b,String c,String d)
  9. {name=a;address=b;phone=c;email=d;}
  10. public String getName(){return this.name;}
  11. public String getAddress(){return this.address;}
  12. public String getPhone(){return this.phone;}
  13. public String getEmail(){return this.email;}
  14. public String toString()
  15. {
  16. return name+"\nAddress :"+address+"\nPhone:"+phone+"\nEmail :"+email;
  17. }
  18. }
  19. class Student extends Person
  20. {
  21. protected static int classState;
  22. Student(){classState=1;}
  23. Student(String a,String b,String c,String d,final int e)
  24. {super(a,b,c,d);//super调用父类构造函数
  25. classState=e;}
  26. public int getClassState(){return Student.classState;}
  27. public String toString()
  28. {return "Student---"+super.toString()+"\nClass :"+classState;}//函数覆盖
  29. }
  30. class Employ extends Person
  31. {
  32. protected String office;
  33. protected int salary;
  34. MyData data;
  35. Employ(){this(null,null,null,null,null,0,null);}//无参调用有参
  36. Employ(String a,String b,String c,String d,String e,int f,MyData g)
  37. {super(a,b,c,d);//super调用父类构造函数
  38. this.office=e;
  39. this.salary=f;
  40. this.data=g;}
  41. public String toString()
  42. {return "Employ---"+super.toString()+"\nOffice :"+office+"\nSalary :"+salary+"\n"+data.toString();}
  43. }
  44. class MyData
  45. {
  46. protected int year,month,day;
  47. MyData(){this(0,0,0);}
  48. MyData(int a,int b,int c){year=a;month=b;day=0;}
  49. public String toString()
  50. {return "The Hired data is :"+year+"-"+month+"-"+day;}
  51. }
     测试类

  1. package test11_2;
  2. public class TestPerson {
  3. public static void main(String[] args)
  4. {
  5. //声明为基类引用,指向子类对象
  6. Person obj1=new Student("李四","湖北荆门","182155107","abcde@163.com",3);
  7. Person obj2=new Employ("王五","湖北孝感","155456782","CHpdn@live.com","KB545",12000,new MyData(2011,2,3));
  8. print(obj1);
  9. print(obj2);
  10. }
  11. // instanceof 判断具体实例对象
  12. static void print(Person obj)
  13. {
  14. if(obj instanceof Student)
  15. System.out.println(((Student)obj).toString());//(Student)类型显式转换
  16. else if(obj instanceof Employ)
  17. System.out.println(((Employ)obj).toString());//(Employ)类型显式转换
  18. else if(obj instanceof Person)
  19. System.out.println(obj.toString());
  20. else
  21. System.out.println("Wrong input!");
  22. }
  23. }
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/641923
推荐阅读
相关标签
  

闽ICP备14008679号