7 复用类
7.1 组合
即在一个类中使用另一个类作为成员变量,这是复用了现有程序代码的功能,而非形式。
7.2 继承
- 关键字:extends,这种复用是形式的复用,是一种可扩展和限制的复用;
- 复用:自动获取基类的所有public(protected)成员和方法,如果基类的成员和方法没有访问限定词,那么这个基类的无限定词方法和成员能在同一个包中的子类中访问,其它包中不行(包访问权限);
- 初始化基类:在子类构造器可访问之前先会调用父类的构造器,如果父类还有父类,将继续向上搜寻并调用构造器;如果不显式调用父类构造器,将调用父类的默认午餐构造器;如果不显式调用父类的构造器而父类没有默认构造器,将无法通过编译,这时需要在子类构造器中显示调用父类的某个非默认构造器;
package com.chenlei.study.thinkinginjava; import static com.chenlei.study.thinkinginjava.Print.*; public class ClassConstructor { public static void main(String[] args) {
new C();
} } class A{
public A() {
print("A constructor");
}
} class B extends A{
private int flag;
public B(int flag) {
this.flag = flag;
print("B constructor");
}
} class C extends B{
public C() {
super(0);//如果不指定调用哪个父类构造器将无法通过编译,由此可见在基类构造器被调用之前一定会调用父类构造器
}
}
/**
* output:
* A constructor
* B constructor
*/
7.3 代理
- Java并不直接支持代理;
- 如果想要复用某个类,使用成员变量组合或者使用继承都会使被复用的类所有的可访问方法都暴露出来,这时可以结合继承和组合,使用“代理”来实现部分方法的“继承”;
- 老板的秘书就是一个代理,想要知道老板的意思,不能从老板处”继承“,不能把老板作为”成员变量“,那么就只能通过秘书来传达通知、请求等,这样就能让员工知道该知道的,屏蔽不应该知道的;
package com.chenlei.study.thinkinginjava; public class Proxy {
public static void main(String[] args) {
BossProxy proxy = new BossProxy();
Print.print(proxy.getEmployeeNum());
}
} abstract class CompanyMember{
private int age;
private String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
} class Boss extends CompanyMember{
private double money;//不能让员工知道
private int employeeNum;
private int wifeNum;//不能让员工知道 Boss(double money,int employeeNum,int wifeNum){
this.money = money;
this.employeeNum = employeeNum;
this.wifeNum = wifeNum;
} public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
public int getEmployeeNum() {
return employeeNum;
}
public void setEmployeeNum(int employeeNum) {
this.employeeNum = employeeNum;
}
public int getWifeNum() {
return wifeNum;
}
public void setWifeNum(int wifeNum) {
this.wifeNum = wifeNum;
}
} class BossProxy extends CompanyMember{
private Boss boss;
public BossProxy() {
if(boss == null){
boss = new Boss(1000d,100,3);
}
}
public int getEmployeeNum(){
return boss.getEmployeeNum();
}
}
由此可见,代理让一些该暴露的暴露了,不该暴露的隐藏起来,这似乎在权限管理中非常有用。虽然没看过设计模式中的代理模式,但是我觉得可以在代理方法前后包裹一些方法和操作来做特定的处理,比如springAOP、spring事务代理?或者是实现同步锁的时候在原子方法保证安全性?懒加载(spring注入)?
7.4 基类中重载
在基类中重载父类的方法是可以的,只要基类的重载方法满足方法名和父类某些方法相同,但参数列表个数、顺序、类型或返回类型不同时都是可以正常重载的,若方法名和返回类型、参数列表一模一样,将是覆盖,而不是重载。
7.5 组合和继承的区别
- 组合:has-a
- 继承:is-a
7.6 protected关键字
- 子类访问权限:继承自基类的子类,可以访问基类的protected域;
- 包访问权限:和具有protected域的类处于同一个package的类能访问该类的protected域
7.7 向上转型
一个通用的方法需要对一种类型的对象进行处理,继承的优势就显现出来了;只需要将参数设置为基类,那么任何该基类的子类都可以作为参数传入而不报错,因为编译器将把这些子类自动转型为基类,而不需要为每个子类写一个方法,便于管理和操作。
7.8 final关键字
- final基础数据:final基本类型必须先实例化,一旦值给定将无法改变;static 所有类对象共享此数据,可以改变,但此值只有一个,一变皆变,不必初始化;所以 final static 必须初始化,而且初始化之后是所有类对象无法改变此值,共享一个;private static final和public static final的区别是前者智能通过对象引用来调用,后者可以直接通过类名调用(访问权限不同);
- final引用对象:无法使用new 操作符重新赋值,也无法直接将新的引用赋值给final对象,但是可以改变引用对象的值,比如
package com.chenlei.study.thinkinginjava; public class Final {
public String string = "haha";
public final String aString = string;
public static void main(String[] args) {
Final final1 = new Final();
final1.string = "123";
System.err.println(final1.aString);
}
}
/**
* output:
* haha
*/ - final数据可以在声明时被初始化,也可在构造器中初始化,但是一定要在使用前得到正确的初始化;
- final参数:在方法参数列表中的final参数,同样的也无法被方法里的任何操作改变;
- final 方法:一是避免继承类修改方法的实现;二是提高效率,一旦编译器发现final方法,方法体将被直接嵌套在程序中而并非调用方法栈(若final方法体积很大,这种效率的提高将被抵消,新版本的jdk已优化这一点:当final方法体很大时,不再嵌套);
- private final方法:private方法本来就是无法继承的,加上final跟没加一样,但是编译器并不会报错,因为所有private方法都被隐式地声明为final;
- final类:final类无法继承;
- 类在被使用时才会加载,构造方法可以视为一个static方法,也就是说,当类的static成员被加载的时候,该类才会被加载;