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

江南骚年

继承 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之间
	}
}

公众号:菜鸡干Java

posted @ 2020-09-17 21:14  沧海一粟Z  阅读(223)  评论(0编辑  收藏  举报