草庐IT

12.类的关系——继承关系

一直流浪 2023-09-17 原文

1、继承关系

1.1 继承的概念

·继承机制是面向对象程序设计不可缺少的关键概念,是实现代码可重用的根基,是提高软件系统的可拓展性与可维护性的主要途径。

·所谓继承是指一个类的定义可以基于另外一个已经存在的类,即子类基于父类,从而实现父类代码的重用,子类能吸收已有类的属性和行为,并能拓展新的能力

形式:【访问权限修饰符】【修饰符】子类名 extends 父类名{子类体}

例:

//类:图形
public class Shape {

    private int x;

    private int y;

    public Shape() {

    }

    // 方法:绘图
    public void draw() {

    }

    public int getX() {
        return x;
    }

    public void setX(int x) {
        this.x = x;
    }

    public int getY() {
        return y;
    }

    public void setY(int y) {
        this.y = y;
    }

}

//类:圆形
public class Circle extends Shape {

    private int r;

    public Circle() {

    }

    // 方法:获取直径
    public int getDiameter() {

        return 2 * r;
    }

    public int getR() {
        return r;
    }

    public void setR(int r) {
        this.r = r;
    }

}

我们观察上面的代码发现,类Circle的定义中通过使用extends关键字继承了类Shape,此时,我们把Shape称为Circle的父类,反之,把Circle称为Shape的子类。而且,子类Circle新增了半径属性r,新增了获取直径的方法getDiameter()。

1.2 继承的作用

继承的主要作用是代码复用。代码表现为子类对象可以直接调用父类的属性和方法。

//类:使用场景
public class Client {

    public static void main(String[] args) {

        Circle circle = new Circle();

        // 访问父类的属性和方法
        circle.setX(1);
        circle.setY(1);

        circle.draw();

        // 访问子类的属性和方法
        circle.setR(9);

        circle.getDiameter();

    }

}

1.3 构造方法与继承

子类构造方法总是先调用父类构造方法。

默认情况下,调用父类无参构造方法。

可以在子类构造方法的第一行,使用super关键字调用父类任意一个构造方法。

1.3.1 继承中构造方法的调用

子类构造方法总是先调用父类构造方法。

默认情况下,调用父类无参构造方法。

可以在子类构造方法的第一行,使用super关键字调用父类任意一个构造方法。

//类:图形
public class Shape {

    public int x;

    public int y;

    public Shape() {

        System.out.println("这是父类的无参构造方法!");

    }

    public Shape(int x, int y) {

        this.x = x;
        this.y = y;

        System.out.println("这是父类的有参构造方法!");
    }

    public void draw() {

    }

    public int getX() {
        return x;
    }

    public void setX(int x) {
        this.x = x;
    }

    public int getY() {
        return y;
    }

    public void setY(int y) {
        this.y = y;
    }

}

//类:圆形
public class Circle extends Shape {

    private int r;

    public Circle() {

        // super();
        // super(0, 0);

        System.out.println("这是子类的无参构造方法!");

    }

    // 方法:获取直径
    public int getDiameter() {

        return 2 * r;
    }

    public int getR() {
        return r;
    }

    public void setR(int r) {
        this.r = r;
    }

}

//测试
public class Client {

    public static void main(String[] args) {

        Circle circle = new Circle();

    }

}

//输出
这是父类的无参构造方法!
这是子类的无参构造方法!

1.3.2 子类调用父类构造方法的原因

Java 语言中,要求子类有责任保证它所继承的父类尽快进入到一个稳定、完整的状态中。如果没有这个约束,那么子类的某个继承自父类的方法可能会使用到父类中的一些变量,而这些变量并没有进行初始化,从而产生一些难以预料的后果。

1.3.3 子类构造方法的执行详解

必须将调用父类构造方法的这条语句放在子类构造方法的第一条语句位置。如果第一条语句没有调用父类的构造方法,系统将会自动地在这个位置上插入一条调用父类默认构造方法的语句,即super(); 由于默认的构造方法不带参数,所以,如果在父类中定义了带参数的构造方法,而没有定义不带参数的构造方法将会出现编译错误。这也正是建议大家在定义带参数的构造方法时,一定要定义一个不带参数的构造方法的原因所在。 父类中那些带参数的构造方法,子类将不会自动地调用它们,必须人工地将调用它们的语句写入子类的构造方法中。

2、this关键字

this关键字其实与继承没有关系,之所以放在这里学习,是因为this和super的作用非常类似。都有两大作用,即调用属性和方法,调用构造方法。区别是,this调用的都是本类的,super调用的都是父类的。

2.1 this关键字用途

2.1.1 this关键字代表类自身

使用this关键字在自身构造方法内部引用其它构造方法。 在一个类的构造方法内部,也可以使用this关键字引用其它的构造方法,这样可以降低代码的重复,也可以使所有的构造方法保持统一,这样方便以后的代码修改和维护,也方便代码的阅读。

例:

 public class Demo {

    private int a;

    private int b;

    public Demo() {

        // this引用构造方法
        this(1, 2);

    }

    public Demo(int a, int b) {
        super();
        this.a = a;
        this.b = b;
    }

    public int getA() {
        return a;
    }

    public void setA(int a) {
        this.a = a;
    }

    public int getB() {
        return b;
    }

    public void setB(int b) {
        this.b = b;
    }

}

2.1.2 this关键字代表自身类的对象

-直接使用this

-使用this关键字引用成员变量

-使用this关键字引用成员方法

注意:this关键字必须放在非静态方法里面

this代表自身对象 在一个类的内部,也可以使用this代表自身类的对象,或者换句话说,每个类内部都有一个隐含的成员变量,该成员变量的类型是该类的类型,该成员变量的名称是this

public class Demo {

    private Demo instance;

    public Demo() {

        instance = this;

    }

}

this引用成员变量 在一个类的方法或构造方法内部,可以使用“this.成员变量名”这样的格式来引用成员变量名,常常用来区分同名的成员变量和局部变量。

public class Demo {

    private int a;

    private int b;

    public Demo() {

    }

    public int getA() {
        return a;
    }

    public void setA(int a) {
        // this引用成员变量
        this.a = a;
    }

    public int getB() {
        return b;
    }

    public void setB(int b) {
        // this引用成员变量
        this.b = b;
    }

}

this引用成员方法 在一个类的内部,成员方法之间的互相调用时也可以使用“this.方法名(参数)”来进行引用。

public class Demo {

    public Demo() {

    }

    public void method1() {

        //this引用成员方法
        this.method2();

    }

    public void method2() {

    }

}

3、super关键字

super关键字的使用方式与this很类似,不过super一定是在继承关系中使用,指的是父类或者父类的对象。

3.1 super关键字用途

3.1.1 super代表父类

在子类构造方法中要调用父类的构造方法,需要注意:super语句只能出现在子类构造方法体的第一行。

3.1.2 super调用父类成员变量

当子类方法体中的局部变量或者子类的成员变量与父类成员变量同名时,即子类局部变量覆盖父类成员变量时,用“super.成员变量名”来引用父类成员变量

3.1.3 super调用父类的成员方法

当子类的成员方法覆盖了父类的成员方法时,也就是子类和父类有完全相同的方法定义(方法体可以不同),此时,用“super.方法名(参数列表)”的方式访问父类的方法。

3.2 super与this的区别

this通常指代当前对象,super通常指代父类。

4、方法覆盖

继承的作用就是复用,即子类直接使用父类的属性和方法。

然而,有些时候,子类希望修改父类的方法的方法体,可以怎么做呢?

第一种做法是子类创建一个不同名字的新方法,实现新的逻辑,然而,这种做法会导致子类依然包含父类中的那个方法,却不应该使用,破坏封装性。 第二种我们希望子类中的方法依然和父类方法的声明形式一样,但是具体方法体却不同,这种做法就叫做方法覆盖

建议:抽象这章从头到尾使用一个例子扩展,一直到抽象部分。

4.1方法覆盖定义及原则

4.1.1方法覆盖的定义

子类可以重写父类中某一个方法,称为方法覆盖,也称方法重写,是继承中非常重要的知识点。如果子类需要修改从父类继承到的方法的方法体,就可以使用方法覆盖。

4.1.2 方法覆盖的原则

同名 同参 同返回值 访问权限不能缩小

方法覆盖例子:

//父类

public class Animal {
    String name;
    Integer age;

    public Animal() {

    }

    public Animal(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    void run() {
        System.out.println("一只" + this.age + "岁的" + this.name + "在奔跑!~~");
    }

    void sound() {
        System.out.println("一只" + this.age + "岁的" + this.name + "在吼叫!~~");
    }
}

//子类:

public class Tiger extends Animal {
    String color;

    public Tiger(String name, Integer age, String color) {
        super(name, age);

        this.color = color;
    }

    void hunt() {
        System.out.println("一只" + this.color + "的" + this.name + "在捕猎!~~");
    }

    void sound() {
        System.out.print("我是一只老虎~");
        super.sound();
    }
}
  此处可以提使用tigerSound()方法,而不使用方法覆盖的方式,sound()会被tiger类继承,这违反了面向对象的封装性特征。对象封装了属性和方法,对象的属性和方法都是该对象可以使用的。因此,上面的方法虽然实现了新算法,却存在严重漏洞,破坏了对象的封装性。如果子类需要修改自父类继承到的新方法,可以使用“方法覆盖”来完成。在子类中,声明一个与父类同名、同参、同返回值类型、访问权限不缩小的方法,就可以将父类中的方法覆盖。子类对象调用该方法,将自动绑定到子类覆盖后的新方法。

示例代码-测试类

public class Tester {

    public static void main(String[] args) {

        Tiger t = new Tiger("东北虎", 1, "白色");

        t.sound();

    }
}

有关12.类的关系——继承关系的更多相关文章

  1. ruby-on-rails - 使用 config.threadsafe 时从 lib/加载模块/类的正确方法是什么!选项? - 2

    我一直致力于让我们的Rails2.3.8应用程序在JRuby下正确运行。一切正常,直到我启用config.threadsafe!以实现JRuby提供的并发性。这导致lib/中的模块和类不再自动加载。使用config.threadsafe!启用:$rubyscript/runner-eproduction'pSim::Sim200Provisioner'/Users/amchale/.rvm/gems/jruby-1.5.1@web-services/gems/activesupport-2.3.8/lib/active_support/dependencies.rb:105:in`co

  2. java - 从 JRuby 调用 Java 类的问题 - 2

    我正在尝试使用boilerpipe来自JRuby。我看过guide从JRuby调用Java,并成功地将它与另一个Java包一起使用,但无法弄清楚为什么同样的东西不能用于boilerpipe。我正在尝试基本上从JRuby中执行与此Java等效的操作:URLurl=newURL("http://www.example.com/some-location/index.html");Stringtext=ArticleExtractor.INSTANCE.getText(url);在JRuby中试过这个:require'java'url=java.net.URL.new("http://www

  3. 没有类的 Ruby 方法? - 2

    大家好!我想知道Ruby中未使用语法ClassName.method_name调用的方法是如何工作的。我头脑中的一些是puts、print、gets、chomp。可以在不使用点运算符的情况下调用这些方法。为什么是这样?他们来自哪里?我怎样才能看到这些方法的完整列表? 最佳答案 Kernel中的所有方法都可用于Object类的所有对象或从Object派生的任何类。您可以使用Kernel.instance_methods列出它们。 关于没有类的Ruby方法?,我们在StackOverflow

  4. ruby - Rails 关联 - 同一个类的多个 has_one 关系 - 2

    我的问题的一个例子是体育游戏。一场体育比赛有两支球队,一支主队和一支客队。我的事件记录模型如下:classTeam"Team"has_one:away_team,:class_name=>"Team"end我希望能够通过游戏访问一个团队,例如:Game.find(1).home_team但我收到一个单元化常量错误:Game::team。谁能告诉我我做错了什么?谢谢, 最佳答案 如果Gamehas_one:team那么Rails假设您的teams表有一个game_id列。不过,您想要的是games表有一个team_id列,在这种情况下

  5. ruby-on-rails - Rails 单表继承 : How to override the value written to the type field - 2

    在我的系统中,我已经定义了STI。Dog继承自Animal,在animals表中有一个type列,其值为"Dog"。现在我想让SpecialDog继承自dog,只是为了在某些特殊情况下稍微修改一下行为。数据还是一样。我需要通过SpecialDog运行的所有查询,以返回数据库中类型为Dog的值。我的问题是因为我有一个type列,rails将WHERE"animals"."type"IN('SpecialDog')附加到我的查询中,所以我不能获取原始的Dog条目。所以我想要的是以某种方式覆盖rails在通过SpecialDog访问数据库时使用的值,使其表现得像Dog。有没有办法覆盖用于类型

  6. [工业相机] 分辨率、精度和公差之间的关系 - 2

    📢博客主页:https://blog.csdn.net/weixin_43197380📢欢迎点赞👍收藏⭐留言📝如有错误敬请指正!📢本文由Loewen丶原创,首发于CSDN,转载注明出处🙉📢现在的付出,都会是一种沉淀,只为让你成为更好的人✨文章预览:一.分辨率(Resolution)1、工业相机的分辨率是如何定义的?2、工业相机的分辨率是如何选择的?二.精度(Accuracy)1、像素精度(PixelAccuracy)2、定位精度和重复定位精度(RepeatPrecision)三.公差(Tolerance)四.课后作业(Post-ClassExercises)视觉行业的初学者,甚至是做了1~2年

  7. ruby - 为什么当我调用类的实例方法时,初始化不显示为方法? - 2

    我正在写一篇关于在Ruby中几乎一切都是对象的博客文章,我试图通过以下示例来展示这一点:classCoolBeansattr_accessor:beansdefinitialize@bean=[]enddefcount_beans@beans.countendend所以从类中我们可以看出它有4个方法(当然,除非我错了):它可以在创建新实例时初始化一个默认的空bean数组它可以计算它有多少个bean它可以读取它有多少个bean(通过attr_accessor)它可以向空数组写入(或添加)更多bean(也通过attr_accessor)但是,当我询问类本身它有哪些实例方法时,我没有看到默认

  8. ruby-on-rails - Rails 中同一个类的多个关联的最佳实践? - 2

    我认为我的问题最好用一个例子来描述。假设我有一个名为“Thing”的简单模型,它有一些简单数据类型的属性。像...Thing-foo:string-goo:string-bar:int这并不难。数据库表将包含具有这三个属性的三列,我可以使用@thing.foo或@thing.bar之类的东西访问它们。但我要解决的问题是当“foo”或“goo”不再包含在简单数据类型中时会发生什么?假设foo和goo代表相同类型的对象。也就是说,它们都是“Whazit”的实例,只是数据不同。所以现在事情可能看起来像这样......Thing-bar:int但是现在有一个新的模型叫做“Whazit”,看起来

  9. ruby - 从外部访问类的实例变量 - 2

    我理解(我认为)Ruby中类变量和类的实例变量之间的区别。我想知道如何从该类外部访问该类的实例变量。从内部(即在类方法中而不是实例方法中),它可以直接访问,但是从外部,有没有办法做MyClass.class.[@$#]variablename?我没有任何具体原因要这样做,只是学习Ruby并想知道是否可行。 最佳答案 classMyClass@my_class_instance_var="foo"class上述yield:>>foo我相信Arkku演示了如何从类外部访问类变量(@@),而不是类实例变量(@)。我从这篇文章中提取了上述内

  10. ruby-on-rails - Resque - 类的未定义方法 'perform' - 2

    我目前对后台队列不太满意。我正在尝试让Resque工作。我已经安装了redis和Resquegem。Redis正在运行。一个worker正在运行(rakeresque:workQUEUE=simple)。使用Web界面,我可以看到工作人员正在运行并等待工作。当我运行“rakeget_updates”时,作业已排队但失败了。我已经用defself.perform和defperform试过了。发条.raketask:get_updates=>:environmentdoResque.enqueue(GetUpdates)end类文件(app/workers/get_updates.rb)c

随机推荐