-
2021-03-14 01:15:16
最近在学习到Java的继承和多态时遇到了这样的一个问题:关于继承链中子类对父类的私有方法是否可以覆盖的问题,在此记录一下自己对这个问题解决以后的一些心得。
首先要明确:子类是不能够覆盖(重写)父类的私有方法的。比较直观的理由是,子类要能够重写父类的方法的前提条件是继承,子类对父类的私有方法并不继承,也没有访问权限,自然也是不能重写的。接下来看一个例子:
[java] view plaincopy
public class Test {
public static void main(String[] args) {
new A().printPerson();
new B().printPerson();
}
}
class A {
public void printPerson() {
System.out.println(getInfo());
}
private String getInfo() {
return "A";
}
}
class B extends A{
public String getInfo() {
return "B";
}
}
运行结果是 A A。如果将private修饰词去掉,换成protected和public修饰,运行结果是A B这是毫无疑问的,因为那正是B重写了父类A的getInfo方法;而加上了private修饰词以后,父类A的getInfo和子类B的getInfo实际上就是两个无关的方法,因为私有方法是不能被子类重写的,私有方法并不是父类接口的一部分,只是隐藏在父类中的一段程序代码。
可是为什么使用子类B的实例调用printPerson,结果是打印A呢?要注意到printPerson方法是在父类A中定义的,因此刚执行new B().printPerson()这行代码时,编译器在子类B中无法找到匹配的printPerson方法,于是到父类A中去寻找;此时在父类A中找到了匹配的printPerson方法,并调用该方法。
此处需要提及一下子类在继承父类时对父类的成员变量及方法继承的问题。对于使用protected或者public修饰的成员变量及方法,会被子类继承,且可通过子类直接调用,那么,对于子类不可见的private成员,以及没有修饰词修饰的成员(若子类与父类在不同的包中,这一类成员也是对子类不可见的),他们不被子类继承,那么在子类的实例所代表的内存空间中,这些成员是否存在呢?答案是肯定的,父类的私有变量及方法虽然不会被子类继承,对于子类来说不可见,但当创建了子类的实例的时候,这些成员一样会被加载入内存,并“隐藏”在内存当中。
因此,通过子类B的实例调用printPerson方法,会在父类的成员中寻找匹配的printPerson方法,找到以后,进入printPerson的方法体,调用getInfo方法,程序回到上层去寻找getInfo方法的匹配,并在内存空间中寻找到了A中的getInfo方法,同时由于该getInfo方法是私有的,无法被重写,因此不会触发java的动态绑定机制,于是直接调用该方法。因此,通过B的实例调用printPerson,打印的结果也是A了。
============================================
最近学习继承,从书中看到子类继承父类,子类拥有父类所有的属性和方法,于是使用程序去验证,发现父类的私有属性和私有方法,子类是不能访问的,当然一些父类的私有属性可能可以通过相应的方法访问到,但是私有的方法似乎不能简单的访问,这里暂不考虑Java反射机制,于是我分析,子类不能继承父类私有的属性及方法,但是分析了内存后,我发现我是错的,在一个子类被创建的时候,首先会在内存中创建一个父类对象,然后在父类对象外部放上子类独有的属性,两者合起来形成一个子类的对象。所以所谓的继承使子类拥有父类所有的属性和方法其实可以这样理解,子类对象确实拥有父类对象中所有的属性和方法,但是父类对象中的私有属性和方法,子类是无法访问到的,只是拥有,但不能使用。就像有些东西你可能拥有,但是你并不能使用。所以子类对象是绝对大于父类对象的,所谓的子类对象只能继承父类非私有的属性及方法的说法是错误的。可以继承,只是无法访问到而已。
当子类覆盖父类的成员变量时,父类方法使用的是父类的成员变量,子类方法使用的是子类的成员变量
(1)子类覆盖父类的方法,必须有同样的参数返回类型,否则编译不能通过
(2)子类覆盖父类的方法,在jdk1.5后,参数返回类可以是父类方法返回类的子类
(3)子类覆盖父类方法,可以修改方法作用域修饰符,但只能把方法的作用域放大,而不能把public修改为private
(4)子类方法能够访问父类的protected作用域成员,不能够访问默认的作用域成员
(5)子类的静态方法不能隐藏同名的父类实例方法
不能继承,子类只能在父类中写一个public的getXXX的方法来获取父类中的private属性,子类就调用父类的getXXX来获取private属性
父类中的公有方法和域(属性),在类继承中将会被子类继承,但是私有的将不能被继承。
那么在继承中子类如何才能继承到父类的私有域呢?
答案是:在子类的构造方法中通过super()方法调用父类的构造方法。
也就是,在构造子类的同时,为子类构造出跟父类相同的域。如此就在子类的对象中,也拥有了父类声明的域了。
并且我们也应该这样做。
[html] view plaincopy
public class Person{
private String name;
public Person(String name){
this.name = name;
}
public void setName(String name){
this.name = name;
}
public String getName(){
return name;
}
}
[html] view plaincopy
public class Student extends Person{
public Student(String name){
super(name);
}
}
[html] view plaincopy
public class TestStudent{
public static void main(String[] args){
Student mStudent = new Student("abc");
String mName = mStudent.getName();
System.out.println("Name is : " + mName);
mStudent.setName("efg");
mName = mStudent.getName();
System.out.println("Name is : " + mName);
}
}
打印结果为:
Name is : abc
Name is efg
============================================
Java-子类没有继承父类的私有变量和私有方法
在Java子类有没有继承父类的私有变量?关于这个问题在网上找了很久,也没有一个明确的答案。于是综合大家的观点,自己总结了一下。
更多相关内容 -
Python实现子类调用父类的方法
2020-12-25 06:22:47本文实例讲述了Python实现子类调用父类的方法。分享给大家供大家参考。具体实现方法如下: python和其他面向对象语言类似,每个类可以拥有一个或者多个父类,它们从父类那里继承了属性和方法。如果一个方法在子类的... -
idea 查看一个类的所有子类以及子类的子类并以层级关系显示
2020-08-25 02:37:54主要介绍了idea 查看一个类的所有子类以及子类的子类并以层级关系显示,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧 -
java子类调用父类的方法中包含子类重写的实例方法
2020-08-25 15:28:00在本篇文章里小编给大家整理了关于java子类调用父类的方法中包含子类重写的实例方法以及相关知识点,需要的朋友们可以学习下。 -
易语言窗口过程子类化
2020-07-21 05:55:05易语言窗口过程子类化源码,窗口过程子类化 -
浅谈将子类对象赋值给父类对象
2020-12-31 11:13:04最近对将子类对象赋值给父类对象有点心得,想和大家分享一下.但本人水平有限,请各位指正和批评. 言归正传,下面是几个小例子,请大家看一看. 测试一 父类: public class Supclass { public void print() { ... -
Java中父类怎么调用子类的方法
2020-08-26 02:58:52主要介绍了Java父类调用子类的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧 -
Java异常 Exception类及其子类(实例讲解)
2020-08-28 21:54:46下面小编就为大家带来一篇Java异常 Exception类及其子类(实例讲解)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧 -
Java子类对象的实例化过程分析
2020-08-25 14:50:38主要介绍了Java子类对象的实例化过程,结合具体实例形式分析了java子类对象的实例化的步骤、原理、实现方法,需要的朋友可以参考下 -
Python 实现子类获取父类的类成员方法
2020-09-19 17:58:13今天小编就为大家分享一篇Python 实现子类获取父类的类成员方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧 -
Java中父类和子类之间的转换操作示例
2020-08-19 05:31:28主要介绍了Java中父类和子类之间的转换操作,结合实例形式分析了Java中父类和子类之间的转换相关原理、操作技巧与使用注意事项,需要的朋友可以参考下 -
java父类和子类初始化顺序的深入理解
2020-09-05 06:00:01本篇文章是对java父类和子类初始化顺序进行了详细的分析介绍,需要的朋友参考下 -
JAVA获取同一路径下所有子类或接口实现类
2020-12-22 16:32:59整个测试代码如下: package find; import java.io.File; import java.io.IOException; import java.net.URL; import java.util.ArrayList;... import java.util.List;... System.out.println("接口实现类: -
浅谈Java中父类与子类的加载顺序详解
2020-09-05 06:01:01本篇文章是对Java中父类与子类的加载顺序进行了详细的分析介绍,需要的朋友参考下 -
C++类继承之子类调用父类的构造函数的实例详解
2020-08-29 11:03:52主要介绍了C++类继承之子类调用父类的构造函数的实例详解的相关资料,希望通过本文大家能够掌握C++类继承的相关知识,需要的朋友可以参考下 -
Java子类的继承性
2020-12-22 21:43:10如果子类和父类在同一个包中,那么子类自然地继承了其父类中不是private的成员变量作为自己的成员变量,并且也自然地继承了父类中不是private的方法作为自己的方法。继承的成员变量以及方法的访问权限保持不变。 ... -
Java编程关于子类重写父类方法问题的理解
2020-08-28 18:47:44主要介绍了Java编程关于子类重写父类方法问题的理解,分享了有关子类重写父类的实例,具有一定参考价值,需要的朋友可以了解下。 -
在父类获取子类属性
2019-04-05 01:17:00NULL 博文链接:https://2528.iteye.com/blog/1523901 -
C#子类舍弃属性转换为父类UtilSubToBase.cs
2019-06-08 01:09:43利用C#反射将子类舍弃多余的属性转换为父类,用途比较少。 -
PHP面向对象程序设计子类扩展父类(子类重新载入父类)操作详解
2020-10-16 20:02:20主要介绍了PHP面向对象程序设计子类扩展父类(子类重新载入父类)操作,涉及php面向对象方法重写与扩展相关操作技巧,需要的朋友可以参考下 -
易语言窗口子类化例程
2020-07-22 08:31:22易语言窗口子类化例程源码,窗口子类化例程,子程序1 -
易语言源码易语言窗口子类化例程源码.rar
2020-02-21 01:15:05易语言源码易语言窗口子类化例程源码.rar 易语言源码易语言窗口子类化例程源码.rar 易语言源码易语言窗口子类化例程源码.rar 易语言源码易语言窗口子类化例程源码.rar 易语言源码易语言窗口子类化例程源码.rar ... -
父类引用指向子类对象
2015-06-25 16:56:18父类引用子类对象,父类对象和子类对象之间的引用关系和区别 -
java中获取泛型类的子类
2016-01-06 00:06:13通过反射获得指定类的父类的泛型参数的实际类型 -
父类赋值给子类
2013-06-07 19:43:39父类赋值给子类的一个小方法,仅供学习参考... -
Java多态性详解——父类引用子类对象
2021-03-08 03:25:36因为子类是对父类的一个改进和扩充,所以一般子类在功能上较父类更强大,属性较父类更独特, 定义一个父类类型的引用指向一个子类的对象既可以使用子类强大的功能,又可以抽取父类的共性。 所以,父类类型的引用可以...面向对象编程有三个特征,即封装、继承和多态。
封装隐藏了类的内部实现机制,从而可以在不影响使用者的前提下改变类的内部结构,同时保护了数据。
继承是为了重用父类代码,同时为实现多态性作准备。那么什么是多态呢?
方法的重写、重载与动态连接构成多态性。Java之所以引入多态的概念,原因之一是它在类的继承问题上和C++不同,后者允许多继承,这确实给其带来的非常强大的功能,但是复杂的继承关系也给C++开发者带来了更大的麻烦,为了规避风险,Java只允许单继承,派生类与基类间有IS-A的关系(即“猫”is a “动物”)。这样做虽然保证了继承关系的简单明了,但是势必在功能上有很大的限制,所以,Java引入了多态性的概念以弥补这点的不足,此外,抽象类和接口也是解决单继承规定限制的重要手段。同时,多态也是面向对象编程的精髓所在。
要理解多态性,首先要知道什么是“向上转型”。
我定义了一个子类Cat,它继承了Animal类,那么后者就是前者是父类。我可以通过
Cat c = new Cat();
实例化一个Cat的对象,这个不难理解。但当我这样定义时:
Animal a = new Cat();
这代表什么意思呢?
很简单,它表示我定义了一个Animal类型的引用,指向新建的Cat类型的对象。由于Cat是继承自它的父类Animal,所以Animal类型的引用是可以指向Cat类型的对象的。那么这样做有什么意义呢?因为子类是对父类的一个改进和扩充,所以一般子类在功能上较父类更强大,属性较父类更独特,
定义一个父类类型的引用指向一个子类的对象既可以使用子类强大的功能,又可以抽取父类的共性。
所以,父类类型的引用可以调用父类中定义的所有属性和方法,而对于子类中定义而父类中没有的方法,它是无可奈何的;
同时,父类中的一个方法只有在在父类中定义而在子类中没有重写的情况下,才可以被父类类型的引用调用;
对于父类中定义的方法,如果子类中重写了该方法,那么父类类型的引用将会调用子类中的这个方法,这就是动态连接。
看下面这段程序:
class Father{
public void func1(){
func2();
}
//这是父类中的func2()方法,因为下面的子类中重写了该方法
//所以在父类类型的引用中调用时,这个方法将不再有效
//取而代之的是将调用子类中重写的func2()方法
public void func2(){
System.out.println("AAA");
}
}
class Child extends Father{
//func1(int i)是对func1()方法的一个重载
//由于在父类中没有定义这个方法,所以它不能被父类类型的引用调用
//所以在下面的main方法中child.func1(68)是不对的
public void func1(int i){
System.out.println("BBB");
}
//func2()重写了父类Father中的func2()方法
//如果父类类型的引用中调用了func2()方法,那么必然是子类中重写的这个方法
public void func2(){
System.out.println("CCC");
}
}
public class PolymorphismTest {
public static void main(String[] args) {
Father child = new Child();
child.func1();//打印结果将会是什么?
}
}
上面的程序是个很典型的多态的例子。子类Child继承了父类Father,并重载了父类的func1()方法,重写了父类的func2()方法。重载后的func1(int i)和func1()不再是同一个方法,由于父类中没有func1(int i),那么,父类类型的引用child就不能调用func1(int i)方法。而子类重写了func2()方法,那么父类类型的引用child在调用该方法时将会调用子类中重写的func2()。
那么该程序将会打印出什么样的结果呢?
很显然,应该是“CCC”。
对于多态,可以总结它为:
一、使用父类类型的引用指向子类的对象;
二、该引用只能调用父类中定义的方法和变量;
三、如果子类中重写了父类中的一个方法,那么在调用这个方法的时候,将会调用子类中的这个方法;(动态连接、动态调用)
四、变量不能被重写(覆盖),”重写“的概念只针对方法,如果在子类中”重写“了父类中的变量,那么在编译时会报错。
****************************************************************************************************************************
多态详解(整理)2008-09-03 19:29多态是通过:
1 接口 和 实现接口并覆盖接口中同一方法的几不同的类体现的
2 父类 和 继承父类并覆盖父类中同一方法的几个不同子类实现的.
一、基本概念
多态性:发送消息给某个对象,让该对象自行决定响应何种行为。
通过将子类对象引用赋值给超类对象引用变量来实现动态方法调用。
java 的这种机制遵循一个原则:当超类对象引用变量引用子类对象时,被引用对象的类型而不是引用变量的类型决定了调用谁的成员方法,但是这个被调用的方法必须是在超类中定义过的,也就是说被子类覆盖的方法。
1. 如果a是类A的一个引用,那么,a可以指向类A的一个实例,或者说指向类A的一个子类。
2. 如果a是接口A的一个引用,那么,a必须指向实现了接口A的一个类的实例。
二、Java多态性实现机制
SUN目前的JVM实现机制,类实例的引用就是指向一个句柄(handle)的指针,这个句柄是一对指针:
一个指针指向一张表格,实际上这个表格也有两个指针(一个指针指向一个包含了对象的方法表,另外一个指向类对象,表明该对象所属的类型);
另一个指针指向一块从java堆中为分配出来内存空间。
三、总结
1、通过将子类对象引用赋值给超类对象引用变量来实现动态方法调用。
DerivedC c2=new DerivedC();
BaseClass a1= c2; //BaseClass 基类,DerivedC是继承自BaseClass的子类
a1.play(); //play()在BaseClass,DerivedC中均有定义,即子类覆写了该方法
分析:
* 为什么子类的类型的对象实例可以覆给超类引用?
自动实现向上转型。通过该语句,编译器自动将子类实例向上移动,成为通用类型BaseClass;
* a.play()将执行子类还是父类定义的方法?
子类的。在运行时期,将根据a这个对象引用实际的类型来获取对应的方法。所以才有多态性。一个基类的对象引用,被赋予不同的子类对象引用,执行该方法时,将表现出不同的行为。
在a1=c2的时候,仍然是存在两个句柄,a1和c2,但是a1和c2拥有同一块数据内存块和不同的函数表。
2、不能把父类对象引用赋给子类对象引用变量
BaseClass a2=new BaseClass();
DerivedC c1=a2;//出错
在java里面,向上转型是自动进行的,但是向下转型却不是,需要我们自己定义强制进行。
c1=(DerivedC)a2; 进行强制转化,也就是向下转型.
3、记住一个很简单又很复杂的规则,一个类型引用只能引用引用类型自身含有的方法和变量。
你可能说这个规则不对的,因为父类引用指向子类对象的时候,最后执行的是子类的方法的。
其实这并不矛盾,那是因为采用了后期绑定,动态运行的时候又根据型别去调用了子类的方法。而假若子类的这个方法在父类中并没有定义,则会出错。
例如,DerivedC类在继承BaseClass中定义的函数外,还增加了几个函数(例如 myFun())
分析:
当你使用父类引用指向子类的时候,其实jvm已经使用了编译器产生的类型信息调整转换了。
这里你可以这样理解,相当于把不是父类中含有的函数从虚拟函数表中设置为不可见的。注意有可能虚拟函数表中有些函数地址由于在子类中已经被改写了,所以对象虚拟函数表中虚拟函数项目地址已经被设置为子类中完成的方法体的地址了。
4、Java与C++多态性的比较
jvm关于多态性支持解决方法是和c++中几乎一样的,
只是c++中编译器很多是把类型信息和虚拟函数信息都放在一个虚拟函数表中,但是利用某种技术来区别。
Java把类型信息和函数信息分开放。Java中在继承以后,子类会重新设置自己的虚拟函数表,这个虚拟函数表中的项目有由两部分组成。从父类继承的虚拟函数和子类自己的虚拟函数。
虚拟函数调用是经过虚拟函数表间接调用的,所以才得以实现多态的。
Java的所有函数,除了被声明为final的,都是用后期绑定。
四. 1个行为,不同的对象,他们具体体现出来的方式不一样,
比如: 方法重载 overloading 以及 方法重写(覆盖)override
class Human{
void run(){输出 人在跑}
}
class Man extends Human{
void run(){输出 男人在跑}
}
这个时候,同是跑,不同的对象,不一样(这个是方法覆盖的例子)
class Test{
void out(String str){输出 str}
void out(int i){输出 i}
}
这个例子是方法重载,方法名相同,参数表不同
ok,明白了这些还不够,还用人在跑举例
Human ahuman=new Man();
这样我等于实例化了一个Man的对象,并声明了一个Human的引用,让它去指向Man这个对象
意思是说,把 Man这个对象当 Human看了.
比如去动物园,你看见了一个动物,不知道它是什么, "这是什么动物? " "这是大熊猫! "
这2句话,就是最好的证明,因为不知道它是大熊猫,但知道它的父类是动物,所以,
这个大熊猫对象,你把它当成其父类 动物看,这样子合情合理.
这种方式下要注意 new Man();的确实例化了Man对象,所以 ahuman.run()这个方法 输出的 是 "男人在跑 "
如果在子类 Man下你 写了一些它独有的方法 比如 eat(),而Human没有这个方法,
在调用eat方法时,一定要注意 强制类型转换 ((Man)ahuman).eat(),这样才可以...
对接口来说,情况是类似的...
实例:
package domatic;
//定义超类superA
class superA {
int i = 100;
void fun(int j) {
j = i;
System.out.println("This is superA");
}
}
// 定义superA的子类subB
class subB extends superA {
int m = 1;
void fun(int aa) {
System.out.println("This is subB");
}
}
// 定义superA的子类subC
class subC extends superA {
int n = 1;
void fun(int cc) {
System.out.println("This is subC");
}
}
class Test {
public static void main(String[] args) {
superA a = new superA();
subB b = new subB();
subC c = new subC();
a = b;
a.fun(100);
a = c;
a.fun(200);
}
}
/*
* 上述代码中subB和subC是超类superA的子类,我们在类Test中声明了3个引用变量a, b,
* c,通过将子类对象引用赋值给超类对象引用变量来实现动态方法调用。也许有人会问:
* "为什么(1)和(2)不输出:This is superA"。
* java的这种机制遵循一个原则:当超类对象引用变量引用子类对象时,
* 被引用对象的类型而不是引用变量的类型决定了调用谁的成员方法,
* 但是这个被调用的方法必须是在超类中定义过的,
* 也就是说被子类覆盖的方法。
* 所以,不要被上例中(1)和(2)所迷惑,虽然写成a.fun(),但是由于(1)中的a被b赋值,
* 指向了子类subB的一个实例,因而(1)所调用的fun()实际上是子类subB的成员方法fun(),
* 它覆盖了超类superA的成员方法fun();同样(2)调用的是子类subC的成员方法fun()。
* 另外,如果子类继承的超类是一个抽象类,虽然抽象类不能通过new操作符实例化,
* 但是可以创建抽象类的对象引用指向子类对象,以实现运行时多态性。具体的实现方法同上例。
* 不过,抽象类的子类必须覆盖实现超类中的所有的抽象方法,
* 否则子类必须被abstract修饰符修饰,当然也就不能被实例化了
*/
以上大多数是以子类覆盖父类的方法实现多态.下面是另一种实现多态的方法-----------重写父类方法
1.JAVA里没有多继承,一个类之能有一个父类。而继承的表现就是多态。一个父类可以有多个子类,而在子类里可以重写父类的方法(例如方法print()),这样每个子类里重写的代码不一样,自然表现形式就不一样。这样用父类的变量去引用不同的子类,在调用这个相同的方法print()的时候得到的结果和表现形式就不一样了,这就是多态,相同的消息(也就是调用相同的方法)会有不同的结果。举例说明:
//父类
public class Father{
//父类有一个打孩子方法
public void hitChild(){
}
}
//子类1
public class Son1 extends Father{
//重写父类打孩子方法
public void hitChild(){
System.out.println("为什么打我?我做错什么了!");
}
}
//子类2
public class Son2 extends Father{
//重写父类打孩子方法
public void hitChild(){
System.out.println("我知道错了,别打了!");
}
}
//子类3
public class Son3 extends Father{
//重写父类打孩子方法
public void hitChild(){
System.out.println("我跑,你打不着!");
}
}
//测试类
public class Test{
public static void main(String args[]){
Father father;
father = new Son1();
father.hitChild();
father = new Son2();
father.hitChild();
father = new Son3();
father.hitChild();
}
}
都调用了相同的方法,出现了不同的结果!这就是多态的表现!
-
【Java子类与继承(完整版)】
2021-03-25 22:53:12Java子类与继承1 子类与父类1.1 子类1.2 类的树形结构2 子类的继承性2.1 子类和父类在同一包中的继承性2.2 子类和父类不在同一包中的继承性2.3 继承关系(Generalization)的UML图2.4 protected的进一步说明3 子类与...Java子类与继承
1 子类与父类
继承是一种由已有的类创建新类的机制。利用继承,我们可以先创建一个共有属性的一般类,根据该一般类再创建具有特殊属性的新类,新类继承一般类的状态和行为,并根据需要增加它自己的新的状态和行为。由继承而得到的类称为子类,被继承的类称为父类(超类) 。
Java不支持多重继承(子类只能有一个父类)
习惯地称子类与父类是"is-a”关系。1.1 子类
使用关键字extends来定义一个类的子类,格式如下:
class 子类名 extends 父类名{
…
}
例如:
class Student extends People {
…
}
说明:把Student类定义为People类的子类、People类是Student
类的父类1.2 类的树形结构
Java的类按继承关系形成树形结构这个树形结构中,根节点是Object类( Object是java.lang包中的类),即Object是所有类的祖先类。除了Object类,每个类都有且仅有一个父类,一个类可以有多个或零个子类。如果一个类(除了Object类)的声明中没有使用extends关键字,这个类被系统默认为是
Object的子类,即类声明“class A”与“class A extends Object"是等同的。2 子类的继承性
类可以有两种重要的成员:成员变量和方法。子类的成员中有一部分是子类自己声明定义的,另一部分是从它的父类继承的。所谓子类继承父类的成员变量就是把继承来的变量作为自己的一个成员变量,就好象它们是在子类中直接声明一样,可以被子类中自己定义的任何实例方法操作。所谓子类继承父类的方法就是把继承来的方法作为子类中的一个方法,就好象它们是在子类中直接定义了一样,可以被子类中自己定义的任何实例方法调用。
//子类的继承 class Father { float weight, height; String head; void speak(String s) { System.out.println(s); } } class Son extends Father { String hand, foot; } public class Test { public static void main(String[] args) { Son s = new Son(); } }
2.1 子类和父类在同一包中的继承性
如果子类和父类在同一个包中,那么,子类自然地继承了其父类中不是private的成员变量作为自己的成员变量,并且也自然地继承了父类中不是private的方法作为自己的方法,继承的成员变量或方法的访问权限保持不变。
下面的例子2.1中有4个类: People, Student.java,UniverStudent.java和Example2_1,这些类都没有包名,其中UniverStudent类是Student的子类,Student 是People的子类。程序运行效果
People.javapublic class People { int age,leg = 2,hand = 2; protected void showPeopleMess() { System.out.printf("%d岁,%d只脚,%d只手\t",age,leg,hand); } }
Student.java
public class Student extends People { int number; void tellNumber() { System.out.printf("学号:%d\t",number); } int add(int x,int y) { return x+y; } }
UniverStudent.java
public class UniverStudent extends Student { int multi(int x,int y) { return x*y; } }
Example2_1.java
public class Example2_1 { public static void main(String[] args) { Student zhang = new Student(); zhang.age = 17; //访问继承的成员变量 zhang.number=100101; zhang.showPeopleMess(); //调用继承的方法 zhang.tellNumber(); int x=9,y=29; System.out.print("会做加法:"); int result=zhang.add(x,y); System.out.printf("%d+%d=%d\n",x,y,result); UniverStudent geng = new UniverStudent(); geng.age = 21; //访问继承的成员变量 geng.number=6609; geng.showPeopleMess(); //调用继承的方法 geng.tellNumber(); //调用继承的方法 System.out.print("会做加法:"); result=geng.add(x,y); //调用继承的方法 System.out.printf("%d+%d=%d\t",x,y,result); System.out.print("会做乘法:"); result=geng.multi(x,y); System.out.printf("%d×%d=%d\n",x,y,result); } }
2.2 子类和父类不在同一包中的继承性
如果子类与父类不在同一个包中,则子类只会继承父类public、protected的变量和方法,继承的变量和方法的访问权限保持不变。
2.3 继承关系(Generalization)的UML图
如果一个类是另一个类的子类,那么UML通过使用一个实线连接两个类的UML图来表示二者之间的继承关系,实线的起始端是子类的UML图,终点端是父类的UML图,但终点端使用一个空心的三角形表示实线的结束。
3 子类与对象
3.1 子类对象的特点
子类创建对象时,子类的构造方法总是先调用父类的某个构造方法,完成父类部分的创建;然后再调用子类自己的构造方法,完成子类部分的创建。如果子类的构造方法没有明显地指明使用父类的哪个构造方法,子类就调用父类的不带参数的构造方法。
子类在创建一个子类对象时,不仅子类中声明的成员变量被分配了内存,而且父类的所有的成员变量也都分配了内存空间,但子类只能操作继承的那部分成员变量。
子类可以通过继承的方法来操作子类未继承的变量和方法
例子3.1中,子类ChinaPeople的对象调用继承的方法操作未被子类继承却分配了内存空间的变量
class People { private int averHeight = 166; public int getAverHeight() { return averHeight; } } class ChinaPeople extends People { int height; public void setHeight(int h) { //height = h+averHeight; // 非法,子类没有继承averHeight height = h; } public int getHeight() { return height; } } public class Example5_2 { public static void main(String[] args) { ChinaPeople zhangSan = new ChinaPeople(); System.out.println("子类对象未继承的averageHeight的值是:"+zhangSan.getAverHeight()); zhangSan.setHeight(178); System.out.println("子类对象的实例变量height的值是:"+zhangSan.getHeight()); } }
3.2 关于instanceof运算符
instanceof运算符是Java独有的双目运算符,其左面的操作元是对象,右面的操作元是类,当左面的操作元是右面的类或其子类创建的对象时,instanceof运算的结果是true,否则是false.
class Test { String s; Test() { stu s = new String(); if (s instanceof String) { System.out.println("YES"); } } }
4 成员变量的隐藏和方法重写
4.1 成员变量的隐藏
➢对于子类可以从父类继承的成员变量,只要子类中声明的成员变量和父类中的成员变量同名时,子类就隐藏了继承的成员变量。
➢在子类中要操作这个与父类同名的成员变量时,子类操作的是子类重新声明的这个成员变量。而不是被隐藏掉的。
➢子类对象仍然可以调用从父类继承的方法操作被子类隐藏的成员变量,也就是说,子类继承的方法操作的成员变量一定是被子类继承或者隐藏的成员变量。例子4.1(Example4_1.java)中, Goods类有一 个 名字为weight的double型成员变量,本来子类CheapGoods可以继承这个成员变量,但是子类CheapGoods又重新声明了一个int型的名字为weight的成员变量.
Goods.javapublic class Goods { public double weight; public void oldSetWeight(double w) { weight=w; System.out.println("double型的weight="+weight); } public double oldGetPrice() { double price = weight*10; return price; } }
CheapGoods.java
public class CheapGoods extends Goods { public int weight; public void newSetWeight(int w) { weight=w; System.out.println("int型的weight="+weight); } public double newGetPrice() { double price = weight*10; return price; } }
Example4_1.java
public class Example4_1 { public static void main(String[] args) { CheapGoods cheapGoods=new CheapGoods(); //cheapGoods.weight=198.98; 是非法的,因为子类对象的weight已经是int型 cheapGoods.newSetWeight(198); System.out.println("对象cheapGoods的weight的值是:"+cheapGoods.weight); System.out.println("cheapGoods用子类新增的优惠方法计算价格:"+ cheapGoods.newGetPrice()); cheapGoods.oldSetWeight(198.987); //子类对象调用继承的方法操作隐藏的double型变量weight System.out.println("cheapGoods使用继承的方法(无优惠)计算价格:"+ cheapGoods.oldGetPrice()); } }
注意:子类继承的方法只能操作子类继承和隐藏的成员变量,子类新定义的方法可以操作子类继承和子类新声明的成员变量,但无法操作子类隐藏的变量(需要使用super关键字操作子类隐藏的成员变量)
4.2 方法重写
同样,子类通过重写可以隐藏已继承的实例方法。
1.重写的语法规则
如果子类继承了父类的实例方法,那么子类就有权利重写这个方法。
方法重写是指:子类中定义一个方法,这个方法的类型和父类的方法的类型一致或是父类方法的类型的子类型,且这个方法的名字、参数个数、参数的类型和父类的方法完全相同.
2.重写的目的
子类通过方法的重写可以隐藏继承的方法,子类通过方法的重写可以把父类的状态和行为改变为自身的状态和行为。
3.重写后方法的调用
子类创建的一个对象,如果子类重写了父类的方法,则运
行时系统调用的是子类重写的方法;
子类创建的一个对象,如果子类未重写父类的方法,则运
行时系统调用的是子类继承的方法;
4.重写的注意事项
重写父类的方法时,不允许降低方法的访问权限,但可以提高访问权限(访问限制修饰符按访问权限从高到低的排列顺序是:public、protected、 友好的、private。 )在下面的例子4_2(Example4_2. java)中,ImportantUniversity是University类的子类,子类重写了父类的enterRule0方法
University.java
public class University { void enterRule(double math,double english,double chinese) { double total=math+english+chinese; if(total>=180) System.out.println("考分"+total+"达到大学最低录取线"); else System.out.println("考分"+total+"未达到大学最低录取线"); } }
ImportantUniversity.java
public class ImportantUniversity extends University{ void enterRule(double math,double english,double chinese) { double total=math+english+chinese; if(total>=220) System.out.println("考分"+total+"达到重点大学录取线"); else System.out.println("考分"+total+"未达到重点大学录取线"); } }
Example4_2.java
public class Example4_2 { public static void main(String args[]) { double math=64,english=76.5,chinese=66; ImportantUniversity univer = new ImportantUniversity(); univer.enterRule(math,english,chinese); //调用重写的方法 math=89; english=80; chinese=86; univer = new ImportantUniversity(); univer.enterRule(math,english,chinese); //调用重写的方法 } }
5 super关键字
5.1 用super操作被隐藏的成员变量和方法
➢子类可以隐藏从父类继承的成员变量和方法,如果在子类中想使用被子类隐藏的成员变量或方法就可以使用关键字super.比如super.x、super.play() 就是访问和调用被子类隐藏的成员变量x和方法play()
例子5.1中,子类使用super访问和调用被子类隐藏的成员变量和方法。class Sum { int n; float f() { float sum=0; for(int i=1;i<=n;i++) sum=sum+i; return sum; } } class Average extends Sum { int n; float f() { float c; super.n=n; c=super.f(); return c/n; } float g() { float c; c=super.f(); return c/2; } } public class Example5_1 { public static void main(String args[]) { Average aver=new Average(); aver.n=100; float resultOne=aver.f();//代码1 float resultTwo=aver.g();//代码2 System.out.println("resultOne="+resultOne); System.out.println("resultTwo="+resultTwo); } }
上述例子中如果交换代码1和代码2,则输出为:
resultOne=50.5
resultTwo=0.05.2 用super调用父类的构造方法
子类不继承父类的构造方法,因此,子类如果想使用父类的构造方法,必须在子类的构造方法中使用,并且必须使用关键字super来表示,而且super必须是子类构造方法中的头一条语句。
注:
创建子类对象时,子类总是按层次结构从上到下的顺序调用所有超类的构造函数,默认调用父类中没有参数的构造方法。如果父类没有不带参数的构造方法,则在子类的构造方法中必须明确的告诉调用父类的某个带参数的构造方法。例子5.2中,UniverStudent是Student的子类,UniverStudent子类
在构造方法中使用了super关键字class Student { int number;String name; Student() { } Student(int number,String name) { this.number=number; this.name=name; System.out.println("我的名字是:"+name+ "学号是:"+number); } } class UniverStudent extends Student { boolean 婚否; UniverStudent(int number,String name,boolean b) { super(number,name); 婚否=b; System.out.println("婚否="+婚否); } } public class Example5_1 { public static void main(String args[]) { UniverStudent zhang=new UniverStudent(9901,"何晓林",false); } }
6 final关键字
final关键字可以修饰类、成员变量、方法中的局部变量和方法。
可以使用final将类声明为final类。final类不能 被继承,即不能有子类。
如:
final class A {
}
如果用final修饰父类中的一个方法,那么这个方法不允许子类重写。
一如果成员变量或局部变量被修饰为final的,就是常量。**例6 **
class A { final double PI=3.1415926;// PI是常量 public double getArea(final double r) { return PI*r*r; } public final void speak() { System.out.println("您好,How's everything here ?"); } } public class Example6 { public static void main(String args[]) { A a=new A(); System.out.println("面积:"+a.getArea(100)); a.speak(); } }
7 对象的上转型对象
假设,A类是B类的父类,当用子类创建一个对象,并把这个对象的引用放到父类的对象中时,称对象a是对象b的上转型对象,比如:
A a; a = new B(); 或者 A a; B b = new B(); a = b;
上转型对象的使用
1.上转型对象不能操作子类新增的成员变量;不能调用子类新增的方法。
2.上转型对象可以访问子类继承或隐藏的成员变量,也可以调用子类继承的方法或子类重写的实例方法。
3.如果子类重写了父类的某个实例方法后,当用上转型对象调用这个实例方法时一定是调用了子类重写的实例方法。**例7 **
class 类人猿 { void crySpeak(String s) { System.out.println(s); } } class People extends 类人猿 { void computer(int a,int b) { int c=a*b; System.out.println(c); } void crySpeak(String s) { System.out.println("***"+s+"***"); } } public class Example7 { public static void main(String args[]) { 类人猿 monkey; People geng = new People(); monkey = geng ; //monkey是People对象geng的上转型对象 monkey.crySpeak("I love this game");//等同于geng.crySpeak("I love this game"); People people=(People)monkey; //把上转型对象强制转化为子类的对象 people.computer(10,10); } }
注意:monkey.crySpeak("I love this game"); 得到的是:***I love this game*** 而不是:I love this game,因为monkey调用的是子类的重写方法crySpeak monkey.computer(10,10);是错误的,因为computer方法是子类新增的方法
1、不要把父类创建的对象和子类对象的上转型对象混淆;
2、可以将对象的上转型对象再强制转换到一个子类对象,这时,该子类对象又具备了子类所有的属性和功能;
3、不可以将父类创建的对象的引用赋值于子类声明的对象;
4、如果子类重写了父类的静态方法,那么子类对象的.上转型对象不能调用子类重写的静态方法,只能调用父类的静态方法。8 继承与多态
多态性就是指父类的某个方法被其子类重写,可以各自产生自己的功能行为。
class 动物 { void cry() { } } class 狗 extends 动物 { void cry() { System.out.println("汪汪....."); } } class 猫 extends 动物 { void cry() { System.out.println("喵喵....."); } } public class Example5_1 { public static void main(String args[]) { 动物 animal; animal = new 狗(); animal.cry(); animal=new 猫(); animal.cry(); } }
9 abstract类和abstract方法
用关键字abstract修饰的类称为abstract类(抽象类)。
例如:
abstract class A {
}
用关键字abstract修饰的方法称为abstract方法(抽象方法)
例如:
abstract int min(int x,int y);abstract类有如下特点
1、和普通的类相比,abstract类里可以有abstract方法。也可以没有。对于abstract方法, 只允许声明,不允许实现,而且不允许使用final修饰abstract方法。
2、对于abstract类,不能使用new运算符创建该类的对象,只能产生其子类,由子类创建对象。
3、如果一个非abstract类是abstract类的子类,它必须具体实现父类的所有的abstract方法。理解的关键点是:
(1)抽象类可以抽象出重要的行为标准,该行为标准用抽象方法来表示。即抽象类封装了子类必需要有的行为标准。
(2)抽象类声明的对象可以成为其子类的对象的上转型对象,调用子类重写的方法,即体现子类根据抽象类里的行为标准给出的具体行为。开发者可以把主要精力放在一个应用中需要那些行为标准(不用关心行为的细节),不仅节省时间,而且非常有利于设计出易维护、易扩展的程序。抽象类中的抽象方法,可以由子类去实现,即行为标准的实现由子类完成。
例9使用了abstract类封装了男孩对女朋友的行为要求,即封装了他要找的任何具体女朋友都应该具有的行为。
abstract class GirlFriend {//抽象类,封装了两个行为标准 abstract void speak(); abstract void cooking(); } class ChinaGirlFriend extends GirlFriend { void speak(){ System.out.println("你好"); } void cooking(){ System.out.println("水煮鱼"); } } class AmericanGirlFriend extends GirlFriend { void speak(){ System.out.println("hello"); } void cooking(){ System.out.println("roast beef"); } } class Boy { GirlFriend friend; void setGirlfriend(GirlFriend f){ friend = f; } void showGirlFriend() { friend.speak(); friend.cooking(); } } public class Example9{ public static void main(String[] args) { GirlFriend girl = new ChinaGirlFriend(); //girl是上转型对象 Boy boy = new Boy(); boy.setGirlfriend(girl); boy.showGirlFriend(); girl = new AmericanGirlFriend(); //girl是上转型对象 boy.setGirlfriend(girl); boy.showGirlFriend(); } }
10 面向抽象编程
在设计一个程序时,可以先声明一个abstract类,通过在类中声明若干个abstract方法,表明这些方法在整个系统设计中的重要性,方法体的内容细节由它的非abstract子类去完成。.然后利用多态实现编程。使用多态进行程序设计的核心技术是使用方法重写和上转型对象,即将abstract类声明对象作为其子类的上转型对象,那么这个上转型对象就可以调用子类重写的方法。
所谓面向抽象编程,是指当设计某种重要的类时,不让该类面向具体的类,而是面向抽象类,即所设计类中的重要数据是抽象类声明的对象,而不是具体类声明的对象。例题:
Pillar类就是面向抽象类Geometry, Cirle和Rectangle 都是Geometry的子类. Application.java 可以用Pillar类创建出具有矩形底或圆形底的柱体了。首先编写一个抽象类Geometry,该抽象类中定义了一个抽象的getArea()方法,Geometry类如下:
Geometry.javapublic abstract class Geometry { //抽象类 public abstract double getArea(); }
上述抽象类将所有计算面积的算法抽象为一个标识:getArea(),即抽象方法,不再考虑算法细节。
现在Pillar类的设计者可以面向Geometry类编写代码,即Pillar类应该把Geometry对象
作为自己的成员,该成员可以调用Geometry的子类重写的getArea()方法。这样一来, Pillar类就可以将计算底面积的任务指派给Geometry类的子类的实例(用户的各种需求将由不同的子类去负责)。以下Pillar类的设计不再依赖具体类,而是面向Geometry类,即Pillar类中的bottom是
用抽象类Geometry声明的对象,而不是具体类声明的对象。重新设计的Pillar类的代码如下:
Pillar.javapublic class Pillar { Geometry bottom; //bottom是抽象类Geometry声明的变量 double height; Pillar(Geometry bottom, double height) { this.bottom = bottom; this.height = height; } public double gerVolume() { if (bottom == null) { System.out.println("没有底,无法计算体积"); return -1; } return bottom.getArea() * height; //bottom可以调用子类重写的getArea方法 } }
下列Circle和Rectangle类都是Geometry的子类,二者都必须重写Geometry类的getArea()方法来计算各自的面积。
Circle.java
public class Circle extends Geometry { // 圆类 double r; Circle(double r) { this.r = r; } public double getArea() { return (3.14 * r * r); } }
Rectangle.java
public class Rectangle extends Geometry { double a, b; Rectangle(double a, double b) { this.a = a; this.b = b; } public double getArea() { return a * b; } }
注意到,当增加了Circle和Rectangle类后,我们不必修改Pillar类的代码,现在,我们就可以用Pillar类创建出具有矩形或者圆底的柱体了,如下列Application.java所示:
public class Application { //主类 public static void main(String[] args) { Pillar pillar; Geometry bottom = null; pillar = new Pillar(bottom, 100);//没有底的柱体 System.out.println("体积" + pillar.gerVolume()); bottom = new Rectangle(12, 22); pillar = new Pillar(bottom, 58);//pillar是具有矩形底的柱体 System.out.println("体积" + pillar.gerVolume()); bottom = new Circle(10); pillar = new Pillar(bottom, 58);//pillar是具有圆形底的柱体 System.out.println("体积" + pillar.gerVolume()); } }
通过面向抽象来设计Pillar类,使得该Pillar类不再依赖具体类,因此每当系统增加新的
Geometry的子类时,例如增加一个 Triangle子类,那么我们不需要修改Pillar类的任何代码,就可以使用Pillar 创建出具有三角形底的柱体。11 开-闭原则
所谓“开闭原则”(Open-Closed Principle) 就是让设计的系统应当对扩展开放,对修改关闭。
在设计系统时,应当首先考虑到用户需求的变化,将应对用户变化的部分设计为对扩展开放,而设计的核心部分是经过精心考虑之后确定下来的基本结构,这部分应当是对修改关闭的,即不能因为用户的需求变化而再发生变化,因为这部分不是用来应对需求变化的。如果系统的设计遵守了“开-闭原则”,那么这个系统一定是易维护的,因为在系统中增加新的模块时,不必去修改系统中的核心模块。
-
子类父类拥有同名的方法时……
2021-02-28 06:56:20子类父类拥有同名的方法时……(这个程序是不能运行的)packageyanzheng;publicclassParentChildTest{publicstaticvoidmain(String[]args){Parentparent=newParent();parent.printValue();Childchild=newChild();child...子类父类拥有同名的方法时……
(这个程序是不能运行的)
packageyanzheng;
public classParentChildTest {
public static voidmain(String[]args) {
Parentparent=newParent();
parent.printValue();
Childchild=newChild();
child.printValue();
parent=child;
parent.printValue();
parent.myValue++;
parent.printValue();
((Child)parent).myValue++;
parent.printValue();
}
}
class Parent{
public int myValue=100;
public voidprintValue() {
System.out.println("Parent.printValue(),myValue="+myValue);
}
}
class Child extendsParent{
public int myValue=200;
public voidprintValue() {
System.out.println("Child.printValue(),myValue="+myValue);
}
}
总结:子类父类拥有同名的方法时:
*当子类与父类拥有一样的方法,并且让一个父类变量引用一个子类对象时,到底调用哪个方法,由对象自己的“真实”类型所决定,这就是说:对象是子类型的,它就调用子类型的方法,是父类型的,它就调用父类型的方法。
*这个特性实际上就是面向对象“多态”特性的具体表现。
*如果子类与父类有相同的字段,则子类中的字段会代替或隐藏父类的字段,子类方法中访问的是子类中的字段(而不是父类中的字段)。如果子类方法确实想访问父类中被隐藏的同名字段,可以用super关键字来访问它。
*如果子类被当作父类使用,则通过子类访问的字段是父类的!
牢记:
在实际开发中,要避免在子类中定义与父类同名的字段。不要自找麻烦!
附录:网络上摘取的一些代码和解释,可以帮助理解。
例1:
public class A {protected String a = "1";public String getA(){return this.a;}}public class B extends A {protected String a = "2";// public String getA(){// return a;// }public static void main(String[] args) {B x = new B();System.out.println(x.getA());}}
输出的是1,父类的方法看到的变量是父类中的a。如果B中也写一个getA方法,看到的a就是子类中的a了。其实就是可见性的问题。父类和子类的变量是同时存在的,即使是同名。子类中看到的是子类的变量,父类中看到的是父类中的变量。它们互相隐藏,而同名的方法则是实实在在的覆盖。如A x = new B();x是一个B,也是一个A,那么调用方法时,是根据对象的实际类型调用的,实际类型是B,所以永远调用子类的方法。而访问成员变量就不同了,它是B时,访问的是子类的成员变量,转型为A的话,访问的就是父类的成员变量了。
例2:
//Main.javaclass c{public int name = 12;}class d extends c{d(){name =13;}void d2(){System.out.println(super.name);}}public class Main{public static void main(String[]args){d d1 = new d();System.out.println(d1.name);d1.d2();}}//运行结果为:13,13//Main1.javaclass c{public int name = 12;}class d extends c{int name =13;void d2(){System.out.println(super.name);}}public class Main{public static void main(String[]args){d d1 = new d();System.out.println(d1.name);d1.d2();}}//运行结果为:13,12
首先要了解super这个关键字的意思,是调用父类的意思,void d2(){System.out.println(super.name);}这句话就是调用父类的name,虽然子类里面有一个name,但是并不是覆盖了父类里面的name,而只是父类的name隐藏起来了,使得直接调用子类里面的name显示的是子类定义的name。第一个main。Java,不用说,子类没有定义name变量,并且父类里面的name变量不是private,子类可以直接继承name,这样子类和父类就共用一个name变量
如果父类定义了一个方法。子类重写了这个方法。那么这两个方法其实也是拥有各自的内存。
例3:
class Parent{
int i=10;// 父类变量
public void setI(int i){
this.i=i;
}
}
class Son extends Parent{
int i=10;// 子类与父类同名的变量
public static void main(String args[]){
Son son=new Son();
System.out.println("son.i="+son.i);
son.setI(100);
System.out.println("After setI(100) : son.i="+son.i);
Parent parent=son;
System.out.println("See son as Parent : son.i="+parent.i);
}
}
在这段代码中,子类定义了一个父类中同名的成员变量int i,在父类中有一个对 i 赋值的方法setI(),而在子类中没有定义这个方法。当子类调用继承而来的setI()方法对成员变量i进行改变,直接打印son.i时,成员变量i然而却没有改变。当但当把son当作Parent类型来使用,再打印它的成员变量i时,输出的结果就对了,是setI()改变之后的值。
java中类是分层次的,当子类和父类的定义同名时,父类变量被隐藏,父类的实例方法被重写,静态方法属于类级别的方法,在子类和父类中互不相碍。
例4:
在java 中,当我们子类要重写父类中的方法,如果父类的方法有异常声明,那么子类重写这个方法时候,所要声明的异常不应该比父类的大。只能是小等,或者可以没有。原因如下。
假如我们自定意义一个异常:
public class MyException extends Exception {
public MyException(String message) {}public MyException() {}
}
那么我们有一个父类它将有一个方法,将声明抛出这个异常。我们把它做成抽象的(无所谓)
public abstract class ParentException {
public abstract void test() throws MyException;
}
那么将有一个类使用到上面这个类
public class ExceptionTest {
private ParentException exception;public ExceptionTest(ParentException exception){
this.exception=exception;
}public void app(){
try {
exception.test();
} catch (MyException e) {
e.printStackTrace();
}
}
}
对于以上的try~catch 我们就直接捕获MyException异常。这是正确的,它在编译的时候是没有问题的。
假如,有一个ParentException的有一个子类
public class ChildException extends ParentException{
public void test() throws Exception{ }
}
他的方法可以声明抛出比父类大的异常,假如它是正确的。那么问题来了。当我们执行ExceptionTest类的时候,我们以这个子类做为对象传进去。exception.test();这个地方将在运行的时候实际调用的是ChildException这个子类的test()方法,而它的声明抛 出却是比父类大的异常,这就使得在ExceptionTest类中,捕获异常处出现了毛病,因为它无法捕获Exception异常。综上所诉,子类重写父类的方法时候不能声明抛出比父类大的异常。
例5:关于方法的重写Overriding和重载Overloading。
方法的重写Overriding和重载Overloading是Java多态性的不同表现。重写Overriding是父类与子类之间多态性的一种表现,重载Overloading是一个类中多态性的一种表现。Overloaded的方法是可以改变返回值的类型。也就是说,重载的返回值类型可以相同也可以不同。
1、重载(Overloading)
a、方法重载是让类以统一的方式处理不同类型数据的一种手段。多个同名函数同时存在,具有不同的参数个数/类型。重载Overloading是一个类中多态性的一种表现。b、Java的方法重载,就是在类中可以创建多个方法,它们具有相同的名字,但具有不同的参数和不同的定义。调用方法时通过传递给它们的不同参数个数和参数类型来决定具体使用哪个方法,这就是多态性。
c、重载的时候,方法名要一样,但是参数类型和个数不一样,返回值类型可以相同也可以不相同。无法以返回型别作为重载函数的区分标准。
2、重写(Overriding)
a、父类与子类之间的多态性,对父类的函数进行重新定义。如果在子类中定义某方法与其父类有相同的名称和参数,我们说该方法被重写(Overriding)。在Java中子类可继承父类中的方法,而不需要重新编写相同的方法。但有时子类并不想原封不动地继承父类的方法,而是想作一定的修改,这就需要采用方法的重写。方法重写又称方法覆盖。b、若子类中的方法与父类中的某一方法具有相同的方法名、返回类型和参数表,则新方法将覆盖原有的方法。如需父类中原有的方法,可使用super关键字,该关键字引用了当前类的父类。
c、子类函数的访问修饰权限不能少于父类的
-
Java多态性详解 (父类引用子类对象)
2021-02-25 20:34:44因为子类是对父类的一个改进和扩充,所以一般子类在功能上较父类更强大,属性较父类更独特, 定义一个父类类型的引用指向一个子类的对象既可以使用子类强大的功能,又可以抽取父类的共性。 所以,父类类型的引用可以... -
父类和子类转换(子类对象赋值给父类对象)(转载)
2021-03-06 22:49:49下面是几个小例子,请大家看一看:测试一父类:public class Supclass{public void print(){System.out.println("this is 父类print()...}}子类:public class Subclass extends Supclass{public static void main(S...