java中类继承,到底继承了什么?
继承的最大好处就是为了实现代码的复用。那么,子类到底从父类得到的什么呢?
实例成员
父类的private成员不会被子类继承,子类不能访问。但是子类对象的确包含父类的私有成员。父类的 包访问成员 继承为子类的包访问成员。就好像他们直接定义在子类中一样。父类的 protected 成员继承为子类的protected 成员。就好像他们直接定义在子类中一样。父类的 public 成员继承为子类的public 成员,就好像他们直接定义在子类中一样。
实例方法
继承到的实例方法在子类中可以直接被使用,还需重点理解是方法的重写和重载。
重写override
一个继承链中,父类的方法对于子类来说具有相同的语义,但是不同的细节操作,因此子类需要override父类的这个方法以满足自己的需求。
注意的点:
1、方法名,参数表一定和父类中的相同,返回类型相同,或者是子类。
1、访问权限一定不低于父类的实例方法
2、抛出的异常一定是父类方法抛出的异常相同,或者子类。
如果拿C++和java对比,那么java中的实例方法默认都是virtual的(java中没有virtual这个key word),因此在java中,子类可以直接重写父类方法的任何非final实例方法,但是在C++中,除非父类使用virtual标记一个方法为虚方法,子类才可以override这个方法。
对于重写的方法,javac是不能确定的具体要调用那个类的方法,而是产生特殊的字节码让jvm去动态决定什么方法。这个就是所谓的前期绑定和后期绑定的差异。
public class Test
{
public static void main(String [] args){ Object o = new SubClass();
o.toString();
}
}
class SubClass extends Object
{
public String toString() //重写Object 的toString方法
{
return "SubClass";
}
}
Compiled from "Test.java"
public class Test {
public Test();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return public static void main(java.lang.String[]);
Code:
0: new #2 // class SubClass
3: dup
4: invokespecial #3 // Method SubClass."<init>":()V
7: astore_1
8: aload_1
9: invokevirtual #4 // Method java/lang/Object.toString:()Ljava/lang/String;
12: pop
13: return
}
其中3处红色标记 的代码,重要的区别就是invokespecial 和 invokevirtual :invokespecial 代表前期绑定,在编译期就能决定调用什么方法,由javac确定调用什么方法。invokevirtual 则是方法的后期绑定,由JVM决定调用什么方法。
第一处是Test类的构造函数调用父类Object的构造函数,编译期确定,他是前期绑定。
第二处是因为new 了一个SubClass对象,调用SubClass的构造函数,编译期确定,他也是前期绑定的。
第三处是因为我们的 Object o 引用了重写了toString方法的SubClass对象,javac不能知道具体调用Object中的toString,还是SubClass中的toString,于是产生特殊代码让JVM去决定。
修饰为 static 、 final 、private 的方法一定是前期绑定,因为他们根本都不存在override。
JVM需要在运行时动态决定调用那个版本的方法,这个过程对JVM来说就是 virtual method lookup(虚方法查找)。JVM将会从实际对象所属的类 和 他的最近的父类中查找。如果自己定义了,则调用自己的版本,如果没有则调用父类的版本。
重载overload
重载的定义:函数重载是指在同一作用域内,可以有一组具有相同函数名,不同参数列表的函数,这组函数被称为重载函数。
注意:重载与否,不考虑函数的返回类型。C++也是如此。也就是说,2个函数的返回类型同不同都不影响他们能不能形成重载,只要他们函数名相同,参数表不同就满足重载。
Java是默认是支持跨类重载的。但是C++就默认不支持在子类中重载父类的实例方法。那也就是说:java中的实例方法会自动导入到子类的作用域中,而C++则不是。
public class Test
{ public static void main(String[] args)
{ Derive d = new Derive(); d.print("hello", 2); d.print("world"); /* output
hello
hello
world
world
world
world
world */ } } class Base
{ public void print(String msg)
{ for(int i=0;i<5;++i)
{
System.out.println(msg);
} } } class Derive extends Base
{ public void print(String msg,int times) //在子类中重载父类方法
{
for(int i=0;i<times;++i)
{
System.out.println(msg);
}
} }
对于C++
#include<iostream>
#include<string> using namespace std; class Base
{ public :
void print(const string& msg) const
{
for(int i=;i<;++i)
{
std::cout<<msg<<std::endl;
}
} }; class Derive:public Base
{ public : /*
*需要使用using Base::print将父类中的版本引如到子类的作用域中,这样才能形成跨类重载,否则子类在使用一个参数版本的print函数时,会出现以下编译错误:
*
* error: no matching function for call to ‘Derive::print(const char [6])’
*意思是编译器在Derive类中找不到print(const char [6])版本的函数
* */
using Base::print;
void print(const string& msg,const int times) const
{
for(int i=;i<times;++i)
{
std::cout<<msg<<std::endl;
}
} }; int main()
{ Derive d ;
d.print("hello",);
d.print("world"); }
实例字段
实例字段没有什么要说的,要说的就是实例字段的隐藏了:在子类中定义一个和父类同名的字段,那么子类中的名称将会隐藏父类中的同名字段。
几乎没有人使用这个技术,如果用到了,那么说明代码设计有问题(bad code)。
class Base
{
protected int i = 100;
} class Derive extends Base
{ private int i = 1000; //隐藏了父类字段 i
public void foo()
{ System.out.println(i); //代表this.i
System.out.println(this.i); System.out.println(super.i); //使用父类被隐藏的i } }
使用父类被隐藏的字段,也可以使用cast,这是最终极的手段。因为super只能引用最近父类的成员,而不能引用父类的父类的成员。但使用cast可以做到。
class A
{
protected int i = 100;
} class B extends A
{
protected int i = 1000;
} class C extends B
{
private int i = 10000; public void foo()
{
System.out.println("this.i:"+this.i); // System.out.println("B.this.i:"+ ((B)this).i ); // System.out.println("A.this.i:"+ ((A)this).i ); //
}
}
static成员
static会被子类继承吗?答案是会。他们会被继承为子类的static成员,而不是子类实例的成员。
同样,private static成员不会被继承,只有 包访问 权限 ,protected public 成员才会被继承。
父类的private成员不会被子类继承,子类不能访问。父类的 包访问成员 继承为子类的包访问成员。就好像他们直接定义在子类中一样。父类的 protected 成员继承为子类的protected 成员。就好像他们直接定义在子类中一样。父类的 public 成员继承为子类的public 成员,就好像他们直接定义在子类中一样。
static 成员同样也可以使用实例成员的访问修饰符 public ,包访问,protected , priavcte。
static方法
static方法不能被override,只能被隐藏。
static字段
和实例字段一样,static字段也可以被隐藏。如果要引用被隐藏的父类static字段,则需要显式的通过父类的类名来使用。隐藏static字段通常也最好不要使用。
构造函数
构造函数不能继承,但是子类一定可以(也必须)借用父类的构造函数。java保证:除了Object类对象,每一个类的实例在构造时,先去调用父类的构造函数。
我们自定义类的构造函数的第一句一定是super(xx,...),如果不是,那么第一句就一定是this(xx,...)去调用本类的另一个构造函数。
如果子类构造函数不显式的调用super(),那么,javac会自动插入super(),也就是父类无参数的构造函数。
对于构造函数,其实类中所有构造函数都是隐式static的。很明显的例证就是 构造函数無需通過实例就可以调用。
欢迎转载,请注明出处:www.cnblogs.com/lulipro
为了获得更好的阅读体验,请访问原博客地址。
限于本人水平,如果文章和代码有表述不当之处,还请不吝赐教。
代码钢琴家
java中类继承,到底继承了什么?的更多相关文章
- Java基础第一天--继承、修饰符
继承 继承的概述: 继承是面向对象三大特征之一.可以使得子类具有父类的属性和方法,还可以在子类中重新定义,追加属性和方法. //创建父类 public class Fu{ public void sh ...
- Java基本概念:继承
一.简介 描述: 现实世界中的继承无处不在.比如:动物细分有哺乳动物.爬行动物等,哺乳动物细分有灵长目.鲸目等. 继承的本质是对某一批类的抽象,从而实现对现实世界更好的建模. 继承是类和类之间的一种关 ...
- Java中类的继承,属性和方法的四种修饰符的作用范围,final关键字,java的三大特点中的2个:封装和多态,以及多态的一个设计模式,模板方法模式(template method)
(一)Java中的继承: 关于继承,在Java中类的继承只能是单继承,不像C+++那样灵活,可以多继承,多继承的后果就是各种关系乱套,就相当于一个孩子有2个母亲一样,社会关系的复杂,不利于程序后期的开 ...
- Java中类的继承深入剖析
在Java开发中,我们常常用到继承这一概念,可以说继承是Java这类面向对象编程语言的基石.正是有了继承这个概念,使得我们可以创建分等级层次的类.今天小编就和大家一起来深入聊聊Java语言的继承. 在 ...
- Java中类继承、接口实现的一些细节(长期更新)
前言 在Java中,子类继承父类,类实现接口是属于常识性的内容了,作为一个Java程序员应该也比较熟悉.不过子类继承父类,类实现接口中还是有一些小细节值得注意一下,本文就从个人工作.学习中入手,总结一 ...
- java类的封装 继承 多态
1.猜数字小游戏 package cn.jiemoxiaodi_02; import java.util.Scanner; /** * 猜数字小游戏 * * @author huli * */ pub ...
- Java三大特征之继承(二)
在<Think in java>中有这样一句话:复用代码是Java众多引人注目的功能之一.但要想成为极具革命性的语言,仅仅能够复制代码并对加以改变是不够的,它还必须能够做更多的事情.在这句 ...
- Java基础学习笔记七 Java基础语法之继承和抽象类
继承 继承的概念 在现实生活中,继承一般指的是子女继承父辈的财产.在程序中,继承描述的是事物之间的所属关系,通过继承可以使多种事物之间形成一种关系体系. 例如公司中的研发部员工和维护部员工都属于员工, ...
- java基础知识总结--继承和接口
什么是继承?什么是接口?他们之间的区别和联系是什么? 什么是继承? 继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能.多个类中存在相同属性和行 ...
- 关于Java的特点之继承
继承--解决之道 继承可以解决代码复用,让我们的编程更加靠近人类思维.当多个类存在相同的属性(变量)和方法时,可以从这些类中抽象出父类(比如刚才的Student),在父类中定义这些相同的属性和方法,所 ...
随机推荐
- FixFFmpeg 修改官方编译的ffmpeg能在 XP 上运行的工具
把 fixff.cmd 和 FixFFmpeg.exe 拷贝到 ffmpeg 所在的目录 运行 fixff.cmd 自动修复; fixffmpeg-20160924.7z
- android so调试时遇到的坑 - arm流水线
直接看下面这段ARM汇编: 此时运行到的代码为ADD R3,PC 此时看一下寄存器窗口的值: 按理来说执行完ADD R3,PC后的效果应该是R3=R3+PC ,R3=40A1D5C8 但是我们可以执行 ...
- iOS 8.0后使用UIAlertController
iOS 8的新特性之一就是让接口更有适应性.更灵活,因此许多视图控制器的实现方式发生了巨大的变化.全新的UIPresentationController在实现视图控制器间的过渡动画效果和自适应设备尺寸 ...
- wow经典台词
永恒岛,磐皂在玄牛上场时喊:你能否独立山巅,任由风霜侵袭,直至沧海变为桑田,高山沉入海底?风刀霜剑,四面受敌.不动如山,亘古不移. 巫妖王:当一切结束,你会跪求我的宽恕...而我,会拒绝你! 伊利丹: ...
- JTabbedPane 和 JScrollBar 联合使用
需求:实现一个JTabbed, 当下拉到Tabbed的底部时,自动加载下一次的数据. 下面是具体代码: import java.awt.*; import javax.swing.table.Defa ...
- iOS开发UI篇—核心动画(UIView封装动画)
iOS开发UI篇—核心动画(UIView封装动画) 一.UIView动画(首尾) 1.简单说明 UIKit直接将动画集成到UIView类中,当内部的一些属性发生改变时,UIView将为这些改变提供动画 ...
- node在centos下的安装
CentOS 下安装 Node.js 1.下载源码,你需要在https://nodejs.org/en/download/下载最新的Nodejs版本,本文以v4.6.2为例: cd /usr/loca ...
- Android Telephony —— 手机信号实时变化源码分析过程记录
源码版本:4.4 跳过InCallActivity等UI实现.先看service以及底层. 1, 在frameworks/opt下面会发现如下文件列表: ./telephony/src/java/co ...
- Fiddler
Fiddler教程: 原文:http://kb.cnblogs.com/page/130367/
- 【Java EE 学习 28 上】【oracle学习第二天】【子查询】【集合运算】【几种数据库对象】
一.子查询 1.为什么要使用子查询:问题不能一步求解或者一个查询不能通过一步查询得到. 2.分类:单行子查询和多行子查询. 3.子查询的本质:一个查询中包含了另外一个或者多个查询. 4.使用子查询的规 ...