Java基础总结8
一、面向对象4
1. 多态
1.1 概述
某一个事物,在不同时刻表现出来的不同状态。
- 多态的前提:
- 继承或者实现【二选一】
- 方法的重写【意义体现:不重写,无意义】
- 父类引用指向子类对象【格式体现】
1.2 多态的体现
多态体现的格式:
父类类型 变量名 = new 子类对象;
变量名.方法名();
父类类型:指子类对象继承的父类类型,或者实现的父接口类型。
代码演示:
Fu f = new Zi();
f.method();
当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,执行的是子类重写 后方法。
代码演示:
定义父类:
public abstract class Animal {
public abstract void eat();
}
定义子类:
class Cat extends Animal {
public void eat() {
System.out.println("吃鱼");
}
}
class Dog extends Animal {
public void eat() {
System.out.println("吃骨头");
}
}
定义测试类:
public class Test {
public static void main(String[] args) {
// 多态形式,创建对象
Animal a1 = new Cat();
// 调用的是 Cat 的 eat
a1.eat();
// 多态形式,创建对象
Animal a2 = new Dog();
// 调用的是 Dog 的 eat
a2.eat();
}
}
1.3 多态中成员访问的特点
-
成员变量:
编译看左边,运行看左边。
-
构造方法:
创建子类对象的时候,会访问父类的构造方法,对父类的数据进行初始化。
-
成员方法:
编译看左边,运行看右边。
-
静态方法:
编译看左边,运行看左边。
1.4 多态的利弊
- 好处:
- 提高了代码的维护性(继承保证)
- 提高了代码的扩展性(由多态保证)
- 弊端:
- 不能使用子类特有的功能
- 解决办法:把父类的引用强制转换为子类的引用。(向下转型)
1.5 多态的内存图解
1.6 引用类型转换
多态的转型分为向上转型和向下转型两种。
-
向上转型:多态本身是子类类型向父类类型向上转换的过程,这个过程是默认的。
-
当父类引用指向一个子类对象时,便是向上转型。
-
使用格式:
父类类型 变量名 = new 子类类型(); 如:Animal a = new Cat();
-
-
向下转型:父类类型向子类类型向下转换的过程,这个过程是强制的。
-
一个已经向上转型的子类对象,将父类引用转为子类引用,可以使用强制类型转换的格式,便是向下转型。
-
使用格式:
子类类型 变量名 = (子类类型) 父类变量名; 如:Cat c =(Cat) a;
-
-
为什么要转型
当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误。也就是说,不能调用子类拥 有,而父类没有的方法。编译都错误,更别说运行了。这也是多态给我们带来的一点"小麻烦"。所以,想要调用子 类特有的方法,必须做向下转型。
转型演示代码:
定义类:
abstract class Animal { abstract void eat(); } class Cat extends Animal { public void eat() { System.out.println("吃鱼"); } public void catchMouse() { System.out.println("抓老鼠"); } } class Dog extends Animal { public void eat() { System.out.println("吃骨头"); } public void watchHouse() { System.out.println("看家"); } }
定义测试类:
public class Test { public static void main(String[] args) { // 向上转型 Animal a = new Cat(); a.eat(); // 调用的是 Cat 的 eat // 向下转型 Cat c = (Cat)a; c.catchMouse(); // 调用的是 Cat 的 catchMouse } }
-
转型异常
转型过程中,不注意就会出现这样的问题,代码如下:
public class Test { public static void main(String[] args) { // 向上转型 Animal a = new Cat(); a.eat(); // 调用的是 Cat 的 eat // 向下转型 Dog d = (Dog)a; d.watchHouse(); // 调用的是 Dog 的 watchHouse 【运行报错】 } }
这段代码可以通过编译,但是运行时,却报出了 ClassCastException ,类型转换异常!这是因为,明明创建了 Cat类型对象,运行时,当然不能转换成Dog对象的。这两个类型并没有任何继承关系,不符合类型转换的定义。
应new一个Dog类赋给对象a,这样a就可以直接调用Dog类的成员了。
2. 抽象类
2.1 概述
把没有方法主体的方法称为抽象方法。Java语法规定,包含抽象方法的类就是抽象类。
- 抽象方法 : 没有方法体的方法。
- 抽象类:包含抽象方法的类。
2.2 abstract使用格式
-
抽象方法:使用 abstract 关键字修饰方法,该方法就成了抽象方法,抽象方法只包含一个方法名,而没有方法体。
定义格式:
修饰符 abstract 返回值类型 方法名 (参数列表);
代码举例:
public abstract void eat()
-
抽象类:如果一个类包含抽象方法,那么该类必须是抽象类。
定义格式:
abstract class 类名 { }
代码举例:
public abstract class Animal { public abstract void eat(); }
-
抽象的使用
继承抽象类的子类必须重写父类所有的抽象方法。否则,该子类也必须声明为抽象类。最终,必须有子类实现该父 类的抽象方法,否则,从最初的父类到最终的子类都不能创建对象,失去意义。
代码举例:
public class Cat extends Animal { public void eat(){ System.out.println("小猫吃鱼"); } } public class CatTest { public static void main(String[] args) { // 创建子类对象 Cat c = new Cat(); // 调用eat方法 c.eat(); } } 输出结果: 小猫吃鱼
此时的方法重写,是子类对父类抽象方法的完成实现,我们将这种方法重写的操作,也叫做实现方法。
2.3 抽象类的成员特点
- 抽象类的成员特点:
- 成员变量:既可以是变量,也可以是常量。
- 构造方法:有。用于子类访问父类数据的初始化。
- 成员方法:既可以是抽象的,也可以是非抽象的。
- 抽象类的成员特性:
- 抽象方法 :强制要求子类做的事情。
- 非抽象方法:子类继承的事情,提高代码复用性。
2.4 注意事项
- 抽象类不能创建对象,如果创建,编译无法通过而报错。只能创建其非抽象子类的对象。
- 抽象类中,可以有构造方法,是供子类创建对象时,初始化父类成员使用的。
- 抽象类中,不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
- 抽象类的子类,必须重写抽象父类中所有的抽象方法,否则,编译无法通过而报错。除非该子类也是抽象类。
2.5 练习
假如我们在开发一个系统时需要对员工(Employee)类进行设计,员工包含3个属性:姓名、工号以及工资(salary)。
经理(Manager)也是员工,除了含有员工的属性外,另为还有一个奖金(bonus)属性。
然后定义工作的方法。
代码演示:
定义类
人类:
abstract class Person {
String name;
String jobNumberID;
int salary;
public abstract void work();
}
员工类:
public class Employee extends Person{
@Override
public void work() {
System.out.println("朝九晚九");
}
}
经理类:
public class Manager extends Person{
int bonus=10000;//奖金
public void supervise(){
System.out.println("监管");
}
@Override
public void work() {
System.out.println("朝九晚五");
}
}
测试类:
public class MyTest {
public static void main(String[] args) {
Person p=new Employee();
p.name="张三";
p.jobNumberID="001";
p.salary=5000;
System.out.println(p.name);
System.out.println(p.jobNumberID);
System.out.println(p.salary);
p.work();
System.out.println("=====================================");
p=new Manager();
p.name="王五";
p.jobNumberID="002";
p.salary=10000;
System.out.println(p.name);
System.out.println(p.jobNumberID);
System.out.println(p.salary);
p.work();
Manager manager=(Manager) p;
manager.supervise();
System.out.println(manager.bonus);
}
}
3. 接口
3.1 概述
-
接口:是Java语言中一种引用类型,是方法的集合,如果说类的内部封装了成员变量、构造方法和成员方法,那么 接口的内部主要就是封装了方法。包含抽象方法,默认方法和静态方法,私有方法。
-
定义:它与定义类方式相似,但是使用 interface 关键字。它也会被编译成.class文件,但一定要明确它并 不是类,而是另外一种引用数据类型。
-
使用:,它不能创建对象,但是可以被实现( implements ,类似于被继承)。一个实现接口的类(可以看做 是接口的子类),需要实现接口中所有的抽象方法,创建该类对象,就可以调用方法了,否则它必须是一个抽象类。
3.2 定义格式
public interface 接口名称 {
// 抽象方法
// 默认方法
// 静态方法
// 私有方法
}
3.3 接口的实现
-
格式:
class 类名 [extends 父类名] implements 接口名1,接口名2,接口名3... { // 重写接口中抽象方法【必须】 // 重写接口中默认方法【不重名时可选】 }
3.4 接口的成员特点
- 成员变量:
- 只能是常量,并且是静态的。
- 默认修饰符:public static final
- 建议:自己手动给出。
- 构造方法:
- 接口没有构造方法。
- 成员方法:
- 只能是抽象方法。
- 默认修饰符:public abstract
- 建议:自己手动给出。
3.5 类与接口的关系
- 类与类:
- 继承关系,只能单继承,可以多层继承。
- 类与接口:
- 实现关系,可以单实现,也可以多实现。
- 并且还可以在继承一个类的同时实现多个接口。
- 接口与接口:
- 继承关系,可以单继承,也可以多继承。
3.6 抽象类与接口的关系
- 成员区别:
- 抽象类:
- 成员变量:可以变量,也可以常量
- 构造方法:有
- 成员方法:可以抽象,也可以非抽象
- 接口:
- 成员变量:只能是常量
- 成员方法:只可以抽象
- 抽象类:
- 关系区别:
- 类与类:
- 继承,单继承
- 类与接口:
- 实现,单实现,多实现
- 接口与接口
- 继承,单继承,多继承
- 类与类:
- 设计理念区别:
- 抽象类:抽象类中定义的是该继承体系的共性功能。
己手动给出。
- 抽象类:抽象类中定义的是该继承体系的共性功能。
3.5 类与接口的关系
- 类与类:
- 继承关系,只能单继承,可以多层继承。
- 类与接口:
- 实现关系,可以单实现,也可以多实现。
- 并且还可以在继承一个类的同时实现多个接口。
- 接口与接口:
- 继承关系,可以单继承,也可以多继承。
3.6 抽象类与接口的关系
- 成员区别:
- 抽象类:
- 成员变量:可以变量,也可以常量
- 构造方法:有
- 成员方法:可以抽象,也可以非抽象
- 接口:
- 成员变量:只能是常量
- 成员方法:只可以抽象
- 抽象类:
- 关系区别:
- 类与类:
- 继承,单继承
- 类与接口:
- 实现,单实现,多实现
- 接口与接口
- 继承,单继承,多继承
- 类与类:
- 设计理念区别:
- 抽象类:抽象类中定义的是该继承体系的共性功能。
- 接口:接口中定义的是该继承体系的扩展功能。