多态
- 多态按字面意思就是"多种状态",是面向对象的程序设计语言最核心的特征。从一定角度看,封装和继承几乎都是为多态而准备的。
多态的分类
- 编译时多态(设计时多态):方法重载
- 运行时多态:Java运行时系统根据调用该方法的实例的类型来决定选择调用哪个方法则被称为运行时多态
- 我们平时说的多态,多指运行时多态
- 一个引用变量到底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。
多态的必要条件
- 继承
- 重写
- 父类引用指向子类对象
向上类型转换:将子类型转换为父类型
- 隐式/自动类型转换,是小类型到大类型的转换
- 对于向上的类型转换,不需要显示指定,既不需要加上前面的小括号和父类类型名
向下类型转换:将父类型转换为子类型
- 将一个指向子类对象的父类引用赋值给一个子类的引用
- 强制类型转换。是大类型到小类型
- 父类型的引用必须指向转型的子类的对象,即指向谁才能转换成谁。不会编译会出错。
- 通过instanceof运算符,来解决引用对象的类型,避免类型转换的安全性问题
package com.imooc.test;
import com.imooc.animal.Animal;
import com.imooc.animal.Cat;
import com.imooc.animal.Dog;
public class test {
public static void main(String[] args) {
//Animal one = new Animal();
/*向上转型、隐式转型、自动转型
父类引用指向实例,可以调用子类重写父类的方法,无法调用子类独有方法
小类转型为大类
*/
Animal two = new Cat();
Animal three = new Dog();
//one.eat();
two.eat();
three.eat();
System.out.println("==========================");
/*向下转型、强制类型转换
子类引用指向父类对象,此处必须进行强转
使用instanceof运算符判断能否强制转换
*/
if(two instanceof Cat) {
Cat temp = (Cat) two;
temp.eat();
temp.run();
temp.getMonth();
System.out.println("two 可以转换为 Cat");
}
if(two instanceof Dog) {
Dog temp = (Dog) two;
temp.eat();
temp.sleep();
temp.getSex();
System.out.println("two 可以转换为 Dog");
}
}
}
代码语言:javascript复制package com.imooc.animal;
public class Cat extends Animal{
private double weight;
public Cat() {
}
public Cat( String name, int weight, int month) {
super(name, month);
this.weight = weight;
}
public double getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
public void run(){
System.out.println("小猫快乐的奔跑");
}
@Override
public void eat(){
System.out.println("猫吃鱼");
}
}
代码语言:javascript复制package com.imooc.animal;
public class Dog extends Animal{
public String sex;
public Dog() {
}
public Dog(String name, int month, String sex) {
this.setMonth(month);
this.setName(name);
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public void sleep(){
System.out.println("小狗要午觉zzz");
}
@Override
public void eat() {
System.out.println("狗爱吃肉");
}
}
注意事项
- 父类引用指向子类实例时,可以调用子类重写父类的方法以及直接继承父类的方法,无法调用子类特有的方法
- 静态static方法属于特殊情况,静态方法只能继承,不能重写。调用的时候用谁引用,则调用谁的版本
Java抽象类
在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。抽象类除了不能实例化对象之外,类的其它功能依然存在,成员变量、成员方法和构造方法的访问方式和普通类一样。 由于抽象类不能实例化对象,所以抽象类必须被继承,才能被使用。也是因为这个原因,通常在设计阶段决定要不要设计抽象类。 父类包含了子类集合的常见的方法,但是由于父类本身是抽象的,所以不能使用这些方法。在Java中抽象类表示的是一种继承关系,一个类只能继承一个抽象类,而一个类却可以实现多个接口。
抽象方法
- abstract也可用于方法,称为抽象方法
- 抽象方法没有方法体
- 抽象方法必须在抽象类里
- 抽象方法必须在子类中被实现,除非子类是抽象类
使用规则
- abstract定义抽象类
- 抽象类不能直接实例化,只能被继承,可以通过向上转型完成对象实例
- abstract定义抽象方法,不需要具体实现
- 包含抽象方法的类是抽象类
- 抽象类中可以没有抽象方法
- 子类如果没用重写父类所有的抽象方法,则也要定义为抽象类
- abstract不能与static 、final、 private共存
- 抽象方法在子类是实现时访问权限必须大于等于父类方法
package com.imooc.animal;
//抽象类:不允许实例化,可以通过向上转型,指向子类实例
public abstract class Animal {
private String name;
private int month;
public Animal() {
}
public Animal(String name, int month) {
this.name = name;
this.month = month;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getMonth() {
return month;
}
public void setMonth(int month) {
this.month = month;
}
//抽象方法:不允许包含方法体,子类中需要重写父类中的抽象方法;否则子类必须是抽象类
// static final private 不能与 abstract 共存
public abstract void eat();
}
接口
接口在JAVA编程语言中是一个抽象类型,是抽象方法的集合,接口通常以interface来声明。一个类通过继承接口的方式,从而来继承接口的抽象方法。接口并不是类,编写接口的方式和类很相似,但是它们属于不同的概念。类描述对象的属性和方法。接口则包含类要实现的方法。除非实现接口的类是抽象类,否则该类要定义接口中的所有方法。接口无法被实例化,但是可以被实现。一个实现接口的类,必须实现接口内所描述的所有方法,否则就必须声明为抽象类。另外,在 Java 中,接口类型可用来声明一个变量,他们可以成为一个空指针,或是被绑定在一个以此接口实现的对象。
接口语法规则
代码语言:javascript复制[修饰符] interface 接口名称 [extends 其他的接口名] {
// 声明变量
// 抽象方法
}
- 接口及接口成员默认的访问权限为 : public 或默认
- 常量默认添加static final 关键字
- 抽象方法默认添加abstract 关键字
- 只有default 方法及static 方法可以添加方法体
- 实现接口的类如果不能实现所有接口中待重写的方法, 则必须设置为 抽象类
- 接口可以实现多继承, 即一个子接口可以同时继承多个父接口
- 一个类可以继承自一个父类, 同时实现多个接口
- 当一个类同时实现多接口, 且其中同时具有相同方法时, 实现类需重 写该方法, 否则会编译报错
接口与类相似点:
- 一个接口可以有多个方法。
- 接口文件保存在 .java 结尾的文件中,文件名使用接口名。
- 接口的字节码文件保存在 .class 结尾的文件中。
- 接口相应的字节码文件必须在与包名称相匹配的目录结构中。
接口与类的区别
- 接口不能用于实例化对象。
- 接口没有构造方法。
- 接口中所有的方法必须是抽象方法。
- 接口不能包含成员变量,除了 static 和 final 变量。
- 接口不是被类继承了,而是要被类实现。
- 接口支持多继承。
接口特性
- 接口中每一个方法也是隐式抽象的,接口中的方法会被隐式的指定为 public abstract(只能是 public abstract,其他修饰符都会报错)。
- 接口中可以含有变量,但是接口中的变量会被隐式的指定为 public static final 变量(并且只能是 public,用 private 修饰会报编译错误)。
- 接口中的方法是不能在接口中实现的,只能由实现接口的类来实现接口中的方法。
抽象类和接口的区别
- 抽象类中的方法可以有方法体,就是能实现方法的具体功能,但是接口中的方法不行。
- 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final 类型的。
- 接口中不能含有静态代码块以及静态方法(用 static 修饰的方法),而抽象类是可以有静态代码块和静态方法。
- 一个类只能继承一个抽象类,而一个类却可以实现多个接口。
package com.imooc.Telephone;
public interface INet {
//接口中抽象方法可不加abstract
//访问修饰符默认public
//当类实现接口时,需要去实现接口中的所有抽象方法,否则需要将类设置为抽象类
public abstract void network();
//JDK1.8新增的
//可以在实现类中重写,并可以通过接口的引用调用
default void connection(){
System.out.println("默认方法");
}
//不可以被重写,可以通过接口名调用
static void stop(){
System.out.println("我是接口中的静态方法");
}
//接口中可以包含常量,默认为public static final
int temp = 20;
}
代码语言:javascript复制package com.imooc.Telephone;
public interface IPhoto {
public void photo();
}
代码语言:javascript复制package com.imooc.Telephone;
public class Computer implements INet,IPhoto{
public static final int temp = 80;
public void photo(){
System.out.println("电脑可以拍照");
}
public void videos(){
System.out.println("电脑可以看视频");
}
public void music(){
System.out.println("电脑可以听音乐");
}
@Override
public void network() {
System.out.println("电脑可以上网");
}
}
代码语言:javascript复制package com.imooc.test;
import com.imooc.Telephone.*;
public class telTest {
public static void main(String[] args) {
Tel4 phone = new Tel4();
phone.call();
phone.network();
System.out.println("==========================");
IPhoto ip = new Tel4();
ip.photo();
ip = new Camera();
ip.photo();
System.out.println("==========================");
//如果接口和实现类中存在同名的信息,当在程序中使用接口的引用指向实现类的时候,依然使用的接口中的信息;
//如果通过实现类的对象调用的信息使用的是实现类中的信息
System.out.println(INet.temp);
INet net = new Computer();
System.out.println(net.temp);
System.out.println("==========================");
net.connection();
}
}