继承
继承的概念
通过“继承”一个现有的类,可以使用已经定义的类中的方法和变量。
类继承的基本语法
<modifier> class <name> [extends <superclass> ]
{
<declaration> *
}
继承的特点
- 类的方法可以被继承;
- 类的构造方法和用private修饰的属性及方法不能被继承;
- 在Java中,一个类只能继承一个父类,这种方式叫做单一继承;
- 一个父类可以被多个子类所继承;
- java.lang.Object类是所有类的父类。
注: 从内存的角度来说,父类中的私有属性和方法其实可以被继承,但子类无法访问。
继承的应用
上转型
语法规则:
<父类型><引用变量名> = new <子类型>();
对象的上转型
父类的引用变量指向子类对象的地址
特点:
- 父类变量可以看到子类继承或隐藏的属性;
- 父类变量不能看到子类新增的属性和方法。
方法的上转型
父类的任何方法都无法找到子类新增的属性和方法。
上转型的作用
- 上转型体现了Java中多态的特点,使实例化的时候可以根据不同的需求实例化不同的对象;
- 使代吗更简洁,如果没有使用上转型,则每新建一个子类需要分别实例化。
隐藏和覆盖
覆盖:
子类对父类方法的重写,要求方法名和参数签名完全一样(参数不能是子类)。
覆盖的特点
- 子类实例方法不能覆盖父类的静态方法;
- 子类的静态方法也不能覆盖父类的实例方法。
隐藏:
子类对父类属性或静态方法的重写,此时父类的同名的属性或者方法形式上不见了,实际是还是存在的。
隐藏的特点
- 当发生隐藏的时候,声明类型是什么类,就调用对应类的属性或者方法,而不会发生动态绑定;
- 属性只能被隐藏,不能被覆盖;
- 变量可以交叉隐藏:子类实例变量/静态变量可以隐藏父类的实例/静态变量。
对象的创建
创建过程:
- 创建子类对象时不会创建父类对象,但会先为父类的私有属性在堆里开辟空间(这块空间和父类中定义的属性、方法一样都会和父类做动态绑定),并默认初始化;
- 为子类从父类继承的属性和新增属性在堆里开辟空间;
- 为父类方法和子类重写(覆盖)父类的方法开辟空间;
- 为子类新增的方法开辟空间。
继承中构造方法的调用
在创建子类对象的时候:编译器会默认首先给子类调用父类的构造方法(传的是子类对象的地址,默认调用无参构造方法),然后再调用子类自己的构造方法。
但是,这个默认调用父类的构造器是有前提的:父类有默认构造器。如果父类没有默认构造器,我们就要必须显示的使用super()来调用父类构造器,而且必须是在子类构造器中做的第一件事(第一行代码),否则编译器会报错:无法找到符合父类形式的构造器。
特点
- 父类的构造方法在当前地址找不到属性时,会通过和该方法动态绑定的类,到该类动态绑定的属性中继续查找;
- 父类的构造方法不能为子类新增的属性初始化;
- 子类的构造方法可以为子类新增的属性和继承自父类的属性初始化,不能为父类的私有属性初始化。
代码示例及内存图分析
class Animal
{
private int weight; //父类中的私有属性
Boolean sex;
public Animal()//父类的构造方法
{System.out.println("Animal(): "+this);//this为Dog类型,指向子类的地址
this.weight=20;//this所指的地址中并没有weight属性,因为它是私有的
//父类的构造方法在当前地址找不到该属性时会在该方法静态绑定的属性中继续查找
this.sex=true;
//this.age=10;//看不到子类新增的属性
} //父类的任何方法无法找到子类新增的属性和方法,这就是方法的上转型
public int getWeight(){
return this.weight;//返回该方法动态绑定的属性weight
}
public void setWeight(int w){
this.weight=w;//this所指的地址中并没有weight属性,因为它是私有的
//父类的方法在当前地址找不到该属性时会在该方法动态绑定的属性中继续查找
this.sex=false;
}
}
class Dog extends Animal
{ /**子类新增属性*/
int age;
public Dog()//子类的构造方法{
System.out.println("Dog(): "+this);//this为Dog类型,指向子类的地址
this.age=10;//子类的构造方法可以为继承的属性和子类新增属性初始化
this.sex=false;
//this.weight=20;//子类的构造方法不能为父类的私有属性初始化
}
/**
*子类新增方法
*/
public void Bark(){
this.sex=false;//子类的方法可以为继承的属性和子类新增属性初始化
System.out.println("Wang~~Wang~~~");
}
}
public class MyDog{
private static Animal myAnimal;//在方法区的静态数据区创建一个类型为Animal的类,并和MyDog.class静态绑定
public static void main(String args[])
{
myAnimal=new Dog();//创建一个Dog类型的子类对象(不会自动创建父类对象)
//子类自动继承父类中除构造方法和用private修饰的属性以外的所有属性
myAnimal.setWeight(50);//调用父类中的方法对父类中的私有属性weight进行初始化
System.out.println("My Dog's Weight is"+myAnimal.getWeight());
// myAnimal.Bark();//父类变量不能看到子类新增的属性和方法,这就是对象的上转型
}
}
内存图(简略版):
说明:本文仅用作学习笔记,无其他用途,如有冒犯可联系本人删除