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+"睡觉....");
    }
}

继承作用:

  1. 减少代码的冗余,实现代码的可复用性
  2. 通过修改父类,便于后期维护。
  3. 让类与类之间产生了关系,是多态的前提。

继承的特性

例子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();
		
	}
}

继承的特性:

  1. 子类可继承父类中的成员变量及成员方法。其中,父类包括所有直接或间接被继承的类。 —— 继承性权限问题
  2. 子类也可定义其自身特有的变量及方法
  3. 单继承:一个类,只能继承一个父类; 一个类,可以有多个子类。
    1. 为什么不能使用多继承:如果使用多继承,父类中都拥有相同的方法时,虚拟机无法判断是调用哪一个父类的方法。
    2. Java支持多重继承 实现多继承。
  4. 提高了类之间的耦合性(继承的缺点,耦合度高就会造成代码之间的联系越紧密,代码独立性越差)

继承性权限问题
父类的对应的访问权限修饰符修饰的成员变量/成员方法,子类可以继承的有:

同一个包中的子类不同包中的子类
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();   //??
    }
}


//

方法——重写

  1. 方法重载 overload
    • 在同一个类中,方法名相同,但是形参不同(参数类型不同,参数个数不同,参数顺序不同)。
    • 方法重载的只能根据方法形参不同来判定,不能根据方法的返回值数据类型不同来判定。
  2. 方法重写 override
  3. 方法重写,指在子类中重新定义父类中已有的方法。
  4. 子类中的方法与父类的方法完全一致叫做重写。

方法重写原则:

  • 方法名和参数列表必须完全与被重写父类方法的相同。
  • 返回类型与被重写方法的返回类型可以不相同,但是必须是父类返回值的派生类(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关键字:表示引用当前对象的父类。

  1. 对于被隐藏的父类中的变量及方法,可通过super关键字调用。
super.variable; //访问父类被隐藏的成员变量
super.Method([参数]); //调用父类中被重写的方法
  1. 调用父类的构造函数
super([参数]);//必须在构造函数的第一句话调用
  • 构造函数无法被重写。
  • 当父类声明的构造函数只有带参的,那么在子类的构造函数中必须显式地通过 super 关键字调用父类的构造器并配以适当的参数列表。
  • 如果父类构造函数是无参的,则在子类的构造函数中可以没有 super() ,系统会自动调用父类的无参构造器。
  • 子类的所有构造方法内部, 第一行会(隐式)自动先调用父类的无参构造函数super();
  • 如果子类构造方法第一行显式调用了父类构造方法,系统就不再调用无参的super()了。
  1. super关键字 和 this关键字均不能在静态方法中使用
    1. super关键字:我们可以通过super关键字来实现对父类成员的访问,用来引用当前对象的父类。
    2. this关键字:指向自己的引用。
    3. 类加载时静态方法首先被初始化,而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类进行演示

  1. 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修饰的方法,无法被重写
	}
}
  1. 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()...");
	}
}
  1. 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类型时,你可以读取使用该参数,但是无法改变该参数的值。