1. 对象转换概念
不同类型的基本数据类型变量之间可以进行类型转换(自动转换与强制转换),在Java中对于具有继承关系的对象类型也可以进行转换。Java允许父类类型的引用变量直接引用子类类型的对象。
2. 上转型对象
假设,A类是B类的父类,当用子类创建一个对象,并把这个对象的引用放到父类的对象中时,称对象a是对象b的上转型对象。
A a = new B();
3. 使用小结
要点1
- 上转型对象不能访问子类新增的数据域;不能直接访问子类新增的方法(子类中定义的覆盖、隐藏方法不算新增)。只有当对象类型强制转换为子类类型,才能进行相应的调用
class Monkey{
void crySpeak(String s) {
System.out.println(s);
}
void crawl(){
System.out.println("crawling");
}
}
class People extends Monkey {
int d =0; //子类新增的数据域
void computer(int a,int b) { //子类新增的方法
int c=a*b;
System.out.println(c);
}
@Override
void crySpeak(String s) {
System.out.println("***"+s+"***");
}
}
public class Example5_10 {
public static void main(String args[]) {
Monkey monkey = new People();
monkey.crySpeak("I love this game"); //调用子类,重写不算新增
monkey.crawl();
//monkey.computer(10,10); //上转型后,子类新增的方法失去
//System.out.println(monkey.d);//上转型后,子类新增的变量失去
People people = null;
if (monkey instanceof People)
people=(People)monkey; //把上转型对象强制转化为子类的对象
people.computer(10,10);
System.out.println(people.d);
}
}
***I love this game***
crawling
100
0
在上述的例子中,父类为Monkey
,子类为People
。其中,在子类中,int d
为新增变量,computer
为新增方法,因此上转型对象无法直接调用这两个。强制转换为子类类型后,才能正常调用。
要点2
- 上转型对象可以访问子类从父类继承来的数据域、方法或子类中对父类覆盖重写的实例方法,但不能直接访问子类中对父类隐藏重写的静态方法和对父类隐藏定义的数据域。
- 如果子类覆盖了父类的某个实例方法后,当用上转型对象调用这个实例方法时,一定是调用子类中的这个实例方法。
- 如果子类隐藏了父类的某个静态方法后,当用上转型对象调用这个静态方法时,一定是调用父类中的这个静态方法,而不是子类中的这个静态方法,输出的值若为静态变量也应该是父类中的静态变量。
public class MethodOverride {
public static void main(String[] args) {
Child c=new Child();
System.out.println(c.getClassName());
System.out.println("----------------");
Parent p=new Parent();
System.out.println(p.getClassName());
System.out.println("----------------");
Parent pc=new Child();
System.out.println(pc.getClassName());
System.out.println(pc.getX());
System.out.println(pc.getZ());
}
}
class Parent{
int x =5;
static int z = 10;
String getClassName(){
return "this is Parent!";
}
int getX(){
return x;
}
static int getZ(){
return z;
}
}
class Child extends Parent{
int x =6;
int y =7;
static int z =11;
String getClassName(){
System.out.println(x+" "+y);
return "this is Child!";
}
int getX(){
return x;
}
static int getZ(){
return z;
}
}
6 7
this is Child!
----------------
this is Parent!
----------------
6 7
this is Child!
6
10
Process finished with exit code 0
在上述MethodOverride
文件中,父类为Parent
,子类为Child
。前两个对象为具体类的实例化,因此调用的是自己所属类的实例方法。
第三个为上转型对象,getClassName()
是对父类的重写方法,因此调用的是子类的getClassName()
,方法中的x,y为间接访问数据域,也为子类的。getX()
也是一个道理。
getZ()
为静态方法,子类虽然对静态变量及静态方法进行了隐藏,但无法直接访问。根据静态调用看左边的原则,调用的应该是父类的getZ()
,z值也为父类的数据域。
要点3
- 子类从父类继承来的方法如果没有被覆盖或隐藏,此方法中如果存在成员变量调用,则此调用是针对父类的成员变量的调用。
package com.codeslogan.inherit;
public class HidingDemo {
public static void main(String[] args) {
A x = new B();
System.out.println("(1) x.i is " + x.i);
System.out.println("(2) (B)x.i is " + ((B)x).i);
System.out.println("(3) x.j is " + x.j);
System.out.println("(4) ((B)x).j is " + ((B)x).j);
System.out.println("(5) x.m1() is " + x.m1());
System.out.println("(6) ((B)x).m1() is " + ((B)x).m1());
System.out.println("(7) x.m2() is " + x.m2());
System.out.println("(8) x.m3() is " + x.m3());
System.out.println("(9) x.z is " + x.z);
System.out.println("(10) x.z is " + x.w);
}
}
class A {
public int i = 1;
public static int j = 11;
public int z = 3;
public static int w = 13;
public static String m1() {
return "A's static m1";
}
public String m2() {
return "A's instance m2";
}
public String m3() {
return "A's instance m3" + j;
}
}
class B extends A {
public int i = 2;
public static int j = 12;
public static String m1() {
return "B's static m1";
}
public String m2() {
return "B's instance m2";
}
}
(1) x.i is 1
//上转型对象实例数据域看父类
(2) (B)x.i is 2
//强转后看子类
(3) x.j is 11
//上转型对象静态数据域看父类
(4) ((B)x).j is 12
//强转后看子类
(5) x.m1() is A's static m1
//静态方法看父类(左)
(6) ((B)x).m1() is B's static m1
//强转后看子类
(7) x.m2() is B's instance m2
//实例方法重写,看子类
(8) x.m3() is A's instance m3 11
//继承来的方法,子类没有其对应的重写,结果看父类
//若其中有涉及数据域,无论是静态数据,还是实例数据,都应该是父类的数据域
(9) x.z is 3
//显而易见,继承而来的实例数据
(10) x.z is 13
//继承而来的静态数据
总结:
- 继承而来(子类没有对其进行额外的操作),全部看父类的内容
- 强转后,无条件看强转后的内容
- 数据域默认看的是父类
- 如果调用了上转型对象调用了父类的方法,那么对应的数据,也应该是父类的数据
要点4
- 上转型对象即使采用父类做一个强制转换,所访问到的被覆盖的实例方法依旧是子类的
A a = new B();
a.getX();
((A)a).getX(); //a.getX();
简单来说就是,原来它的类型本来就是A,再转为A相当于没转。
package com.codeslogan.inherit;
public class TestChange {
public static void main(String[] args) {
Animal x=new Tiger();
System.out.println("(1):x.news is "+x.news);
System.out.println("(2):((Tiger)x).news is "+((Tiger)x).news);
System.out.println("(3):x.smile() is "+x.smile());
System.out.println("(4):((Tiger)x).smile() is "+((Tiger)x).smile());
System.out.println("(5):x.getNews() is "+((Animal)x).getNews());
System.out.println("(6):x.getNews() is "+x.getNews());
System.out.println("(7):x.getMessage() is "+x.getMessage());
System.out.println("(8):((Tiger)x).eat() is "+((Tiger)x).eat());
}
}
class Animal{
public String news="Animal's news";
public String message="Animal's message";
public static String smile(){
return "smile from Animal";
}
public String getNews(){
return news;
}
public String getMessage(){
return message;
}
}
class Tiger extends Animal{
public String news="Tiger's news";
public String message="Tiger's message";
public static String smile(){ //隐藏
return "smile from Tiger";
}
public String getNews(){ //覆盖
return news;
}
//新增的方法
public String eat(){ //新
return "need eat meat";
}
}
(1):x.news is Animal's news
(2):((Tiger)x).news is Tiger's news
(3):x.smile() is smile from Animal
(4):((Tiger)x).smile() is smile from Tiger
(5):x.getNews() is Tiger's news
(6):x.getNews() is Tiger's news
(7):x.getMessage() is Animal's message
(8):((Tiger)x).eat() is need eat meat
经过上面的沉淀,到这里应该没有问题了,有问题底下留言
4. *构造方法
本小节讨论的是上转型对象的构造方法,以及构造方法调用实例方法所出现的情况,具体如下:
class A {
int value = 5;
A( ) {
setValue(10);
System.out.println( "A:" + value);
}
void setValue(int v) {
value = 2 * v; }
}
class B extends A {
B( ) {
//super();
System.out.println( "B:" + value);
}
@Override
void setValue(int v) {
value = 5 * v; }
}
public class TestC {
public static void main(String[ ] args){
new A( );
new B( );
A c =new B();
}
}
运行答案如下:
A:20
-------------
A:50
B:50
-------------
A:50
B:50
刚刚接触这道题我是有点意外的,没想到Java还能这么考,感觉自己平常学的Java和老师教的Java不是一个东西,但是还是得默默承受(4.0万岁!!!) 话不多说,进入正题
- 第一值输出为20,创建的是父类对象,调用其构造方法+实例方法,结果无需多言
- 第二组为两个50,在创建
B
对象时,因为它是A
类的子类,默认会先调用A的构造方法。但是在A的构造方法中,调用了个实例方法。根据前面的知识我们知道,如果子类对父类的实例方法进行了重写覆盖,那么优先调用的应该是子类的方法,所以value
值为50。 - 第三组的答案与第二组相同,道理同上,作为上转型只是一个障眼法。