继承
一种机制,可以进行代码的重用 。允许重用现有类(基类(base class)),亦称超类(super class)、父类(parent class)创建新类(子类(sub class)),亦称派生类(derived class)、孩子类(child class)的过程。子类的对象拥有父类的全部属性与方法,称作子类对父类的继承。
在java中,一个子类只能有一个基类(单一继承),单一继承减少了代码的复杂性,使代码更可靠。不支持多重继承,即一个派生类只能继承于一个基类。
- 派生类的声明:
class People{
public String name;
public int age;
}
class Man extends People{
public int age;
}
- super关键字:super关键字用于从派生类访问基类的成员。
(1)super(参数):调用基类的构造函数;调用构造函数,构造函数不会被继承,必须放在第一行;
(2)super.date :访问基类的数据成员;
(3)super.func() :访问基类的成员方法。
class Base{
public int ma;
public Base(int ma){ //基类的构造函数/构造方法
this.ma = ma;
}
public void fun1(){ //基类的实例方法
System.out.println("Base.fun1()");
}
public void fun2(){
System.out.println("Base.fun2()");
}
}
class Derieve extends Base{ //派生类继承基类 调用构造函数
private int mb;
public Derieve(int a,int b){ //派生类的构造函数
super(a); //super 关键字,调用构造函数
super.ma = 10; //访问基类的数据成员ma
//mma = a;error
super.fun1(); //访问基类的实例方法
this.mb = b;
System.out.println("Derieve.init{}");
}
}
public class Inherit {
public static void main(String[] args) {
Base base = new Base(100);
Derieve derieve = new Derieve(1000,9999);
base = derieve;//多态的基础
//derieve = base;error
}
}
- 派生类构造对象的初始化过程及顺序:
class Base{
public int ma;
public Base(int ma){ //基类的构造函数
fun1();
System.out.println("Base.init()");//构造函数初始化
this.ma = ma;
}
static {
System.out.println("Base.static{}");//基类静态块初始化
}
{
System.out.println("Base.instance{}");//基类实例块初始化
}
public void fun1() {
System.out.println("Base.fun1()"+ this.ma);
}
public static void fun2() {
System.out.println("Base.fun2()");
}
}
class Derieve extends Base{ //派生类继承基类 调用构造函数
private int mb;
public Derieve(int a,int b){ //派生类的构造函数
super(a);//super 关键字,调用构造函数,构造函数不会被继承,必须放在第一行
super.ma = 10; //访问基类的数据成员ma
//mma = a;error
super.fun1(); //访问基类的实例方法
this.mb = b;
System.out.println("Derieve.init{}");//构造函数
}
static {
System.out.println("Derieve.static{}");//静态块
}
{
System.out.println("Derieve.instance{}");//实例块
}
public void fun1() { System.out.println("Derieve.fun1()");
}
public void fun1(int a) { System.out.println("Derieve.fun1(int)");
}
public static void fun2() {
System.out.println("Derieve.fun2()");
}
}
基类初始化顺序:静态块实例化–> 实例块初始化–> 构造函数初始化;
- 派生类初始化顺序:
基类静态块实例化 ----》派生类静态块初始化 ----》
基类实例块初始化 ----》基类构造函数初始化 ----》派生类实例块初始化 ----》派生类构造函数初始化
Base.static{}
Derieve.static{}
Base.instance{}
Base.init()
Derieve.instance{}
Derieve.init{}
- 基类的数据成员在派生类中的访问权限:
- 基类和派生类之间的相互赋值: 基类 = 派生类 //多态的基础
base = derieve;
//derieve = base;error
多态
对象的多态性是指在父类中定义的属性或方法被子类继承之后,可以具有不同的数据类型或表现出不同的行为。
基类引用了派生类的对象,并且,基类和派生类都有同名的覆盖方法。
- java中提供两种多态机制: 重载和重写
- Java的多态性体现在两个方面:
由方法重载实现的静态多态性(编译时多态)
方法重写实现的动态多态性(运行时多态)
编译时多态:在编译阶段,具体调用哪个被重载的方法,编译器会根据参数的不同来静态确定调用相应的方法。
运行时多态:由于子类继承了父类所有的属性(私有的除外),所以子类对象可以作为父类对象使用。程序中凡是使用父类对象的地方,都可以用子类对象来代替。一个对象可以通过引用子类的实例来调用子类的方法。
- 重载:函数名相同,参数列表不同,与函数返回值无关。(overloade)
- 并不一定在同一个类当中,继承关系上也可构成重载。
- 这种编译时确定的模式,又称“静态绑定”。
class Base{
public void fun1() {
System.out.println("Base.fun1()");
}
public static void fun2() {
System.out.println("Base.fun2()");
}
}
class Derieve extends Base{
public void fun1() { System.out.println("Derieve.fun1()");
}
public void fun1(int a) { System.out.println("Derieve.fun1(int)");
}//重载的方法
public static void fun2() {
System.out.println("Derieve.fun2()");
}
}
- 重写/覆盖: 函数名相同,参数列表相同,函数返回值相同。(overwrite)
- 程序运行时,根据运行时对象的类型,调用相应类实现重写的方法。
- 这种运行时确定的模式,称为“动态绑定”。
class A{
public void GetVal(){
System.out.println("Parent");
}
}
class B extends A{
public void GetVal(){
System.out.println("Child");
}
}
public class JICheng {
public static void main(String[] args) {
B b = new B();
A a =(A)b;//将b强制转换为A的类型,相当于A a = new B();
a.GetVal();
}
}
Child
基类引用派生类对象,打印派生类方法。
- 面试问题:构造函数内能否发生多态?
- 可以发生多态
class Base {
private int ma;
public Base(int ma) {
this.ma = ma;
}
public void fun1() {
System.out.println("Base.fun1()");
}
public static void fun2() {
System.out.println("Base.fun2()");
}
}
class Derieve extends Base {
private int mb;
public Derieve(int a,int b) {
super(a); //调用基类的构造函数
this.mb = b;
}
public void fun1() {
System.out.println("Derieve.fun1()");
}
public static void fun2() {
System.out.println("Derieve.fun1()");
}
}
public class DuoTai {
public static void main(String[] args) {
Base base = new Base(10);
base.fun1();
Derieve derieve = new Derieve(1,2);
derieve.fun1();
}
}
Base.fun1()
Derieve.fun1()
对其进行反编译:
- 基类引用派生类对象时,运行期间的多态,又叫动多态。
注意:基类通过实例化一个派生类的对象,看到的是基类的实例调用基类的方法,而反编译实际运行的是派生类的方法,因为基类的方法地址覆盖了派生类的地址。
Base base = new Derieve();
base.fun1();//静多态
base.fun2();//动多态 Derieve.fun1();
反编译:invokespecial 构造函数 invokevirtual 一般函数 invokestatic 静态方法
动多态:发生在运行时—invokevirtual
静多态:发生在编译时—invokestatic
- 总结:
内存分配:
(1)在编译时生成方法表,一个类型只能产生一个方法表。
(2)方法表和类型一一对应,class对象与类型一一对应。
动多态:RunTime—Type—Information(RTTI 机制)
Over…在动多态时,通过反编译观察运行,好好理解。