当父子类具有共性的方法,需要定义在父类中的方法,但是父类无法实现功能,需要由具体的子类来实现,此时需要将父类中定义的共性方法,去掉方法体,变为概念。称这样的方法为抽象方法(没有方法体,能够节省栈内存空间);抽象方法必须定义在抽象类中
// 抽象类
public abstract class Animal {
// 姓名
public String name;
// 年龄
public int age;
// 身高
public int high;
// 抽象方法
public abstract void run();
...
}
public abstract class 类名{
public abstract 返回值类型 方法名 ([参数列表]);
}
// 抽象父类
public abstract class Animal {
// 姓名
public String name;
// 年龄
public int age;
// 身高
public int high;
// 抽象方法
public abstract void run();
...
}
// 实现父类抽象方法
public class Cat extends Animal{
private String crawl;
// 实现继承的抽象类中的抽象方法
@Override
public void run() {
System.err.println("跑");
}
...
}
// 抽象子类继承抽象父类
public abstract class Cat extends Animal{
...
}
① 当前子类重写父类已实现的方法 称之为重写
② 当子类中定义父类抽象方法时,称之为实现(implements)
③ 抽象类会增加类与类之间的耦合度
① 由于java中只支持单继承,为实现多继承的效果,Java中提出类似“多继承的操作”,也就是多实现接口,避免单继承的局限性
② 父类只需要定义概念,有具体的子类实现,因此该方法应该是抽象的
③ 由于不能实现多继承,需要使用接口来定义抽象方法,由具体的子类实现
public interface 接口名{
抽象方法;
}
// 接口中方法默认为抽象方法,abstract可省略
// 可将访问权限修饰符设置为缺省
public interface Door {
public abstract void open();
void close();
}
public interface Lock {
void on();
void off();
}
// 实现多继承
public class Heart implements Door,Lock{
@Override
public void open() {
System.err.println("打开你的心门");
}
@Override
public void close() {
}
@Override
public void on() {
}
@Override
public void off() {
}
}
① 成员变量:默认public static final修饰
② 成员方法:默认public abstract修饰
③ 构造方法:接口中不能有构造方法
比较项 | 普通类 | 抽象类 | 接口 |
---|---|---|---|
定义关键字 | class | abstract | interface |
继承或实现的关键字 | extends | extends | implements |
成员字段 | 常量 变量 | 常量 变量 | 常量 |
构造器 | 既能定义也能实例化 | 只能定义,不能实例化 | 既不能定义也不能实例化 |
成员方法 | 普通方法,静态方法 | 抽象方法,普通方法,静态方法 | 只能定义抽象方法(abstract可以省略) |
① 当需要继承属性的时候,推荐使用抽象方法;其余都用接口;(便于定义统一的操作规则)
② 当继承类和实现接口同时出现的时候,一定是先继承在实现
public class Heart extends Animal implements Door,Lock{
}
同一方法可以根据发送对象的不同而采取多种不同的行为方式,即一个对象的实际类型是确定的,但可指向对象的引用类型有很多,可实现动态编译
类与类之间必须是继承关系,否则会报类型转换异常(ClassCastException),子类重写父类方法(static,属于类不能重写,私有方法,final),父类引用指向子类对象(Father f1 =new son();),多态是方法的多态,属性没有多态性
// 父类
public class Person {
public void run(){
System.err.println("跑");
};
}
// 子类
public class Student extends Person{
@Override
public void run() {
// 子类重写父类方法
System.err.println("小跑");
}
// 子类特有方法
public void eat(){
System.err.println("吃");
}
}
// 多态测试类
public class Test {
public static void main(String[] args) {
// 对象的实际类型是确定的(指new的对象)
// new Person();
// 但指向的引用类型是可变的
// Student能调用的方法都是自己的或者继承父类的
Student s1 =new Student();
// 父类引用指向子类的类型
// Person父类型,可以指向子类,但不能调用子类特有的方法
Person s2 =new Student();
Object s3 =new Student();
// 在使用多态后的父类引用变量调用方法时,会调用子类重写后的方法
// 父类调用子类中对父类重写的方法,称这个重写的方法为回调函数
s2.run();// 小跑
// 子类继承父类的全部方法 可以输出父类方法和子类方法
// 子类重写父类方法后 执行子类方法
s1.run();
// 对象能执行那些方法,主要看左边的类型,和右边关系不大
// 调用子类特有方法时需要强转,不能直接调用
//s2.eat();// 不可用
s1.eat();// 可用
// 需要强转,强转后可调用子类特有方法
((Student) s2).eat();
}
}
①对象是哪个类的对象:看对象前面写的是什么
②对象指向哪个类的引用:看new关键字后面的构造器
① 向上溯型(向上造型):将子类的构造器赋值给父类的对象,引用数据类型自转
父类 对象名 = new 子类([参数列表])
② 向下溯型(向下造型):将父类对象构造器赋值给子类对象,俗称引用数据类型强转
子类 对象名 =(子类) new 父类([参数])
③ 当创建父类对象,想调用子类特有方法时,需要强转
public class Play {
public static void main(String[] args) {
Person person=new Student();
Student student= (Student) person;
((Student) person).eat();
student.eat();
}
}
这段代码首先创建了一个类型为Person的对象person,并将其实例化为Student类型。这是一种向上转型,因为Student是Person的子类。
然后,将person对象强制转换为Student类型,并将其赋值给student变量。这是一种向下转型,因为我们知道person对象实际上是Student类型的。
接下来,通过强制转型的方式调用person对象的eat()方法。由于person对象实际上是Student类型的,所以可以调用Student类中的eat()方法。
最后,通过student变量直接调用eat()方法,这是因为student变量已经是Student类型,可以直接调用Student类中的方法。
public class Play {
public static void main(String[] args) {
Person person=new Person();
if(person instanceof Student){
System.err.println("yes1");
}else if(person instanceof Person){
System.err.println("ok1");
}
Student student=new Student();
if(student instanceof Student){
System.err.println("yes2");
}else if(student instanceof Person){
System.err.println("ok2");
}
}
}
① 首先定义了一个Person对象,并使用instanceof操作符检查person对象是否属于Student类。由于person对象是Person类的实例,而不是Student类的实例,所以第一个条件不满足,输出"ok1"。
② 接着定义了一个Student对象,并使用instanceof操作符检查student对象是否属于Student类。由于student对象是Student类的实例,所以第一个条件满足,输出"yes2"。
③ 总结:instanceof操作符用于检查一个对象是否属于某个类或其子类的实例。如果对象是该类或其子类的实例,则返回true;否则返回false。在这段代码中,由于person对象是Person类的实例,而student对象是Student类的实例,所以输出结果分别为"ok1"和"yes2"。
① instanceof :判断指定对象是否是指定类或其子类对象的运算符,结果一定是布尔型
② implements:实现的关键字
③ interface:定义接口的关键字
封装、继承、多态、抽象
① 提高代码安全性 – 封装
② 简化代码 – 封装(简化主函数)、继承和抽象(简化子类)、多态(简化业务)
③ 提高代码重用性(复用性)-- 继承、抽象、多态
④ 减低类与类的耦合度-- 封装