[Java] 对象类型转换和运算符 instanceof 学习笔记 [#子类转父类 #父类转子类 #对象和引用的区别]

[Java] 对象类型转换和运算符 instanceof 学习笔记

欢迎大家收看 把简单的问题变复杂系列 : )

一、类型转换使用的情景:

  在需要将一个继承链下把一个类的对象转换为另一个类的对象(注意:这里强调转换的是对象而不是类),更确切地说其实应该是引用类型对象类型不一致的时候考虑到对象类型转换

这里拓展一下引用和对象的区别

  • 对象:类实例化出来的叫做对象,Jvm 对对象进行操作
  • 引用:全称是引用变量,可以理解成指向对象的指针。可以通过对引用变量来操作引用变量所管理的那个对象。正因为这个性质,导致引用变量的生命周期和其管理的对象相同。
  • 内存区别:对象被实例化后要开辟内存空间,而引用对象只需管理已经被实例化出来的对象,它不会另外开辟内存空间
  • 类型区别:对象和引用不是同一类型结构,在判断(对象==引用)返回的是 False
▲ 为了更好地理解引用和实例对象,这里举个例子
class Person {
    Person tony;   ▲ 该语句只是声明了一个叫tony的Person引用类型,并没有指向任何对象
    Person john = new Person();  ▲ 该语句不仅声明了一个john的Person引用类型,还把john指向了一个Person对象
    Person amy = john;  ▲ 声明了一个Person的引用类型Amy,然后Amy又指向john,所以Amy也可以管理Person对象
}

特别注意引用也是有自己的类型的,就像上面tony和amy,在声明时并没有指向一个对象,但他们已经作为Person的引用类型被创建在栈中。所以类型转换的问题就发生在如果这个类型的引用指向的不是自己类型的对象会怎么样。

下面详细介绍一下引用类型的赋值:当我们将一个引用赋值给另一个引用时,我们实际上复制的是对象的地址。两个引用将指向同一个对象。如:amy = john 将指向同一个对象。因此,任何一个引用对对象的操作都会对另一个引用产生影响,因为操作的是对象本身。

二、转换的方向:

  子类向上转父类:就是子类的对象当父类对象用,这种转换是推荐的或者说是安全的,因为子类是父类的超集,父包含的信息子类都有,转换后无非是把子类多余的信息剔除。

  父类向下转子类:同理,把父类的对象当子类对象用,这种做法通常是不太推荐的,因为不安全,子类的信息父类不一定全部都有,转换后使用不了子类的功能。

  本质上是引用指向发生改变

三、子类转父类详解

▲ 下面用代码来简单解释一下子类向上转父类
▲ 先规定一系列的类
class Animal {
    恰饭();
    睡觉();
}
class Cat extends Animal {
    睡觉();    ▲ 注意这里对父类的方法进行了重写!
    猫叫();
}
class Dog extends Animal {}

▲ 在同包新类中进行测试
class Test extends Cat {
    Animal a = new Animal();  ▲ 该语句通过new在堆里创建了一个Animal对象,让引用a指向该Animal类的对象的引用
    Cat kitty = new Cat();    
    
    a = kitty;     ▲ a作为Aniaml引用类型指向Cat的引用对象kitty,但a的可视范围是Cat类的Animal部分(向上转型)
                   ▲ 用 a = (Animal)kitty 理解或许更能体现子类转父类的思想
    a.恰饭();       ▲ OK
    a.睡觉();       ▲ OK 但这里是执行Cat类的睡觉,而不是Animal类的睡觉方法!
    a.猫叫();       ▲ 不行鸭!一个动物怎么就一定会猫叫呢?
    
    *** 再强调一遍,a作为Animal类型它指向了Cat类型,但只能用Cat类里面Animal部分的功能,或者就理解为把Cat当成动物用吧qwq ***
    
    Cat tom = (Cat) a;   ▲ 这里声明tom是Cat类型的,可以访问a的Cat部分内容和Animal部分内容
    tom.恰饭();           ▲ ok
    tom.睡觉();           ▲ ok
    tom.猫叫();           ▲ ok
}


看到这里可能会有小伙伴会问,那我转来转去的最后啥类型的引用还是只能用啥类型的功能嘛,转型有啥必要呀?

(害,别急嘛)下面我就来讲讲向上转型到底会有啥效果

  • 方法的变化:如果子类重写了父类的方法,那么父类的引用默认调用的是子类的重写的方法。如果不存在重写方法,他默认调用的为父类的方法。

  • 属性的变化:因为属性不存在重写,那么父类的引用调用的为父类的属性,因为子类实例的数据类型为父类的类型。

简单的说,指向子类的父类引用可以用子类的重写方法(多态的思想就体现在这了)

四、父类转子类详解

▲ 下面用代码来简单解释一下父类向下转子类,继承关系还是用上面的例子
▲ 先规定一系列的类
class Animal {
    恰饭();
    睡觉();
}
class Cat extends Animal {
    猫叫();
}
class Dog extends Animal {}
class test02 extends Cat{
    Animal a = new Animal();
    Cat kitty = new Cat();
    a = kitty;         ▲子转父,a指向Cat对象
    kitty = (Cat)a;    ▲虽然a是Animal引用类型,但已经指向Cat对象,所以能转型成功
                       ▲再次强调上面的“特别注意”,引用的类型不变,但引用指向的对象可以变
    
    Dog pluto = new Dog();
    
    pluto = (Dog)a;    ▲这里编译不报错但运行报错,因为a指向的是Cat对象,而Cat和Dog无继承关系
}

五、instanceof的使用

instanceof 是个检测 引用 能不能指向 某个对象的运算符

语法: <引用变量> instanceof <类、接口>

判断引用变量是不是后面的类、子类、实例

boolean flag = kitty instanceof Cat;            ▲检测Kitty引用的是不是Cat类,返回true
boolean flag = undefinedkitty instanceof Cat;   ▲因为未定义的Kitty不存在,是null,返回false
boolean flag = pluto instanceof Cat             ▲返回false
发布了1 篇原创文章 · 获赞 2 · 访问量 31

猜你喜欢

转载自blog.csdn.net/HD_TMG/article/details/104869488
今日推荐