《Java核心技术 卷Ⅰ 》第五章笔记——继承

目录

继承的基本概念

继承的注意事项

代码块

子类构造器

多态

抽象

方法调用的过程

Object类

equals()方法与hashCode()方法

toString()方法

泛型数组列表——ArrayList类

对象包装器

继承的设计技巧


继承的基本概念

1.继承的基本思想是基于已有的类创造新的类。

2.反射是指在程序运行期间更多地了解类及其属性的能力。

3.关键字extend表示正在构造的新类派生于一个已存在的类,用法如下:

public class A {
    // methods and fields
}

public class B extends A {
    // added methods and fields
}

其中,这个已经存在的类成为超类、基类或父类,新类成为子类、派生类或孩子类。常用的名词为超类和子类。

4.一个对象变量可以指示多种实际类型的现象称为多态。在运行时能够自动选择适当的方法,称为动态绑定

5.不允许扩展的类称为final类,final类不可以被定义子类,这是阻止继承的一种方法。

6.如果一个方法没有被覆盖并且很短,编译器就可以对其进行优化处理,这个过程称为内联

继承的注意事项

1.Java中,所有的继承都是公有继承

2.子类无需显示定义超类的方法和字段,即只需指出子类与超类的不同之处。

3.应将最一般的方法放在超类中,将特殊方法放在子类中。

4.子类方法不可以访问超类的私有字段,需要与其他方法一样使用公有接口。

5.覆盖方法指超类和子类中同名但功能不同的方法:

  • 若子类希望调用自身的同名方法,只需要像调用其他普通方法一样写出函数名和参数列表即可。
  • 若子类希望调用超类的同名方法,则需要使用super关键字。

例如:

public class A {
    // methods and fields
    public void func1() {
        // do something
    }
}

public class B extends A {
    // added methods and fields
    public void func1() {
        super.func1();
        // do something
    }
}

6.super与C++的this并不相同,super不是一个对象的引用。

7.Java中动态绑定是默认的行为,若不希望如此,可以使用final关键字。

8.覆盖一个方法时,子类方法不能低于超类方法的可见性。即超类方法为public,子类方法必须也为public。

9.使用final关键字确保方法不被子类覆盖。final类的所有方法将被自动标记为final方法,但final类的字段并不会自动成为final字段。

10.使用instanceof操作符查看能否成功进行强制类型转换。例如:

if (staff[1] instanceof Manager)
{
    boss = (Manager) staff[1];
    // do something else...
}

if语句检测staff[1]是否可以成功强制类型转换为Manager类。

11.访问修饰符(权限越来越高)

  1. 仅对本类可见——private
  2. 对本包可见——[default]
  3. 对本包和所有子类可见——protected
  4. 对外部类完全可见——public

12.使用@override注解来确定其下的是否为有效的覆盖重写。

13.子类方法返回值必须小于等于父类方法返回值。

代码块

1.静态代码块:用static 修饰的代码块

  1. 可以有输出语句。
  2. 可以对类的属性、类的声明进行初始化操作。
  3. 不可以对非静态的属性初始化。即:不可以调用非静态的属性和方法。
  4. 若有多个静态的代码块,那么按照从上到下的顺序依次执行。
  5. 静态代码块的执行要先于非静态代码块。
  6. 静态代码块随着类的加载而加载,且只执行一次。

2.非静态代码块:没有static修饰的代码块

  1. 可以有输出语句。
  2. 可以对类的属性、类的声明进行初始化操作。
  3. 除了调用非静态的结构外,还可以调用静态的变量或方法。
  4. 若有多个非静态的代码块,那么按照从上到下的顺序依次执行。
  5. 每次创建对象的时候,都会执行一次。且先于构造器执行。

子类构造器

1.使用super关键字调用超类的构造器,语法如下:

public B(int a, double b, String s)
{
    super(a, b, s);
    int extra_var = 0;
}

使用super调用构造器的语句必须是子类构造器的第一条语句。

2.如果子类没有显示调用超类构造器,将自动调用超类的无参数构造器。

3.构造器的参数可以使用this传递给当前类的另一个构造器,也可以使用super传递给超类的构造器。

4.无论是this还是super,这一条语句必须是当前构造器的第一条语句。二者不可同时使用。

多态

1.判断是否适合继承关系的原则——替换原则(“is-a规则”):子类的每个对象也是超类的对象,程序中出现超类对象的任何地方都可以使用子类对象替换。

2.当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,执行的是子类重写后的方法。

3.在Java中,对象变量是多态的。可以将子类的对象赋给超类变量,即一个超类变量可以引用任何一个子类的对象。但反之,不能将超类的引用赋给子类的变量,因为这样可能会出现该子类变量无法调用子类特有的方法的现象。这是向下转型的必要性。

尽管如此,编译器只将超类变量看做一个超类的对象,对其使用子类方法将产生错误。

4.向下转型的代码规则:

子类类型 变量名 = (子类类型) 父类变量名;

5.在Java中,子类引用的数组可以不发生强制类型转换而直接转换成超类引用的数组,但它们实际上引用的是同一个数组,因此实际中要牢记初始数组的类型并避免发生类型转换。 

6.在多态的代码当中,成员方法的访问规则是: 看new的是谁,就优先用谁,没有则向上找。

口诀:编译看左边,运行看右边。

对比一下:

  • 成员变量:编译看左边,运行还看左边。
  • 成员方法:编译看左边,运行看右边。

抽象

1.使用abstract关键字表示超类中的某个方法无需实现而在子类中才被具体实现,包含一个或多个抽象方法的类本身必须被声明为抽象的。

  • 若将子类中所有超类的抽象方法仍保留为抽象方法,则该子类也应该被定义为abstract类。
  • 即是类中无抽象方法也可以将类声明为抽象类。
  • 抽象类不可以被实例化,但可以定义抽象类的对象变量,它只能引用非抽象子类的对象。

2.不能用abstract修饰变量、代码块、构造器。

3.不能用abstract修饰私有方法、静态方法、final的方法、final的类。

4.抽象类是用来模型化那些父类无法确定全部实现,而是由其子类提 供具体实现的对象的类。

方法调用的过程

1.编译器首先查看对象的声明类型方法名,即编译器会列出子类所有名字与被调用方法名相同的方法和超类可访问的方法。

声明类型是指对象在声明时所指出的类型,对应的是实际类型,即被引用对象的实际类的类型。

2.然后,编译器要确定方法调用中提供的参数类型。

  • 如果编译器没有找到与参数类型匹配的方法,或发现经过类型转换后有多个方法与之匹配,则编译器会报错。
  • 如果子类中定义了一个与超类签名(名字和参数列表)相同的方法,那么子类中的方法就会覆盖超类的同签名方法,这两个方法的返回值可以不同,称为有可协变的返回类型

3.如果是static/ final/ private方法,则编译器可以准确知道应该调用哪种方法,称为静态绑定

4.反之,若要调用的方法依赖于隐式参数的实际类型,则使用动态绑定。这时虚拟机必须调用与该对象变量所引用的对象的实际类型对应的那个方法,若没有,则在其超类中寻找该方法。

Object类

可以使用Object类型的变量引用任何类型的对象。

equals()方法与hashCode()方法

1.在子类定义equals方法时,应该首先调用超类的equals。

2.hashCode方法返回一个散列码,是由对象导出的一个整型值。

3.字符串的散列码是由内容导出的,而Object类默认的hashCode方法会从对象的存储地址得出散列码。

4.重新定义equals方法后,就需要重新对hashCode方法。

5.objectName.hashCode()方法返回一个散列码,而Object.hashCode(objectName)则先判断是否为null对象,是返回0,否则调用objectName.hashCode()方法。

6.如果equals方法返回true,则两个对象的hashCode方法也必须返回相同的散列码。

7.完备equals()方法建议:

  1. 显式参数命名为otherObject,之后强转为other类型。
  2. 检测this是否与otherObject相等,若相等返回true。
  3. 检测otherObject是否为null,若为null则返回false。
  4. 使用equals方法或getClass语句检测,若不等返回false。若子类中重新定义了equals,则需要包含super.equals()调用。
  5. 将otherObject强制转换为当前类的类型。
  6. 使用==对类中字段进行比较,当且仅当全部相等时返回true,否则返回false。

toString()方法

1.toString方法会返回表示对象值的字符串。其中,可以使用getClass.getName()方法获得类名的字符串,这样子类也可以通过使用super.toString()获得超类部分对象值,在后面添加子类新增字段即可。

2.事实上,只要对象与一个字符串通过操作符“+”连接起来,Java编译器就会自动调用toString方法获得这个对象的字符串描述。

3.若想输出数组的内容,则需要调用静态方法Arrays.toString(arr)

泛型数组列表——ArrayList类

1.声明语法:

ArrayList<Integer> arr1 = new ArrayList<>();
ArrayList<Integer> arr2 = new ArrayList<Integer>();
var arr3 = new ArrayList<Integer>();

建议使用var来避免重复写类名,不使用var时,右边尖括号中的类型可省略。

2.添加元素语法:

arr1.add(10);
arr2.add(new Integer(10));
arr1.add(1,100);
arr3.ensureCapacity(100);
ArrayList<Integer> arr4 = new ArrayList<Integer>(100);

完整添加语法为第二行,也可简写成第一行。第三行表示在索引1处添加值为10的元素,索引1表示位置1及之后的所有元素都要向后移动一个位置,把元素填入索引1这个位置上。

第三行使用ensureCapacity,这个方法将分配arr3一个包含100个对象的内部数组,这样前100次add操作就不会带来很大的开销来重新分配空间,也可使用第四行语句简单设置初始容量。

3.使用size()方法确定数组列表中包含的实际元素数量,大小确定后,可以使用trimToSize()方法将数组列表调整为当前元素容量大小。

4.访问元素语法:

arr1.get(index);
arr2.set(index, e);

使用get方法获得列表中下表为index(从0开始)的元素,使用set方法将元素e替换下标为index的元素。(添加请用add)

注意:这两种方法都需要数组列表大小大于index时才可以调用,否则会出现类似数组越界访问的错误。

5.使用toArray()方法将数组元素拷贝到一个数组中。

6.删除元素语法:

arr1.remove(0);

对象包装器

1.尖括号中的类型参数不允许是基本类型,需要使用基本类型转化而来的包装器类型。由于每个值都被分别包装在对象中,因此ArrayList<Integer>的效率远低于int[]。

2.将基本类型转换为对象的类叫做对象包装器,如Integer,Double,Float等。

3.包装器类是不可变的,一旦构造便不可改变包装在其中的值。同时包装器是final类,不允许派生。

4.在ArrayList<Integer> list中执行list.add(1)会将数值1转换为Integer.valueOf(1),这个过程称为自动装箱。同理,若对Integer n = 3执行n++语句,则编译器会自动插入一条自动拆箱的语句,自增后再进行自动装箱。

5.==运算符可以用于对象包装器,但检测的是对象是否有相同的内存位置,对于[-128, 127)的short和int型数据来说,比较结果一定成功,否则不一定。

6.装箱和拆箱是编译器的工作,而非虚拟机。

继承的设计技巧

1.将公共方法和字段放在超类中。

2.不要使用受保护的字段。

  • 需要指出,protected方法对于那些不提供一般用途而需要在子类中重新定义的方法十分有用。

3.使用继承实现“is-a”关系。在使用继承前,考虑二者是否为包含关系,若不是,则慎重使用继承。

4.除非所有继承的方法都有意义,否则不要使用继承。

5.在覆盖方法时,不要改变预期的行为。

6.使用多态,而不要使用类型信息。

7.不要滥用反射。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值