多态:同一事物,具有多个不同的表现形式
1. 前提1. 继承或者实现【二选一】
2.方法的重写【意义体现:不重写,无意义】
3.父类引用指向子类对象【格式体现】
2 多态的意义class Test {
public static void main(String[] args) {
// 父类的引用 stu 指向了一个 new Student() 子类对象
Person p = new Student(); //前提1:Student继承Person
p.sleep(); // 子类重写了父类的方法,执行的是子类自己的方法
}
}class Person {
void sleep() {
System.out.println("人正在睡觉");
}
}class Student extends Person {
@Override
void sleep() {
System.out.println("学生正在教室里睡觉");
}
}
查看一下代码,思考这段代码是否可以进行优化:
class Test {
public static void main(String[] args) {
Dog dog = new Dog();
Cat cat = new Cat();
func(dog);
func(cat);
}
private static void func(Dog dog) {
dog.eat();
}
private static void func(Cat cat) {
cat.eat();
}
}
class Dog {
public void eat() {
System.out.println("小狗正在啃骨头");
}
}
class Cat {
public void eat() {
System.out.println("小猫正在吃鱼");
}
}
上述代码中,根据不同的参数类型,我们定义了不同的func方法,这个方法出现了重载。但是,我们从func方法的内部实现上来看,这两个方法都是调用对象的eat方法。
其实,在上述代码里,我们可以考虑给Dog类和Cat类定义一个父类,通过传递父类参数的形式,来简化代码。
实际开发的过程中,父类类型作为方法形式参数,传递子类对象给方法,进行方法的调用,更能体现出多态的扩展性与便利。
class Test {
public static void main(String[] args) {
Dog dog = new Dog();
Cat cat = new Cat();
func(dog);
func(cat);
}
private static void func(Animal animal) { // 参数只需要指定 Animal 父类即可
animal.eat();
}
}
class Animal{ // 定义一个 Animal 类,让Dog和Cat都继承自它
public void eat() {
System.out.println("动物正在吃东西");
}
}
class Dog extends Animal {
public void eat() {
System.out.println("小狗正在啃骨头");
}
}
class Cat extends Animal {
public void eat() {
System.out.println("小猫正在吃鱼");
}
}
3. 多态的特点
1.属性和多态无关,编译和运营都是看等号左边的类型
2.方法调用时,编译看等号左边的变量类型,运行是看等号右边对象的具体类型
如果要调用右边子类里定义的方法,需要强制类型转换
public class Test {
public static void main(String[] args) {
Animal animal = new Dog();
animal.eat(); // 会调用 Dog 类的 eat 方法
// animal.watchDog();编译会报错,因为编译看左边animal类中没有watchDog()方法
((Dog) animal).watchDog();
}
}
class Animal { // 定义一个 Animal 类,让Dog和Cat都继承自它
public void eat() {
System.out.println("动物正在吃东西");
}
}
class Dog extends Animal {
@Override
public void eat() {
System.out.println("小狗正在啃骨头");
}
public void watchDog() {
System.out.println("小狗正在看家");
}
}
public class Test {
public static void main(String[] args) {
Animal animal = new Dog();
System.out.println(animal.type); // 动物
}
}
class Animal { // 定义一个 Animal 类,让Dog和Cat都继承自它
String type = "动物";
public void eat() {
System.out.println("动物正在吃东西");
}
}
class Dog extends Animal {
String type = "狗";
@Override
public void eat() {
System.out.println("小狗正在啃骨头");
}
}
4. getclass()
获取对象指向的具体类型(看右边的实际类型)
5. 引用数据类型转换Person p = new Student();
Person p2 = new Person();
System.out.println(p.getclass()); //Student
System.out.println(p2.getclass()); //Person
向上转型:
父类类型 变量名 = new 子类类型();
如:Animal a = new Cat();
Student s = new Student();
(Person) s //向上转型
Student s = new Student();
((Person) s).test(); //调用的是子类的方法
System.out.println(((Person) s).name) //调用的是父类的属性
向下转型:
5.1 类型转换的意义子类类型 变量名 = (子类类型) 父类变量名;
如:Student s =(Student) person;
调用方法时,首先检查父类中是否有该方法,如果没有则编译错误。也就是说,不能调用子类拥有而父类没有的方法。因此,要调用子类特有的方法,必须做向下转型。
5.2 转型的异常public class Test {
public static void main(String[] args) {
Animal dog = new Dog();
// dog.watchDoor(); 编译报错,调用方法时,编译时看左边的类型,是Animal,Animal类里没有 watchDoor方法
((Dog) dog).watchDoor();
}
}
class Animal {
String type = "动物";
}
class Dog extends Animal {
public void watchDoor() {
System.out.println("狗正在看门");
}
}
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对象的。这两个类型并没有任何继承关系,不符合类型转换的定义。
为了避免ClassCastException的发生,Java提供了 instanceof 关键字,给引用变量做类型的校验,格式如下:
变量名 instanceof 数据类型
如果变量属于该数据类型,返回true。
如果变量不属于该数据类型,返回false。
public class Test {
public static void main(String[] args) {
// 向上转型
Animal a = new Cat();
a.eat(); // 调用的是 Cat 的 eat// 向下转型
if (a instanceof Cat){
Cat c = (Cat)a;
c.catchMouse(); // 调用的是 Cat 的 catchMouse
} else if (a instanceof Dog){
Dog d = (Dog)a;
d.watchHouse(); // 调用的是 Dog 的 watchHouse
}
}
}



