继承是实现类复用的重要手段,但继承带来了一个最大的坏处:破坏封装。相比之下,组合也是实现类复用的重要方式,而采用组合方式来实现类复用则能提供更好的封装性。
在继承中,为了保证父类有良好的封装性,不会被子类随意改变,设计父类通常应该遵循如下规则:
1.尽量隐藏父类的内部数据。把父类所有成员都设置为private访问类型。
2.不要让子类可以随意访问、修改父类的方法。父类中仅为辅助其他的工具方法,应使用private访问控制符修饰,子类无法访问;父类中需要被外部类调用的方法用public访问控制符修饰,如不希望子类重写该方法,使用final修饰符修饰;如果希望父类的某个方法被子类重写,但不希望被其它类自由访问,应该使用protected修饰。
总之,继承要表达的是一种“是(is—a)”的关系,而组合表达的是“有(has—a)”的关系。
如果需要复用一个类,除了把这个类当成基类(父类)来继承外,还可以把该类当作另一个类的组合成分,从而允许新类直接复用该类的public方法,不管是继承还是组合,都允许在新类中直接复用旧类的方法。组合是直接把旧类对象作为新类的属性嵌入,用于实现新类的功能,通常需要在新类里使用private修饰符嵌入该类对象。
在聚合关系中,代表部分事物的可以属于多个聚合对象,可以为多个聚合对象共享,而且可以随时改变它所从属的聚合对象。代表部分事物的对象与代表聚合事物。对象的生存期无关,一旦删除了它的一个聚合对象,不一定也就随即删除代表部分事物的对象。在组合关系中,代表整体事物的对象负责创建和删除代表部分事物的对象,代表部分事物只属于一个组合对象。一旦删除了组合对象,也就随即删除了相应的代表部分事物的对象。
组合和聚合是有很大区别的,这个区别不是在形式上,而是在本质上:
比如A类中包含B类的一个引用b,当A类的一个对象消亡时,b这个引用所指向的对象也同时消亡(没有任何一个引用指向它,成了垃圾对象),这种情况叫做组合,反之b所指向的对象还会有另外的引用指向它,这种情况叫聚合。现实生活中:人和手,脚是组合关系,因为当人死亡后人的手也就不复存在了。人和他的电脑是聚合关系。
Java使用构造器对单个对象进行初始化的操作,与构造器作用非常相似的是初始化块,也可以对Java对象进行初始化操作。
1.一个类里可以有多个初始化块,相同类型的初始化块之间有顺序,前面定义的初始化块先执行,格式如下:
2.初始化块只能用static修饰符修饰,称为静态初始化块。
3.当创建Java对象时,系统总是先调用该类里定义的初始化块。
4.初始化块也是Java类中的一个成员,但是没有名字,因此不能通过类、对象来调用初始化块。
5.初始化块只在创建Java对象时隐式执行,而且在执行构造器之前执行。
注意:
当Java 创建一个对象时,系统先为该对象的所有实例变量分配内存(前提是该类已经被加载过了),接着程序开始对这些实例变量执行初始化,其初始化顺序是:先执行初始化块或声明实例变量时指定的初始值(这两个地方指定初始值的执行允许与它们在源代码中的排列顺序相同),再执行构造器里指定的初始值。
在定义初始化块时使用static修饰符,这个初始化块称为静态初始化块(类初始化块),因为普通初始化块负责对对象执行初始化,类初始化块则负责对类执行初始化。
静态初始化块时类相关的,系统在类初始化的阶段就执行,而不是在创建对象时才执行。因此,静态初始化块总是比普通初始化块先执行。
注意:
Java系统加载并初始化某个类时,总是保证该类的所有父类(包括直接父类和间接父类)全部加载并初始化。
当JVM第一次主动使用某个类时,系统会在类准备阶段为该类的所有静态成员变量分配内存;在初始化阶段则负责初始化这些静态成员变量,初始化静态成员变量就是执行类初始化代码或者声明类成员变量时指定的初始值,它们的执行顺序与源代码中的排列顺序相同。
原文:https://www.cnblogs.com/discoverspace/p/12254406.html