封装
概述:
把类中的属性私有化,提供公共的访问方式,保证代码的安全性。步骤:
使用private 修饰成员变量
让成员变量私有化,提供set…方法让用户可以设置值,提供get…方法让用户可以获取值。
private:被此关键字修饰的成员是私有成员,只能在本类中使用。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();
}
}
多态的优点
- 消除类型之间的耦合关系
- 可替换性
- 可扩充性
- 接口性
- 灵活性
- 简化性
多态的好处:可以使程序有良好的扩展,并可以对所有类的对象进行通用处理
多态下的成员变量访问
多态下会访问父类里的成员变量
访问成员变量的的两种方式:
- 直接通过对象名称访问成员变量,看等号左边是谁,优先用谁,没有则向上找
- 间接通过成员方法访问: 看该方法属于谁,优先用谁,没有则向上找
多态下的成员方法访问
在多态下成员方法的访问规则
- 首先检查父类中是否有该方法
- 如果没有,则编译错误
- 如果有,再去调用子类的同名方法
引用类型转换
多态的转型分为向上转型与向下转型两种:
向上转型
概述:
- 向上转型:多态本身是子类类型向父类类型向上转换的过程,这个过程是默认的。
- 当父类引用指向一个子类对象时,便是向上转型。
- 向上转型一定是安全的没有问题的,正确的
缺点:
- 对象一但向上转型为父类,就无法调用子类原本特有的内容
使用格式:
- 父类类型 变量名 = 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;