
08-继承
08-继承
目录
- 继承
- 方法重写及变量隐藏
- final关键字
- super关键字
继承
1. 生活中的继承
为了认识世界,把事物 进行归纳 分类,形成一类类 不同的事物
兔子🐰和山羊🐐是食草动物; 狮子🦁和豹🐆是食肉动物。<
br/>食草动物 和 食肉动物 都是 动物;
所以,符合继承的关系是:is-a : 小白兔 是一个 食草动物
—— 父类更通用,子类更具体。 子类会具有父类的一般特性也会具有自身的特性。
2. Java中的继承
- 继承是从已有的类中派生出新的类,新的类能吸收已有类的数据属性和行为,并能扩展新的能力。即可以通过一个已经存在的类A,派生出一个新的类B。
- 此时,类A被称为父类,类B则是类A的子类。
语法格式
- 由extends关键字来实现。
[访问修饰符] class 父类 { // 所有类 都是 object的子类
...
}
[访问修饰符] class 子类 extends 父类 {
...
}
- 缺省extends子句,则该类为java.lang.Object的子类,Object类是所有类的父类。
继承得到的类——子类(SubClass);被继承的类——父类(SuperClass 超类、基类)。
- 父类包括直接或者间接被继承的类。
为什么要用继承?
例子1:Teacher类和Student类
public class Student {
String name;//学生姓名
int age; //学生年龄
int id;//学生id
public void eat(){
System.out.println(name+"吃饭....");
}
public void sleep(){
System.out.println(name+"睡觉....");
}
public void study(){
System.out.println(name+"学习....");
}
}
public class Teacher{
String name;//老师姓名
int age; //老师年龄
int id;//编号
double salary;//薪水
public void eat(){
System.out.println(name+"吃饭....");
}
public void sleep(){
System.out.println(name+"睡觉....");
}
public void PreLesson(){
System.out.println(name+"备课....");
}
}
存在什么问题:1)代码存在相似部分;2)代码行数多。3)后期维护,需要一个个修改。
方法:提取两个类存在的相似部分,创建一个父类 person类。
例子2:定义person类
/**
* 面向对象的三大特征:封装\继承\多态
* 探讨继承的特性:
* 1、通过extends关键字实现继承
* 2、Java只存在单继承
* 3、缺省extends时,父类指向java.lang.Object类
* @author wyn
*
*/
public class Person {
public String name;//姓名
protected int id; //编号:身份证号\学号\工号
private int age; //年龄
private static String city="厦门";
public Person() {
System.out.println("这是Person类的构造函数..");
}
public int getAge() {
return age;
}
//在赋值方法加入 增加对属性的限制
public void setAge(int age) {
if(age<0 || age>120) {
System.out.println("年龄有误,请重新输入...");
return;
}else {
this.age = age;
}
}
public static String getCity() {
return city;
}
public static void setCity(String city) {
Person.city = city;
}
//吃饭
public void eat(){
System.out.println(name+"吃饭....");
}
//睡觉
public void sleep(){
System.out.println(name+"睡觉....");
}
}
继承作用:
- 减少代码的冗余,实现代码的可复用性
- 通过修改父类,便于后期维护。
- 让类与类之间产生了关系,是多态的前提。
继承的特性
例子3:在student类中定义main方法进行测试
探讨子类对父类中的变量和方法的应用
/**
* 测试 老师类和学生类 继承 Person类的使用情况:
* 探讨继承的特性:
* 1、子类继承父类
* 1)子类可以继承(直接访问和修改)父类的非私有的成员变量/成员方法;
* 2)子类也可以定义自身独有的成员变量/成员方法
* 2、单继承:
* 一个类,只能继承一个父类; 一个类,可以有多个子类.
*
* @author wyn
*/
public class Student extends Person {
// String name;//学生姓名
// int age;
// char sex;
// int id;//学生id
//子类也可以定义自身独有的成员变量/成员方法
private static String School ="集大";
public static String getSchool() {
return School;
}
public static void setSchool(String school) {
School = school;
}
public void study(){
System.out.println(name+"学习...."); //继承而来,所以直接访问
}
public static void main(String[] args) {
//子类对父类中的成员变量和成员方法的使用--子类可以直接拿来用
//父类可以用子类中的方法吗?---父类不能用子类中单独声明的方法
Student s1 = new Student();
Teacher t1 = new Teacher();
s1.name = "王小虎";//public,谁都可以直接使用
s1.id=19245000; //protected,父类和子类同包中可以使用,不同包,子类可以用父类的
s1.sex='男'; //缺省,父子类同包可以使用,不同包无法使用。
s1.setAge(18); //private,除了它本类可以直接使用,其他类包括子类只能通过公共方法对该属性进行访问
t1.name="李老师";
t1.setAge(26);
t1.id=191100;
t1.setSalary(564564);
System.out.println("s1在"+Student.getCity()+",t1在"+Teacher.getCity());
s1.eat();
t1.sleep();
s1.study();
t1.preLesson();
}
}
继承的特性:
- 子类可继承父类中的成员变量及成员方法。其中,父类包括所有直接或间接被继承的类。 —— 继承性权限问题
- 子类也可定义其自身特有的变量及方法
- 单继承:一个类,只能继承一个父类; 一个类,可以有多个子类。
- 为什么不能使用多继承:如果使用多继承,父类中都拥有相同的方法时,虚拟机无法判断是调用哪一个父类的方法。
- Java支持多重继承 实现多继承。
- 提高了类之间的耦合性(继承的缺点,耦合度高就会造成代码之间的联系越紧密,代码独立性越差)
继承性权限问题
父类的对应的访问权限修饰符修饰的成员变量/成员方法,子类可以继承的有:
同一个包中的子类 | 不同包中的子类 | |
---|---|---|
public | ✔ | ✔ |
protected | ✔ | ✔ |
缺省(默认不写) | ✔ | ❌ |
private | ❌ | ❌ |
例子4:定义EnglishTeacher类
/**
* 子类可以直接访问父类中的非私有成员变量和成员方法;其中父类包括直接父类或者间接父类。
* object类是所有类的父类,也叫根类。
* Java只能单继承:子类只能继承一个父类,但是父类可以有多个子类,所以可以通过间接继承实现多继承。
* @author wyn
*/
public class EnglishTeacher extends Teacher {
public void teach() {
preLesson(); //直接父类Teacher中的方法
System.out.println(name+"教英语。。。");//间接父类Person中的成员变量name
}
public static void main(String[] args) {
EnglishTeacher english = new EnglishTeacher();
english.name="张老师";
english.teach();
}
}
继承引发的问题
变量——变量隐藏
变量隐藏,指在子类中重新定义父类中已有的变量。
子类中变量名与父类的变量名相同(与数据类型无关),则继承自父类的变量将被隐藏。
class Teacher{
//姓名 年龄 id
String name ="张三";
public void prelesson(){
System.out.println(name+"备课");
}
}
class English extends Teacher{ //子类类体
//子类中定义了与父类相同的 成员变量 ,
//成员变量的优先级:就近原则 ,子类把父类的相同成员变量隐藏掉
String name ="Mr.陈";
//子类定义了 一个 与父类 相同的方法名、参数个数、返回类型、
//子类重新定义了父类的方法,子类只能获取到 自己改造后的方法。
public void prelesson(){
System.out.println(name+"英语老师备课。。。");//Mr.陈
}
public static void main(String[] args){
English e =new English();
e.prelesson(); //
Teacher t = new Teacher();
t.prelesson(); //??
}
}
//
方法——重写
- 方法重载 overload
- 在同一个类中,方法名相同,但是形参不同(参数类型不同,参数个数不同,参数顺序不同)。
- 方法重载的只能根据方法形参不同来判定,不能根据方法的返回值数据类型不同来判定。
- 方法重写 override
- 方法重写,指在子类中重新定义父类中已有的方法。
- 子类中的方法与父类的方法完全一致叫做重写。
方法重写原则:
- 方法名和参数列表必须完全与被重写父类方法的相同。
- 返回类型与被重写方法的返回类型可以不相同,但是必须是父类返回值的派生类(java5 及更早版本返回类型要一样,java7 及更高版本可以不同)。
- 访问权限不能比父类中被重写的方法的访问权限更严格。例如:如果父类的一个方法被声明为 public,那么在子类中重写该方法就不能声明为 protected。
- 重写的方法能够抛出任何非强制异常,无论被重写的方法是否抛出异常。但是,重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更广泛的强制性异常,反之则可以。
- 非强制性异常 强制性异常
- 无法被重写的方法:1)父类的构造函数
例子5:定义ChineseTeacher类探讨继承引发问题情况
package day08_extends;
import java.awt.AWTError;
import java.io.EOFException;
import java.io.FileNotFoundException;
import java.io.IOError;
import java.io.IOException;
/**
* 探讨继承引发的问题
* 1.变量隐藏:子类与父类的成员变量存在同名情况下,在子类中继承父类的成员变量被隐藏.--即无法访问到父类同名变量.
* 2.方法重写:子类重新定义父类已有的方法--子类中方法和父类方法一致.
* 重写原则:1)子类的方法名、方法形参与父类方法保持一致;
* 2)子类方法返回值的数据类型可被兼容(继承关系为子类)
* 3)子类的方法访问权限不能比父类方法更低(更严格)
* 4)子类的方法抛异常,不能比父类产生更多的例外,异常范围要小于父类。
* @author wyn
*
*/
public class ChineseTeacher extends Teacher {
//变量隐藏
private String department ="语文课程";
String name = "郑老师";
public void test1() {
System.out.println("直接父类存在变量department,此时department="+department);
System.out.println("间接父类存在变量name,此时name="+name);
}
//方法重写
//重写的方法抛异常,比父类方法的异常例外小;或者抛出任何非强制异常.
@Override
public void preLesson() throws Error,RuntimeException {//重写的方法可以抛出任何非强制异常
System.out.println("5445");
}
/*@Override //注解,表示重写父类方法
public void preLesson() throws IOException{ //若要抛出强制异常则必须是被重写方法抛出的强制异常及子类.
//super.preLesson();
System.out.println("子类重写父类方法:"+name+"在备课--"+department);
}*/
/*public ChineseTeacher preLesson() { //兼容:子类方法返回值数据类型是父类方法返回值数据类型的子类
//super.preLesson();
System.out.println("子类重写父类方法:"+name+"在备课--"+department);
return this;
}*/
public void test2() {
System.out.println("父类方法preLesson(),此时访问prelesson():");
preLesson();
}
public static void main(String[] args) {
ChineseTeacher ct = new ChineseTeacher();
//ct.test1();
ct.test2();
}
}
super关键字
super关键字:表示引用当前对象的父类。
- 对于被隐藏的父类中的变量及方法,可通过super关键字调用。
super.variable; //访问父类被隐藏的成员变量
super.Method([参数]); //调用父类中被重写的方法
- 调用父类的构造函数
super([参数]);//必须在构造函数的第一句话调用
- 构造函数无法被重写。
- 当父类声明的构造函数只有带参的,那么在子类的构造函数中必须显式地通过 super 关键字调用父类的构造器并配以适当的参数列表。
- 如果父类构造函数是无参的,则在子类的构造函数中可以没有 super() ,系统会自动调用父类的无参构造器。
- 子类的所有构造方法内部, 第一行会(隐式)自动先调用父类的无参构造函数super();
- 如果子类构造方法第一行显式调用了父类构造方法,系统就不再调用无参的super()了。
- super关键字 和 this关键字均不能在静态方法中使用
- super关键字:我们可以通过super关键字来实现对父类成员的访问,用来引用当前对象的父类。
- this关键字:指向自己的引用。
- 类加载时静态方法首先被初始化,而super和this都是指向当前对象,此时对象还未创建。
例子6:定义一个Teacher类的子类
/**
* super关键字:
* 1)可以通过super关键字来访问被隐藏的父类成员;
* 语法: super.父类成名变量; super.父类成员方法名();
* 2)构造函数无法被重写,但是子类的构造函数,可以通过super()来调用父类的构造函数,
* 执行顺序:间接父类>直接父类>子类
* super()必须放在第一句执行.
* @author wyn
*
*/
public class MathTeacher extends Teacher {
protected String name;
public MathTeacher() { //如果父类构造函数是有参的,必须加上super(),若是无参的,则可以省略,系统默认调用父类构造函数。
super(); //父类构造函数,必须放在第一句
this.name = "陈老师";
super.name ="吴老师";
System.out.println("MathTeacher构造函数...");
}
@Override
public void preLesson() {
super.preLesson(); //调用父类prelesson()
System.out.println(name + "数学备课...");
}
public static void main(String[] args) {
MathTeacher mt = new MathTeacher();
//mt.preLesson();
}
}
final关键字
修饰对象 | 解释说明 | 备注 |
---|---|---|
类 | 最终类,无子类,不可以被继承 | final类中的方法默认是final的 |
方法 | 方法不能被子类重写 | final方法可以被子类继承 |
变量 | 不能被二次修改,可以理解为常量 | 用final修饰的成员变量表示常量,final修饰的变量有三种:静态变量、实例变量和局部变量,分别表示三种类型的常量。 |
语法格式
//1.声明final类
final class 类名 {
/*类体*/
}
//2.声明final方法
访问修饰符 final 返回值类型 方法名(){
/*方法体*/
}
//3.声明final变量,此时变量名全大写。
访问修饰符 final 数据类型 XXX_XXX_XXX = value;
例子7:定义TestFinalClzz、TestFinalFun、TestFinalVar类进行演示
- final修饰类,无法被继承
//The type ExtendsFianlClzz cannot subclass the final class TestFinal
//ExtendsFianlClzz不能继承最后一个类TestFinal
public class TestFinalClzz extends FinalClzz{
}
final class FinalClzz {
public String userName; //类的成员变量 还是可以二次修改。
public void finalFun() { //默认为final修饰的方法,无法被重写
}
}
- final修饰方法,无法被重写
public class TestFinalFun extends FianlFun{
//重写由final 修饰的父类方法
//Cannot override the final method from TestFinalFun
//无法重写
public void finalFun() {
}
}
class FianlFun{
public final void finalFun() {
System.out.println("TestFinalFun finalFun()...");
}
}
- final修饰变量,变量值无法二次修改,为常量
public class TestFinalVar {
//final修饰变量 1)变量名全大写,可用下划线分隔每个单词;
// 2)此时,变量不可二次修改,可理解为常量.
public final int USER_ID_ERR_NUM = 190003;
public int age = 20;
public static void main(String[] args) {
TestFinalVar tv = new TestFinalVar();
tv.age=10;
System.out.println(tv.age);
// tv.USER_ID_ERR_NUM=192220;
int id = tv.USER_ID_ERR_NUM; //常量只能被存储和访问,无法修改.
System.out.println(tv.USER_ID_ERR_NUM);
}
}
1. final类
final类不能被继承,因此final类的成员方法没有机会被覆盖,默认都是final的。在设计类时候,如果这个类不需要有子类,类的实现细节不允许改变,并且确信这个类不会载被扩展,那么就设计为final类。
2. final方法
如果一个类不允许其子类覆盖某个方法(即不允许被子类重写),则可以把这个方法声明为final方法。
使用final方法的原因有二:
- 把方法锁定,防止任何继承类修改它的意义和实现。
- 高效。编译器在遇到调用final方法时候会转入内嵌机制,大大提高执行效率。
3. final变量(常量)
用final修饰的成员变量表示常量,值一旦给定就无法改变!
final修饰的变量有三种:静态变量、实例变量和局部变量,分别表示三种类型的常量。
一旦给final变量初值后,值就不能再改变了。
另外,final变量定义的时候,可以先声明,而不给初值,这中变量也称为final空白,无论什么情况,编译器都确保空白final在使用之前必须被初始化。
但是,final空白在final关键字final的使用上提供了更大的灵活性,
为此,一个类中的final数据成员就可以实现依对象而有所不同,却有保持其恒定不变的特征。
补充:final参数
当函数参数为final类型时,你可以读取使用该参数,但是无法改变该参数的值。