面向对象高级——继承的实现和进一步研究
2016-04-10 15:08
501 查看
继承的实现
继承的基本概念在没有使用继承之前,如下所示
class Person{ // 定义Person类 private String name ; // 定义name属性 private int age ; // 定义age属性 public void setName(String name){ this.name = name; } public void setAge(int age){ this.age = age ; } public String getName(){ return this.name ; } public int getAge(){ return this.age ; } }; class Student{ // 定义Student类 private String name ; // 定义name属性 private int age ; // 定义age属性 private String school ; // 定义school属性 public void setName(String name){ this.name = name; } public void setAge(int age){ this.age = age ; } public String getName(){ return this.name ; } public int getAge(){ return this.age ; } public void setSchool(String school){ this.school = school ; } public String getSchool(){ return this.school ; } };会发现这两个类的设计并不合理,因为Student类中有部分代码是和Person类重复的,而且学生还不是人?
因此,使用继承就可以解决此类问题。
类的继承格式
在Java中使用extends关键字完成类的继承关系,操作格式如下:
class 父类{} // 定义父类
class 子类 extends 父类{} // 使用extends关键字实现继承
继承:子类继承父类,可以拓展已有类的功能。
class Person{ // 定义Person类 private String name ; // 定义name属性 private int age ; // 定义age属性 public void setName(String name){ this.name = name; } public void setAge(int age){ this.age = age ; } public String getName(){ return this.name ; } public int getAge(){ return this.age ; } }; class Student extends Person{ // 定义Student类 // 此处不添加任何的代码 }; public class ExtDemo02{ public static void main(String arsg[]){ Student stu = new Student() ;// 实例化子类对象 stu.setName("张三") ; // 此方法在Student类中没有明确定义 stu.setAge(30) ; System.out.println("姓名:" + stu.getName() + ",年龄:" + stu.getAge() ) ; } };可以把父类中的内容拿过来在子类中继续使用,这样一来,子类如果有一些重复的方法就不用重复定义了。
extends关键字:成为拓展,子类拓展一个类,子类也有时候被称为派生类。
class Person{ // 定义Person类 private String name ; // 定义name属性 private int age ; // 定义age属性 public void setName(String name){ this.name = name; } public void setAge(int age){ this.age = age ; } public String getName(){ return this.name ; } public int getAge(){ return this.age ; } }; class Student extends Person{ // 定义Student类 private String school ; // 定义school属性 public void setSchool(String school){ this.school = school ; } public String getSchool(){ return this.school ; } }; public class ExtDemo03{ public static void main(String arsg[]){ Student stu = new Student() ;// 实例化子类对象 stu.setName("张三") ; // 此方法在Student类中没有明确定义 stu.setAge(30) ; stu.setSchool("清华大学") ; System.out.println("姓名:" + stu.getName() + ",年龄:" + stu.getAge() + ",学校:" + stu.getSchool() ) ; } };子类已经扩孔了已有的功能。
继承的限制
在Java中只允许单继承,不能使用多继承,即:一个子类只能继承一个父类。但是允许进行多层继承,即:一个子类可以有一个父类,一个父类还可以有一个父类。
继承的类图如下:
访问限制
也就是说子类中是不能直接访问父类的私有属性的,只能通过setter或getter方法进行访问。
class Person{ // 定义Person类 private String name ; // 定义name属性 private int age ; // 定义age属性 public void setName(String name){ this.name = name; } public void setAge(int age){ this.age = age ; } public String getName(){ return this.name ; } public int getAge(){ return this.age ; } }; class Student extends Person{ // 定义Student类 public void fun(){ System.out.println("父类中的name属性:" + name) ; // 错误,无法访问 System.out.println("父类中的age属性:" + age) ; // 错误,无法访问 } };换种方式访问,其实就是通过setter getter方法访问
class Person{ // 定义Person类 private String name ; // 定义name属性 private int age ; // 定义age属性 public void setName(String name){ this.name = name; } public void setAge(int age){ this.age = age ; } public String getName(){ return this.name ; } public int getAge(){ return this.age ; } }; class Student extends Person{ // 定义Student类 public void fun(){ System.out.println("父类中的name属性:" + getName()) ; // 正确,间接访问 System.out.println("父类中的age属性:" + getAge()) ; // 正确,间接访问 } };总结:
1、继承的主要目的:拓展类的功能。
2、Java中一个子类只能继承一个父类。
3、Java中不允许多重继承,但是允许多层继承。
继承的进一步研究:
研究目的:掌握子类对象的实例化过程
掌握方法覆写的概念及实现
掌握super关键字的作用。
在继承的操作中,对于子类对象的实例化也是有要求的:子类对象在实例化之前必须首先要调用父类中的构造方法之后再调用子类自己的构造方法。如下所示:
class Person{ // 定义Person类 private String name ; // 定义name属性 private int age ; // 定义age属性 public Person(){ System.out.println("父类Person中的构造。") ; } public void setName(String name){ this.name = name; } public void setAge(int age){ this.age = age ; } public String getName(){ return this.name ; } public int getAge(){ return this.age ; } }; class Student extends Person{ // 定义Student类 private String school ; // 定义school属性 public Student(){ super() ; // 默认隐藏 System.out.println("子类Student中的构造。") ; } public void setSchool(String school){ this.school = school ; } public String getSchool(){ return this.school ; } }; public class InstanceDemo{ public static void main(String arsg[]){ Student stu = new Student() ;// 实例化子类对象 stu.setName("张三") ; // 此方法在Student类中没有明确定义 stu.setAge(30) ; stu.setSchool("清华大学") ; System.out.println("姓名:" + stu.getName() + ",年龄:" + stu.getAge() + ",学校:" + stu.getSchool() ) ; } };
如果直接实例化一个子类对象,如果在构造方法中没有特别指明调用父类的哪个构造方法进行实例化从父类来的成员变量时(即没有明确使用super调用父类的某个构造方法进行实例化),会默认调用父类的无参构造方法。这时如果父类中没有任何构造方法(jvm会提供一个缺省的无参无操作的构造方法以供使用)或包含有无参构造方法 都不会出错。注意:如果父类中不包含无参构造方法 但是有别的构造方法且子类构造方法中也没有使用super关键字来实例化父类成员,会由于找不到父类的无参构造方法实例化父类成员而出错。
方法的覆写
在继承的关系中也存在着方法覆写的概念,所谓的方法覆写就是指子类定义了与父类中同名的方法,但是在方法覆写时必须考虑权限,即:被子类覆写的方法不能拥有比父类方法更加严格的访问权限。
最常见的三种访问权限:private、default、public,这三种访问权限的大小关系是:private< default <public
所以,如果在父类中使用public定义的方法,子类的访问权限必须是public,否则无法编译。
在Java中,访问权限一共有四种,所见的已经有三种如下所示:
private :最小的访问权限。
default:什么都不声明,即默认的访问权限。
public:最大的访问权限。
class Person{ // 定义父类 void print(){ // 默认的访问权限 System.out.println("Person --> void print()。") ; } }; class Student extends Person{ // 定义继承关系 public void print(){ System.out.println("Student --> void print()。") ; } }; public class OverrideDemo01{ public static void main(String args[]){ Student s = new Student() ; s.print() ; } };以上的代码是正确操作,以下是一个不正确的操作。
class Person{ // 定义父类 public void print(){ // 默认的访问权限 System.out.println("Person --> void print()。") ; } }; class Student extends Person{ // 定义继承关系 void print(){ // 错误的,降低了访问权限 System.out.println("Student --> void print()。") ; } };从之前的正确操作中可以发现,如果现在子类将父类的方法覆写了,调用的时候肯定是调用被覆写过的方法,那么如果现在非要调用父类的方法该怎么办呢?
通过super关键字就可以完成功能,super关键字可以从子类访问父类中的内容。如果要访问被覆写过的方法:super.方法()。
class Person{ // 定义父类 void print(){ // 默认的访问权限 System.out.println("Person --> void print()。") ; } }; class Student extends Person{ // 定义继承关系 public void print(){ super.print() ; // 访问父类中被子类覆写过的方法 System.out.println("Student --> void print()。") ; } }; public class OverrideDemo03{ public static void main(String args[]){ Student s = new Student() ; s.print() ; } };
如果要使用super不一定非要在方法覆写之后使用,也可以明确的表示某个方法是从父类中继承而来的。使用super只是更加明确的说,要从父类中查找,就不再从子类进行查找了。
新的问题:
如果现在将父类的一个方法定义成private访问权限,在子类中将此方法声明成default访问权限,那么这样做还叫覆写吗?
代码如下:
class Person{ // 定义父类 private void print(){ // 默认的访问权限 System.out.println("Person --> void print()。") ; } public void fun(){ // 定义一个fun方法 System.out.println(this); this.print() ; // 调用print()方法 } }; class Student extends Person{ // 定义继承关系 void print(){ // 覆写父类中的方法 // super.print() ; // private权限,无法访问父类中被子类覆写过的方法 System.out.println("Student --> void print()。") ; } void sun(){ System.out.println(this); this.print(); } }; public class OverrideDemo04{ public static void main(String args[]){ Student s = new Student() ; s.fun() ; s.sun(); s.print(); } };
此时,方法并没有被覆盖,而是相当于在子类中又定义了一个新的方法出来。
注意:此种情况很特殊,就是当父类中定义一个私有方法且子类中定义一个同名但是却是不同访问权限的方法时,如果在父类中调用此方法时调用的实际上是父类中的私有方法,如果在子类中的方法中调用此方法时 调用的则是子类中的方法。 除了此种情况之外,其他情况都是覆写。
属性的覆盖:
方法的覆写有很多要求,属性的覆盖,主要是指在子类中声明了与父类同名的属性。
class Person{ // 定义父类 public String info = "MLDN" ; // 定义一个公共属性 }; class Student extends Person{ // 定义继承关系 public String info = "LXH" ; // 定义了一个与父类中属性名称一致的属性 void print(){ // 覆写父类中的方法 System.out.println("父类中的属性:" + super.info) ; System.out.println("子类中的属性:" + this.info) ; } }; public class OverrideDemo05{ public static void main(String args[]){ Student s = new Student() ; s.print() ; } };
重载与覆写的区别如下:
super关键字
super表示从子类调用父类中的指定操作:例如:调用属性、方法、构造等。因为子类实例化的时候会默认调用父类的无参构造,如果现在希望调用有参构造,则必须在子类中进行明确的声明。
class Person{ // 定义Person类 private String name ; // 定义name属性 private int age ; // 定义age属性 public Person(String name,int age){ this.setName(name) ; this.setAge(age) ; } public void setName(String name){ this.name = name; } public void setAge(int age){ this.age = age ; } public String getName(){ return this.name ; } public int getAge(){ return this.age ; } public String getInfo(){ return "姓名:" + this.getName() + ";年龄:" + this.getAge() ; } }; class Student extends Person{ // 定义Student类 private String school ; // 定义school属性 public Student(String name,int age,String school){ super(name,age) ; // 明确调用父类中有两个参数的构造 this.school = school ; } public void setSchool(String school){ this.school = school ; } public String getSchool(){ return this.school ; } public String getInfo(){ return super.getInfo() + ";学校:" + this.getSchool() ; } }; public class SuperDemo01{ public static void main(String arsg[]){ Student stu = new Student("张三",30,"清华大学") ;// 实例化子类对象 System.out.println(stu.getInfo()) ; } };
this与super的区别如下:
对于this和super关键字都可以调用构造方法,而且调用的时候都必须放在构造方法的首行,所以这两个关键字肯定不能同时出现。
容易混淆的概念:如果现在子类中所有的构造方法都使用了this()调用的形式,这样一来是不是就不会调用父类的构造了?
class Person{ // 定义Person类 private String name ; // 定义name属性 private int age ; // 定义age属性 public Person(){ } public Person(String name,int age){ this.name = name ; this.age = age ; } }; class Student extends Person{ // 定义Student类 private String school ; // 定义school属性 public Student(){ // this("LXH",30,"MLDN") ; // 会在此处调用父类的构造,默认调用的是无参 } public Student(String name,int age){ this() ; } public Student(String name,int age,String school){ this(name,age) ; // 明确调用父类中有两个参数的构造 this.school = school ; } }; public class SuperDemo02{ public static void main(String arsg[]){ Student stu = new Student("张三",30,"清华大学") ;// 实例化子类对象 System.out.println(stu.getInfo()) ; } };
相关文章推荐
- 队列的实现
- 剑指offer系列之2:字符串空格的替换
- Json基础及常用示例
- 双三次插值法(Bicubic interpolation)
- 第一次自己电脑的实验
- Windows驱动开发(1) - 驱动程序结构
- linux中文件描述符fd和struct file结构体的释放
- modelAndView传值给JSP
- 一、Python 进阶 之 函数式编程
- c++实验作业——3
- [转]TCP的拥塞控制
- 静态数据成员时间类
- C++字符串
- 第六周学习进度
- github项目拖管
- java动态代理
- maven项目开启时报错
- vector 汇总
- xcode插件管理目录
- 邁向IT專家成功之路的三十則鐵律 鐵律二十:IT人證照之道-收斂