5.1 子类与父类
子类(派生类)
1、由其他类派生出的类
2、子类继承父类的所有非私有成员(不包含构造器)
3、子类只能继承自一个父类(单继承)
父类(超类、基类)
1、派生出子类的类叫做父类
2、包含了一些共同特征(属性与行为)
继承语法--extends关键字
class 子类名 extends 父类{
//类体代码
}
5.1.1 子类
以下是在父类A下定义子类B:
class A {
…
}
class B extends A { //定义A的子类B
…
}
5.1.2 类的树形结构
- Java的类按继承关系可形成树形结构
- 根节点为Object类(Java自带类)
- 没有使用extends关键字的类,父类默认为Object类(“祖先类”)
5.2 子类的继承性
为什么要有继承,有很多子类可能会用到一样的方法,一个一个的子类中打太麻烦,直接在它们父类中定义好就搞定。你后期想写“物种多样性”也可以再在子类中重写方法也可以。(重写方法会在后面讲)
5.2.1 子类和父类在同一包中的继承性
- 用关键字extends定义的一个子类,其父类中的没带private的方法或变量都可被我这个子类定义的实例对象使用。
- private英文即私有的,“爸爸不想给儿子的东西”,子类定义的实例变量就不可以用private了的方法或变量。
- 其实继承就是,子类继承了父类的方法或变量,相当于子类自己定义了一样。如下面两例代码效果相当:
例1
class A {
void f1(){
…
}
private void f2() {
…
}
}
class B extends A {
}
例2
class B {
void f1(){
…
}
}
5.2.2 子类和父类不在同一包中的继承性
子类父类不在同一包中,子类只能继承到父类的protected和public的方法或变量。
5.3 子类与对象
当用子类的构造方法创建一个对象时,不仅子类中声明的成员变量被分配了内存空间,而且父类的成员变量也都被分配了内存空间,但只将其中一部分(即子类继承的那部分成员变量)作为分配给子类对象的变量
5.3.1子类对象的特点
也不是父类中有private的变量,子类的实例对象就获取不到它的值了。就算是未被子类继承的变量,也可以通过被子类继承的方法然后获取到其值(其指的是未被子类继承的变量)。
例:
class A {
private int a = 555; //私有变量,子类不可继承
int f() {
return a; //这个方法不是私有的,但返回了其类的私有变量的值,像个间谍一样,“出卖我的爱,没了良心在~”
}
}
class B extends A {
}
public class MainC {
public static void main(String args[]) {
B abc = new B();
System.out.println(abc.f()); //输出的是555,那个“被出卖的a”
//System.out.println(abc.a); //非法
}
}
运行结果:
简单来说就是环环相扣,子类B的对象可以调用继承的方法f(),f()又可以返回父类A中的private了的a的值。
5.3.2 Instanceof运算符
语法:对象 instanceof 类
作用:instanceof运算符是Java独有的运算符,其左边的操作元是对象,右边的操作员是类。当左边的对象属于右边的类或其子类创建的对象,该运算结果为true,否则结果为false。
例:
//Apple是Fruit的子类
Fruit fruit = new Fruit();
Apple apple = new Apple();
System.out.println(fruit instanceof Apple); // 输出 false
System.out.println(apple instanceof Apple); // 输出 true
System.out.println(apple instanceof Fruit); // 输出 true
5.4 成员变量的隐藏和方法重写
作用:把父类的“改”成自己的。
成员变量的隐藏和方法重写,这个名词看上去好像挺复杂,其实基本上是一个东西。
5.4.1 成员变量的隐藏
子类继承的父类的变量,子类又把这个变量写了遍,之后调用 子类的实例对象.变量 返回的就是子类中的变量值了。
以下是一个河豚案例,
例:
class Fish {
int heTun = 20;
}
class AdultFish extends Fish {
float heTun = 20f;
}
public class MainC {
public static void main(String[] args){
AdultFish fishCount = new AdultFish();
System.out.println(fishCount.heTun); //输出为20.0并非20
}
}
运行结果:
就像爸爸我留给你了一辆保时捷跑车(父类中的变量),之后儿子你很爱惜它,把它留在了温室中,整天开着Jeep(子类中的同名变量)出去兜风。
5.4.2 方法重写(方法覆盖)
方法重写,如字面意思,同上面的变量的隐藏差不多,只不过多了一些重写方法时的要求(不按要求,会非法):
1.子类不可重写父类中的final方法 ,final方法如下:
class A {
final void f() {
…
}
}
class B extends A {
void f() { //非法
…
}
}
2.不可降,可提高:即访问权限不可降,只可提高。(访问限制修饰符权限降序:public>protected>友好的>private)
class A {
protected int f(int a) {
…
}
}
class B extends A {
int f(int a) { //默认友好,protected>友好,不可降低访问权限,非法
…
}
}
class C extends A {
public int f(int a) { //public>protected,提高了访问权限,合法
…
}
}
3.参数数据类型一致:由于涉及到关系重载,在子类中重写某个父类中的某方法时,参数数量和方法返回值的数据类型等都得和前者一样。
class A {
int f(int a) {
…
}
}
class B extends A {
int f(int a, int b) { //参数数量不一,非法
…
}
}
class C extends A {
float f(int a) { //返回的数据类型变了,非法
…
}
}
5.5 super关键字
5.5.1 用super操作被隐藏的成员变量和方法
super可以获取到父类的成员变量的值。
例:
class A {
int n = 6;
int f() {
return n; // 6
}
}
class B extends A {
int n;
int f() {
n = super.n; //🍍,其中调用了super.n,也就是A类中的n的值6,所以abc.f()返回值为6而不是8。
return n; // 6
}
}
public class MainC {
public static void main(String[] args) {
B abc = new B(); //从这开始读——————
abc.n = 8; //让B类下的n的值为8
System.out.println(abc.f()); //进入重写的f()函数,(现在跳转到上面的🍍处继续阅读)
}
}
运行结果:
5.5.2 使用super调用父类的构造方法
在子类的构造方法中使用super可以调用父类的构造方法
//每个子类的构造方法的头一个语句默认有
super();
在父类中定义多个构造方法时应当包括一个不带参数的构造方法,以防在子类的构造方法省略写super方法时出错。
例:
class A {
int a;
A() { //A的构造方法
}
A(int a) { //A的构造方法的重载
this.a = a;
System.out.println("a:" + this.a);
}
}
class B extends A {
int b = 6;
B(int b) {
super(6); //调用的是父类A中的A(int a)方法
this.b = b;
System.out.println("b:" + this.b);
}
}
public class MainC {
public static void main(String[] args) {
B abc = new B(8);
}
}
运行结果:
说白了,super就是为了方便子类调用父类的构造方法。
5.6 final关键字
final可修饰:①类②成员变量③方法中的局部变量
final class A {
…
}
final类
“没儿子,断子绝孙了”
final方法
父类中的final方法,子类中老老实实继承,不能重写。
“就像传家宝,你总不能卖了买其他的吧。”
final修饰变量
final修饰的常量就是常量,和C中差不多,定义时就要赋值,且之后不能被改变。
final double PI = 3.1415926;