面向对象三大特性


封装

  1. 概述:
    把类中的属性私有化,提供公共的访问方式,保证代码的安全性。

  2. 步骤:

  3. 使用private 修饰成员变量

  4. 让成员变量私有化,提供set…方法让用户可以设置值,提供get…方法让用户可以获取值。
    private:被此关键字修饰的成员是私有成员,只能在本类中使用。

  5. this关键字:
    当成员变量和局部变量同名时,可以使用this.变量表示访问成员变量。

继承

概述

子类会继承父类的所有的非私有成员(构造方法除外);
使用 extends关键字;
所有的类都直接或间接继承自Object类;

为什么要使用继承

  • 好处:
    • 代码的复用性
    • 是多态的前提。
  • 弊端:让类和类之间产生了关系(耦合的产生)

注意

  • 在new子类对象时,JVM会先new出父类对象
  • 构造函数第一行默认有个super(),super是指向父类的引用

继承示例

//子类 继承 父类
public class 子类名 extends 父类名{
}

方法重写

概述:

  • 当子类出现和父类中完全相同的方法时,子类会重写父类中的方法。

面试题:

  • 方法重载和方法重写的区别?或overwrite和overload()的区别?
    • 方法重载:在同一个类中,方法名相同,参数列表不同构成重载关系。
    • 方法重写:子类出现和父类完成相同的方法时,子类会重写父类中的方法。

多态

概述

  • 多态是同一个行为具有多个不同表现形式或形态的能力。
  • 多态就是同一个接口,使用不同的实例而执行不同操作

多态存在的三个必要条件

  • 继承
  • 重写
  • 父类引用指向子类对象:Parent p = new Child();

多态的格式

// 多态存在的三个必要条件
//    继承
//    重写
//    父类引用指向子类对象:Parent p = new Child();
public class Parent{
  public void demo(){System.out.println("父类打印信息...")}
}
public class Child extends Parent{// 1 继承
  // 2 写父类方法
  public void demo(){System.out.println("子类打印信息...")}
}
public class Run{
  public static void main(String [] args){
    // 3 父类引用指向子类对象
    Parent p = new Child();
  }
}

多态的优点

  1. 消除类型之间的耦合关系
  2. 可替换性
  3. 可扩充性
  4. 接口性
  5. 灵活性
  6. 简化性

多态的好处:可以使程序有良好的扩展,并可以对所有类的对象进行通用处理

多态下的成员变量访问

多态下会访问父类里的成员变量
访问成员变量的的两种方式:

  1. 直接通过对象名称访问成员变量,看等号左边是谁,优先用谁,没有则向上找
  2. 间接通过成员方法访问: 看该方法属于谁,优先用谁,没有则向上找

多态下的成员方法访问

在多态下成员方法的访问规则

  1. 首先检查父类中是否有该方法
  2. 如果没有,则编译错误
  3. 如果有,再去调用子类的同名方法

引用类型转换

多态的转型分为向上转型与向下转型两种:

向上转型

概述:

  • 向上转型:多态本身是子类类型向父类类型向上转换的过程,这个过程是默认的。
  • 当父类引用指向一个子类对象时,便是向上转型。
  • 向上转型一定是安全的没有问题的,正确的

缺点:

  • 对象一但向上转型为父类,就无法调用子类原本特有的内容

使用格式:

  • 父类类型 变量名 = new 子类类型();
    // 如:
    Animal a = new Cat();

向下转型

概述:

  • 父类类型向子类类型向下转换的过程,这个过程是强制的。
  • 一个已经向上转型的子类对象,将父类引用转为子类引用,可以使用强制类型转换的格式,便是向下转型。

使用格式:

  • 子类类型 变量名 = (子类类型) 父类变量名;
    // 如:
    Cat c =(Cat) a;

为什么要转型

使用多态调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误。
也就是说,不能调用子类拥有,而父类没有的方法。
编译都错误,更别说运行了。这也是多态给我们带来的一点”小麻烦”。所以,想要调用子类特有的方法,必须做向下转型。

转型报错(ClassCastException)

ClassCastException,类型转换异常!
这是因为,这两个类型并没有任何继承关系,不符合类型转换的定义。
为了避免ClassCastException的发生,Java提供了instanceof关键字,给引用变量做类型的校验,格式如下:

变量名 instanceof 数据类型
如果变量属于该数据类型,返回true。 
如果变量不属于该数据类型,返回false。

多态内存描述 转载自CSDN博主学术期刊编辑

1:Animal a = new Cat();
1.1:在栈中创建区域,类型为Animal,变量名:a;
1.2:在堆中new Cat();占用一块区域。地址值:[0x3a4]
1.3:spuer()实例化父类Animal。
1.3.1:new Animal();占用一块区域,地址值:0x3ab;
1.3.2:引用着在方法区中初始化[Animal中的所有方法,该引用为:[0x754]]。
1.3.3:将Animal()引用赋给spuer();spuer引用着Animal();
1.4:在方法区中初始化Cat类的所有方法,引用值为0x343。
1.5:将0x3a4赋给栈中的变量a;a就开始引用Cat()。


2:a.eat();
2.1:通过[0x3a4]找到Cat。
2.2:编译时期:先spuer()找到Animal中的方法。如果没有,则报错。
2.3:运行时:直接在Cat中找到eat(),当Cat中没有,再去Animal中找。
2.4:将eat()方法要方法区压栈,执行,输出:SOP(猫吃鱼);


3:a = new Dog();
3.1:在堆new Dog(),开辟一块新区域,地址值:0x87xfds
3.2:spuer();实例化父类Animal
3.2.1:new Animal()开辟新区域,地址值0x33fa;
3.2.2:成员方法引用着方法区中已初始化的[0x754];
3.2.3:将Animal地址值0x33fa赋给spuer();;spuer引用着Animal();
3.3:在方法区中初始化Dog类的所有方法,引用值为0x422ac。
3.4:将[0x87xfds]赋给栈中的变量a; a不再引用Cat,而是引用着Dog;这时堆中的Cat已成为垃圾,等待JVM空闲时来回收。


4:a.eat();
4.1:通过a变量引用值找到堆中标记为0x87xfds的区域。
4.2:编译时:先进super();去检查Animal引用的方法区中有没有eat()方法,如有没有则报错。
4.3:运行时:直接去Doa方法区中找到eat();如果Doa没有,再去执行super()调用父类的eat()方法。
4.4:从方法区中将eat()压栈,执行(SOP(‘狗吃粮’))。


5:a.shudy();
5.1:通过a变量引用值找到堆中标记为0x87xfds的区域。
5.2:编译时:先进super();去检查Animal引用的方法区中有没有shudy()方法,结果Animal中没有shudy()方法,所以就在编译时期就报错。


6:Dog d = (Dog)a;
6.1:在栈中开辟区域,存储类型为Dog,变量名d
6.2:将a向下转型,从Animal转为Dog来引用Dog;(将a变量赋给d)
6.3:a和d都指向堆中同一个Dog对象。


7:d.eat();//从Dog方法中压栈执行eat()方法,然后弹栈;
8:d.shudy();//从Dog方法中压栈执行shudy()方法,然后弹栈;
9:Cat c = (Cat) a;
9.1:在栈中开辟一块区域,存储类型:Cat,变量名称:c
9.2:将a向下转型,将Anmail引用Dao转为Cat引用Dog;结果抛出类型转换异常。Dog不能被转为Cat;


文章作者: zrh
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 zrh !
  目录