当父类的对象引用没有指向父类的对象,而是指向了子类的对象时,调用方法或访问变量时会怎样呢?

假设父类为Person,子类为Student,有下面的两行定义:

Student sTest = new Student();

Person pTest = sTest;

其中,pTest就是父类的对象引用,sTest是子类的对象引用;pTest和sTest指向了同一个子类对象。

那么,

(1).如果子类的成员变量与父类的成员变量的类型及名称都相同,则用sTest访问时,访问到的是子类的成员变量;用pTest访问时,访问到的是父类的成员变量;

(2).如果子类的静态成员变量与父类的静态成员变量的类型及名称都相同,则用sTest访问时,访问到的是子类的静态成员变量;用pTest访问时,访问到的是父类的静态成员变量;

(3).如果子类的静态成员方法重写了父类的静态成员方法,则用sTest调用时,调用的是子类的静态成员方法;用pTest调用时,调用的是父类的静态成员方法;

      (1)、(2)、(3)都称为隐藏,可以理解成父类的这些变量和静态成员方法被放到抽屉里暂时藏起来了,当用父类对象引用访问或调用时,把抽屉拉开就可以看到了;

(4).如果子类的成员方法重写了父类的成员方法,则用sTest调用时,调用到的是子类的成员方法;用pTest调用时,调用的也是子类的成员方法;

      此时称为覆盖,可以理解成父类的这些方法被子类重写后的方法用胶带给粘上了,撕不下来了,即使父类对象引用调用时也只能看到子类重写后的方法;

(5).用sTest调用未覆盖的父类成员方法时,该方法中如果使用到了被隐藏的变量或方法时,规则同上;

还是以简单的示例来详细说明。

Person类为父类,Student类为子类,TestMain类为测试类。代码分别如下:

Person类的代码为:

package human;

public class Person {
String name;
int age;
String gender; public String education;
private String hobby;
protected String residence; static String citizenship = "Chinese"; public Person() { } public void setName(String n) {
this.name = n;
} public String getName() {
return name;
} public void informationPrint() {
System.out.println("My name is(getName) " + getName());
System.out.println("My name is(name) " + name);
System.out.println("I am " + getAge() + " years old"); if(getGender() == "female")
System.out.println("I am a girl");
else
if(getGender() == "male")
System.out.println("I am a boy");
else
System.out.println("Something is wrong!");
System.out.println("My hobby is " + hobby); if(citizenship == "Chinese")
System.out.println("I am a chinese");
//test:静态变量是否在构造方法之前初始化
else
if(citizenship == "US")
System.out.println("I am an American");
else
System.out.println("Oh,something is wrong");
} //test:覆盖
public void testModifierPublic() {
System.out.println("Person:Public");
} //test:隐藏
public static void staMethodHide() {
System.out.println("Person:static Method");
}
}

Student类的代码为:

package human;

public class Student extends Person {
String stuNumber;
int score; //test:隐藏
String name = "ha";
static String citizenship = "US"; public Student() { } public Student(String n, String g) {
super(n,g);
} //test:隐藏
public void setName(String n) {
this.name = n;
} //test:隐藏
public String getName() {
return name;
} //test:覆盖
public void testModifierPublic() {
System.out.println("Student:Public");
} //test:super
public void testSuper() {
System.out.println("Super:");
super.testModifierPublic();
} //test:隐藏
public static void staMethodHide() {
System.out.println("Student:static Method");
}
}

  

TestMain类的代码为:

package human;

public class TestMain {

    public static void main(String[] args) {
Student sTest = new Student();
Person pTest = sTest; System.out.println("下面是以子类对象sTest来访问变量、调用方法的结果:");
sTest.testModifierPublic();
System.out.println(sTest.name);
System.out.println(sTest.getName());
System.out.println(sTest.citizenship);
sTest.staMethodHide();
sTest.testSuper();
sTest.informationPrint(); System.out.println("下面是以父类对象pTest来访问变量、调用方法的结果:");
pTest.testModifierPublic();
System.out.println(pTest.name);
System.out.println(pTest.getName());
System.out.println(pTest.citizenship);
pTest.staMethodHide();
} }

输出结果为:

 下面是以子类对象sTest来访问变量、调用方法的结果:
Student:Public
ha
Student:getName()
ha
US
Student:static Method
Super:
Person:Public
Student:getName()
My name is(getName) ha
My name is(name) null
I am 0 years old
Something is wrong!
My hobby is null
I am a chinese
下面是以父类对象pTest来访问变量、调用方法的结果:
Student:Public
null
Student:getName()
ha
Chinese
Person:static Method

下面对结果进行分析:

(1).前两条语句为:

Student sTest = new Student();
Person pTest = sTest;

第一条语句定义了子类对象引用sTest,并指向了一个子类对象;第二条语句定义了父类对象引用pTest,并被赋值为sTest的值;大体的内存结构见图1:

图1

其中,子类的String型成员变量name与父类的name重名,子类的name值为“ha”,父类的name默认初始化为NULL;String型静态成员变量citizenship与父类的citizenship重名,子类的citizenship值为“US”,父类的citizenship值为“Chinese”。

(2).第4条语句为:sTest.testModifierPublic();

输出结果为第2行:Student:Public

第12行语句为:pTest.testModifierPublic();

输出结果为第18行:Student:Public

sTest、pTest都调用了方法testModifierPublic(),子类重写了父类的此方法,当sTest调用时,很显然要调用子类重写后的方法;pTest调用时,由于该方法已被子类的方法覆盖,所以调用的也是子类重写后的方法。

(3).第5、6条语句分别为:

System.out.println(sTest.name);

System.out.println(sTest.getName());

输出结果为第3、4、5行:

ha

Student:getName()

ha

第13、14条语句分别为:

System.out.println(pTest.name);

System.out.println(pTest.getName());

输出结果为第19、20、21行:

null
     Student:getName()
     ha

sTest.name是直接访问成员变量name,sTest.getName()是通过调用getName()方法间接获得name的值;两种方式都输出了“ha”;虽然name隐藏了父类的name,getName()重写了父类的getName(),但调用者是sTest,所以使用的都是子类的变量和方法;

name虽然被隐藏,但pTest是父类对象引用,所以访问是是父类的name,所以输出为NULL;但父类的getName()被覆盖,所以调用的是子类的方法。

(4).第7、8条语句分别为:

System.out.println(sTest.citizenship);

sTest.staMethodHide();

输出结果为第6、7行:

US

Student:static Method

第15、16条语句分别为:

System.out.println(pTest.citizenship);

pTest.staMethodHide();

输出结果为第22、23行:

Chinese
     Person:static Method

对静态成员变量和静态方法而言,被隐藏时,由子类对象引用访问或调用时,访问或调用到的就是子类的变量或方法;由父类对象引用访问或调用时,访问或调用到的就是父类的变量或方法。

(5).第9条语句为:sTest.testSuper();

输出结果为:

Super:

Person:Public

sTest调用子类成员方法testSuper(),方法体中用到了super.testModifierPublic();,用super来显式调用父类的方法,所以输出的是Person:Public。

(6).第10条语句为:sTest.informationPrint();

输出结果为第10到16行:

Student:getName()

My name is(getName) ha

My name is(name) null

I am 0 years old

Something is wrong!

My hobby is null

I am a chinese

<1>.子类没有重写父类的informationPrint()这个成员方法。

<2>.输出的第10、11行,通过getName()方法得到的name值是“ha”,输出的第12行,通过直接访问name得到的值为NULL;

说明调用的是子类getName(),但访问的父类的name;

也就是说,子类调用父类未重写的成员方法时,成员方法体中如果调用到某个方法被子类重写了,则实际调用子类重写后的方法;

       如果访问到某个被隐藏的成员变量,则实际访问到的是父类的成员变量;

       这时可以理解成,子类对象中包含了一个父类对象,由这个父类对象来访问或调用其变量或方法,如果是隐藏的情况,则访问到的是父类的值,如果是覆盖的情况,则调用的是子类重写后的方法。

<3>.输出的第16行,可以看出访问的静态成员变量也是父类的变量。

Java学习笔记12---向上转型-父类的对象引用指向子类对象的更多相关文章

  1. 6.5(java学习笔记)其他流(字节数组流,数据流,对象流,打印流)

    一.字节数组流 之前使用输入输出流的操作的对象是文件,而这里字节数组流操作的对象是内存,内存可以看做是一个字节数组. 使用字节数组流读写就可以看做是从内存A到内存B的读写,对象时内存即字节数组. 1. ...

  2. java学习笔记(12) —— Struts2 通过 xml /json 实现简单的业务处理

    XML 1.引入dom4j-2.0.0.jar 2.引入jquery-1.8.2.js 3.新建common.js getInfo = function(){ $.post("getXmlA ...

  3. Java学习笔记12

    循环 打印一个字符串(例如: "Welcome to Java!") 100次,就需要吧下面的输出语句重复写100遍,这是相当繁琐的: System.out.println(&qu ...

  4. Java学习笔记-12.传递和返回对象

    1.Clone()方法产生一个object,使用方法后必须产生的object赋值. Vector v2 = (Vector)v.clone(); 2.Clone()方法在object中是保护类型方法, ...

  5. Java学习笔记12(面向对象五:构造方法、this再探)

    在开发中,经常需要在创建对象的同时明确对象对的属性值, 比如一个Person对象创建时候就应该有age和name等属性 那么如何做到在创建对象的同时给对象的属性初始化值呢? 这里介绍构造方法: 1.构 ...

  6. java学习笔记12(final ,static修饰符)

    final: 意思是最终的,是一个修饰符,有时候一个功能类被开发好了,不想被子类重写就用final定义, 用final修饰的最终数据成员:如果一个类的数据成员用final修饰符修饰,则这个数据成员就被 ...

  7. Java学习笔记16---抽象类与接口的浅显理解

    抽象类是由abstract修饰的类,定义方式如public abstract class A{...}. 接口由interface修饰,定义方式如public interface B{...}. 抽象 ...

  8. Java学习笔记9(面象对象9:多态)

    多态概述 多态是继封装.继承后,面对对象的第三大特性. 现实事物经常会出现多态,如学生,学生是人的一种,则一个具体的同学张三既是学生也是人,即出现两种形态. Java作为面向对象的语言,同样可以描述一 ...

  9. Java学习笔记14---this作为返回值时返回的是什么

    有时会遇到this作为返回值的情况,那么此时返回的到底是什么呢? 返回的是调用this所处方法的那个对象的引用,读起来有点绕口哈,有没有想起小学语文分析句子成份的试题,哈哈. 一点点分析的话,主干是& ...

随机推荐

  1. python安装(python2.7)

    1.下载python 进入官网下载安装 点击打开链接(官网地址:https://www.python.org/downloads/),进入官网后根据自己需求选择python2 或者 python3 2 ...

  2. JavaWeb之Maven配置

    Maven和C#的nuget类似,可以通过设置就能引入框架等第三方,方便又省事.Java中使用Maven来管理第三方.今天尝试着配置了一下. 一.JDK的安装 关于JDK的安装可以查看百度经验,设置P ...

  3. 初学者易上手的SSH-hibernate01环境搭建

    这里我们继续学习SSH框架中的另一框架-hibernate.那么hibernate是什么?Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,使得Java程序 ...

  4. Python并发编程__多进程

    Python并发编程_多进程 multiprocessing模块介绍 python中的多线程无法利用多核优势,如果想要充分地使用多核CPU的资源(os.cpu_count()查看),在python中大 ...

  5. Bootstrap 引用的标准模板

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8&quo ...

  6. 【转】漫谈linux文件IO--io流程讲的很清楚

    [转]漫谈linux文件IO--io流程讲的很清楚 这篇文章写的比较全面,也浅显易懂,备份下.转载自:http://blog.chinaunix.net/uid-27105712-id-3270102 ...

  7. angular.js基础

    内置指令 所有的内置指令的前缀都为ng,不建议自定义指令使用该前缀,以免冲突.首先从一些常见的内置指令开始.先列出一些关键的内置指令,顺便简单说说作用域的问题. ng-model 将表单控件和当前作用 ...

  8. 【ASP.NET MVC 学习笔记】- 04 依赖注入(DI)

    本文参考:http://www.cnblogs.com/willick/p/3223042.html 1.在一个类内部,不通过创建对象的实例而能够获得某个实现了公开接口的对象的引用.这种"需 ...

  9. ASP.NET Core 2.0 in Docker on Windows Container

    安装Docker for Windows https://store.docker.com/editions/community/docker-ce-desktop-windows 要想将一个ASP. ...

  10. clone对象

    在JavaScript中,当对象作为参数传给函数的时候,在函数内部对这个对象的属性进行修改时,函数外部的对象属性也会跟着被修改,而有些时候我们并不想原来的对象数据发生改变,这时候就需要切断对象之间的引 ...