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