第7章 复用类
组合:在新的类中产生现有类的对象,该方法是复用了现有程序代码的功能,而非它的形式。
继承:按照现有类的类型来创建新类,并在其中添加新代码。
7.1 组合语法
public class Table {
public void print(){
System.out.println("hello");
}
}
public class Space {
public Table table;
}
public static void main(String[] args) {
Space space = new Space();
space.table = new Table();
space.table.print();
}
初始化引用的四种方式:
- 一:在定义对象的地方,意味着在构造器被调用前初始化;
- 二:在类的构造器中;
- 三:在正要用这些对象前,这被称为惰性初始化;
- 四:使用实例初始化
public class Space {
// 一
public Table table = new Table();
{
// 四
table = new Table();
}
public Space(){
// 二
table = new Table();
}
}
public static void main(String[] args) {
Space space = new Space();
// 三
space.table = new Table();
space.table.print();
}
什么时候使用实例初始化
- 初始化代码需要处理异常
- 初始化有复杂的运算
- 虽然可以写在构造函数中,但是当有多个构造函数时就会代码重复
7.2 继承语法
public class Table {
public void test1(){
System.out.println("基类test1");
}
public void test2(){
System.out.println("基类test2");
}
}
public class SmallTable extends Table {
@Override
public void test2() {
super.test2();
System.out.println("导出类test2");
}
public void test3(){
System.out.println("导出类test3");
}
}
public static void main(String[] args) {
SmallTable st = new SmallTable();
st.test1();
st.test2();
st.test3();
}
/*
基类test1
基类test2
导出类test2
导出类test3
*/
- 每个类中都可以有main()方法,这样方便测试(现在不需要这样做)
- 一般情况下,将基类的数据成员指定为private,方法指定为public
基类的初始化
// 默认初始化
public class Table {
public Table(){
System.out.println("基类初始化");
}
}
public static void main(String[] args) {
SmallTable st = new SmallTable(); // 基类初始化
}
// 显示初始化
public class SmallTable extends Table {
public SmallTable(){
// 必须放在开头
super();
}
}
// 有参初始化
public class Table {
// 没有无参构造器是不合理的,这里只是为了展示使用
public Table(int i){
System.out.println("基类初始化");
}
}
public class SmallTable extends Table {
// 必须指定对应的基类构造器
public SmallTable(){
super(1);
}
}
7.3 代理
Java并没有提供直接的支持,它是继承和组合之间的中庸之道,方式是将成员对象置于新类中,同时暴露该成员对象的所有方法。
public class A {
void print(){
System.out.println("hello");
}
void test(){
System.out.println("test");
}
}
public class B {
private A a = new A();
public void print(){
a.print();
}
public void test(){
a.test();
}
}
public static void main(String[] args) {
B b = new B();
b.print();
b.test();
}
本质上是加了一层包装,但是相对于组合和继承的优势是在不改变方法内部的实现逻辑下,可以对方法进行增强处理。
7.4 结合使用组合和继承
public class Shirt {
}
public class Animal {
public Animal(int i) {
System.out.println("初始化Animal");
}
public void print(int i) {
System.out.println("打印数字:" + i);
}
public void dispose() {
System.out.println("清理Animal");
}
}
public class Person extends Animal {
private Shirt shirt;
public Person(int i) {
// 编译器强制要求初始化父类
super(i);
// 必须手动初始化成员对象,否则该成员对象无法使用
shirt = new Shirt();
}
public void print(String s){
System.out.println("打印字符串"+s);
}
@Override
public void dispose() {
System.out.println("清理Person");
// 调用清理方法时,应当先清理子类中的元素,再清理父类中元素,因为子类可能依赖父类中的元素
super.dispose();
}
}
public static void main(String[] args) {
Person person = new Person(1);
try {
// 子类重载父类中的方法,父类的方法不会被覆盖,除非重写
person.print(1);
person.print("s");
}finally {
// 肯定执行执行清理工作
person.dispose();
}
}
7.5 在组合与继承之间选择
当希望在新类中使用现有类功能时,用组合,它们是has-a的关系;当希望用现有类的接口时,用继承,它们是is-a的关系。
7.6 protected关键字
对于子类或包内的其他类,该成员是可访问的;对于基类而言,应当对域使用private,同时提供protected方法,让子类可以修改。
7.7 向上转型
将子类引用转换为基类引用的动作,称为向上转型。
**称为向上转型的原因:**类继承图的绘制是将根置于顶端,子类置于下方,所以子类向父类的转换称为向上转型。
**再论组合与继承:**继承应当慎用,如果新类需要向上转型,则继承是需要的,否则应当使用组合。
7.8 final关键字
用到final的三种情况:数据、方法、类。
7.8.1 final数据
- 一个永不改变的编译时常量;
- 在运行时被初始化,随后不再改变。
public class FinalData {
private static Random random = new Random(47);
// 基本类型
public final int i1 = 1; // 一个对象存在一份
public final static int I2 = 2; // 仅存在一份
public final int i3 = random.nextInt();
public final static int I4 =random.nextInt();
// 引用类型
public final String s1 = "a";
public final static String S2 = "b";
// 数组
public final int[] a1 = {1,2,3};
public final static int[] a2 = {1,2,3};
// 空白final
public final int i5;
public final String s3;
public FinalData(){
// 空白的final必须在构造器中赋值,这样保证了在使用前,肯定被初始化
i5 = 1;
s3 = "c";
}
// 对参数使用final,在方法内无法更改参数的引用
public void test(final String str){
}
}
7.8.2 final方法
把方法锁定,防止继成类修改它。
final和private关键字private方法都是隐式的指定为final,因为无法取得,所有无法覆盖。
public class Animal {
private void print(int i) {
System.out.println("打印数字:" + i);
}
}
public class Person extends Animal {
public void print(int s){
System.out.println(s);
}
}
public static void main(String[] args) {
Person person = new Person();
person.print(1);
Animal animal = person;
// 下面写法报错,说明无法向上转型,证明子类只是含有一个和基类同名的方法
// animal.print(4);
}
7.8.3 final类
final类不能被继承,同时隐含了方法为final,因为类不能被继承,所有也不存在被重写的情况。
7.9 初始化及类的加载
public class Animal {
private static int i1 = setValue("基类静态域初始化");
public int i2;
public Animal() {
System.out.println("基类构造方法初始化" + i2);
i2 = 1;
}
public static int setValue(String s) {
System.out.println(s);
return 1;
}
}
public class Person extends Animal {
private static int i3 = setValue("子类静态域初始化");
private int i4 = setValue("子类成员变量初始化");
public Person(){
System.out.println("子类构造方法初始化"+i2);
}
}
public static void main(String[] args) {
Person person = new Person();
}
// 注意i2值的前后变化
/*
基类静态域初始化
子类静态域初始化
基类构造方法初始化0
子类成员变量初始化
子类构造方法初始化1
*/
加载Person类->加载基类[->上层基类……]->基类static初始化->子类static初始化->基类实例初始化->基类构造方法初始化->子类实例初始化->子类构造方法初始化。