发布于2024-11-02 10:30 阅读(938) 评论(0) 点赞(3) 收藏(2)
目录
在 Java 中,使用类来对现实中的实体进行描述,例如
定义一个 Cat 类来描述猫:
- public class Cat {
- String name;
- int age;
- public void eat() {
- System.out.println("吃饭");
- }
- }
定义一个 Dog 类来描述狗:
- public class Dog {
- String name;
- int age;
- public void eat() {
- System.out.println("吃饭");
- }
- }
而上述代码中的 name、age 以及 eat 方法,都是重复的
此时,就可以对这些共性进行抽取
而在面向对象思想中提出了 继承 的概念,专门用来进行共性抽取,实现代码复用
继承(inhertance):是面向对象编程的特征,它允许在保持原有类的基础上进行扩展,增加新的功能,这样产生的新类,称之为派生类。通过继承,能够实现共性的抽取,从而实现代码的复用
我们将上述 Cat 和 Dog 的共性进行抽取,使用继承的思想来达到复用效果:
如上图所示,Dog 和 Cat 都继承了 Animal 类,其中,Animal 称为 父类(或 超类、基类),Dog 和 Cat 称之为 Animal 的 子类(或 派生类),继承之后,子类可以复用父类中的成员,子类在实现时只需关心自己新增的成员即可
在 Java 中若要表示类之间的继承关系,需要使用 extends 关键字:
修饰符 class 子类 extends 父类{
...
}
- public class Animal {
- String name;
- int age;
-
- public void eat() {
- System.out.println("吃饭");
- }
- }
Cat 类继承 Animal,还可以新增需要的成员变量或方法
- public class Cat extends Animal{
- public void miaow() {
- System.out.println("喵喵叫");
- }
- }
子类会将父类中的成员变量或成员方法继承到子类中
子类在继承父类之后,可以添加自己特有的成员
子类将父类中的方法和变量继承之后,那么,子类中能否直接访问父类中继承下来的成员呢?
当子类和父类不存在同名对象时
- public class A {
- int a = 10;
- }
- public class B extends A {
- int b = 20;
-
- public static void main(String[] args) {
- B b = new B();
- System.out.println(b.a);
- System.out.println(b.b);
- // System.out.println(b.c); // 编译失败,子类和父类中都不存在 c
- b.method();
- }
- }
运行结果:
子类自己有就访问自己的成员变量,若没有,就访问父类的成员变量
当子类和父类存在同名对象时
- public class A {
- int a = 10;
- int b = 20;
- }
- public class B extends A {
- int a = 100; // 与父类中的成员同名,且类型相同
- char b = 'b'; // 与父类中的成员同名,但类型不同
- public void method() {
- System.out.println("a: " + a + " b: " + b);
- }
-
- public static void main(String[] args) {
- B b = new B();
- b.method();
- }
- }
运行结果:
存在同名变量时,优先访问子类的成员变量
从上述两个示例中,可以看出:
若访问的成员变量子类中有,则访问子类自己的成员变量
若访问的成员变量子类中没有,则访问父类中继承下来的,若父类中也没有,则会编译报错
若访问的成员变量,父类子类中都有,则优先访问自己的成员变量
即 自己有就优先自己的,若没有再向父类中找
成员方法名不同
- public class A {
- int a = 10;
- int b = 20;
- public void methodA() {
- System.out.println("methodA...");
- }
- }
- public class B extends A {
- int a = 100; // 与父类中的成员同名,且类型相同
- char b = 'b'; // 与父类中的成员同名,但类型不同
- public void methodB() {
- System.out.println("methodB...");
- }
-
- public static void main(String[] args) {
- B b = new B();
- b.methodA();
- b.methodB();
- // b.methodC(); // 编译失败,在继承体系中没有发现 methodC()
- }
- }
运行结果:
当成员方法没有同名时,在子类方法中或通过子类对象访问方法时,若自己有,访问自己的,若自己没有,则在父类中找,若父类中也没有,则报错
成员方法名相同
- public class B extends A {
- int a = 100; // 与父类中的成员同名,且类型相同
- char b = 'b'; // 与父类中的成员同名,但类型不同
- public void methodA(int a) {
- System.out.println("methodA..." + a);
- }
-
- public static void main(String[] args) {
- B b = new B();
- b.methodA();
- b.methodA(10);
- }
- }
运行结果:
当子类方法名和父类方法名相同,但其参数列表不同时,就构成了重载,根据调用方法时传递的参数选择合适的方法访问,若不存在该方法,则报错
- public class B extends A {
- int a = 100; // 与父类中的成员同名,且类型相同
- char b = 'b'; // 与父类中的成员同名,但类型不同
-
- @Override
- public void methodA() {
- System.out.println("B methodA...");
- }
-
- public static void main(String[] args) {
- B b = new B();
- b.methodA();
- }
- }
当子类方法名和父类方法名相同,且其参数列表也相同时,就构成了重写,此时,会访问子类的方法
那么,当子类中存在和父类相同的成员时,如何在子类中访问父类同名成员呢?
当子类和父类中存在相同名称的成员时,若要在子类方法中访问父类同名成员时,是不能直接访问的。Java 提供了 super 关键字,super 的主要作用:在子类方法中访问父类成员
- public class B extends A {
- int a = 100; // 与父类中的成员同名,且类型相同
- char b = 'b'; // 与父类中的成员同名,但类型不同
-
- public void methodB() {
- System.out.println(super.a);
- System.out.println(super.b);
- }
-
- public static void main(String[] args) {
- B b = new B();
- b.methodB();
- }
- }
- public class B extends A {
- int a = 100; // 与父类中的成员同名,且类型相同
- char b = 'b'; // 与父类中的成员同名,但类型不同
-
- public void methodB() {
- super.methodA();
- System.out.println("methodB...");
- }
-
- public static void main(String[] args) {
- B b = new B();
- b.methodB();
- }
- }
需要注意的是:只能在非静态方法中使用 super
在子类对象构造时,会先调用父类构造方法,然后再执行子类构造方法
为 A 添加带一个参数的构造方法
- public class A {
- int a = 10;
- int b = 20;
- public A(int a) {
- System.out.println("a");
- }
- public void methodA() {
- System.out.println("methodA...");
- }
- }
编译报错:A 中没有可用的默认构造函数
为什么会报错呢?
这是因为在 A 中没有定义的构造方法时,编译器提供了默认的无参构造方法
子类 B 中也没有定义构造方法,编译器也提供了默认的无参构造方法,且在 B 的构造方法中第一行默认有隐含的 super() 调用父类的无参构造方法
当添加了带有一个参数的构造方法后,编译器也就不再提供无参的构造方法,此时也就会报错
我们可以在 A 中添加无参构造方法
或是在 B 的无参构造方法中调用 A 带有一个参数的构造方法
- public B(int a) {
- super(a);
- }
当父类中有多个构造方法时,就需要在子类构造方法中选择一个合适的父类构造方法调用,否则编译失败
为什么要先调用父类的构造方法呢?
子类对象中的成员是由两部分组成的:父类继承下来的成员 和 子类新增的成员
因此,在构造子类对象时,就需要先调用父类的构造方法,将从父类继承下来的成员构造完整,然后再调用子类自己的构造方法,将子类自己新增的成员初始化完整
在子类构造方法中,通过 super(...) 调用父类构造,且 super(...) 必须是子类构造函数中的第一条语句
因此,super(...) 只能在子类构造方法中出现一次,且必须在第一行
而若需要在构造方法中使用 this 关键字调用类中的其他构造方法,也必须在第一行使用,也就是说,super(...) 和 this(...) 不能同时出现
相同点
(1)都是 Java 中的关键字
(2)都可以在成员方法中用来访问成员变量和调用其他成员方法,都可以作为构造方法的第一条语句
(3)只能在类的非静态方法中使用,用来访问非静态成员方法和变量
(4)在构造方法中调用时,必须是构造方法的第一条语句,并且不能同时存在
不同点
(1)this 表示当前对象(实例方法的对象)的引用,而 super 表示子类对象重父类继承下来部分成员的引用
(2)在非静态成员方法中,this 用来访问本类的方法和属性,super 用来访问父类继承下来的方法和属性
(3)在构造方法中,this(...) 用于调用本类构造方法,super(...) 用于调用父类构造方法,两种调用不能同时在构造方法中出现
(4)构造方法中一定会存在 super(...) 的调用,但 this(...) 若没有写则没有
在前面的文章 Java 代码块-CSDN博客 中,我们学习了代码块,重点学习了 实例代码块 和 静态代码块,以及它们的执行顺序,接下来,我们就来看存在继承关系时,它们的执行顺序
- public class A {
- int a;
- int b;
- {
- a = 10;
- b = 20;
- System.out.println("A 构造代码块执行...");
- }
-
- static {
- System.out.println("A 静态代码块执行...");
- }
- public A(int a) {
- System.out.println("A 构造方法执行...");
- }
- public void methodA() {
- System.out.println("methodA...");
- }
- }
- public class B extends A {
- int a = 100; // 与父类中的成员同名,且类型相同
- char b = 'b'; // 与父类中的成员同名,但类型不同
-
- static {
- System.out.println("B 静态代码块执行...");
- }
-
- {
- System.out.println("B 构造代码块执行...");
- }
- public B(int a) {
- super(a);
- System.out.println("B 构造方法执行...");
- }
-
- public void methodA() {
- System.out.println("methodB...");
- }
-
- public static void main(String[] args) {
- B b1 = new B(10);
- System.out.println("---------------------");
- B b2 = new B(20);
- }
- }
运行结果:
通过运行结果,可以看到:
静态代码块先执行,并且只执行一次,在类加载阶段执行
父类静态代码块优先于子类静态代码块执行,且最早执行
当有对象创建时,才会执行实例代码块
父类实例代码块和父类构造方法先执行,然后再执行子类的实例代码块和子类的构造方法
为了实现封装性,Java 引入了访问限定符,主要限定:类或类中成员能否在类外或其他包中被访问
范围 | private | default | protected | public |
---|---|---|---|---|
同一个包中的同一个类 | √ | √ | √ | √ |
同一个包中的不同类 | √ | √ | √ | |
不同包中的子类 | √ | √ | ||
不同包中的非子类 | √ |
public:在任何类都可以访问
protected:在同一个包中的类或不同包中的子类可以访问
default:同一个包中的类可以访问
private:只有该类内部可以访问
当 A 和 B 位于同一个包中时:
- public class A {
- public int a;
- int b;
- protected int c;
- private int d;
- }
- public class B extends A {
- public void method() {
- super.a = 10;
- super.b = 20;
- super.c = 30;
- super.d = 40;
- }
- }
由于变量 d 是 A 私有的,即只能在 A 类中访问,因此,子类中不能直接访问
但是,父类中的 private 成员变量虽然不能在子类中直接访问,但是也继承到子类中了
子类可以通过父类提供的方法来进行修改
- public class A {
- public int a;
- int b;
- protected int c;
- private int d;
-
- public int getD() {
- return d;
- }
-
- public void setD(int d) {
- this.d = d;
- }
- }
- public class B extends A {
- public void method() {
- super.a = 10;
- super.b = 20;
- super.c = 30;
- super.setD(40);
- }
- }
当 A 和 B 位于不同包中:
- public class B extends A {
- public void method() {
- super.a = 10; // 父类中 public 修饰的成员在不同包子类中可以直接访问
- super.b = 20; // 父类中 protected 修饰的成员在不同包子类中可以直接访问
- // super.c = 30; // 编译报错,父类中默认访问权限修饰的成员在不同包子类中不能直接访问
- // super.d = 40; // 编译报错,父类中默认访问权限修饰的成员在不同包子类中不能直接访问
- }
- }
在 Java 中,支持的继承方式有:
(1)单继承
(2)多层继承
(3)不同类继承同一个类
(4)多继承(不支持)
Java 中不支持多继承
类是对现实事物的抽象,而当情况比较复杂时,涉及到的类也会比较多,类之间的关系也会比较复杂
但即使如此,我们并不希望类之间的继承层次太复杂,一般不希望出现超过三层的继承关系
此时,若想从语法上限制继承,就可以使用 final 关键字
final 可以用来修饰变量、成员方法以及类
当修饰变量时,表示该变量为常量(不可变)
当修饰方法时,表示该方法不能被重写
当修饰类时,表示该类不能被继承
与继承类似,组合也是一种表达类之间关系的方式,也能够达到代码重用的效果。
但组合并没有涉及到特殊的语法,仅仅是将一个类的实例作为另一个类的字段
继承表示的对象之间是 is a 的关系,如:猫 是 动物
组合表示的对象之间是 has a 的关系,如:电脑 有 键盘
组合:
- public class Person {
- private Student[] students;
- }
此时 Student 属于 Person 的一部分, 在 Person 中,可以复用 Student 中的属性和方法
继承:
- public class Student extends Person{
-
- }
Student 是 Person 的子类,继承了父类的成员,能够复用父类中的属性和方法
组合和继承都可以实现代码复用,是使用组合还是继承,需要根据具体的情况来进行选择
原文链接:https://blog.csdn.net/2301_76161469/article/details/142621979
作者:想要飞翔的天使
链接:http://www.javaheidong.com/blog/article/691574/94ca023d3e1b0cd2d6b4/
来源:java黑洞网
任何形式的转载都请注明出处,如有侵权 一经发现 必将追究其法律责任
昵称:
评论内容:(最多支持255个字符)
---无人问津也好,技不如人也罢,你都要试着安静下来,去做自己该做的事,而不是让内心的烦躁、焦虑,坏掉你本来就不多的热情和定力
Copyright © 2018-2021 java黑洞网 All Rights Reserved 版权所有,并保留所有权利。京ICP备18063182号-2
投诉与举报,广告合作请联系vgs_info@163.com或QQ3083709327
免责声明:网站文章均由用户上传,仅供读者学习交流使用,禁止用做商业用途。若文章涉及色情,反动,侵权等违法信息,请向我们举报,一经核实我们会立即删除!