多态的格式
代码当中体现多态性,其实就是一句话:父类引用指向子类对象
格式:父类名称 对象名=new 子类名称()
或者:接口名称 对象名称=new 实现类名称()
public class demo01Polymorphism {
public static void main(String[] args) {
//使用多态的写法,左侧父类的引用,只想了右侧子类的对象
Fu obj=new Zi();
obj.method();
obj.methodFu();
}
}
创建一个父类名为Fu
public class Fu {
public void method(){
System.out.println("父类方法!");
}
public void methodFu(){
System.out.println("父类特有方法!");
}
}
创建一个子类名为Zi
子类中重写父类中的方法,然而父类中有一个方法是父类特有的,再我们调用子类方法时,可以通过创建对象方式调用,使用多态:父类名称 对象名=new 子类名称(),就如同“猫是动物”这个说法一样,右侧子类被当作子类使用
public class Zi extends Fu{
@Override
public void method(){
System.out.println("子类方法!");
}
}
多态中成员变量使用特点
访问成员变量的两种方式:
1.直接通过对象名称访问成员变量:看等号左边是谁,优先用谁,没有则往上找【注意成员变量不能覆盖重写的】不会往下查找
2.间接通过成员方法访问成员变量:看该方法属于谁,优先用谁,没有则向上找
public class demo01MultiField {
public static void main(String[] args){
Fu obj=new Zi();
System.out.println(obj.num);
//System.out.println(obj.age);子类中有一变量age,但是我以Fu obj=new Zi()写法,只会在父类中查找,不会往下查找
obj.showNum();//子类没有覆盖重写,那么就是父类中的变量,如果子类覆盖重写,那就是子类中的变量
}
}
创建一个父类名为Fu
public class Fu {
int num=10;
public void showNum(){
System.out.println(num);
}
}
创建一个子类名为Zi
public class Zi extends Fu{
int num=20;
int age=12;
@Override
public void showNum() {
System.out.println(num);
}
}
当我们在间接通过成员方法访问成员变量时,如果在子类中覆盖重写了成员方法,那么调用方法访问的成员变量时子类的,如果没有覆盖重写方法,那么访问的成员变量就是Fu类中的成员变量,不糊往下查找
多态中成员方法使用特点
在多态的代码当中,成员方法访问规制时是:看new的是谁就优先用谁,没有则往上找
口诀:编译看左,运行看右,在编译时,如果左边有这个类中有这个方法,那么编译正确,再运行,如果运行时子类中有这个方法
那么就是用这个子类的方法,如果没有,就向上寻找。
对比:
成员变量:编译看左边,运行看左边
成员方法:编译看左边,运行看右边
public class demo02MoltiMethod {
public static void main(String[] args) {
Fu obj=new Zi();
obj.method();//父子都有method,优先用new后面的Zi类中的方法
obj.methodFu();//子类中没有。而父类中有,所以使用的父类中的方法
//编译看左边,左边是Fu,Fu当中没有methodZi方法,所以编译报错。
// obj.methodZi();错误写法
}
}
创建一个父类名为Fu
public class Fu {
int num=10;
public void showNum(){
System.out.println(num);
}
public void method(){
System.out.println("父类方法使用");
}
public void methodFu(){
System.out.println("父类特有方法执行");
}
}
创建一个子类名为Zi
public class Zi extends Fu {
int num =10;
int age=12;
@Override
public void showNum() {
System.out.println(num);
}
@Override
public void method() {
System.out.println("子类方法使用");
}
public void methodZi(){
System.out.println("子类特有方法执行");
}
}
对象的向上转型
1.对象的向上转型其实是多态的写法:
格式:父类名称 对象名称=new 子类名称()
含义:右侧创建一个子类对象把它当作父类来看待使用。
注意事项:向上转型一定是安全的。是从小范围转换成大范围。
类似于double num=100;//这是真确的,int—>double,自动类型转换。
public class demo01 {
public static void main(String[] args) {
//对象的向上转型就是:父类引用指向子类的对象。
Animal animal=new Cat();
animal.eat();
}
}
创建父类名为animal
public abstract class Animal {
public abstract void eat();
}
创建子类Cat
public class Cat extends Animal{
@Override
public void eat() {
System.out.println("猫吃鱼");
}
}
结果输出为“猫吃鱼”,向上转型即为多态
向下转型
向上转型一定是安全的,没有问题的正确的,但是有一个弊端:
对象一旦向上转型为父类,那么就无法调用子类原本特有的内容。
如果要调用子类的特有方法,那么就要向下转型【还原】
2.对象向下转型其实是一个还原的动作。
格式:子类名称 对象名=(子类对象名称)父类对象;这有点想强制转换的格式
含义:将父类对象【还原】成为本来的子类对象。
注意事项:1.必须保证对象本来创建的时候就是要还原的类,那样才能还原成为本类
2.如果对象创建的时候本来不是本类,现在非要向下转化成本类就会报错!
public class demo01 {
public static void main(String[] args) {
Animal animal=new Cat();
animal.eat();
//animal.catmouse();一旦向上转型之后,不能再调用子类特有方法,如同例子中,猫抓老鼠,但是动物不一定能抓老鼠
//向下转型,进行”还原“动作
Cat cat=(Cat) animal;
cat.catchMouse();//这样运行结果就是猫抓老老鼠,如果转换成为其他类就会报错成为ClassCastException
}
}
创建父类Animal
public abstract class Animal {
public abstract void eat();
}
创建子类Cat
public class Cat extends Animal{
@Override
public void eat() {
System.out.println("猫吃鱼 ");
}
public void catchMouse(){
System.out.println("猫抓老鼠");
}
}
如何知道一个父类引用的对象
如何才能知道一个父类引用的对象,本来是什么子类?
格式:对象 instenceof 类名称
这将会得到一个boolean值结果,也就是判段前面的对象能不能当作后面类型的实例。
public class demo02Instenceof {
public static void main(String[] args) {
Animal animal=new Cat();
animal.eat();
//如果希望调用子类特有方法需要向下转型
//判断一下父类引用是不是Cat
if(animal instanceof Dog){
Dog dog=(Dog)animal;
}if(animal instanceof Cat){
Cat cat=(Cat)animal;
}
}
}