Fork me on Gitee

Java向上下转型中的陷阱{详细}

1: 多态

  多态时继承下面的产物,之所以存在向上向下转型的目的,就是解决参数传递的不变形,体现面向接口编程的重要性,

1.1 方法的多态性

  ①. 方法的重载:同一个方法名称可以根据参数的类型或个数不同调用不同的方法体。
  ②. 方法的覆写:同一个父类的方法,可能根据实例化子类的不同也有不同的表现形式。

1.2. 对象的向上转型

  出现了父类指向了子类那么出现 向上转型  对象的向上转型:父类 父类对象 = 子类实例

class Person{
    public void say() {
        System.out.println("我是Person");
    }
}

public class Student extends Person {
    
    public void say() {
        System.out.println("我是Student");
    }
    public void gotoSchool() {
        System.out.println("我的职责是上学");
    }
    public static void main(String[] args) {
        // 向上转型 父类实例对象指向子类实例 只保留父类子类同名的方法,且子类变量覆盖父类变量
        Person p=new Student();
        p.say();
    }
}

输出: 我是Student

目的: 用于参数统一化,假设父类有n个子类,方法要接受子类的实例,如果没有向上转型,就需要定义n个方法接收不同的对象

1.3 对象的向下转型

 对象的向下转型:子类 子类对象 = (子类)父类实例

class Person{
    public void say() {
        System.out.println("我是Person");
    }
}

public class Student extends Person {
    
    public void say() {
        System.out.println("我是Student");
    }
    public void gotoSchool() {
        System.out.println("我的职责是上学");
    }
    public static void main(String[] args) {
        // 向上转型 父类实例对象指向子类实例 只保留父类子类同名的方法,且子类变量覆盖父类变量
        Person p=new Student();
        p.say();
        // 向下转型 子类实例指向父类 可以拥有子类自己的方法,
        Student s=(Student) p;
        s.say();
        s.gotoSchool();
    }
}

注意:  向下转型之前一定要进行向上转型!!(让父类先指向子类)
否则在转型时会出现ClassCastException(类型转换异常–运行时异常)

问题: 如果向下转型存在安全隐患,那么如何转型才靠谱

 

class Person{
    public void print()
    {
        System.out.println("我是人");
    }
    public void p()
    {
        System.out.println("伤心的一天");
    }
}
class Student extends Person{
    public void print()
    {
        System.out.println("我是学生");
    }
    public void fun()
    {
        System.out.println("开心的一天!");
    }
}
public class Test{
    public static void main(String[] args)
    {
        Person per = new Student();
        //per是否能表示Person实例
        System.out.println(per instanceof Person);
        //per是否能表示Student实例
        System.out.println(per instanceof Student);
        if(per instanceof Student)
        {
            Student stu = (Student)per;
            stu.fun();
        }
    }
}

 

注意: 虽然增强了 程序的健壮性 但是,仅仅是这样,你还是需要 在这之前 进行 父类指向子类实例的过程  父类 父类对象 = 子类实例

 1,4 扩展调用的例子

class Person{
    public void print()
    {
        System.out.println("我是人");
    }
}
class Student extends Person{
    public void print()
    {
        System.out.println("我是学生");
    }
}
class Worker extends Person{
     public void print()
    {
        System.out.println("我是工人");
    }
}
public class Test{
    public static void main(String[] args)
    {
        whoAreYou(new Student());
        whoAreYou(new Worker());
    }
    public static void whoAreYou(Person per)
    {
        per.print();
    }
}

 

 

2: 存在公共变量的分析过程

 先类看一个代码:

class BB{
    public String S="B";
    public String getS() {
        return this.S;
    }
    public void setS(String s) {
        this.S = s;
    }
}

public class AA extends BB{
    public String S="A";
    public String getS() {
        return this.S;
    }
    public void setS(String s) {
        this.S = s;
    }
    
    public static void main(String[] args) {
            AA aa = new AA();
            BB bb = new BB();
            System.out.println(aa.S);
            System.out.println(bb.S);
            aa.setS("AA");
            bb.setS("BB");
            System.out.println(bb.S);
            bb=aa;
            aa=(AA) bb;
            System.out.println(bb instanceof BB);
            System.out.println(bb instanceof AA);
            System.out.println(aa.S);
            System.out.println(bb.S);
            System.out.println(aa.getS());
            System.out.println(bb.getS());
            System.out.println(bb.S);
    }
}

 

一般我们认为输出

1:A
2:B
3:BB
4:true
5:true
6:AA
7:BB
8:AA
9:AA
10:BB

上面输出 前6个没有问题,第七个由于前面执行了B.setS 所以我们认为应该为BB 

正确输出结果为:

1:A
2:B
3:BB
4:true
5:true
6:AA
7:B
8:AA
9:AA
10:B

 

 那么为什么我们 第 7 10输出结果为 B呢, 我们打上断点看一下:

1: aa实例 由于继承了bb 所以域中存在S='B'

 

 

当执行了 设置BB的时候, 值按照我们的意思改变了

 

 当执行了向上转型的时候,AA指向父类的实例,所以发生改变

 

 

所以第 System.out.println(bb.S); 能够输出 B,

而 System.out.println(bb.getS());为什么输出AA 因为当前指向指向子类 aa aa中的getS方法与父类同名,那么执行子类的方法,注意:如果这里为不同名称的方法,那么执行父类的方法,

我给出这个代码:

package LL;

class BB2{
    public int SB=1;
    public int getSB() {
        return SB;
    }
    public void setSB(int sB) {
        SB = sB;
    }
}

public class PP extends BB2{
    public int SA=2;
    public int getSA() {
        return SA;
    }
    public void setSA(int sA) {
        SA = sA;
    }
    public static void main(String[] args) {
            PP aa = new PP();
            BB2 bb = new BB2();
            System.out.println(aa.SA);//2
            System.out.println(bb.SB);//1
            aa.setSA(22);
            bb.setSB(11);
            System.out.println(bb.SB);//11
            bb=aa;
            System.out.println(bb instanceof BB2);
            System.out.println(bb instanceof PP);
            System.out.println(aa.SA);//22
            System.out.println(bb.SB);//1
            System.out.println(aa.getSA());//22
            System.out.println(bb.getSB());//22
            System.out.println(bb.SB);//1
    }
}

3:总结

  • 最好将变量private 私有化 ,以免阅读程序麻烦
  • 存在public变量时候,继承的子类 执行的时候 回拷贝一份父类的变量,(表示 父类修改变量不会影响子类拷贝这个变量的值)
  • 当发生向上转型的时候,父类指向子类实例,那么父类实例拥有与子类实例一样的参数变量,
  • 转型的时候 子类只会保留与父类中同名的方法,其他放弃
  • 最后: 向下转型的时候 ,一定必须让父类实例指向子类

 

 

 

posted @ 2019-11-14 22:38  ---dgw博客  阅读(555)  评论(0编辑  收藏  举报