类的继承与对象创建过程

继承基本概念

根父类Object

Java中,没有声明父类,也会有一个隐含的父类Object。它是所有类的父类
提供了基础的方法:获取对象的类型getClass、对象哈希值hashCode、equals、clone、toString;
notify、notifyAll和wait方法与synchronized配合使用,用于加锁之后的信号量等待和通知发送;
finalize与GC相关,当垃圾收集器将其对象标记为可回收时,会调用,可以重写该方法实现特殊的回收处理;
hashCode方法是native方法,在子类中如果需要使用HashMap、HashSet等,可能要对hashCode进行重写,以保证对象hash值的唯一性;
equals方法默认是比较两个对象的内存索引,一般在子类中需要重写,否则等同于"==",规则过于严苛。

    public boolean equals(Object obj) {
        return (this == obj);
    }

toString方法默认实现是类名@对象hash值的十六进制(根父类Object无法获知子类的细节,这样可以描述每一个对象)

    public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }

clone方法需要在子类声明实现了标记接口Cloneable才可调用,否则抛出CloneNotSupportedException;需要注意的是,jdk默认提供的拷贝是浅拷贝(即值拷贝),对于非基本数据类型属性,拷贝的是该属性的内存索引,当拷贝对象变动时会影响到原对象。通常需要根据需要重写clone方法,实现深拷贝。
在这里插入图片描述
Java只允许单一继承,即一个类最多有一个直接父类;除了私有的属性和方法外,子类继承了父类的其他属性和方法。
单一继承的优势:避免了多个父类中存在相同方法时的二义性。
Java中可以implements多个接口,也达到了多继承的目的;但是对于接口而言,根据接口隔离和单一职责原则,接口间不应该存在相同的方法,避免了多继承的二义性。

继承(或实现),是从分类的角度来考虑的。而从使用的角度考虑,使用者一般只能知道某种父类(更多的是接口)的定义,并且依赖于父类进行编程。而对象的具体行为却是通过实际类型对象动态绑定的,这就是多态

super和this的差异
super用来代指直接父类;this代指本对象;通过super或this调用方法时都要在方法的首行使用;但是this引用到一个对象,是真实存在的,可以作为函数参数、返回值,但super只是一个关键字。

继承的细节

构造方法
对于存在有参构造方法的类,jdk不会再默认生成无参构造方法,要自己补充默认构造方法;避免在子类中使用super()报错;
另一方面,构造方法中尽量不要调用函数,如果必须调用也应该只调用private的方法,或者final的方法,避免调用可以被子类重写的方法。
重名与静态绑定
对于父类和子类中重名的变量或静态方法,是根据访问对象的类型进行绑定的,在编译阶段即可决定,是静态绑定
重载和重写
对于重名的方法,首先是按照参数类型进行静态绑定的(在所有版本中匹配最符合的方法);如果仍然存在同名方法,则根据对象的具体类型进行动态绑定。
父子类型转换
子类向上转型为父类没有问题,但是子类独有的方法或访问权限会被约束,只能使用父类暴漏出去的方法;
父类向下转型为子类存在风险,只有该对象真的是某一子类的实例时(instanceof),才没有问题;因此,要在转型前做具体类型判断。
访问权限
private < 默认(包可见) < protected(子类+包可见) < public
在模板方法中经常使用到,父类定义一系列的动作,由子类对每个动作进行重写,protected权限保证了子类的可见性
可见性重写
子类在重写父类方法时,不能降低父类的可见性(可以提升)。以保证继承的"is-a"关系。
final防止继承
继承实际上破坏了封装性,体现在子类和父类间可能存在着实现细节上的依赖。根据设计原则,接口和组合要优于类的继承。

对象创建过程

对象创建时,首先判断该类是否被加载,如果没有加载会先加载类,然后完成初始化。Jdk只有在首次使用某个类时才会加载该类。
类加载
类的信息包括:类变量、类初始化代码、类方法、实例变量、实例初始化代码、实例方法、父类信息引用;
类初始化代码包括:定义静态变量时的赋值语句、静态初始化代码块;
实例初始化代码包括:定义实例变量时的赋值语句、实例初始化代码块、构造方法;
类加载过程:分配内存保存类的信息;给类变量赋默认值;加载父类;设置父子关系;执行类初始化代码;
后续补充——类的加载过程和双亲委派

对象创建
分配内存,对所有实例变量赋默认值,执行实例初始化代码。

因此一个对象在创建时,

  1. 加载类(已加载则不会再次加载):会先后执行到父、子类的类初始化代码
  2. 父类属性实例化过程:对实例变量赋初值、实例化初始代码块(先后执行声明时赋值、实例化代码块、构造方法);
  3. 子类属性实例化过程:对实例变量赋初值、实例化初始代码块;
public class ExtendTest {
    public static void main(String[] args) {
        Child child = new Child();
    }
}

class Base {
    public static int s;
    private int a = 2;
    static {
        System.out.println("Base static code block, S: " + s);
        s = 10;
    }
    {
        System.out.println("Base code block, a=" + a);
        a = 1;
    }
    public Base() {
        System.out.println("Base constructor, a=" + a);
        a = 5;
    }
}

class Child extends Base{
    private int a;
    static {
        System.out.println("Child static code block, S: " + s);
        s = 100;
    }
    {
        System.out.println("Child code block, S: " + s);
        System.out.println("Child code block, a=" + a);
        a = 1;
    }
    public Child() {
        System.out.println("Child constructor, a=" + a);
        a = 5;
    }
}
Base static code block, S: 0
Child static code block, S: 10
Base code block, a=2
Base constructor, a=1
Child code block, S: 100
Child code block, a=0
Child constructor, a=1
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值