多态:同一个引用类型,使用不同的实例而执行 不同操作 。
指两个或多个属于不同类的对象,对同一个消息 (方法调用)作出不同响应的能力
同一个类在不同场合下表现出不同的行为特征
程序中的多态
父类引用, 子类对象
1、封装:隐藏内部实现,稳定外部接口
- 封装指的是属性私有化,根据需要提供setter和getter方法来访问属性。即隐藏具体属性和实现细节,仅对外开放接口,控制程序 中属性的访问级别。
封装目的:增强安全性和简化编程,使用者不必在意具体实现细节,而只是通过外部接口即可访问类的成员。
2、继承:子类继承父类成员,实现代码复用
- 继承是指将多个相同的属性和方法提取出来,新建一个父类。
Java中一个类只能继承一个父类,且只能继承访问权限非private的属性和方法。 子类可以重写父类中的方法,命名与父类中同名的属性。
继承目的:代码复用。
3、多态:不同子类对同一个消息作出不同的反映
- 多态可以分为两种:设计时多态和运行时多态。
设计时多态:即重载,是指Java允许方法名相同而参数不同(返回值可以相同也可以不相同)。
运行时多态:即重写,是指Java运行根据调用该方法的类型决定调用哪个方法。
多态目的:增加代码的灵活度。
4、抽象:
- 就是对同一个事的共有的属性(特征)和方法(功能/行为)进行抽取、归纳、总结。如:汽车都有轮子、发动机等这些就是汽车的属性,汽车能跑、能载人载物等这些就是汽车的功能。这样就可以把汽车的这些功能与属性抽取出来写在一个类中,供汽车这一类事物使用。
一、封装
-
隐藏类内部细节(属性),再提供共有访问细节的方法,在方法内可添加控制语句 封装所表示的含义就是隐藏内部细节,提供公开的接口给其他人使用,其他人再使用时 无需关注内容,只需调用即可。
-
对类中属性的封装
1、将属性私有化 添加 private 修饰符
2、添加 get/set 方法,提供对属性的修改、获取。(get/set方法快捷键shift+alt+s)
3、在方法内添加控制语句实现对数据的验证
public class Demo1 {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
private int age;
}
二、继承
-
当多个类中有重复的代码(属性和方法),我么可以将重复的属性和方法提取到另一个类中,再继承该类,实现代码的重用,相当于对类的进一步抽象
这两个类就是继承关系,符合继承关系的类,一定符合 IS-A 关系 -
子类继承父类,哪些不能被继承?
1、私有的属性和方法
2、代码块
3、构造函数
4、如果属性是默认的访问权限、又不在同一包下 -
子类继承父类的属性和方法后,可以直接使用,相当于是子类自己的属性和方法一样
在子类中this表示当前类对象,还可以用super表示父类对象 -
当我们new出一个子类对象时
1、找到子类的父类,查看该父类是否又继承了其他类,如果还有父类,那么继续找父类对象
2、直到找到Object(默认所有类的父类),从最顶级开始的创建对象,从上到下创建出所有的祖宗对象,创建对象默认调用无参构造函数,所以要求一定要有无参构造函数
3、最后才创建出当前的子类对象
4、在new子类对象时,子类的构造函数会隐式的调用父类的无参构造函数,如果父类没有默认的无参构造函数,要求在子类的构造函数中显示的调用父类有参的构造函数(super(xxxx)) -
super的使用
在子类中使用super表示父类对象,可以通过super.属性 super.方法 使用父类的属性和方法
还可以通过super调用父类的构造函数 super(),super(有参)
this也可以与super一样使用this(),this(有参)调用当前类的构造函数
构造函数只能在构造函数内使用,不能在普通方法内使用,并且this与super不能同时使用
当子类对象调用重写的方法时,调用的是子类的方法,而不是父类中被重写的方法。
要想调用父类中被重写的方法,则必须使用关键字 super。 -
抽象类
在面向对象编程过程中,我们如果new出了一个抽象的对象,那么将无意义,我们可以通过abstract关键字修饰这个类,让这个类无法new出对象
public abstract class 类名
这个类我们叫做抽象类,抽象类不能实例化,给其他类继承用的 -
访问修饰符protected(受保护的)
访问修饰符 | 本类 | 同包 | 子类 | 其他 |
---|---|---|---|---|
private | ✓ | |||
默认(friendly) | ✓ | ✓ | ||
protected | ✓ | ✓ | ✓ | |
public | ✓ | ✓ | ✓ | ✓ |
-
重写
子类方法的名称、参数定义和返回类型(可以是其子类)必须与父类方法的名称、参数定义和返回类型一致;
子类方法不能缩小父类方法的限定修饰符;
子类方法不能抛出比父类方法更多的异常;
方法覆盖只存在于子类和父类之间;
父类的静态方法,子类不能重写,可以定义与父类一模一样的方法 -
重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写!
重写的好处在于子类可以根据需要,定义特定于自己的行为。 也就是说子类能够根据需要实现父类的方法。
重写方法不能抛出新的检查异常或者比被重写方法申明更加宽泛的异常。例如: 父类的一个方法申明了一个检查异常 IOException,但是在重写这个方法的时候不能抛出 Exception 异常,因为 Exception 是 IOException 的父类,只能抛出 IOException 的子类异常。
在面向对象原则里,重写意味着可以重写任何现有方法。 -
方法的重写规则
参数列表必须完全与被重写方法的相同。
返回类型与被重写方法的返回类型可以不相同,但是必须是父类返回值的派生类(java5 及更早版本返回类型要一样,java7 及更高版本可以不同)。
访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类的一个方法被声明为 public,那么在子类中重写该方法就不能声明为 protected。
父类的成员方法只能被它的子类重写。
声明为 final 的方法不能被重写。
声明为 static 的方法不能被重写,但是能够被再次声明。
子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为 private 和 final 的方法。
子类和父类不在同一个包中,那么子类只能够重写父类的声明为 public 和 protected 的非 final 方法。
重写的方法能够抛出任何非强制异常,无论被重写的方法是否抛出异常。但是,重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更广泛的强制性异常,反之则可以。
构造方法不能被重写。
如果不能继承一个方法,则不能重写这个方法。 -
重载
重载(overloading) 是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。
每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表。
最常用的地方就是构造器的重载。
- 重载规则:
被重载的方法必须改变参数列表(参数个数或类型不一样);
被重载的方法可以改变返回类型;
被重载的方法可以改变访问修饰符;
被重载的方法可以声明新的或更广的检查异常;
方法能够在同一个类中或者在一个子类中被重载。
无法以返回值类型作为重载函数的区分标准
- 重写与重载
比较点 | 重载 | 重写 | |
---|---|---|---|
1 | 英文单词 | OverLoad | Override |
2 | 概念 | 方法名相同,参数的类型或个数不同 | 方法名称,参数的类型或个数完全相同 |
3 | 注意点 | - - | 访问权限不能变小 |
4 | 范围 | 在一个类中发生 | 在继承类中发生 |
区别点 | 重载方法 | 重写方法 |
---|---|---|
参数列表 | 必须修改 | 一定不能变小 |
返回类型 | 可以修改 | 一定不能修改 |
异常 | 可以修改 | 可以减少或删除,一定不能抛出新的或者更广的异常 |
访问 | 可以修改 | 一定不能做更严格的限制(可以降低限制) |
三、多态
- 多态:同一个引用类型,使用不同的实例而执行不同操作
里氏替换原则(LSP):在一个软件系统中,子类对象可以替换所有使用的父类对象,且程序行为没有变化
什么时候使用多态:当程序中需要使用一个类型代表多个类型的时候 - 多态使用的前提:
1、继承(实现接口)
2、重写
3、父类引用指向子类对象(父类(接口) 变量名 = new 子类(); ) - 如何使用多态
1、创建父类变量,实例化子类对象
2、把父类类型作为参数类型,该子类及子类对象作为参数传入
3、把父类类型作为方法的返回类型,那么任意子类对象都可以返回
代码例子
package day24;
public class Test {
public static void main(String[] args) {
show(new Cat()); // 以 Cat 对象调用 show 方法
show(new Dog()); // 以 Dog 对象调用 show 方法
Animal a = new Cat(); // 向上转型
a.eat(); // 调用的是 Cat 的 eat
Cat c = (Cat)a; // 向下转型
c.work(); // 调用的是 Cat 的 work
}
public static void show(Animal a) {//创建父类变量
a.eat();
// 类型判断
if (a instanceof Cat) { // 猫做的事情
Cat c = (Cat)a;
c.work();
} else if (a instanceof Dog) { // 狗做的事情
Dog c = (Dog)a;
c.work();
}
}
}
abstract class Animal {
abstract void eat();
}
class Cat extends Animal {
public void eat() {
System.out.println("吃鱼");
}
public void work() {
System.out.println("抓老鼠");
}
}
class Dog extends Animal {
public void eat() {
System.out.println("吃骨头");
}
public void work() {
System.out.println("看家");
}
}
- 输出:
吃鱼
抓老鼠
吃鱼
抓老鼠
吃骨头
看家 - 多态的优点
1、消除类型之间的耦合关系
2、可替换性
3、可扩充性
4、接口性
5、灵活性
6、简化性 - instanceof运算符
该运算符用来判断一个对象是否属于一个类或者实现了一个 接口,(对象 instanceof 类或接口)结果为true或false
在强制类型转换之前通过instanceof运算符检查对象的真实 类型,可以避免类型转换异常,从而提高代码健壮性
例子如上面代码判断是属于猫对象还是狗对象