继承和组合、单例类及不可变类


继承和组合、单例类及不可变类

继承 inheritance:

继承是实现类复用的重要手段,所谓复用,就是可以多次使用,或者再次利用,不用继续重写成员变量和方法。但不代表没有缺点,最不好的地方:破坏封装。子类拓展父类时,若访问权限允许,则可直接访问父类的成员变量和方法,破坏了良好的封装性(Encapsulation) ,造成子类与父类的耦合。

因而,设计父类时应注意以下几点:

  • 尽量隐藏内部数据
  • 不要让子类随意访问
  • 不要在父类的构造器中调用将被子类重写的方法

组合 combination

对于继承而言,子类可以获取父类的public方法,使用子类时,可以访问子类从父类继承的方法;组合则是把旧类对象作为成员变量组合进来。我们不需要看到被组合进来的对象的方法,所以通常用private修饰。在功能上,两者没什么区别。

class animal{
    private void beat(){
        System.out.println("心跳!");
    }
    public void breath(){
        System.out.println("呼吸!");
    }
}
class bird{
    private animal a;
    public bird(animal){
        this.a = a;
    }
    public void breath(){
        a.breath();//复用animal的方法
    }
    public void fly(){
        System.out.println("芜湖起飞!");
    }
}

注意

使用组合关系实现复用,需要创建两个animal对象,是否意味着开销更大?

当创建一个子类对象时,系统不仅需要为该子类定义实例变量分配内存空间,而且需要为它父类所定义的实例变量分配内存。采用继承的方式,假设父类定义了两个实例的变量,子类定义了3个变量。创建子类实例时需要为子类分配5块内存空间。当采用组合设计时,先创建父类实例,此时需要分配2快内存,再创建整体类实例,也需要分配3块内存,只是多了一个引用变量来引用所组合的对象。因此,组合和继承设计开销不会有太大差别。

单例类

这种类不像其他类,某些时候允许其他类自由创建该类对象没有什么意义,所以要降低对它的访问权限,让这样的类只能创建一个实例。

方法是把该类的构造方法用private修饰起来,但又需要创建一个对象,所以需要提供一个public方法去用于创建该类的对象,但是不是通过对象来创建对象,而是用类本身去创建,因此方法要用static修饰。

除此之外,该类还必须缓存对象,记录已创建的对象。同时让该静态方法能访问到,保存对象的成员变量也需要static修饰。

class person{
    //静态成员变量来保存对象
    private static person OnlyPerson;
    //对构造器隐藏
    private person(){}
    //公共方法
    public static person getPerson(){
        if(OnlyPerson==null){
            OnlyPerson = new person();
        }
        return OnlyPerson;
    }
}

不可变类 immutable

不可变类意思是创建该类的实例后,该实例的变量不可变。Java提供的8个包装类和String类都是不可变类!

Double d = new Double(5.6);
String s = new String("abc");

上面创建了一个Double和String对象,并传入两个值,但程序无法继续修改该值,所以Double和String类没有修改值的方法。

如果要创建一个不可变类,需要遵守如下规则:

  1. 使用privatefinal修饰符来修饰该类成员
  2. 提供带参构造器,用于初始化成员
  3. 仅提供get方法
  4. 确保引用的对象不会被修改
  5. 重写hashCode()方法和equals()方法

与此对应的是可变类,比如JavaBean,提供了getter()和setter方法。

缓存实例的不可变类

不可变类的实例状态不可改变,可以方便被多个对象共享,主要是为了减小开销。不同的缓存方式有着不同的性能,可以用数组、集合等来缓存。

以Java的java.lang.Integer类为例,new一个构造器,每次返回新的Integer对象;如果用valueOf()方法来创建Integer对象,则会缓存该对象。

public class IntegerCacheTest{
    public static void main(){
        //new一个对象
        Integer a1 = new Integer(6);
        //生成新对象,并缓存该对象
        Inreger a2 = Integer.valueOf(6);
        Integer a3 = Integer.valueOf(6);
        System.out.println(a1 == a2);//false
        System.out.println(a2 == a3);//true
        //注意:缓存的数只能在-128~127之间
    }
}

文章作者: 流浪舟
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 流浪舟 !
评论
 上一篇
变量分类和运行机制及自动装拆箱 变量分类和运行机制及自动装拆箱
成员变量和局部变量及运行机制两者的区别在于定义变量的位置不同,运行的机制也有差异。成员变量定义在类中,局部变量定义在定义在方法中。 成员变量分为类变量和实例变量两种,局部变量分为形参(方法内)、方法局部变量和代码块内局部变量,比如循环内的。
2020-09-19
下一篇 
String与BigDecimal基础类 String与BigDecimal基础类
String与BigDecimalString, StringBuffer and StringBuilderstring有11种构造方法 1. 可变性 String不可变 StringBuffer 和 StringBuilder 可变
2020-09-16
  目录