JavaSE语法(8)——详细解读Java中的继承、组合、方法重写

目录

前言

✏️继承

1.继承的引入

2. 继承的概念及语法

3.访问父类成员

3.1 子类中访问父类的成员变量

3.2 子类中访问父类的成员方法

4.protected访问修饰符、final关键字以及方法的重写

4.1 protected访问修饰符

4.2 final关键字

4.3 方法重写

5.super关键字

5.1 super在内存中的体现

5.2 super与this的异同点

6.子类构造方法

7.代码块在继承中的执行顺序

8.继承方式

✏️组合


前言

        我的JavaSE语法专栏地址👇,可以看看哦,可能你会有不一样的收获。JavaSE_会飞的阿喵的博客-CSDN博客https://blog.csdn.net/che__dan/category_11970438.html?spm=1001.2014.3001.5482

        继承和多态可以说是Java这种面向对象语言的精髓了,本篇会详细的介绍继承,让你对继承有进一步的认识,多态就留在下一篇来介绍咯。



✏️继承


1.继承的引入

        Java是面向对象语言,Java中使用类来对现实世界的实体来进行描述,类经过实例化之后的对象来表示现实中的实体,但是事物之间可能会有一些联系。

        现在我们用Java对 狗与猫 进行描述。有以下代码:

public class Cat {
    String name;
    String color;
    int age;

    public void eat(){
        System.out.println("俺在吃饭!!!");
    }

    public void mew() {
        System.out.println("喵喵喵~");
    }

}

----------------------------------Dog.java-----------------------------------------------
public class Dog {
    String name;
    String color;
    int age;

    public void eat(){
        System.out.println("俺在吃饭!!!");
    }

    public void Bark() {
        System.out.println("旺旺旺~");
    }

}

        容易发现,上面的代码有点累赘,重复的太多了,只有 “叫声” 的方法不一样。那能否将这些共性抽取呢?面向对象思想中提出了继承的概念,专门用来进行共性抽取,实现代码复用。


2. 继承的概念及语法

        继承:是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特 性的基础上进行扩展,增加新功能,这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构, 体现了由简单到复杂的认知过程。继承主要解决的问题是:共性的抽取,实现代码复用。

        语法:表示继承的关系要用到extends关键字。

修饰符 class 子类 extends 父类 {
// ...
}

        那么上面的“狗”与“猫”类可以继承“动物”这个类:

        上述图示中,Dog和Cat都继承了Animal类,其中:Animal类称为父类/基类或超类,Dog和Cat可以称为Animal的子类/派生类继承之后,子类可以复用父类中成员,子类在实现时只需关心自己新增加的成员即可。

        看代码:

public class Animal {
    String name;
    String Color;
    int age;

    public void eat(){
        System.out.println("俺在吃饭!!!");
    }
}


--------------------------------Cat.java-------------------------------------------------
public class Cat extends Animal{

    public void mew() {
        System.out.println("喵喵喵~");
    }

}
--------------------------------Dog.java-------------------------------------------------
public class Dog extends Animal{

    public void Bark() {
        System.out.println("旺旺旺~");
    }

}

        子类会将父类中的成员变量或者成员方法继承到子类中,这时Cat与Dog类可以访问Animal类的成员。

public class Main {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.name = "小白";
        dog.color = "白色";
        dog.age = 2;
        System.out.println(dog.name);
        System.out.println(dog.color);
        System.out.println(dog.age + "岁");
        dog.eat();
        dog.Bark();
    }
}

结果:
    小白
    白色
    2岁
    俺在吃饭!!!
    旺旺旺~

3.访问父类成员

3.1 子类中访问父类的成员变量

  • 当子类和父类 不存在 同名成员变量

public class Father {
    int a;
    int b;
    int c;
}
------------------------------------Son.java-----------------------------------------------
public class Son extends Father {
    int d;
    public void Test(){
        a = 40; //访问从父类中继承下来的 a
        b = 50; //访问从父类中继承下来的 b
        c = 60; //访问从父类中继承下来的 c
        d = 70; //访问子类自己的d
    }
}

--

  • 子类和父类成员变量同名
public class Father {
    int a = 12;
    String b = "我是父类的b";
    int c;
}
---------------------------------------Son.java------------------------------------------
public class Son extends Father {
    char a; //与父类中成员 b 同名,但 类型不同.
    String b; //与父类中成员 c 同名,且类型相同.

    public void print(){
        a = 'a'; // 访问父类继承的a,还是子类自己新增的a?
        b = "我是子类的b"; // 访问父类继承的b,还是子类自己新增的b?
        c = 200; // 子类没有c,访问的肯定是从父类继承下来的c
        System.out.println("a:" + a);
        System.out.println("b:" + b);
        System.out.println("c:" + c);

    }
}
---------------------------------------Main.java-----------------------------------------
public class Main {
    public static void main(String[] args) {
        Son son = new Son();
        son.print();
    }
}
结果:
a:a
b:我是子类的b
c:200

可以看到:

(1)如果访问的成员变量子类中有,优先访问自己的成员变量。

(2)如果访问的成员变量子类中无,则访问父类继承下来的,如果父类也没有定义,则编译报错。

(3)如果访问的成员变量与父类中成员变量同名(尽管是类型不同),则优先访问自己的。

        其实就是 就近原则,自己有优先自己的,如果没有则向父类中找。

3.2 子类中访问父类的成员方法

  • 成员方法名字不同
public class Father {
    public void A () {
        System.out.println("Father类中的A方法!!!");
    }
}
--------------------------------Son.java-------------------------------------------------
public class Son extends Father {
    public void B(){
        System.out.println("Son类中的B方法!!!");
    }
    public void C(){
        System.out.println("Son类中的C方法!!!");
    }

    public void test() {
        A(); //访问父类中的A()方法
        B(); //访问子类中的B()方法
        C(); //访问子类中的C()方法
    }
}
--------------------------------Main.java------------------------------------------------
public class Main {
    public static void main(String[] args) {
        Son son = new Son();
        son.test();
    }
}
结果:
Father类中的A方法!!!
Son类中的B方法!!!
Son类中的C方法!!!

总结:

        成员方法没有同名时,在子类方法中或者通过子类对象访问方法时,则优先访问自己的,自己没有时再到父类中找,如果父类中也没有则报错。

--

  • 成员方法名字相同
public class Father {
    public void A () {
        System.out.println("Father类中的A方法!!!");
    }

    public void B(){
        System.out.println("Father类中的B方法!!!");
    }

}
----------------------------------Son.java-----------------------------------------------
public class Son extends Father {

    public void A(int x){
        System.out.println("Son类的 A(int x)方法!!!");
    }
    public void C(){
        System.out.println("Son类中的C方法!!!");
    }

    public void test() {
        A();    //没有传参数,访问的时父类的A方法
        A(4); //传了参数,访问的是子类的A方法
        B();    //子类没有,访问的是父类的方法
        C();    //永远访问到的都是子类中的C(),因为父类没有
    }
}
--------------------------------------Main.java------------------------------------------
public class Main {
    public static void main(String[] args) {
        Son son = new Son();
        son.test();
    }
}


结果:
Father类中的A方法!!!
Son类的 A(int x)方法!!!
Father类中的B方法!!!
Son类中的C方法!!!

总结:        

  • 通过 子类的对象(实例) 访问 父类与子类中 不同名方法时,优先在子类中找,找到则访问,否则在父类中找,找到则访问,如果都没有找到就报错。
  • 通过 子类的对象(实例) 访问 父类与子类 同名方法时,如果父类和子类同名方法的参数列表 不同(也就是重载),根据调用方法适传递的参数选择合适的方法访问,如果没有找到则报错。(这就是跟方法重载一样的)
  • 这里用到了方法重载子类可以重载父类的方法。 (其实还有方法重写,重写就是方法名、参数列表相同,并且不能修改,这两者本质是不同的)。

4.protected访问修饰符、final关键字以及方法的重写

4.1 protected访问修饰符

        (在这篇文章👇中介绍了其它的访问修饰符)

JavaSE语法(6)——【类和对象(类的内存模型、访问限定符public、private等、包的导入……)】_会飞的阿喵的博客-CSDN博客https://blog.csdn.net/Che__dan/article/details/127669373?spm=1001.2014.3001.5502

        

         父类中不同访问权限的成员,在子类中的可见性又是什么样子的呢?

案例:

demo1包:

public class FatherDemo1 {

    public int a;

    int b;

    private int c;

    protected int d;

}
-------------------------------SonDemo1,java---------------------------------------------

public class SonDemo1 extends FatherDemo1 {
    public void test () {

        a = 1; //父类中public成员在相同包子类中可以直接访问

        b = 2; //父类中默认访问权限修饰的成员在相同包子类中可以直接访问

        c = 3; //编译报错,父类private成员在相同包子类中不可见

        d = 4; //父类中protected成员在相同包子类中可以直接访问

        System.out.println("a:" + a + " b:" + b + " c:" + c + " d:" + d);

    }
}
-------------------------------Main.java------------------------------------------------
public class Main {
    public static void main(String[] args) {
        SonDemo1 son = new SonDemo1();
        son.test();
    }
}

结果:

        结果显而易见,在同一个包中就 private 不能被访问。

--

demo2包:

import demo1.FatherDemo1;//导包

public class SonDemo2 extends FatherDemo1 {
    public void test () {
        a = 1; //父类中public成员在 不同包子类中 可以直接访问

        b = 2; //编译报错,父类中默认访问权限修饰的成员在 不同包子类中 不可以直接访问

        c = 3; //编译报错,父类private成员在 不同包子类中 不可见

        d = 4; //父类中protected成员在 不同包子类中 可以直接访问
    }
}

---------------------------------Main.java-------------------------------------------------
public class Main {
    public static void main(String[] args) {
        SonDemo2 son = new SonDemo2();
        son.test();
    }
}

结果:

        在不同包中的子类 (父类在另一个包中),protectde修饰符能被访问,而默认修饰符与private都不能被访问。

--

demo3包:

import demo1.FatherDemo1;
public class SonDemo3 {
    public void test () {
        FatherDemo1 fatherDemo1 = new FatherDemo1();

        fatherDemo1.a = 1; //父类中public成员在 不同包的非子类中 可以直接访问

        fatherDemo1.d = 4; //编译报错,父类中protected成员在 不同包的非子类中 不能直接访问

        fatherDemo1.b = 2; //编译报错,父类中默认访问权限修饰的成员在 不同包的非子类中 不可以直接访问

        fatherDemo1.c = 3; //编译报错,父类private成员在 不同包的非子类中 不可见
        System.out.println("a:" + fatherDemo1.a + " b:" + fatherDemo1.b + " c:" + fatherDemo1.c + " d:" + fatherDemo1.d);
    }
}
-------------------------------------Main.java---------------------------------------------
public class Main {
    public static void main(String[] args) {
        SonDemo3 son = new SonDemo3();
        son.test();
    }
}

结果:

         这里与上一个案例的区别是SonDemo3没有继承父类了,当一个包中的类引用了另一个包中的类,protectde修饰符是不能被访问的(前提是该类不是引用类的子类)。

        如果是在同一个包中引用(两个类在一个包),就算是没有继承,protectde修饰符也能被访问!

4.2 final关键字

        final 跟C语言中的 const 相似,修饰一个变量的时候,这个变量就变成了常量,也就是不能再修改了。

        final可以修饰:成员变量(方法内的局部变量也行)、类、方法

--

  • 修饰变量,表示常量:
public static void main(String[] args) {
    final int a = 10;
    a = 11;
    System.out.println(a);
}

结果:

- -

  •  修饰类(则该类不能被继承):
final public class Father {

}
----------------------------------Son.java------------------------------------------------
public class Son extends Father{

}
----------------------------------Main.java------------------------------------------------
public class Main {
    public static void main(String[] args) {

        Son son = new Son();

    }
}

 结果:

- -

  • 修饰方法,表示该方法不能被重写👇。

4.3 方法重写

        重写是 子类 对 父类的非静态、非private修饰、非final修饰的方法进行重新编写, 返回值形参不能改变。即外壳不变,核心重写。

--

  • 子类在重写父类的方法时,方法名、形参(顺序、类型)、返回值类型  要保持一致。
public class Animal {

    String name;
    int age;
    //……

    public void eat () {
        System.out.println("在吃饭!!!");
    }

}
----------------------------------Cat.java-----------------------------------------------
public class Cat extends Animal{

    //重写父类的eat方法
    public void eat () {
        System.out.println("在吃鱼!!!");
    }

}
----------------------------------Dog.java-----------------------------------------------
public class Dog extends Animal {

    //重写父类的eat方法
    public void eat () {
        System.out.println("在吃骨头!!!");
    }

}
----------------------------------Main.java----------------------------------------------

public class Main {
    public static void main(String[] args) {

        Animal animal = new Animal();
        Dog dog = new Dog();
        Cat cat = new Cat();

        animal.eat();
        dog.eat();
        cat.eat();
    }
}

结果:

在吃饭!!!
在吃骨头!!!
在吃鱼!!!

        重写的好处在于子类可以根据需要,定义特定于自己的行为。这里案例:狗吃饭是吃骨头,猫是吃🐟。我们需要根据子类的具体情况来重写方法。

--

  • 被重写的方法返回值类型可以不同,但是必须是具有父子关系的,就是返回值是 类 类型,两个类为继承和被继承关系。
public class Animal {

    String name;
    int age;

    public Animal WhatAnimal(){
        return this;
    }

}
----------------------------------Cat.java-----------------------------------------------
public class Cat extends Animal{

    public Cat WhatAnimal(){

        return this;

    }

}
----------------------------------Dog.java-----------------------------------------------
public class Dog extends Animal {

    public Dog WhatAnimal(){

        return this;

    }

}
----------------------------------Main.java----------------------------------------------
public class Main {
    public static void main(String[] args) {

        Animal animal = new Animal();
        Dog dog = new Dog();
        Cat cat = new Cat();
        
        //返回 那个类的 包名 + 类名
        System.out.println(dog.WhatAnimal().getClass().getName());
        System.out.println(cat.WhatAnimal().getClass().getName());

    }
}

结果:

demo9.Dog
demo9.Cat

        (上面用到了反射的知识,不用理会,后面会介绍,这里就当看一个乐子)这里就简单的举了个例子,子类重写方法的返回值都是Animal的子类类型,即返回值类型不同,但是程序能通过。

--

  • 访问权限不能比父类中被重写的方法的访问权限更低。

例如:如果父类方法被public修饰,则子类中重写该方法就不能声明为 protected。

public class Father {

    public void test () {
        System.out.println("Father类的text()!!!");
    }

}
-------------------------------Sno.java-------------------------------------------------
public class Son extends Father{
    protected void test(){
        System.out.println("Son类的text()!!!");
    }

}
-------------------------------Main.java-----------------------------------------------
public class Main {
    public static void main(String[] args) {
        Son son = new Son();
        son.test();
    }
}

结果:

--

  • 父类中被static、private、final修饰的方法不能被重写。

        private这里就不举例了,private修饰的方法,外面根本访问不到,private本身就是描述“私有的”不可能让子类“看到”,就无法重写。如果子类写了一个一摸一样的方法,就不叫重写了,就是重新定义一个方法罢了。

 --

  • 重写与重载的区别


5.super关键字

        上文演示了很多示例,知道了 子类与父类成员同名的时候最先调用的是子类的成员,那有没有方法可以访问同名成员中的父类那一个呢?

        如果要在子类方法中访问父类同名成员时,直接访问是无法做到的,Java提供了super关键字,该关键字主要作用:在子类方法中访问父类的成员。
 

public class Father {

    int a;
    int b;

    public void A () {
        System.out.println("Father类中的A方法!!!");
    }

    public void B(){
        System.out.println("Father类中的B方法!!!");
    }
}
----------------------------------Son.java-----------------------------------------------
public class Son extends Father {

    int a;   // 与父类中成员变量同名且类型相同
    String b;// 与父类中成员变量同名但类型不同

    // 与父类中A()构成重载
    public void A(int x){
        System.out.println("Son类的 A(int x)方法!!!");
    }

    // 与父类中B()构成重写
    public void B(){
        System.out.println("Son类中的B方法!!!");
    }


    public void test(){

        // 访问父类的成员变量时,需要借助super关键字
        super.a = 200;
        System.out.println(super.a);
        super.b = 200;
        System.out.println(super.b);

        // 对于同名的成员变量,直接访问时,访问的都是子类的
        a = 100;
        System.out.println(a);
        b = "我是子类的b";
        System.out.println(b);



        // 父类和子类中构成重载的方法,直接可以通过参数列表区分清访问父类还是子类方法
        A(); // 没有传参,访问父类中的A()
        A(40);// 传递int参数,访问子类中的A(int)

        // 如果在子类中要访问重写的基类方法,则需要借助super关键字
        B();
        super.B();
    }
}
----------------------------------Main.java----------------------------------------------
public class Main {
    public static void main(String[] args) {
        Son son = new Son();
        son.test();
    }
}


结果:

200
200
100
我是子类的b
Father类中的A方法!!!
Son类的 A(int x)方法!!!
Son类中的B方法!!!
Father类中的B方法!!!

总结:super是获取到子类对象中从父类继承下来的部分。

--

重要:super关键字只能在非静态方法中使用。

public class Father {

    int a;
    int b;

}
----------------------------------Son.java------------------------------------------------
public class Son extends Father {

    int a;   // 与父类中成员变量同名且类型相同
    String b;// 与父类中成员变量同名但类型不同

    public static void test2(){
        super.a = 100; //报错
    }
}


5.1 super在内存中的体现

        案例:

public class Father {

    int a;
    int b;

    public void A () {
        System.out.println("Father类中的A方法!!!");
    }

    public void B(){
        System.out.println("Father类中的B方法!!!");
    }

}
----------------------------------Son.java-----------------------------------------------
public class Son extends Father {

    int a;   // 与父类中成员变量同名且类型相同
    String b;// 与父类中成员变量同名但类型不

    // 与父类中A()构成重载
    public void A(int x){
        System.out.println("Son类的 A(int x)方法!!!");
    }

    // 与父类中B()构成重写
    public void B(){
        System.out.println("Son类中的B方法!!!");
    }

}
----------------------------------Main.java----------------------------------------------
public class Main {
    public static void main(String[] args) {
        Son son = new Son();
    }
}

        super代表的是子类的父类型特征,不是父类的引用。Object类是所有类的父类,只不过被隐藏了,这里可以不考虑。(Object类具体的在下一篇介绍)

        Son它是Father的子类,先初始化Father类型的特征(最先初始化的是Object的特征),再初始化Son自己的特征,Son的super代表这Father的特征。

5.2 super与this的异同点

        相同:

  • 只能在类的非静态方法中使用,用来访问非静态成员方法和字段。
  • 在构造方法中调用时,必须是构造方法中的第一条语句,并且不能同时存在。

        不同:

  • this是 当前对象的引用,super相当于是子类对象中从 父类继承下来部分成员的引用
  • 在非静态成员方法中,this用来访问本类的方法和属性,super用来访问父类继承下来的方法和属性。
  • 在构造方法中:this()用于调用本类构造方法,super()用于调用父类构造方法,两种调用不能同时在构造方法中出现。
  • 构造方法中一定会存在super()的调用,没有写编译器也会增加,但是this()用户不写则没有。


6.子类构造方法

       JavaSE语法(6)——【类和对象(类的内存模型、访问限定符public、private等、包的导入……)】_会飞的阿喵的博客-CSDN博客https://blog.csdn.net/Che__dan/article/details/127669373?spm=1001.2014.3001.5502        在上面👆的文章中我介绍过 this在构造方法中的应用,就是 this() 表示本类的构造方法;同理,在这里 super() 表示父类的构造方法。 

        new一个子类对象时,需要先调用父类的构造方法,然后执行子类的构造方法。

public class Father {

    public Father() {
        System.out.println("Father类的构造方法!!!");
    }

}
----------------------------------Son.java------------------------------------------------
public class Son extends Father {

    public Son(){
        super();//父类的构造方法
        System.out.println("Son类的构造方法!!!");
    }
}
----------------------------------Main.java------------------------------------------------
public class Main {
    public static void main(String[] args) {
        Son son = new Son();
    }

}

结果:
Father的构造方法!!!
Son类的构造方法!!!

--

  • 没有写super()时,编译器会自动添加,而且super()必须是子类构造方法中第一条语句,并且只能出现一次。
public class Father {

    public Father() {
        System.out.println("Father类的构造方法!!!");
    }

}
----------------------------------Son.java------------------------------------------------
public class Son extends Father {

    public Son(){
        System.out.println("Son类的构造方法!!!");
    }
}
----------------------------------Main.java------------------------------------------------
public class Main {
    public static void main(String[] args) {
        Son son = new Son();
    }

}

结果:
Father的构造方法!!!
Son类的构造方法!!!

-- 

  • 若父类显式定义无参 或者 为默认的构造方法,则在 子类构造方法 第一行默认有隐含的super()调用,即调用父类(基类)构造方法。

--

  • 如果父类构造方法是带有参数的,此时需要为子类显式定义构造方法,并在子类构造方法中选择合适的父类构造方法调用,否则编译失败。
public class Father {

    String f_name;
    int f_age;

    public Father(String f_name,int f_age) {
        this.f_name = f_name;
        this.f_age = f_age;
        System.out.println("Father类的构造方法!!!");
    }

}
----------------------------------Son.java-----------------------------------------------
public class Son extends Father {

    String s_name;
    int s_age;

    public Son(String f_name,int f_age,String s_name,int s_age){
        super(f_name,f_age);
        this.s_name = s_name;
        this.s_age = s_age;
        System.out.println("Son类的构造方法!!!");
    }

    public void print(){
        System.out.println("f_name:" + f_name +" f_age:" + f_age + " s_name:" + s_name + " s_age:" + s_age);
    }

}
----------------------------------Main.java----------------------------------------------
public class Main {
    public static void main(String[] args) {
        Son son = new Son("张三",40,"张小",19);
        son.print();
    }

}

结果:
Father类的构造方法!!!
Son类的构造方法!!!
f_name:张三 f_age:40 s_name:张小 s_age:19

-- 

  • super()只能在子类构造方法中出现一次,并且不能和this同时出现

7.代码块在继承中的执行顺序


JavaSE语法(7)——【static关键字、代码块】_会飞的阿喵的博客-CSDN博客https://blog.csdn.net/Che__dan/article/details/127875995?spm=1001.2014.3001.5502        在上一篇文章中👆介绍了代码块在一个类中的执行顺序,这里介绍一下在继承中它们是如何执行的。

public class Father {
    String name;
    int age;
    //……

    public Father() {
        System.out.println("父类的构造方法!!!");
    }

    {
        System.out.println("父类的实例代码块!!!");
    }

    static {
        System.out.println("父类的静态代码块!!!");
    }
    
}
------------------------------Son.java---------------------------------------------------
public class Son extends Father{

    public Son(){
        System.out.println("子类的构造方法!!!");
    }

    {
        System.out.println("子类的实例代码块!!!");
    }

    static{
        System.out.println("子类的静态代码块!!!");
    }
}
-------------------------------Main.java-------------------------------------------------
public class Main {
    public static void main(String[] args) {

        Son son = new Son();
        System.out.println("============================================================");
        Son son1 = new Son();
       
    }
}



结果:

父类的静态代码块!!!
子类的静态代码块!!!
父类的实例代码块!!!
父类的构造方法!!!
子类的实例代码块!!!
子类的构造方法!!!
============================================================
父类的实例代码块!!!
父类的构造方法!!!
子类的实例代码块!!!
子类的构造方法!!!

总结:
(1)父类静态代码块 优先于 子类静态代码块执行,且是最早执行。

(2)第二次实例化子类对象时,父类和子类的静态代码块都将不会再执行。

(3)剩下的就是 父类 ->子类    实例->构造 


8.继承方式

  • 单继承

         例子就不举了,上面的全是。

--

  • 多层继承

public class Father {

}
-------------------------------------------------------------------------------------------
public class A extends Father{

}
-------------------------------------------------------------------------------------------
public class B extends A{

}

--

  • 不同的类继承同一个类

public class Father {

}
-------------------------------------------------------------------------------------------
public class A extends Father{

}
-------------------------------------------------------------------------------------------
public class B extends Father{

}

--

  • Java中不能多继承!

public class Father {

}
-------------------------------------------------------------------------------------------
public class Father2 {

}
-------------------------------------------------------------------------------------------
public class Son extends Father,Father2{

}

         这里它会报错。



✏️组合

        继承、组合都是一种 表达类之间关系 的方式。组合没有关键字,不像继承那样 有extends。组合的特征就是 将 某一个类的实例作为另一个类的成员变量。

继承表示对象之间是is-a的关系,比如:狗是动物,猫是动物。

组合表示对象之间是has-a的关系,比如:汽车有轮子、有发动机、有椅子……。

public class Engine {

}
---------------------------------------------------------
public class Wheel {

}
---------------------------------------------------------
public class Car {
    //它的轮子
    Wheel wheel;

    //它的发动机
    Engine engine;

    //……
}

        上面这样的形式就是组合。

--

案例:计算圆锥的体积

        圆锥的底是一个圆,在计算体积时,首先得计算圆的面积,我们这里创建两个对象。

(1)Circle类 (圆对象)

(2)Cone类 (圆锥对象)

        圆锥对象在计算体积时,先委托圆锥的底 bottom(圆对象的一个实例)  调用自己的方法getArea()来计算自己的面积,然后圆锥对象再根据bottom计算出的结果来计算自己的体积。

-- 

  • Circle类 (圆对象)
public class Circle {

    //半径
    public double radius;
    //面积
    public double area;

    //构造方法
    public Circle() {}
    public Circle(double r) {
        this.radius = r;
    }


    //设置半径
    public void setRadius(double r){
        this.radius = r;
    }
    //获取半径
    public double getRadius(){
        return radius;
    }

    //计算面积
    public double getArea(){
        area = 3.14 * radius * radius;
        return area;
    }

}

-- 

  • Cone类 (圆锥对象)
public class Cone {
    //自己的底,底是一个圆
    public Circle bottom;
    //高
    public double height;

    //构造方法
    public Cone() {}

    public Cone(double h,Circle c){
        this.height = h;
        this.bottom = c;
    }


    //设置高
    public void setHeight(double h){
        this.height = h;
    }

    //设置底的半径
    public void setBottomRadius(double r) {
        bottom.setRadius(r);
    }

    //计算自己的体积
    public double getVolume() {
        //避免底没有传进来
        if(bottom == null){
            return -1;
        } else {
            //开始计算
            return bottom.getArea() * height / 3.0;
        }
    }

}

-- 

  • Main
public class Main {
    public static void main(String[] args) {
        //创建一个圆锥
        Circle circle = new Circle(4);
        double h = 12;
        Cone cone = new Cone(h,circle);

        double V = cone.getVolume();
        System.out.println("底面半径为4,高为12的圆锥的体积:" + V);

        //修改底与高,半径改为24,高改为6,再计算体积
        cone.setBottomRadius(24);
        cone.setHeight(6);

        V = cone.getVolume();
        System.out.println("底面半径为24,高为6的圆锥的体积:" + V);
    }
}

        这里只是简单地写了一些功能,便于理解,你可以自己拓展一下。

        这里就用到了组合,圆锥它有一个底(为圆),它们的关系为have-a的关系。圆锥类委托了圆类,叫圆类求自己的面积,圆锥类只管调用,并不会去管圆类是如何实现的。

        ps:本篇文章的知识点有一小点的多,写到这儿,加上代码总共有17884个字,嗯嗯嗯……不如关注+收藏一下,以免找不到我了哦!

  • 8
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值