Java 面向对象之 继承
2016-08-15 15:22
441 查看
继承是对现实世界中表示事物之间所属关系的一种描述,这种关系的最下层也就是末端是具体的事物,而上层是对下层的抽象,越往上层抽象性越强,例如人是一种抽象概念,男人是人这个大类里面的一小类(男人继承人),而阮英文是男人中一个具体的对象(阮英文是男人类的一个具体实例)。
上面说明了继承产生的原由是现实世界中事物关系的一种计算机语言呈现,那么如何做好继承呢? 子类继承父类的属性和方法,然后对于父类不具备的自己特有的属性与方法,需要子类去扩展。
继承使Java面向对象的核心-多态成为可能。多态就是在父类对象中存放着子类对象,这样在方法调用时统一调用父类的方法,在程序运行过程中,虚拟机会按照对象具体的类型调用相应的方法,至于是调用父类的方法还是调用子类的方法,或者是具体是哪个子类的方法,都是运行时动态决定的,这个过程叫做动态绑定。
下面用代码简单说明如何定义父类,和定义继承父类的子类
父类—Person
子类1—Employee
子类2—Student
在这里,Person 是一个抽象类,包含一个抽象方法,包含抽象方法的类一定是抽象类,但是抽象类不一定包含抽象方法。
上面的代码中出现了this关键字和super关键字,
关键字this有两个用途:一是引用隐式参数,二是调用该类其他的构造器。
super关键字也有两个用途:一是调用超类的方法,二是调用超类的构造器。
一个继承关系中的子类,在另一个继承关系中可以最为父类出现;同样,一个继承关系中的父类在另一个继承关系中可以作为子类。
上面的代码中,Employee类是Person类的子类,下面代码中,类Manager继承Employee,此时Employee变成了父类。
Employee类的子类—Manager
多态是指任何父类出现的地方都可以用子类替代,这样如果用一个父类容器包装所有的子类,程序调用父类的方法,在程序执行之前并不知道程序会具体执行哪个类的方法,只有在程序运行时,确定当时所处的是那个具体类,然后调用对应的方法,这个过程就是动态绑定。如果是private方法、static方法、final方法,编译器将可以准确地知道应该调用那个方法法,这种调用方式就是静态绑定。
下面的代码体现了动态绑定的神奇所在
程序打印结果为:
继承.Employee:with name=dandan;age=24;gender=女;Wed Jan 01 00:00:00 CST 2014;salary=10000.0
继承.Student:with name=jiayan;age=23;gender=女;major=英语
继承.Employee:with name=dandan;age=24;gender=女;Wed Jan 01 00:00:00 CST 2014;salary=10000.0
程序中同样都是调用 Person类的 toString方法,得到的结果去不一样,程序在运行时,调用的相应的类中的toString方法。
被标记为final的类是不可以被继承的,标记为final的方法是不能被子类覆盖的,final类中的所有方法都自动为final。
有些程序员认为:除非有足够的理由使用多态,应该将所有的方法都声明为final。
为了动态绑定带来的系统开销而使用final关键字,如果一个方法没有被覆盖并且很短,编译器就能够对它进行优化处理,这个过程成为内联。
2、对象强制类型转换
有时候需要将某个类的对象引用转换成另外一个类的对象引用,这时就需要类型转换。
进行类型转换的唯一原因是:在暂时忽视对象的实际类型之后,使用对象的全部功能。例如,在InheritTest类中,由于某些项是普通雇员,某些对象是学生,所以persons数组必须是他们共同的父类Person对象的数组。将yingwen对象存入数组之前,我们需要先用Manager对象对其进行初始化,而为了设置经理奖金,必须使用正确的类型。
类型转换的特点:
只能在继承层次内进行类型转换
在将超类转换成子类之前,应该使用instanceof进行检查
Employee类和Manager类对象都能够正确地调用个体Salary方法,这是因为实现多态性的动态绑定机制能够自动地找到相应的方法;只有在使用Manager中特有的方法时才需要进行类型转换,如setBonus方法。在一般情况下,应该尽量少用类型转换和instanceof运算符。
3、抽象类与抽象方法:
被标记为abstract的类就是抽象类,如上面代码中的Person类,同理被标记为abstract的方法就是抽象方法,如上面代码中Person类中的getDescription方法。抽象方法充当着占位的角色,他们的具体实现在子类中。
是否可以省略Person类中的getDescription抽象方法,而仅在Employee和Student子类中定义getDescription方法呢?如果这样的话,就不能通过persons数组中的对象调用getDescription方法了,编译器只允许调用在类中声明的方法。
4、编写一个完美的equals方法的建议
(1)、显示参数命名为otherObject,稍后需要将它转换成另一个叫做other的变量。
(2)、检测this和otherObject是否引用同一个对象。
(3)、检测otherObject是否为null,如果为null,返回false。
(4)、比较this与otherObject是否属于同一个类,如果equals的语义在每个子类中有所改变,就使用getClass检测,
如果所有的子类都拥有统一的语义,就是用instanceof检测。
(5)、将otherObject转换为相应的类类型变量
(6)、现在开始对所有需要比较的域进行比较。
上面说明了继承产生的原由是现实世界中事物关系的一种计算机语言呈现,那么如何做好继承呢? 子类继承父类的属性和方法,然后对于父类不具备的自己特有的属性与方法,需要子类去扩展。
继承使Java面向对象的核心-多态成为可能。多态就是在父类对象中存放着子类对象,这样在方法调用时统一调用父类的方法,在程序运行过程中,虚拟机会按照对象具体的类型调用相应的方法,至于是调用父类的方法还是调用子类的方法,或者是具体是哪个子类的方法,都是运行时动态决定的,这个过程叫做动态绑定。
下面用代码简单说明如何定义父类,和定义继承父类的子类
父类—Person
public abstract class Person { // 抽象类 public abstract String getDescription(); // 抽象方法 private String name; private int age; private String gender; public Person(String n,int a,String g) { this.name=n; this.age=a; this.gender=g; } public String getName() { return name; } public int getAge() { return age; } public String getGender() { return gender; } public boolean equals(Object otherObject) { if(this==otherObject) return true; if(otherObject==null) return false; if(this.getClass()!=otherObject.getClass()) return false; Person other=(Person) otherObject; return Objects.equals(this.name,other.name)&& this.age==other.age&&Objects.equals(this.gender,other.gender); } public int hashCode() { return Objects.hash(name,age,gender); } public String toString() { return this.getClass().getName()+":with "+"name=" + name + ";age=" + age + ";gender=" + gender; } }
子类1—Employee
public class Employee extends Person { private double salary; private Date hireDay; public Employee(String n, int a, String g,double s,int year,int month,int day) { super(n, a, g); this.salary=s; GregorianCalendar calendar=new GregorianCalendar(year,month-1,day); this.hireDay=calendar.getTime(); } @Override public String getDescription() { return this.toString(); } public double getSalary() { return salary; } public Date getHireDay() { return hireDay; } public void raiseSalary(double byPercent) { double raise=salary*byPercent/100; salary+=raise; } @Override public boolean equals(Object otherObject) { if(this==otherObject) return true; if(!super.equals(otherObject)) return false; if(this.getClass()!=otherObject.getClass()) return false; Employee other=(Employee) otherObject; return Objects.equals(this.hireDay,other.hireDay)&& this.salary==other.salary; } @Override public int hashCode() { // TODO Auto-generated method stub return 7*super.hashCode()+11*this.hireDay.hashCode()+13*Objects.hashCode(hireDay); } @Override public String toString() { // TODO Auto-generated method stub return super.toString()+";"+hireDay.toString()+";salary="+salary; }
子类2—Student
public class Student extends Person { private String major; public Student(String n, int a, String g,String m) { super(n, a, g); this.major=m; } @Override public String getDescription() { return this.toString(); } @Override public boolean equals(Object otherObject) { if(this==otherObject) return true; if(!super.equals(otherObject)) return false; if(this.getClass()!=otherObject.getClass()) return false; Student other=(Student) otherObject; return Objects.equals(this.major,other.major); } @Override public int hashCode() { // TODO Auto-generated method stub return 7*super.hashCode()+11*major.hashCode(); } @Override public String toString() { // TODO Auto-generated method stub return super.toString()+";major="+major; } }
在这里,Person 是一个抽象类,包含一个抽象方法,包含抽象方法的类一定是抽象类,但是抽象类不一定包含抽象方法。
上面的代码中出现了this关键字和super关键字,
关键字this有两个用途:一是引用隐式参数,二是调用该类其他的构造器。
super关键字也有两个用途:一是调用超类的方法,二是调用超类的构造器。
一个继承关系中的子类,在另一个继承关系中可以最为父类出现;同样,一个继承关系中的父类在另一个继承关系中可以作为子类。
上面的代码中,Employee类是Person类的子类,下面代码中,类Manager继承Employee,此时Employee变成了父类。
Employee类的子类—Manager
public class Manager extends Employee { private double bonus; public Manager(String n, int a, String g, double s, int year, int month, int day) { super(n, a, g, s, year, month, day); this.bonus=0; // TODO Auto-generated constructor stub } public double getSalary() { double baseSalary=super.getSalary(); return baseSalary+bonus; } public void setBonus(double b) { this.bonus=b; } @Override public String getDescription() { return this.toString(); } @Override public boolean equals(Object otherObject) { if(this==otherObject) return true; if(!super.equals(otherObject)) return false; if(this.getClass()!=otherObject.getClass()) return false; Manager other=(Manager) otherObject; return this.bonus==other.bonus; } @Override public int hashCode() { // TODO Auto-generated method stub return 7*super.hashCode()+11*Objects.hashCode(bonus); } @Override public String toString() { // TODO Auto-generated method stub return super.toString()+";bonus="+bonus; } }
多态是指任何父类出现的地方都可以用子类替代,这样如果用一个父类容器包装所有的子类,程序调用父类的方法,在程序执行之前并不知道程序会具体执行哪个类的方法,只有在程序运行时,确定当时所处的是那个具体类,然后调用对应的方法,这个过程就是动态绑定。如果是private方法、static方法、final方法,编译器将可以准确地知道应该调用那个方法法,这种调用方式就是静态绑定。
下面的代码体现了动态绑定的神奇所在
public class InheritTest { public static void main(String[] args) { // TODO Auto-generated method stub Person [] persons=new Person[3]; persons[0]=new Employee("dandan", 24, "女", 10000, 2014, 1, 1); Manager yingwen=new Manager("benben", 27, "男", 12000, 2016, 7, 1); yingwen.setBonus(500); persons[1]=yingwen; persons[2 ]=new Student("jiayan", 23, "女", "英语"); for(int i=0;i<3;i++) { System.out.println(persons[i].toString()); } } }
程序打印结果为:
继承.Employee:with name=dandan;age=24;gender=女;Wed Jan 01 00:00:00 CST 2014;salary=10000.0
继承.Student:with name=jiayan;age=23;gender=女;major=英语
继承.Employee:with name=dandan;age=24;gender=女;Wed Jan 01 00:00:00 CST 2014;salary=10000.0
程序中同样都是调用 Person类的 toString方法,得到的结果去不一样,程序在运行时,调用的相应的类中的toString方法。
下面极少几个零散的知识点
1、阻止继承:final类和方法被标记为final的类是不可以被继承的,标记为final的方法是不能被子类覆盖的,final类中的所有方法都自动为final。
有些程序员认为:除非有足够的理由使用多态,应该将所有的方法都声明为final。
为了动态绑定带来的系统开销而使用final关键字,如果一个方法没有被覆盖并且很短,编译器就能够对它进行优化处理,这个过程成为内联。
2、对象强制类型转换
有时候需要将某个类的对象引用转换成另外一个类的对象引用,这时就需要类型转换。
进行类型转换的唯一原因是:在暂时忽视对象的实际类型之后,使用对象的全部功能。例如,在InheritTest类中,由于某些项是普通雇员,某些对象是学生,所以persons数组必须是他们共同的父类Person对象的数组。将yingwen对象存入数组之前,我们需要先用Manager对象对其进行初始化,而为了设置经理奖金,必须使用正确的类型。
类型转换的特点:
只能在继承层次内进行类型转换
在将超类转换成子类之前,应该使用instanceof进行检查
Employee类和Manager类对象都能够正确地调用个体Salary方法,这是因为实现多态性的动态绑定机制能够自动地找到相应的方法;只有在使用Manager中特有的方法时才需要进行类型转换,如setBonus方法。在一般情况下,应该尽量少用类型转换和instanceof运算符。
3、抽象类与抽象方法:
被标记为abstract的类就是抽象类,如上面代码中的Person类,同理被标记为abstract的方法就是抽象方法,如上面代码中Person类中的getDescription方法。抽象方法充当着占位的角色,他们的具体实现在子类中。
是否可以省略Person类中的getDescription抽象方法,而仅在Employee和Student子类中定义getDescription方法呢?如果这样的话,就不能通过persons数组中的对象调用getDescription方法了,编译器只允许调用在类中声明的方法。
4、编写一个完美的equals方法的建议
(1)、显示参数命名为otherObject,稍后需要将它转换成另一个叫做other的变量。
(2)、检测this和otherObject是否引用同一个对象。
(3)、检测otherObject是否为null,如果为null,返回false。
(4)、比较this与otherObject是否属于同一个类,如果equals的语义在每个子类中有所改变,就使用getClass检测,
if(getClass()!=otherObject.getClass())return false;
如果所有的子类都拥有统一的语义,就是用instanceof检测。
if(!(otherObject instanceof ClasName)) return false;
(5)、将otherObject转换为相应的类类型变量
ClassName other=(ClassName) otherObject;
(6)、现在开始对所有需要比较的域进行比较。
相关文章推荐
- java面向对象—抽象类、接口与多继承
- java面向对象—抽象类、接口与多继承
- JAVA菜鸟(二)面向对象之封装,继承,多态
- Java程序员从笨鸟到菜鸟之(三)面向对象之封装,继承,多态(下)
- Java程序员从笨鸟到菜鸟之(二)面向对象之封装,继承,多态(上)
- java编程-面向对象---继承和多态
- Java语言基础-面向对象(继承)
- Java程序员从笨鸟到菜鸟之(二)面向对象之封装,继承,多态(上)
- Java程序员从笨鸟到菜鸟之(三)面向对象之封装,继承,多态(下)
- 黑马程序员学习笔记四——Java 面向对象 特点之 继承
- java中对面向对象的三大特征:封装、继承、多态的理解
- Java程序员从笨鸟到菜鸟之(二)面向对象之封装,继承,多态(上)
- Java程序员从笨鸟到菜鸟之(二)面向对象之封装,继承,多态(上)
- Java程序员从笨鸟到菜鸟之(三)面向对象之封装,继承,多态(下)
- Java程序员从笨鸟到菜鸟之(三)面向对象之封装,继承,多态(下)
- java面向对象—抽象类、接口与多继承
- java 基础---面向对象(继承)
- Java程序员从笨鸟到菜鸟之(三)面向对象之封装,继承,多态(下)
- Java面向对象——继承和接口
- Java基础之面向对象(三)--继承、抽象类、接口、final修饰符、模版方法模式