java基础---->继承性
继承
一、继承的限制
1、 子类可以继承父类的全部操作(属性、方法),但是对于所有的公共操作是可以直接继承的,而所有的私有操作是无法直接继承的,而是通过其他的方式间接访问。
2、 一个子类只能继承一个父类,属于单继承
3、 在Java中允许多层继承。
二、this与super的区别
No. |
区别点 |
this |
super |
1 |
使用 |
调用本类中的属性或方法 |
从子类调用父类中的属性或方法 |
2 |
构造 |
可以调用本类构造,且有一个构造要作为出口 |
从子类调用父类的构造方法,不管子类如何安排最终一定会去调用,默认调用的是父类中的无参构造方法 |
3 |
要求 |
调用构造的时候一定要放在构造方法首行 |
放在子类构造方法首行 |
使用super和this调用构造方法的语句是不可能同时出现的 |
|||
4 |
特殊 |
表示当前对象 |
无此概念 |
三、子类对象的初始化与清理
按照正常的思维来讲,肯定是先有父类产生再有子类产生,所以在子类对象实例化的时候实际上都会默认去调用父类构造方法。
假设有个名为Cartoon的类,继承自Drawing,Drawing又继承自Art
class Art { static {System.out.println("Art 的静态块");} Art() { System.out.println("Art constructor"); } } class Drawing extends Art { static {System.out.println("Drawing的静态块");} Drawing() { System.out.println("Drawing constructor"); } } public class Cartoon extends Drawing { static {System.out.println("Cartoon的静态块");} public Cartoon() { System.out.println("Cartoon constructor"); } public static void main(String[] args) { Cartoon x = new Cartoon(); } } /*Output: Art 的静态块 Drawing的静态块 Cartoon的静态块 Art constructor Drawing constructor Cartoon constructor */
1.当首次创建型为Cartoon的对象时,Java解释器查找类路径,定位Cartoon.class文件。
2.Java解释器会根据Cartoon.class定位其基类Drawing.class、再根据Drawing.class定位到基类Art.class文件,有关静态初始化的动作从基类到子类依次执行。
3.当你用new Cartoon()创建对象的时候,首先将在堆上为Cartoon对象(包括其基类Drawing和Art中的域)分配足够的存储空间。
4.这块存储空间会被清零,这就自动地将Cartoon中的所有基本类型数据(包括其基类Drawing和Art中的)设置成了默认值(对数字来说就是0,对布尔型和字符型也相同),而引用(包括其基类Drawing和Art中的)则被置成了null。
5.执行基类Art中所有出现于域定义处的初始化动作。
6.执行基类Art构造器。
7.执行基类Drawing中所有出现于域定义处的初始化动作。
8.执行基类Drawing构造器。
9.执行子类Cartoon中所有出现于域定义处的初始化动作。
10.执行子类Cartoon构造器。
即:class是从子类到基类依次查找,有关静态初始化的动作从基类到子类依次执行。
在为所创建对象的存储空间清零后,找到继承链中最上层的基类,执行a、b两步:
a.执行其出现在域定义处的初始化动作
b.然后再执行其构造器
然后从基类到子类依次执行这两步操作。
也就是说,不管子类如何操作,肯定都会调用父类中的构造方法。也可以通过super显示调用父类中指定的构造方法。
四、覆写
在子类中定义了和父类完全一样的方法或者是定义了一样的属性,那么此时实际上就发生了覆写操作。
1、方法的覆写
所谓方法的覆写就是指一个子类中定义了一个与父类完全一样的方法名称,包括返回值类型、参数的类型及个数都是完全一样的,但是需要注意的是,被覆写的方法不能拥有比父类更严格的访问控制权限。
class A { public void print() { // 定义方法 System.out.println("hello"); } }; class B extends A { public void print() { System.out.println("world"); } }; public class OverrideDemo01 { public static void main(String args[]) { B b = new B(); b.print(); } };
在子类中一旦方法被覆写之后,实际上最终调用的方法就是被覆写过的方法,但是,如果此时在子类中的方法的访问权限已经降低的话,则编译的时候将出现错误:Cannot reduce the visibility of the inherited method from A
问题?
如果现在父类中有一个方法是private声明,子类将其方法定义成了default访问权限,其他不变,是否叫覆写呢?
class A { public void print() { // 定义方法 this.getInfo(); } private void getInfo() { System.out.println("A --> getInfo()"); } }; class B extends A { void getInfo() { System.out.println("B --> getInfo()"); } }; public class OverrideDemo02 { public static void main(String args[]) { B b = new B(); b.print(); //A --> getInfo() } }; /*output A --> getInfo() */
记住使用private声明的方法子类是无法覆写的,虽然语法编译上不会产生任何的问题,但是子类中被“覆写”过的方法永远无法找到,而且这种代码在实际中没有任何的意义。只有非私有方法才可以被覆盖
如果现在子类中需要调用父类中已经被子类覆写过的方法,可以通过super关键字完成。super就是直接由子类找到父类中指定的方法,而如果使用的是this的话,则会先从本类查找,如果查找到了就直接使用,查找不到,则再去父类中查找,super是直接从父类中查找。
关于@override报错的问题
@Override是JDK5 就已经有了,但有个小小的Bug,就是不支持对接口的实现,认为这不是Override。而JDK6 修正了这个Bug,无论是对父类的方法覆盖还是对接口的实现都可以加上@Override
解决办法:
选中项目->Project->Properties->Java Compiler,在弹出的页面中修改编译器版本。
2、回顾函数重载
函数重载:靠参数列表(类型,个数,顺序)区分,返回值不能区分(为了副作用而调用f())。
如果传入的数据类型“小于”重载方法中声明的形式参数的类型,实际数据类型就会提升,会自动向那种数据类型进行“转型”处理。大于的话,必须强制转换,否则编译出错。
3、重载及覆写的区别
No. |
区别点 |
重载 |
覆写 |
1 |
定义 |
方法名称相同,参数的类型或个数不同 |
方法名称、参数的类型或个数、返回值相同 |
2 |
权限 |
没有权限要求 |
被覆写的方法不能拥有比父类更严格的权限 |
3 |
范围 |
发生在一个类之中 |
发生在继承关系中 |
4 |
单词 |
OverLoading |
Override |
五、向上转型与向下转型
·向上转型(子类->父类):父类名称 父类对象 =子类实例 ; 自动完成
·向下转型(父类->子类):子类名称 子类对象 =(子类名称)父类实例 ; 强制完成
所谓的向上转型,实际上指的就是一个子类变为父类接收,但是调用的方法肯定是被子类所覆写过的操作。
class A { public void print() { System.out.println("A --> public void print(){}"); } public void fun() { this.print(); } }; class B extends A { public void print() { System.out.println("B --> public void print(){}"); } }; public class PolDemo01 { public static void main(String args[]) { A a = new B(); // 子类对象变为父类对象 a.fun(); // B --> public void print(){} } }; /*Output B --> public void print(){} */
正确的向下转型
public class PolDemo02 { public static void main(String args[]){ A a = new B() ; // 子类对象变为父类对象 B b = (B) a ; // 父类对象变为子类对象 b.fun() ; } }
错误的向下转型
public class PolDemo03 { public static void main(String args[]){ A a = new A() ; // 父类对象实例化 B b = (B) a ; // 父类对象变为子类对象 b.fun() ; } };
以上是错误的向下转型:程序执行的时候将出现以下的错误:java.lang.ClassCastException,表示的是一个类转换异常,主要的功能是由于两个类之间没有任何的关系所发生的转换,在进行向下转型之前必须首先发生向上转型的关系,建立关系。所以,以后在进行向下转型操作之前,一定要先使用instanceof关键字进行验证,验证通过了,才可以放心安全的执行向下转型的操作。
对象多态性中,虽然可以发生向上转型的关系,一旦发生了向上转型后,子类中自己的定义的操作是无法通过父类对象找到的。
class A { public void print(){ System.out.println("A --> public void print(){}") ; } public void fun(){ this.print() ; } }; class B extends A { public void print(){ System.out.println("B --> public void print(){}") ; } public void printB(){ System.out.println("Hello B") ; } }; public class PolDemo04 { public static void main(String args[]){ A a = new B() ; // 父类对象实例化 // a.printB() ; // 找不到 错误的 B b = (B) a ; // 向下转型 b.printB() ; } };