多态
多态
- 概述:表示事物的多种状态
对象的多态性:一个对象可以有不同的引用来接收,可以使用不同的类型来表示
类型的多态性:一个类型将来可以有不同的子类来实现,一个类型的引用可以接收不同的子类对象 - 多态的前提:
(1)必须要有子父类的继承关系(接口和实现类的实现关系)
(2)必须要有父类的引用指向子类的对象(接口的引用指向实现类的对象) - 目的:
方法的重写
多态中访问成员变量
- 编译看左边,运行看左边
- 如果需要访问某一个变量,在编译阶段就看=左边的类型中,有没有该变量的定义,如果有定义就编译成功。
- 运行某一个变量,看=左边的类型中是如何给该变量赋值,左边类型如何赋值如何使用
多态中访问方法的特点
- 编译看左边,运行看右边
- 编译时期看左边的类型中有没有该方法的定义,有就编译成功,否则编译失败
- 运行时期看右边类型中,该类型时如何对方法进行实现的,右边类型如何实现,如何使 用
多态中访问静态方法的特点
- 编译看左边,运行看左边
- 注意事项:
(1)父类中定义的静态方法,子类可以继承,但是不能被重写
(2)如果子类中定义了一个和父类静态方法一模一样的声明,子类只能使用自己的静态方法,父类继承的静态方法会被隐藏。
向上转型和向下转型
- 向上转型:
使用父类的引用指向子类的对象
特点:不管是访问方法还是访问变量,都需要先编译看左边父类中有定义,才可以使用
本质:限定了对象的访问范围,只能使用父类中定义好的内容 - 向下转型:
将指向子类对象的父类引用,恢复为子类的引用
格式:
子类引用 子类对象 = (子类类型)指向子类的父类引用;
本质:恢复了子类对象的访问访问
public class E {
public static void main(String[] args) {
Animal a = new Cat();
//编译看左边,运行看左边,使用父类中定义的内容,子类中定义的color编译失败
System.out.println(a.name+" "+a.age);//动物 3
//编译看左边,运行看右边,子类中定义的work方法编译失败
a.eat();//猫吃鱼
a.sleep();//动物休息
//向下转型
Cat b = (Cat)a;
b.work();
}
}
class Animal{
String name = "动物";
int age = 3;
String color = "绿色";
public void eat() {
System.out.println("动物吃食物");
}
public void sleep() {
System.out.println("动物休息");
}
}
class Cat extends Animal{
String name = "小猫";
int age = 4;
@Override
public void eat() {
System.out.println("猫吃鱼");
}
public void work() {
System.out.println("猫抓老鼠");
}
}
多态的作用
- 提高代码的扩展性:
可以在某一个方法的参数列表中,定义一个父类的引用,将来可以接收任何子类的对象而且执行的结果也可以根据传入的子类对象,执行子类的特殊内容
public class F {
public static void main(String[] args) {
JuiceMachine jm = new JuiceMachine();
jm.makeJuice(new Apple());
jm.makeJuice(new WaterMelan());
}
}
class JuiceMachine{
public void makeJuice(Fruit f) {
f.flow();
}
}
class Fruit{
public void flow() {
System.out.println("水果汁");
}
}
class Apple extends Fruit{
@Override
public void flow() {
System.out.println("苹果汁");
}
}
class WaterMelan extends Fruit{
@Override
public void flow() {
System.out.println("西瓜汁");
}
}
instanceof关键字
- 格式:
对象名 instanceof 类型 - 特点:
(1)是一个二元运算符(类似于==,只不过是判断是否类型属于)
(2)可以对一个对象进行判断,如果左边的对象属于右边的类型,结果为真;如果左边的对象不属于右边的类型,结果为假 - 作用:
避免向下转型的时候,出现类型转换异常
抽象类
抽象方法
- 抽象:抽取相同的或者相似的内容
- 抽象方法:
(1)只有方法的声明,没有方法的实现内容,为了表示这个方法是一个抽象方法,所以使用一个关键字abstract修饰。
(2)将来子类中都有自己的实现方式,父类中定义的实现内容不需要使用,所以就干脆只定义一个方法的声明即可。
抽象类特点
- 可以定义抽象方法的类型就是一个抽象类,为了表示一个抽象类,也需要使用一个关键字abstract来修饰。
- 抽象类和抽象方法的关系:
抽象方法必须在抽象类中定义
抽象类中可以不存在抽象方法
抽象类中既可以定义抽象方法,也可以定义非抽象方法 - 抽象类不能实例化(创建对象)
因为如果抽象类可以创建对象,就可以调用自己的方法
如果调用了抽象方法,那么没有实现内容去执行 - 抽象类子类的前途:
如果子类重写完父类中定义的每一个抽象方法,子类就变为一个普通类,可以正常创建对象
如果子类没有重写完父类中定义的抽象方法,子类就只能定义为一个抽象类,不能创建对象 - 虽然类型是一个抽象类,但是和其他类一样编译之后,形成一个独立的字节码文件
//定义一个抽象类
abstract class A{
//定义一个抽象方法
public abstract void get();
//可以定义一个普通方法
public void show() {
System.out.println("A");
}
}
//子类没有重写父类的抽象方法,变成抽象类
abstract class B extends A{
}
//子类重写父类抽象方法,为普通类
class C extends A{
@Override
public void get() {
System.out.println("C");
}
}
抽象类成员特点
- 抽象类中既可以定义变量也可以定义常量,但是不能被抽象因为定义的变量和常量名已经有一层被抽象的含义,所以不需要再进行抽象
- 抽象类中既可以定义抽象方法,也可以定义非抽象方法
如果定义了抽象方法,子类需要去重写
如果定义了普通方法,子类可以去继承 - 抽象类需要定义构造方法吗?
抽象类需要定义构造方法,因为抽象类有子类,子类需要访问父类的构造方法
接口
接口
- 概述:
生活中的角度:
接口其实就是表示一类规则,想要使用某些事物,就需要满足规则多需要的要求,否则就不能使用。
Java语言:
用来定义规则特殊类型
专门用来定义方法规则的特殊类型
专门用来定义抽象方法的特殊类型 - 好处:
(1)接口本身表示一类规则,一旦将规则定义好之后,只需要按照接口中的规则实现 对应的功能即可。
(2)接口可以降低类与类之间的耦合性。
接口的特点
- 接口的定义:需要使用一个关键字 interface
- 接口和抽象方法的关系:
(1)接口中只能定义抽象方法,如果不加abstract,接口可以默认提供
(2)不能定义非抽象方法 - 接口本身不能实例化(创建对象)
- 接口没有子类,但是接口可以有实现类
(1)如果一个类型想要实现一个接口,需要使用implements
(2)以后接口和类之间不是子父类的关系,而是接口和实现类的关系
(3)如果一个类型实现一个接口之后,这个类型中,就可以继承接口中的抽象方法 - 接口的实现类前途:
(1)如果实现类重写了接口中的每一个抽象方法,就变为一个普通类
(2)如果实现类没有重写完每一个抽象方法,这个类就变为一个抽象类 - 接口在编译时候,同样会生成对应的字节码文件
//定义一个接口
interface Inter{
//接口中只能定义抽象方法
public abstract void show();
}
//重写接口中的抽象方法,变成普通类
class MyInter implements Inter{
public void show() {
System.out.println("实现类");
}
}
//不重写接口的抽象方法,变成抽象类
abstract class MyInter2 implements Inter{
}
接口中的成员特点
- 接口中不能存在变量,可以存在常量
接口中可以定义变量,但是默认加上public static final强制变为一个公共的静态常量
接口中可以定义常量,也会提供public static - 接口和方法的关系:
接口中只可以定义抽象方法,用于让实现类去重写
接口中不能定义非抽象方法 - 接口中需要定义构造方法?
不需要
因为实现类将来访问的是父类的构造方法,不需要访问接口的构造方法
因为接口中不能定义成员变量,就不需要给变量赋值,所以不需要定义构造
类、接口互相的关系
- 类与类之间:
继承关系 extends
继承特点:可以单继承、不能多继承、可以多层继承 - 类与接口:
实现关系 implements
实现特点:可以单实现,可以多实现,不可以多层实现
多实现:多个接口之间使用逗号分隔即可
实现了多个接口之后,多个接口的抽象方法,实现类都可以继承
如果多个接口中有一些相同的方法声明,实现类只需要重写一个即可
不可以多层实现:一个类型实现一层之后,那就是类与类的关系
一个实现类,可以在继承一个父类之后,可以同时实现多个接口 - 接口与接口:
继承关系
特点:
可以单继承,可以多继承,可以多层继承
可以多继承:每一个父类的抽象方法,子类都可以继承
可以多层继承:最底层的子接口,可以拥有以上所有父类和间接父类的抽象方法 - 抽象类和接口的区别:
(1)抽象类/类:一般定义物体本身固有的属性和行为
(2)接口:一般定义物体通过扩展或者学习得来的行为
匿名内部类
- 概述:没有名字的类型
匿名内部类是属于局部内部类的一种 - 格式:
new 父类类名(接口名){
对父类或者接口方法1的重写
对父类或者接口方法1的重写
};
父类引用(接口引用) = new 父类类名(接口名){
对父类或者接口方法1的重写
对父类或者接口方法1的重写
};
3、使用场景:
如果只需要对接口或者父类中的抽象方法调用一次,可以匿名内部类方式创建一个匿名 对象来简化操作