Java中的继承、组合和代理

一、介绍

  继承是OOP语言必不可少的一部分,当创建一个类时我们总是在继承,因为所有类都是Objiect的子类,同时,你也可以自己选择你想要继承的类,但简单的继承在实际应用中可能会出现一些问题,所以就出现了组合和代理,下面我们来深入了解一下它们之间的区别:

二、三者之间的区别

1、继承

  一个类可以利用extends关键字显式的继承另一个类,而被继承的类称为父类,继承该类的类称为子类,如:

1 class animal {
2     public void eat{
3         System.out.println("x");
4     }
5 }
6 class Dog extends animal{
7     String name;
8 }

  如以上代码,animal被Dog继承,此时animal称为父类(基类),而Dog称为animal的子类(导出类),如果两个类存在继承关系,则子类会自动继承父类的方法和变量,在子类中可以调用父类的方法和变量。在java中,只允许单继承,也就是说 一个类最多只能显示地继承于一个父类。但是一个类却可以被多个类继承,也就是说一个类可以拥有多个子类。

 

子类继承父类的成员变量:

  当子类继承了某个类之后,便可以使用父类中的成员变量,但是并不是完全继承父类的所有成员变量。具体的原则如下:

  1)能够继承父类的public和protected成员变量;不能够继承父类的private成员变量;

  2)对于父类的包访问权限成员变量,如果子类和父类在同一个包下,则子类能够继承;否则,子类不能够继承;

  3)对于子类可以继承的父类成员变量,如果在子类中出现了同名称的成员变量,则会发生隐藏现象,即子类的成员变量会屏蔽掉父类的同名成员变量。如果要在子类中访问父类中同名成员变量,需要使用super关键字来进行引用。

 

子类继承父类的方法:

  同样地,子类也并不是完全继承父类的所有方法。

  1)能够继承父类的public和protected成员方法;不能够继承父类的private成员方法;

  2)对于父类的包访问权限成员方法,如果子类和父类在同一个包下,则子类能够继承;否则,子类不能够继承;

  3)对于子类可以继承的父类成员方法,如果在子类中出现了同名称的成员方法,则称为覆盖,即子类的成员方法会覆盖掉父类的同名成员方法。如果要在子类中访问父类中同名成员方法,需要使用super关键字来进行引用。

  注意:隐藏和覆盖是不同的。隐藏是针对成员变量和静态方法的,而覆盖是针对普通方法的。

  覆盖只有在某方法是基类的接口的一部分时才会出现,如果一个基类的方法是private,那它就不是接口的一部分,也就是说子类是无法继承的,现在子类中出现一个同名方法时,就不叫覆盖。

 

构造器:

  子类是不能够继承父类的构造器,但是要注意的是,如果父类的构造器都是带有参数的,则必须在子类的构造器中显示地通过super关键字调用父类的构造器并配以适当的参数列表。如果父类有无参构造器,则在子类的构造器中用super关键字调用父类构造器不是必须的,如果没有使用super关键字,系统会自动调用父类的无参构造器。看下面这个例子就清楚了:

 1 class x{
 2     x(){
 3         System.out.println("x");
 4     }
 5 }
 6 class y extends x{
 7     y(){
 8         System.out.println("y");
 9     }
10 }
11 class z extends y{
12     z(){
13         System.out.println("z");
14     }
15 
16     public static void main(String[] args) {
17         z test = new z();
18     }
19 }
输出:
x
y
z

  上述代码是默认调用父类的无参构造,还可以利用super调用父类的指定构造器,注意super需要在构造器内书写:

 1 class x{
 2     x(){
 3         System.out.println("x");
 4     }
 5     x(int x){
 6         System.out.println("x"+x);
 7     }
 8 }
 9 class y extends x{
10     y(){
11         System.out.println("y");
12     }
13     y(int x){
14         super(1);//调用父类有参构造
15         System.out.println("y"+x);
16     }
17 }
18 class z extends y{
19     z(){
20         super(1);//调用父类有参构造
21         System.out.println("z");
22     }
23 
24     public static void main(String[] args) {
25         z test = new z();
26     }
27 }
输出:
x1
y1
z

 

super主要有两种用法:

  1)super.成员变量/super.成员方法;

  2)super(parameter1,parameter2....)

  第一种用法主要用来在子类中调用父类的同名成员变量或者方法;第二种主要用在子类的构造器中显示地调用父类的构造器,要注意的是,如果是用在子类构造器中,则必须是子类构造器的第一个语句。

 

2、组合

  组合的逻辑其实很简单,如果你在a类中需要使用到b类的方法或者变量,就可以在a类中直接创建一个b类的对象,然后利用这个对象直接调用其中的方法,如:

class x {
   public void x1(){
        System.out.println("this is x");
    }
    
}
class y{
    x x1 = new x();
    public void y1{
        x1.x1();
    }
}

 

  可以看到我们我们可以利用这个方法去调用x1类里的方法,但很容易发现一个问题,当我们在y类创建一个x的对象,那么x里的所有东西在y里都是透明的,这是和继承的区别之一,继承只能继承那些父类想要你看到的东西,有很高的灵活性。

  组合中对象的初始化:我们在一个类中定义了另一个类的对象,这就涉及到这个对象初始化的问题,可以有三种方法:

  1、在定义对象处进行初始化

  2、在类的构造器中进行初始化

  3、在使用该对象之前进行初始化,这种称为惰性初始化,在生成对象时没必要每次都初始化,这样可以减少额外的负担

  如果不进行初始化,这个类对象会自动被编译器赋值一个null,与其他成员变量一样,如int的初值为0,等待编写者给它进行操作

 

3、代理

  代理Java并没有提供给它的直接支持,它是介于继承和组合之间的中庸之道,代理同样将一个成员对象放置于所要构造的类中(就像组合一样),但不同的是,代理将成员对象设置为私有的,然后另写方法去调用这个成员的必要方法,从而实现对基类所有成员的适当保护。具体可以看我的文章:https://www.cnblogs.com/kxxiaomutou/p/15616068.html

 

  

 

posted @ 2021-11-28 18:26  空心小木头  阅读(321)  评论(0编辑  收藏  举报