java基础:父类与子类之间变量和方法的调用
1)父类构造函数
java中当调用某个类的构造方法的时候,系统总会调用父类的非静态初始化块进行初始化,这个调用是隐式的,而且父类的静态初始化代码
块总是会被执行,接着调用父类的一个或者多个构造器执行初始化,这个调用也可以通过super进行显式调用。
例如:
父类代码如下:
public class Creature {//父类
{//非静态代码块
System.out.println("creature的非静态代码块正在执行");
}
public Creature(){
System.out.println("creature的构造函数正在执行");
}
}
子类代码如下:
public class Animal extends Creature {
{
System.out.println("animal的初始化代码块正在执行");
}
public Animal(){
System.out.println("animal的构造方法正在执行");
}
public static void main(String[] args){
Animal a = new Animal() ;
}
}
则运行程序后的结果为:
creature的非静态代码块正在执行
creature的构造函数正在执行
animal的初始化代码块正在执行
animal的构造方法正在执行
从结果中可以看出:调用某个类的构造方法的时候总是会先执行父类的非静态代码块,然后执行父类的构造方法,最后才是执行当前类的
非静态代码块和构造方法。执行过程中有先后顺序。
若果想要显式调用父类的构造方法则可以使用super(),来调用,但是super关键字和this关键字都必须放在构造放的第一行,而且只能使
用一个,为什么要放在第一行呢?因为如果不放在第一行则先调用子类的初始化代码,再调用父类的初始化代码,则父类中的初始化后的值
会覆盖子类中的初始化的值。
注:super用于显式调用父类的构造器,this可以显式调用本类中的重载的构造器。
2)访问子类对象的实例变量
子类的方法可以访问父类中的实例变量,这是因为子类继承父类就会获得父类中的成员变量和方法,但是父类方法不能访问子类的实例变量
,因为父类根本无法知道它将被哪个类继承,它的子类将会增加怎么样的成员变量。但是,在极端的情况下,父类也可以访问子类中的变量。
例如:
父类代码如下:
public class Base {//父类
private int i = 2 ;
public Base(){
this.display() ;
}
public void display(){
System.out.println(i);
}
}
子类中代码如下:
public class Derived extends Base {
private int i = 22 ;
public Derived(){
i = 222 ;
}
public void display(){
System.out.println(i);
}
}
测试用例如下:
public class Test {
public static void main(String[] args) {
new Derived() ;
}
}
程序的执行结果为:
0
也许你会感到奇怪,为什么不是2?22?222?怎么会是0呢,下面我们看一下程序的执行过程。当我们调用new Derived() ;创建Derived
实例的时候,系统会为Derived对象分配内存空间,Derived会有两个i实例变量,会分配两个空间来保存i的值。分配完空间以后i的值为0
,如果有引用类型则引用类型的值为null。接下来程序在执行Derived的构造器之前会执行Base的构造器,表面上看Base的构造器中只有
一行代码,但是在父类中定义i的时候执行的初始值2,因此经过编译之后,该构造方法中应该包含如下两行代码:
i =2 ;
this.display() ;
程序先将Base中的i赋值为2,然后执行display方法。此处有一个关键字this,this到底代表谁呢?表面上看this代表的是Base的当前实例,
但是实际上代码是放在Derived的构造器中的,所以this最终代表的是Derived的当前实例(编译类型是Base而实际引用一个Derived对象),
所以如果在父类的构造方法中直接输出System.out.println(this.i) ;则输出的结果为2。但是调用this.display()方法,此时调用的是
子类中重写的display方法,输出的变量i也是子类中的i,但是此时子类中的变量i还没有赋值,所以输出结果为0。
为了详细的看清楚this变量到底代表什么实例,我们将Base的构造方法修改如下:
public Base(){
System.out.println(this.i);
System.out.println(this.getClass());
this.display() ;
}
再次运行程序,结果为:
2
class edu.qichao.chapter2.Derived
0
可以看到this代表的是Derived的实例,但是编译的时候类型为Base,所以输出this.i的值为2。
3)调用被子类重写的方法
默认情况下,子类可以调用父类的方法,但是父类不能调用子类的方法,因为父类不知道它将被哪个子类继承,也不知道子类将增加怎么
样的方法。
例如:
父类Animal的代码如下:
public class Animal {
private String desc ;
public Animal(){
this.desc = getDesc() ;
}
public String getDesc(){
return "Animal" ;
}
public String toString(){
return desc ;
}
}
子类Wolf的代码如下:
public class Wolf extends Animal {
private String name ;
private double weight ;
public Wolf(String name , double weight){
this.name = name ;
this.weight = weight ;
}
public String getDesc(){
return "Wolf[name=" + name + ",weight=" + weight + "]" ;
}
public static void main(String[] args){
System.out.println(new Wolf("灰太狼" , 3));
}
}
程序的运行结果为:
Wolf[name=null,weight=0.0]
现在我们分析一下程序执行的过程。在main方法中通过new Wolf("灰太狼" , 3);来创建一个Wolf的实例,子类会隐式调用父类的构造方
法,在父类构造方法中初始化desc变量this.desc = getDesc() ;此处需要注意this变量,虽然这个this放在Animal的构造放中,但是是在
Wolf的构造方法中调用父类的构造方法,所以this编译时类型为Animal,运行时类型为Wolf,此处调用的getDesc方法是子类Wolf的方法,
但是子类中的name和weight变量都没有初始化,默认为null和0.0.所以程序的最终结果为:Wolf[name=null,weight=0.0]
4)继承成员变量和成员方法的区别
java中队成员变量的继承和成员方法的继承是不同的。
例如:
父类代码如下:
public class Base {
int count = 2 ;
public void display(){
System.out.println(this.count);
}
}
子类代码如下:
public class Derived extends Base {
int count = 20 ;
@Override
public void display(){
System.out.println(this.count);
}
}
测试用例如下:
public class Test {
public static void main(String[] args) {
Base b = new Base() ;
System.out.println(b.count);
b.display() ;
System.out.println("-----------------");
Derived d = new Derived() ;
System.out.println(d.count);
d.display() ;
System.out.println("-----------------");
Base bd = new Derived() ;
System.out.println(bd.count);
bd.display() ;
System.out.println("-----------------");
Base d2b = d ;
System.out.println(d2b.count);
}
}
程序运行结果为:
2
2
-----------------
20
20
-----------------
2
20
-----------------
2
在上面的程序中,不管是d变量、还是bd变量、还是都d2b变量。只要他们指向一个Derived对象,则不管他们声明时用了什么类型,当通过
这些变量调用方法时,方法的行为总是表现出他们的实际类型的行为,但是如果通过这些变量来访问他们所指向对象的实例变量的时候,
这些实例变量的值总是表现出声明这些变量所用类型的行为。由此可见,java处理成员变量和成员方法的继承时是有区别的。
java基础:父类与子类之间变量和方法的调用的更多相关文章
- Java特性之多态父类与子类之间的调用
问题描述: Java三大特性,封装.继承.多态,一直没搞懂其中多态是什么,最近研究了一下,关于父类和子类之间的调用.下面是一个测试类,源代码如下: package com.test; public c ...
- JAVA 基础基本语法---常量与变量
JAVA 基础基本语法---常量与变量 语法:计算机能够识别的语言的规则: 0. 基本语法 编写Java程序时,应注意以下几点: 大小写敏感:Java是大小写敏感的,这就意味着标识符Hello与hel ...
- java中父类与子类, 不同的两个类中的因为构造函数由于递归调用导致栈溢出问题
/* 对于类中对成员变量的初始化和代码块中的代码全部都挪到了构造函数中, 并且是按照java源文件的初始化顺序依次对成员变量进行初始化的,而原构造函数中的代码则移到了构造函数的最后执行 */ impo ...
- Java 多态 父类和子类方法的访问控制权限
Java 多态 父类和子类方法的访问控制权限 @author ixenos 父类和子类方法的访问控制权限 继承是为了扩展类的功能,而这种扩展显然就是对一个原始类的扩展,目的还是向上转型来调用,所以这就 ...
- 《深入Java虚拟机学习笔记》- 第19章 方法的调用与返回
<深入Java虚拟机学习笔记>- 第19章 方法的调用与返回
- Java基础-父类-子类执行顺序
代码解析 子类 package com; /** * 子类 * @author huage * */ public class Test extends Test1{ public static vo ...
- java基础(七)之子类实例化
知识点;1.生成子类的过程2.使用super调用父类构造函数的方法 首先编写3个文件. Person.java class Person{ String name; int age; Person() ...
- java中父类和子类初始化顺序
顺序 1. 父类中静态成员变量和静态代码块 2. 子类中静态成员变量和静态代码块 3. 父类中普通成员变量和代码块,父类的构造函数 4. 子类中普通成员变量和代码块,子类的构造函数 其中“和”字两端的 ...
- Java中父类和子类代码执行顺序
执行顺序:父类静态块-->子类静态块-->父类非静态块-->父类构造方法-->子类非静态块-->子类构造方法 当父类或子类中有多个静态方法时按在代码中的顺序执行 pack ...
随机推荐
- git 命令使用速查手册( 个人版)
1. 克隆远程库 git clone repository_address 通过 git clone 获取的git库只是远程库中的当前工作分支,如果想获取其它分支信息,可参考下面. 2. 查看远程 ...
- asp.net mvc 4多级area实现技巧
今天在工作要实现这个多级area.其原因是这个项目需要多级的功能,大的类别里有小的类别,小的类别里有具体的功能项,每一个功能项还有若干动作Action,所以在菜单和mvc工程的结构上都需要有体现多级的 ...
- SQL 子查询 EXISTS 和 NOT EXISTS
MySQL EXISTS 和 NOT EXISTS 子查询语法如下: SELECT … FROM table WHERE EXISTS (subquery) 该语法可以理解为:将主查询的数据,放到子查 ...
- Mysql 常用函数集
1.分类导航 一共分为5类函数 . 字符型函数 . 数值型函数 . 日期型函数 . 统计型函数 . 其它型函数 2.字符型函数[约48个] ascii(str) bin(n) bit_length(s ...
- FPGA研发之道(25)-管脚
管脚是FPGA重要的资源之一,FPGA的管脚分别包括,电源管脚,普通I/O,配置管脚,时钟专用输入管脚GCLK等. 本文引用地址:http://www.eepw.com.cn/article/2664 ...
- 史上最简单的springcloud微服务入门实例,满足企业日常需求,开箱即用,工资翻倍不是梦
在传统的IT行业软件大多都是各种独立系统的堆砌,这些系统的问题总结来说就是扩展性差,可靠性不高,维护成本高.到后面引入了SOA服务化,但是,由于 SOA 早期均使用了总线模式,这种总线模式是与某种技术 ...
- 【算法拾遗(java描写叙述)】--- 插入排序(直接插入排序、希尔排序)
插入排序基本思想 每次将一个待排序的记录按其keyword大小插入到前面已经拍好序的子文件的适当位置,直到全部记录插入完毕为止. 直接插入排序 基本思想 直接插入排序的基本操作是将一个记录插入到已排好 ...
- JavaScript之RegExp对象
ECMAScript 通过 RegExp 类型来支持正则表达式.使用下面类似 Perl 的语法,就可以创建一个正则表达式 var expression = / pattern / flags ; 其中 ...
- LINUX内核升级-更新网卡驱动
因项目需要,将当前内核(2.6.32-220.el6.x86_64)升级到目标内核(2.6.33-110.el6.x86_64),但是编译的目标 内核(2.6.33-110.el6.x86_64)的对 ...
- uva753 A Plug for UNIX 网络流最大流
C - A Plug for UNIX You are in charge of setting up the press room for the inaugural meeting of t ...