Java第七课. 面向对象特征2
回顾:
1.封装:把细节隐藏,提高安全性
步骤 1:把属性私有化; 2:提供2个公共的方法设置/获取
2.重载:在[同一个类中,方法名相同,参数项不同(个数,数据类型,顺序),与返回值无关];
3.构造方法:方法名与类名相同,无返回值类型;(构造方法一定是重载,反之不成立)
作用: 1. 实例化对象(默认调用无参构造);
2. 创建对象的同时给属性直接赋值(调用有参构造)
4.this 关键字: 1.构造方法间的相互调用(这种情况下要放第一行);
2.指代当前对象;
1. 继承
1.1 继承的意义
也是面向对象特征之一
引入例子:定义学生类 幼儿园,小学生,初中生,大学生会有公共的东西,也有不同的东西
[问题]:太多相同代码
[解决问题]:使用继承.为什么引入继承?刚才的案例分析?定义的幼儿园,小学生,大学生都是学生,他们有共同的属性和方法,如果按照我们刚刚的编码,代码有很多冗余(相同的代码)
可以这么去处理:定义一个学生类Student,如果一个类(BigStudent)是另外一个类(Student),?可以用BigStudent extends Student,就是大学生是学生,所以大学生继承学生类,我们称BigStudent叫做子类(派生类) ,Student是父类(基类)
1.2 extends关键字
基本语法:
[访问权限修饰符] [修饰符] 子类名 extends 父类名{
子类体
}
由于Java是单亲继承体系,因此在描述类与类的继承关系时, extends 关
键字后面只能是一个名字,而不能是一个列表(后续接口继承的情况,
extends 后面可以是一个列表)
• Java中的继承树根节点为 Object;
• 所有Java中的类都直接或间接继承自 Object ;
/**
* 小学生继承学生类
* @author Administrator
* extends:继承
*/
public class SmallStudent extends Student{
}
学生类(父类):
public class Student extends Object{
private String stuNo;
private String stuName;
public String getStuNo() {
return stuNo;
}
public void setStuNo(String stuNo) {
this.stuNo = stuNo;
}
public String getStuName() {
return stuName;
}
public void setStuName(String stuName) {
this.stuName = stuName;
}
/**
* 学习的方法
*/
public void study() {
System.out.println(stuName+"在学习中~");
}
public Student() {
// TODO Auto-generated constructor stub
}
}
大学生类(子类):
public class BigStudent extends Student{
private String sex;
@Override
public void study() {
// 调用父类的study()
super.study();
System.out.println("边泡妞边学习");
}
public void play() {
System.out.println("游戏");
}
}
小学生类(子类):
public class MinniStudent extends Student{
}
测试类:
public class TestMiniStudent {
public static void main(String[] args)
MinniStudent student=new MinniStudent();
student.setStuNo("202001");
student.setStuName("张三");
BigStudent bigStudent=new BigStudent();
bigStudent.setStuNo("202001");
bigStudent.setStuName("李四");
bigStudent.study();
}
}
张三在学习中~
李四在学习中~
边泡妞边学习
1.3 继承的特点
[子类能够继承父类的所有的公共的部分(公共的属性和方法),但是构造方法除外];
miniStudent.setStuNo("202001");
miniStudent.setStuName("张三");
这2个公开的方法其实是从Student类继承而来的;
继承的优点: 减少代码的冗余,方便代码的复用(父类定义后,子类继承父类就可以使用公共部分)
[Java中的类之间只能支持单继承]:BigStudent继承了Student,而Student默认继承了obdanject,这种关系叫做[多层继承];(A继承B,B继承了C,A可能也从C这边继承了一些公共的属性和方法)
extends 除了继承的含义,还有一个含义: 扩展
子类可以扩展父类,子类也可以包含自己的特有部分;
1.4 方法的重写(覆写)@Override
概念:
发生在继承关系的子类中,在子类中的某一个方法,方法的修饰符,返回值类型,方法名,参数列表和父类的某一个方法完全一样,称为方法的重写;(构造方法不能被重写)
jdk1.5新特性:@Override,会帮我们检查下面的方法是否是重写,如果不是重写,那么就会报错;
定义一个动物类,动物都会吃,狗类(Dog),继承动物类,定义一个羊类,继承动物类,分别重写dog类和sheep类的吃的方法.定义测试类 ,分别测试dog对象和sheep对象,测试其吃的过程;
/**
* 自定义动物类
* @author Administrator
*
*/
public class Animal {
//定义私密属性
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void eat() {
System.out.println(name+"在吃");
}
}
public class Dog extends Animal{
/**
* 重写父类的吃的方法
*/
@Override
public void eat() {
System.out.println(getName()+"在吃骨头~");
}
public class Sheep extends Animal{
/**
* 重写父类的吃的方法
*/
@Override
public void eat() {
System.out.println(getName()+"在拱小白菜~");
}
}
测试类:
public class TestAnimal {
public static void main(String[] args) {
// Animal animal=new Animal("动物");
Dog dog=new Dog();
dog.setName("旺财");
dog.eat();
Sheep sheep=new Sheep();
sheep.setName("喜羊羊");
sheep.eat();
}
}
旺财在吃骨头~
喜洋洋在拱小白菜~
1.5 构造方法与继承
public class Animal {
//定义私密属性
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Animal(String name) {
//super();//调用父类的无参构造
this.name = name;
System.out.println("我是父类的带参构造");
}
public Animal() {
System.out.println("我是父类的无参构造");
}
//eat方法
public void eat() {
System.out.println(name+"在吃");
}
}
public class Dog extends Animal{
/**
* 重写父类的吃的方法
*/
@Override
public void eat() {
System.out.println(getName()+"在吃骨头~");
super.eat();//super.方法名() 调用父类的普通方法,普通方法中,这个super.方法名() 放在哪个位置都可以
}
public Dog() {
//第一行代码
//super();//调用父类的无参构造 这行代码写或者不写,都会调用父类的无参构造
super("旺财");//显式调用父类中的带参数构造
System.out.println("我是子类Dog的无参构造");
}
}
public class Sheep extends Animal{
/**
* 重写父类的吃的方法
*/
@Override
public void eat() {
System.out.println(getName()+"在拱小白菜~");
}
public Sheep() {
super("喜羊羊");
System.out.println("我是子类sheep的无参构造");
}
}
public class TestAnimal {
public static void main(String[] args) {
//Animal animal=new Animal("动物");
Dog dog=new Dog();//默认调用子类的无参构造
dog.setName("旺财");
dog.eat();
Sheep sheep=new Sheep();
sheep.setName("喜洋洋");
sheep.eat();
}
}
我是父类的带参构造
我是子类Dog的无参构造
旺财在吃骨头~
旺财在吃
我是父类的带参构造
我是子类sheep的无参构造
喜羊羊在拱小白菜~
[重点]:
当创建一个子类对象的时候,默认会调用子类的无参数构造,但是因为存在继承关系,所以实例化子类的对象时,父类的无参构造先执行(那是因为子类的构造方法,包括有参和无参构造里面会有默认的super()存在调用父类的无参构造),然后再执行子类的构造方法;
解决带参数报错:2种方法
1. 子类默认调用父类的无参,那就添加个无参;
2. 在子类中指定调用父类的某个构造方法,就要用到super;
1.4 super关键字
[可以使用super关键字来调用父类的构造方法以及普通方法];
[调用父类构造方法的时候,必须把super放在构造方法中的第一行];
但是父类的无参构造和有参构造不能同时调用,每个类有且仅有一个构造函数会被实例化;
调用普通方法:
1.41关于super 与 this 能不能同时使用的问题
答案是不能,因为 super 和 this 在子类调用构造方法时都必须要放在第一行;我们先来看为什么这种情况下 this 和 super 要放在第一行;
[super]:
因为继承的原因,在子类进行初始化的时候,必须要先初始化父类,这就需要调用父类的构造方法,而 super就起到了调用父类的构造方法的作用,也就是初始化父类的作用;因为在java中不允许调用未初始化的成员;
再说为什么要放在第一行,因为编译器会检测构造函数第一行有没有调用父类构造函数(包括有参和无参),如果没有会默认添加 super(),有则不会;如果再第二行调用 super,会对父类进行两次初始化,会造成资源的浪费;而且每个类有且仅有一个构造函数会被实例化;
[this]:
this 的作用就是调用本类的其它构造函数,上面说过每个构造函数会有默认的 super(),或者自定义了带参的 super,这样就已经初始化父类了,所以写了 this 就不能写 super 了,上面也说了不能出现两个 super;至于要放第一行的原因和 super 差不多;
2. 总结
• 类的继承由关键字 extends 确定,Java语言为单亲继承,及一个子类只能有一个父类,而一个父类可以有多个子类;
• 子类可以重写父类中某一个方法,称为方法覆盖,也称方法重写,是继承中非常重要的知识点。如果子类需要修改从父类继承到的方法的方法体,就可以使用方法覆盖;
• 当构建子类对象时会优先隐式自动调用父类的无参构造方法,而且这个构建调用过程是从父类“向外”递归扩散的,也就是从父类开始向子类一级一级地完成构建,即如果C继承自B,而B继承自A,那么构建C的对象时,会先调用A的构造方法,然后调用B的构造方法,最后调用C的构造方法,以此类推;
• 如果没有无参的父类构造方法,子类必须要使用 super 显示的调用父类的构造方法,而且必须是在子类构造器中做的第一件事;
• this 引用对象自身、调用自己的构造方法,而 super 调用父类定义的成员变量、方法或构造方法, super 不能当作引用传递给其他的调用者而 this 可以;