7.1组合语法
组合:在新类中使用现有类的对象。新类是由现有类的对象所组成的,所以这种方式成为组合
1.对于非基本类型的对象,必须将其引用放置在新类中
toString 当编译器需要我们提供字符串 而我们提供的是对象时,会自动调用对象的toStirng方法
对象初始化的位置:
1.在定义对象的地方 这意味着他们总是能够在构造器被调用之前被初始化
2.在构造器进行初始化
3.在使用对象之前初始化 惰性初始化
4.使用实例初始化
7.2继承语法
当创建一个类时总是在继承,对于java如果我们不指定继承 那么类就继承了Object
extends 意思是延伸 继承就是使用该关键词来描述类间的关系
class A extends B {
}
这么做时 A将从B拿到所有的域和方法 除了private 修饰的成员或者域
调用父类中的方法 使用关键词 super super.scrub();
7.2.1初始化基类
当我们创建子类对象时,该对象就会包含一个基类的对象
这个对象和你直接用基类创建的对象是一样的 差别在于 基类子对象被包含在导出类(子类)对象内部,直接new 出的对象来自于外部
java在初始化导出类对象时,会自动调用基类构造器 来创建子类
public class C {
public C(){
Log.e("bac","--c--");
}
}
public class B extends C {
public B(){
Log.e("bac","--b--");
}
}
public class A extends B{
public A() {
Log.e("bac", "--a--");
}
}
我们初始化A a = new A()
打印log如下: --c--
--b--
--a--
正如打印所看到的 调用构造器 目的就是初始化 当子类对象初始化时 基类对象也进行了初始化
整个过程是从基类“向外”扩散的,所以基类在导出类构造器可以访问前就已经先完成了初始化
a 对象中包含 b 对象
b对象包含 c对象
带参数的构造器
7.3代理
代理是继承与组合间的中庸之道
代理的形式:我们将一个成员对象置于所需构建的类中(组合),但与此同时将新类中暴露该成员对象的所有方法(好像继承似的)
简单的举个例子:
好处在哪里?
1.我们可以不停的更换实现功能的类 把C换成B D F 都行 灵活
2.更强的控制力 我们可以屏蔽掉 不需要的方法 只提供C类中部分接口行为
7.4结合使用组合和集成(略过...)
7.4.1确保正确清理
一般情况下是将垃圾回收交给GC来处理,但是当有特叔情况时,需要自己处理手动释放一些内存,例如我们自己写些释放资源的方法,在finally中释放资源
但是有一点需要注意 不要使用finalize()方法 在资源释放时一定要注意使用资源的地方的,,,要判断处理 避免出现异常
7.4.2名称屏蔽
java的基类拥有多个重载方法,那么子类中重新定义该方法名称对基类中的方法没有屏蔽效果,毫无影响,重载机制是可以正常工作的
java中一般使用注解 @override 来表示 子类中该方法 重写基类中的相同特征的方法,
添加该注解后 当你重载该方法,而不是重写时,编译器会提示你错误
7.5 在组合与继承之间选择
组合和继承都在新类中添加子类对象
组合显示的放置子类对象并操作
继承隐士的添加子类对象并操作(调用基类的构造器 导出类对象中包含基类对象)
二者的区别在哪里?
组合:如果想在新类中使用现有类的功能,而不是接口,在新类中嵌入某个对象,让其实现新类的功能,由新类提供接口,而不是使用嵌入对象的接口。一般情况下我们会,使用privte修饰的对象
某些情况下,使用public 修饰嵌入对象,也是很方便的,比如
public class Car{
public Engine engine;
public Car(){
engine = new Engine;
}
}
public class Engine{
public void start(){}
public void stop(){}
public void go(){}
public void back(){}
}
main(){
Car car = new Car();
car.engine.start();// 这种情况下 看起来代码也很简单明了, 有助于客户端程序猿对代码组合的理解
}
其实看是来是这样的:似乎应不应该使用组合很明显,餐馆类 需要厨师类实现功能,但他很显然并不需要厨师接口,他只负责自己的接口 比如 产出菜品的接口
继承:使用一个现有的类,来开发一个特有版本的类,通常意味着使用一个通用类,来根据需要创建一个特殊的类。
is-a关系 是一个...是一种典型的继承关系
has-a 拥有一个 是典型的组合关系
7.6 protected关键字
1. 控制包访问权限
2.控制子类访问权限
7.7 向上转型
“为新的类提供方法”并不是继承中最关键的一面,更重要的则是一种与基类的关系,简单来说 就是 导出类属于基类类型
将导出类对象指向基类引用 称之为向上转型
7.7.1为什么称为向上转型
向上转型是安全的 子类转换成父类,会造成子类特有的方法丢失或特有成员变量的丢失,但不会产生任何不良后果,转换成父类只能使用父类的成员变量与父类接口(只允许子类对象使用继承自父类的接口与成员变量)。
7.7.2 再论组合与继承
类就是一种封装 将数据与方法封装在一起
继承很重要但是并不意味着我们需要常常用他,事实上我们应该更多的使用组合,如何判断是否应该使用继承请参照两个标准
1.是 is-a 关系 还是 has-a关系
2.是否需要向上转型 (如果不需要就没有必要使用)
7.8 final关键字
final 使用在数据前做修饰
用在基本类型前面表明数据的值不必变
一个在运行时被初始化的值,不希望它被改变
final修饰对象引用 对象引用不可变,对象引用不可以在指向其他对象,已经指向的对象本身是可以改变的(对数组同样有效,数组也是对象)
(不管是修饰对象引用还是基本数据类型,都必须要初始化)
空白final
修饰引用或基本数据了类型,但并不初始化,在使用这些引用或者基本类型 必须要在使用前初始化
java允许空白final (final 修饰成员变量但是并未初始化)
要求必须在使用前进行初始化,在构造方法之前包括构造方法进行初始....
final参数
java允许在参数列表中将参数指明为final
这意味着参数引用指向的对象将无法修改
这种特性主要用来处理匿名内部类的数据传递
7.8.2 final 方法
使用final修饰方法的原因:
1.把方法锁定 防止继承类修改它(final修饰后 将无法重写方法 可以进行重载)
2.提高代码执行效率(在就的jvm中),但是最近看法的jvm中已经不建议这么做了,jvm和编译器会帮你处理效率问题
java早期版本,当编译器发现final来修饰方法时,就会跳过插入代码这种正常方式而执行方法回调机制,并且以方法体中的实际代码的副本来代替方法调用,这将消除方法调用的开销
final和private关键字
类中所有private方法都隐式的指定是final.在private 方法前添加 final是没有额外意义的
private修饰的方法,不属于基类接口逇一部分,他仅仅是隐藏在类中的一部分代码,如果你在导出类中使用 public protected+相同基类private的方法名 其实并没有覆盖private方法,只是产生了一个新方法
final使用在类前面做修饰
将final放在class 前做修饰,表明该类无法进行继承
当类被指定为final 其实隐式的将该类中所有的方法指定为final
7.8.4有关final的忠告
要考虑清楚是否要想使用者提供相应的方法调用,不能太松弛,也不能苛刻
7.9 初始化及类的加载
java中的类的初始化与加载:类的代码在初次使用时才加载的,通常是加载发生于创建类的第一个对象之时,但是当访问static域或static方法时,也会发生加载
这里注意:构造器也是static 只是static关键词没有显示出来,更准确的说,类是在其任何static成员被访问时,加载的
7.9.1继承与初始化
class A extends B{
}
class B extends C{
}
main(){
A a = new A ();
}
类的初始化如下: 加载器开始启动并加载A类的编译代码(A.class),进行加载过程中,发现它还有一个基类(由extends可知),于是就继续进行加载B类的编译代码,不管是否打算产生一个基类对象,这是又发现B类的基类c,于是就开始加载C类的编译代码,根基类的Static初始化被执行,然后是子类的初始化
静态成员初始化以后,类加载完毕,可以生产对象了,对象中的基本类型都被设置为默认值,对象引用设置为null-通过将对象内存设置为二进制的0而生成的,然后构造器别调用,从基类依次到子类。
生成A对象 我们总结写:先加载类的.class编译代码,由于类是导出类,所以加载基类.class,生成Class对象,这个过程中初始化基类的static成员,生成导出类的Class对象,然后是导出类的static成员初始化。接着生成对象,先生成基类对象 即先初始化基类中的成员变量以及对象引用(非static)为默认值,调用构造器方法,然后在生成子类对象,即初始化子类对象的成员变量
7.10 总结
组合是将现有类型作为新类型底层实现的一部分加以复用
继承就是依靠现有类来实现一个特殊的现有类,复用现有类的接口
代理是介于继承与组合之间的,利用先用类型来实现,复用的现有类型接口。
在使用是多使用组合和代理 少使用继承