Java extends

写在开头

 Java语言的三大特点之一:继承
 是面向对象语言的一大基石,因为它允许创建分等级层次的类。
 继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。
 今天我们就来简单说一下这部分内容

继承概念

  • 面向对象语言三大特征之一
  • 类与类之间,存在一中包含与被包含的关系,抽象理解为父与子的关系,子类可以继承父类的变量和方法
  • extends关键字表示这种继承关系
  • class A extends B
  • 父类也叫超类 基类
  • 子类也叫派生类
  • 不支持多继承 但是支持多层继承

使用场景

  1. 动物界里的继承

兔子和羊属于食草动物类,狮子和豹属于食肉动物类。

食草动物和食肉动物又是属于动物类。

所以继承需要符合的关系是:is-a,父类更通用,子类更具体。

虽然食草动物和食肉动物都是属于动物,但是两者的属性和行为上有差别,所以子类会具有父类的一般特性也会具有自身的特性。

2.细化一点表示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
什么时候使用继承关系比较合适:
举例:
猫(class) : 吃,睡,抓老鼠
狗(class): 吃,睡,看家

共性: 吃,睡
特性: 猫 抓老鼠; 狗 看家

Animal(class) : 动物类,猫或狗都属于动物,猫或狗在代码中,可以理解为,是动物的子类(孩子)
继承关系: is a ------ Cat is an animal ;

Animal作为父类的话,里面应该拥有一些子类所共性的内容(吃,睡)
class Cat extends Animal{
猫内容: 可以使用Animal中的内容(成员变量,方法)
}

class Dog extends Animal{
狗内容: 可以使用Animal中的内容(成员变量,方法)
}

子类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

// 猫继承了动物,是动物的子类
public class Cat extends Animal
{
/*public void eat(){

}
public void sleep(){

}*/
public void catchMouse(){
System.out.println("猫抓老鼠");

}
}

子类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 狗继承了动物,是动物的子类
public class Dog extends Animal
{
/*public void eat(){

}

public void sleep(){

}*/

public void lookHome(){

System.out.println("狗看家");
}
}

父类:

1
2
3
4
5
6
7
8
9
10
11
12
//动物类
public class Animal
{
public void eat(){
System.out.println("动物吃饭");

}
public void sleep(){
System.out.println("动物睡觉");

}
}

实现类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class  TestAnimal
{
public static void main(String[] args)
{
/*
运行结果如下:
猫抓老鼠
动物吃饭
狗看家
动物睡觉
*/
Cat c = new Cat();
c.catchMouse();//猫抓老鼠
c.eat();//动物吃饭,猫类本身没有定义eat方法,这个方法是从父类继承来使用的
Dog d = new Dog();
d.lookHome();//狗看家
d.sleep();//动物睡觉,狗类本身没有定义sleep方法,这个方法是从父类继承来使用的
}
}

继承的优势和弊端

1
2
3
4
5
6
7
8
9
10
11
12
继承的优势: 
1) 继承提高代码的复用性
2) 继承提高了代码的维护性
3) 给多态提供了前提

继承的弊端:
增加了类与类之间的耦合性
耦合性: 指类与类之间的关联性,增加了(Animal类中eat和sleep方法,cat和dog使用eat和sleep都依赖Animal, 如果:Animal类删除了, cat和dog里面的eat和sleep,都会出现问题)

Java中开发,尽量做到:
高内聚,低耦合
内聚: 一个类中,能实现很多的功能(对内,属于高内聚);对外(对其他类),与别的类关联性尽量低

使用继承的注意事项

1
2
3
4
5
6
7
8
9
10
11
1. 父类中的私有成员(成员变量,方法),不能被子类继承到
原因: 私有只能在本类中使用.父类中的私有成员,也只能在父类当中去使用,因此子类无法继承到私有

2.构造方法不能被继承
原因: 父类中的构造方法名,父类名;
子类中的构造方法名,子类名;
构造方法就表示: 方法名必须与类名完全一致,父类的名字与子类不一样,因此构造不能被继承

但是: 子类中,可以调用父类中的构造方法

3.不要为了几个小的功能,硬要去实现继承关系

成员变量在继承中的关系

1
2
3
4
5
6
7
8
9
10
11
12
13
子类继承父类后,可以无条件的继承父类中的非私有成员

1. 父类中定义的成员,子类中没有定义,子类可以直接使用父类中的非私有成员
父类中没有定义的成员,子类中定义了,父类不能使用子类特有的成员
总结: 子类即可以使用自身的成员,也可以使用父类中的成员
父类只能使用自己的成员
2. 父类中定义的成员,子类中又从新定义了一遍,子类中访问这个变量,优先使用子类定义
原因: 变量的就近访问原则
访问原则:
子类中使用变量,先看方法中有没有定义局部变量,定义了,使用局部变量
没有局部,找类中的成员变量,有成员,使用自己类的成员
没有成员(如果有继承关系),到父类中去找成员,找到,使用父类成员
父类没有成员(再去找父类的父类,直到找到为止),最高级别的父类,Object,如果找到了最高级,都没有,才报错.

子类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//子类
public class Zi extends Fu
{

int d = 77;// 子类自己的成员
int a = 100;// 子类与父类都有的成员

// 子类继承父类后,可以无条件的继承父类中的非私有成员
public static void main(String[] args)
{

Zi z = new Zi();
System.out.println(z.a);// 100,变量就近访问原则,调用子类的成员a
//System.out.println(z.b); 错误代码,b在父类中私有的,不能被子类继承到
System.out.println(z.c);// 99,子类继承了父类中的c变量
System.out.println(Zi.c);// 99,因为c变量是静态,因此可以类名直接调用
System.out.println(z.d);// 77,d为子类自己定义的成员变量

Fu f = new Fu();
//System.out.println(f.d); 错误代码,父类中没有定义变量d

}
}

父类:

1
2
3
4
5
6
7
// 父类
public class Fu
{
int a = 10;// 普通成员变量
private int b = 25;// 私有的成员变量
static int c = 99;// 静态的成员变量
}

super 关键字的使用

this : 本类对象的引用 this的引入: 解决本类中成员变量和局部变量重名问题

super : 表示父类对象的引用

super使用场景:
用于解决子父类中 : 1) 变量重名问题 ; 2)方法重名问题; 3)构造方法

使用方法:

  1. 子父类变量重名问题解决:
    在子类中使用 super.变量 : 表示调用父类中的成员变量

  2. 子父类变量重名问题解决:
    在子类中使用 super.方法名() : 表示调用父类中的方法

  3. 子类中调用父类的构造方法:
    在子类构造方法中第一行写 super() 或者super(参数列表) 调用父类的指定构造方法

super使用的注意事项: super表示父类对象,为非静态结果,不能在static静态方法中使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//子类
public class Zi extends Fu
{
int a = 100;// 子类与父类都有的成员
public static void main(String[] args)
{
Zi z = new Zi();
System.out.println(z.a);// 100,变量就近访问原则,调用子类的成员a
// this : 本类对象的引用
// super : 父类对象的引用 new Fu().a
// static : 静态中不能访问非静态, this 不能用,super不能用
z.getA();// 打印结果为 10

}
public void getA(){
System.out.println("父类的a"+super.a);//10
}
}

** this 和super在内存中的运行过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// this和super的使用
public class Zi3 extends Fu
{
int a = 100;// 重新定义变量a,子父类共有
public void getA(){
int a = 1000;// 局部变量a

System.out.println(a);// 局部变量--- 1000
System.out.println(this.a);// 成员变量---100
System.out.println(super.a);// 父类成员变量---- 10
}

public static void main(String[] args)
{
Zi3 zi = new Zi3();
zi.getA();
}
}

成员方法在继承中的关系

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
子类继承父类后,可以无条件的继承父类中的非私有方法

1. 父类中定义的方法,子类中没有定义,子类可以直接使用父类中的非私有方法
子类中定义的特有方法,父类不能使用

2. 子类中,重写(Override)父类中的方法,子类将父类中的方法在自己的类中,重新写一遍
子类重写的规则:
1. 子类重写了父类的方法后,调用的就是子类的重写方法
2. 重写有要求:
1) 要求子类中的方法与父类中方法 方法名,参数列表,返回值都必须保持一致
2) 子类的修饰符权限,大于等于父类的修饰符权限
目前 : 权限修饰符 public (公共的,最大的访问权限)
private(私有的,最小权限修饰,只能在本类中使用)
类中修饰方法或者成员时,没有给任何修饰,就相当于是默认修饰符, public > 默认 > private
3) 如果验证一个方法是重写的方法 @Override
使用方法: 在方法声明之上,写上@Override,如果没报错,证明这个方法是子类重写父类的方法,报错了,就证明不是重写父类的方法
4) static修饰的方法不能被重写,因为static修饰的方法属于类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//子类
// 成员方法在继承中的关系
public class Zi1 extends Fu
{
public static void main(String[] args)
{
Zi1 zi = new Zi1();
zi.function1();// 调用子类自己重写的function1
// zi.function2(); 错误代码,父类中私有的方法,不能被子类继承到
zi.function3();// 调用父类的function3方法
zi.getFunction();// 父类中的function1
}

@Override
public void function1(){
System.out.println("Zi1++++++function1");
}

public void getFunction(){
super.function1();// 调用父类的function1方法
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 父类
public class Fu
{
public void function1(){
System.out.println("function1");
}

private void function2(){
System.out.println("function2");
}

public static void function3(){
System.out.println("function3");
}

}

构造方法在继承中的关系

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
1. 构造方法不能继承
但是可以调用,子类的构造方法中,可以调用父类的构造方法
前提: class A extends B{// 如果有继承关系,父类要优先于子类加载到内存中
main(){
A a = new A();
int result = a.a1;
}

}
B中变量 int a1 = 10;
A 中什么内容都没写,上述a1的变量,来自父类,只有父类先进入到内存中,父类中的成员才能使用,因此在继承关系中,父类优先进入内存

2.子类调用父类的构造方法
1) 子类的构造方法第一行,系统默认有一行隐式(隐藏没有写出来的),super();
super() : 表示调用父类的空参是构造方法,确保父类要先进入到内存中
2) 在子类的构造方法中,如果你自己手动的写了super() 或者 spuer(参数),那么系统就不会再给你默认写一个super()
3) super() 在构造方法中,必须写在第一行
this() 在同类构造方法互相调用中,也必须写在第一行
在构造方法中,this() 和super(),只能二选一

final修饰词

final : 表示最终的,不可变的
final修饰符

  1. 修饰类: 使用final修饰的类,不能当父类
  2. 修饰方法: 使用final修饰的方法,不能被重写
  3. 修饰变量: 使用final修饰的变量,不能二次赋值

为什么要使用final:

  1. 有很多类,本身功能已经很健全,不希望子类重写我的任何方法,不要子类,不能被继承使用一切正常
  2. 很多方法,本身功能很完善,不希望子类重写这个方法,但可以正常使用
  3. final修饰的变量,通常叫常量(程序执行过程中,值不变,称之为常量)
    final修饰的变量,只能手动赋值一次,以后不能修改,修改报错;
    final修饰的成员变量,赋值时机,需要在对象创建完成之前完成
    赋值方式: 1) 定义就直接赋值(最推荐) 2)构造代码块或者构造方法赋值(不太推荐)

final修饰变量举例:
派 π : 全世界,3.14…..
将π定义成final类型的变量 static final double PI = 3.1415926….; // 静态常量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// final关键字修饰变量,不能被二次赋值
public class finalDemo
{

final int a = 10;// a变量用final修饰的,已经赋值了,不能二次赋值
final int b = 99; // JVM动态赋值 0

/*public finalDemo(int a){
this.a = a;//无法为最终变量a分配值

}*/
public finalDemo(int b){
//this.b = b;//无法为最终变量a分配值

}
public static void main(String[] args)
{
finalDemo fd = new finalDemo(88);
//fd.b = 99;// 错误代码,无法为最终变量a分配值
System.out.println(fd.b);
// final修饰fd , 表示fd对应的这个地址值不能变
final finalDemo fd1 = new finalDemo(88);
fd1 = new finalDemo(88);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
//final关键字修饰类不能当父类,案例
// final修饰类
public final class finalDemo1//finalDemo1是final修饰的,这个类,特点: 不能做父类
{

public void print(){
System.out.println("Hello World!");
}
public static void main(String[] args)
{
System.out.println("Hello World!");
}
}
1
2
3
4
5
6
7
8
// 错误的类继承,原因finalDemo1是final修饰的
public class finalDemo1Sub extends finalDemo1
{
public static void main(String[] args)
{
System.out.println("Hello World!");
}
}
1
2
3
4
5
6
7
// 父类
public class Fu
{
public final void function1(){
System.out.println("function1");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// final修饰的方法不能被重写
public class Zi4 extends Fu
{
/*
此处注释的为错误的代码,因为function1在父类中是final修饰的,不能被子类重写
@Override
public void function1(){
System.out.println("function1");
}*/

public static void main(String[] args)
{
Zi4 zi = new Zi4();
zi.function1();// final修饰的方法,可以正常使用
}
}

总结

  1. 代码块:
    局部代码块:限定变量的生命周期
    构造代码块:初始化成员变量,每个构造方法执行之前都会执行一次构造代码块
    静态代码块:随着类的加载而执行【常用】
  2. 继承概述:
    extends
  3. 好处:
    提高代码的复用性
    提高代码可维护性
    多态的前提
  4. 注意事项:
    私有的成员不能被继承:不能在子类中直接访问,只能间接访问
    构造方法不能被继承,子类中可以通过super关键字调用父类中的构造方法
    不要因为部分功能而定义继承关系,子类是父类的一种
  5. 继承中成员变量的关系:
    子父类中出现了同名的变量,根据就近原则,先在子类成员位置寻找,再到父类的成员位置寻找
  6. this和super
    关键字this表示本类当前对象的引用,哪个对象再调用this所在的方法,this就表示哪个对象
    关键字super表示本类当前对象父类的的引用,哪个对象在调用super所在的方法,super就表示哪个对象中,父类部分的数据
    Super只能访问父类中的成员、this可以访问子类和父类中的所有成员
  7. 继承中构造方法的关系
    子类的构造方法一定要访问父类的构造方法,子类的构造方法在执行之前,一定要先运行父类的构造方法
    子类的构造方法第一句,默认是super()
    如果手动访问了父类的构造,super()不给默认提供
    如果手动访问了子类的其他构造,super()不给默认提供
    this语句和super语句都必须在构造方法的第一行
    this语句和super语句不能共存
  8. 方法的重写:
    在子父类中,出现了一模一样的方法声明,但是有不一样的方法实现
    用@Override注解检查子类方法的重写
  9. 方法重写的注意事项:
    私有方法不能重写
    重写的权限不能越来越小
  10. final关键字
    修饰类,类不能被继承
    修饰方法,方法不能被重写
    修饰变量,变成常量,只能赋值一次

晚安

 今天就到这里了,明天见,加油!

-------------本文结束感谢您的阅读-------------
0%