04_java基础_继承、抽象、多态
2015-05-05 08:30
411 查看
------- android培训、java培训、.net培训、IOS培训
期待与您交流! -------
什么时候定义继承?
必须保证类与类之间有所属(is a)关系。
比如,苹果是水果中一种。狗是犬科中一种。
在Java中继承的体现:
Java允许单继承。不直接支持多继承,将多继承进行其他方式的体现。
单继承:一个子类只能有一个父类。
多继承:一个子类可以有多个父类。
继承如何使用?
多继承:
多重继承:
学习继承体系时,应该首先参阅顶层类中的内容。
了解这个体系的基本功能。
使用这个体系功能,需要创建最子类的对象。
看顶层,建底层。
Super和this:
子父类中定义了一模一样的成员变量。
都存在于子类对象中。
如何在子类中直接访问同名的父类中的变量呢?
通过关键字 super来完成。
super和this的用法很相似。
this:代表的是本类的对象的引用。
super:代表的是父类的内存空间。
但是,父类中私有的内容子类不可以直接访问。
如果子类中定义了和父类一样的函数,那么运行时,运行的是子类的函数。这种事继承关系中的另一个特性:覆盖或者叫重写或者叫复写。
复写什么情况下使用?
当父类的函数功能已经不能满足子类需要的时候,可以复写父类的函数。也就是对父类的函数进行升级。
复写的注意事项:
当复写父类方法时,子类实现的方法权限必须大于等于父类该方法的权限。
写法上必须要一模一样,比如函数的返回类型,函数名,参数列表都要一样,才能达到复写的效果。
子父类中构造函数的特点:
如果父类中有空参的构造函数,那么子类中所有的构造函数中,第一行都有一个隐式的super()。原因是,子类继承父类是要使用父类中的内容的,而只有将父类初始化才能更方便的使用父类中的内容。
如果父类中没有空参的构造函数,那么子类的构造函数中必须显式地使用super(加参数)来指定使用父类的哪个构造函数来初始化父类。
如果子类构造函数中第一行用this调用了本类的一个构造函数,那么原来隐式的super()就没有了,原因是this()或者super()只能定义在构造函数的第一行。
父类构造函数的第一行是不是也是隐式的super?
是的,只要是构造函数,第一行默认的都是super()。Java中Object是所有的类的父类。
描述狗,行为:吼叫。
描述狼,行为:吼叫。
发现他们之间有共性,可以进行向上抽取。
当然是抽取它们的所属共性类型:犬科。
犬科这类事物:都具备吼叫行为,但是具体怎么叫,是不确定的,是由具体的子类来明确的。
这时在描述犬科时,发现了有些功能不具体,这些不具体的功能,需要在类中标识出来,通过java中的关键字abstract(抽象)。
定义了抽象函数的类也必须被abstract关键字修饰,被abstract关键字修饰的类是抽象类。
抽象类和抽象方法都要用abstract修饰。
抽象类不能创建实例,因为抽象类没有实际意义。
子类只有在复写了父类的抽象方法后,子类才可以实例化对象,否则子类还是抽象类。
抽象类一定是父类,因为抽象类某些类的共性组成的,而这些共性来自于子类。
抽象类也有构造函数,但是不能给自己初始化,只能给自己的子类初始化。
抽象类和普通类之间的比较:
相同之处:
都是用来描述事物的。
都可以定义属性和方法。
不同之处:
一般类描述的事物是具体的,抽象类描述的事物是不具体的。
抽象类可以多定义一种成员:抽象函数。
一般类可以实例化对象,抽象类不能实例化对象。
抽象类也可以不定义抽象方法,而此时将该类定义为抽象类的目的是为了不让该类初始化。
不能和abstract共同存在的关键字:final,private,static。
定义接口的关键字:interface。
实现接口的关键字:implements
接口中的变量修饰符固定:public static final
接口中的方法修饰符固定:public abstract
接口的特点:
接口和抽象类一样,不能创建对象。因为接口和抽象方法一样,没有实际意义。
子类必须复写接口中所有的方法之后,该子类才可以实例化。不然子类就是一个抽象类。
定义一个接口:
实现接口的类必须复写掉接口中所有的方法:
接口重要性的体现:
接口的出现可以用多实现代替多继承,解决多继承的弊端。
其实核心原因就是在于多继承父类中方法有主体,而导致调用运行时,不确定运行哪个主体内容。
为什么多实现就解决了呢?
因为接口中的功能都没有方法体,由子类来明确。
多继承的弊端:
当一个类继承了多个父类之后,如果多个父类中定义了名字相同的实例变量,那么子类在调用该实例变量的时候将产生歧义,无法确定要使用哪一个父类的变量。
当一个类继承了多个父类之后,如果多个父类中定义了相同的方法,而子类中有没有对其进行复写,那么在子类调用该方法的时候同样会产生歧义。
多实现演示:
写到这里,我想到如果某个子类实现了两个接口,而两个接口中又定义了相同的变量,那么多实现是不是也有多继承的弊端呢?
于是我写了如下测试代码:
如果上面代码中接口B中哪一行代码不注释掉的话,Eclipse是报错的。还有要注意的就是,抽象类中定义变量是不一定要用到static final关键字的,而接口中只要定义变量,必然使用这两个修饰符。
定义一个类的时候,既可以继承,又可以实现。
子类通过继承父类来获取子类的基本功能,如果子类还需要扩展其他的功能,就可以通过接口来实现。
类与类之间是继承(is a)关系,类与接口之间是实现(like a)关系,接口与接口之间是继承关系,而且可以多继承。
接口的实现问题:
但是:
为了使用接口中的部分方法。而覆盖了全部的方法,而且每一个子类都要这么做,复用性差。
将这些不用的方法都单独抽取到一个独立的类中。
让这个类去实现接口,并覆盖接口中的所有方法。
这个类知道这些方法的具体实现内容吗?不知道。
所以只能为了后期子类创建对象方便,而进行空实现。
而这时,这个类创建对象有意义吗?没有意义。这个类创建对象就不需要,直接将其抽象化。
这就是没有抽象方法的抽象类。
这样就可以在使用其中某一个方法的时候不实现其他无关的方法。
效果如下:
父类的引用或者接口的引用指向了自己的子类对象。
Dog d = new Dog();//Dog对象的类型是Dog类型。
Animal a = new Dog();//Dog对象的类型右边是Dog类型,左边Animal类型。
多态是如何提高程序的可扩展性的:
多态的弊端:
通过父类引用操作子类对象时,只能使用父类中已有的方法,不能操作子类特有的方法。
多态的前提:
1,必须有关系:继承,实现。
2,通常都有重写操作。
那么使用多态创建的对象如何调用子类的方法:
对该对象进行向下转型
Animal a = new Dog();//这一步骤也叫向上转型。
这里Animal是Dog的父类。
向下转型方法:
强制类型转换
Dog d = (Dog)a
但是向下转型是要注意,类型转换是否正确,比如上面的a对象,如果对其进行向下转型应该转成Dog类型,如果进行如下转换就会出ClassCastException异常。
所以为了避免这个问题,需要在向下转型前,做类型的判断。
判断类型用的是关键字 instanceof
多态中,成员调用的特点。
1,成员变量。
当子父类中出现同名的成员变量时。
多态调用该变量时:
编译时期:参考的是引用型变量所属的类中是否有被调用的成员变量。没有,编译失败。
运行时期:也是调用引用型变量所属的类中的成员变量。
简单记:编译和运行都参考等号的左边。
编译运行看左边。
2,成员函数。
编译,参考左边,如果没有,编译失败。
运行,参考右边的对象所属的类。
编译看左边,运行看右边。
对于成员函数是动态绑定到对象上。
3,静态函数。
编译和运行都参考左边。
静态函数是静态的绑定到类上。
(这些都是可以理解的,没必要死记硬背)
------- android培训、java培训、.net培训、 IOS培训 期待与您交流!
-------
期待与您交流! -------
继承
继承的初衷是为了提高代码的复用,我们可以把不同对象所具有的共同属性提取出来封装到一个类中,作为其他类的父类。当某个类需要使用这些共性方法的时候,只需要从父类继承过来就可以了,不用再在新类中去书写。A继承B,B是父类,A是子类。继承的关键字是extends。什么时候定义继承?
必须保证类与类之间有所属(is a)关系。
比如,苹果是水果中一种。狗是犬科中一种。
在Java中继承的体现:
Java允许单继承。不直接支持多继承,将多继承进行其他方式的体现。
单继承:一个子类只能有一个父类。
多继承:一个子类可以有多个父类。
继承如何使用?
多继承:
<pre name="code" class="java">class Fu1 { void show1() {} } class Fu2 { void show2() {} } //多继承。 class Zi extends Fu1,Fu2 { } Zi z = new Zi();//子类继承了父类,子类就有了父类的方法。 z.show1(); z.show2();
多重继承:
class A {} class B extends A {} class C extends B {}多重继承,形成了继承体系。
学习继承体系时,应该首先参阅顶层类中的内容。
了解这个体系的基本功能。
使用这个体系功能,需要创建最子类的对象。
看顶层,建底层。
Super和this:
子父类中定义了一模一样的成员变量。
都存在于子类对象中。
如何在子类中直接访问同名的父类中的变量呢?
通过关键字 super来完成。
super和this的用法很相似。
this:代表的是本类的对象的引用。
super:代表的是父类的内存空间。
但是,父类中私有的内容子类不可以直接访问。
class Fu { int num = 3; } class Zi extends Fu { int num = 4; void show() { System.out.println("zi num="+this.num);//4 System.out.println("fu num="+super.num);//3 } } class ExtendsDemo { public static void main(String[] args) { Zi z = new Zi(); z.show(); } }
如果子类中定义了和父类一样的函数,那么运行时,运行的是子类的函数。这种事继承关系中的另一个特性:覆盖或者叫重写或者叫复写。
复写什么情况下使用?
当父类的函数功能已经不能满足子类需要的时候,可以复写父类的函数。也就是对父类的函数进行升级。
复写的注意事项:
当复写父类方法时,子类实现的方法权限必须大于等于父类该方法的权限。
写法上必须要一模一样,比如函数的返回类型,函数名,参数列表都要一样,才能达到复写的效果。
子父类中构造函数的特点:
如果父类中有空参的构造函数,那么子类中所有的构造函数中,第一行都有一个隐式的super()。原因是,子类继承父类是要使用父类中的内容的,而只有将父类初始化才能更方便的使用父类中的内容。
如果父类中没有空参的构造函数,那么子类的构造函数中必须显式地使用super(加参数)来指定使用父类的哪个构造函数来初始化父类。
如果子类构造函数中第一行用this调用了本类的一个构造函数,那么原来隐式的super()就没有了,原因是this()或者super()只能定义在构造函数的第一行。
父类构造函数的第一行是不是也是隐式的super?
是的,只要是构造函数,第一行默认的都是super()。Java中Object是所有的类的父类。
抽象
抽象类:在描述事物时,没有足够的信息描述一个事物,这时该事物就是抽象事物。描述狗,行为:吼叫。
描述狼,行为:吼叫。
发现他们之间有共性,可以进行向上抽取。
当然是抽取它们的所属共性类型:犬科。
犬科这类事物:都具备吼叫行为,但是具体怎么叫,是不确定的,是由具体的子类来明确的。
这时在描述犬科时,发现了有些功能不具体,这些不具体的功能,需要在类中标识出来,通过java中的关键字abstract(抽象)。
定义了抽象函数的类也必须被abstract关键字修饰,被abstract关键字修饰的类是抽象类。
抽象类和抽象方法都要用abstract修饰。
抽象类不能创建实例,因为抽象类没有实际意义。
子类只有在复写了父类的抽象方法后,子类才可以实例化对象,否则子类还是抽象类。
抽象类一定是父类,因为抽象类某些类的共性组成的,而这些共性来自于子类。
抽象类也有构造函数,但是不能给自己初始化,只能给自己的子类初始化。
抽象类和普通类之间的比较:
相同之处:
都是用来描述事物的。
都可以定义属性和方法。
不同之处:
一般类描述的事物是具体的,抽象类描述的事物是不具体的。
抽象类可以多定义一种成员:抽象函数。
一般类可以实例化对象,抽象类不能实例化对象。
抽象类也可以不定义抽象方法,而此时将该类定义为抽象类的目的是为了不让该类初始化。
不能和abstract共同存在的关键字:final,private,static。
接口
抽象类中可以定义抽象方法,当一个抽象类中的方法全是抽象的时候,这个抽象类就可以用接口来体现。定义接口的关键字:interface。
实现接口的关键字:implements
接口中的变量修饰符固定:public static final
接口中的方法修饰符固定:public abstract
接口的特点:
接口和抽象类一样,不能创建对象。因为接口和抽象方法一样,没有实际意义。
子类必须复写接口中所有的方法之后,该子类才可以实例化。不然子类就是一个抽象类。
定义一个接口:
interface Demo//定义一个名称为Demo的接口。 { public static final int NUM = 3; public abstract void show1(); public abstract void show2(); }
实现接口的类必须复写掉接口中所有的方法:
class DemoImpl implements Demo//子类实现Demo接口。 { //重写接口中的方法。 public void show1(){} public void show2(){} }
接口重要性的体现:
接口的出现可以用多实现代替多继承,解决多继承的弊端。
其实核心原因就是在于多继承父类中方法有主体,而导致调用运行时,不确定运行哪个主体内容。
为什么多实现就解决了呢?
因为接口中的功能都没有方法体,由子类来明确。
多继承的弊端:
当一个类继承了多个父类之后,如果多个父类中定义了名字相同的实例变量,那么子类在调用该实例变量的时候将产生歧义,无法确定要使用哪一个父类的变量。
当一个类继承了多个父类之后,如果多个父类中定义了相同的方法,而子类中有没有对其进行复写,那么在子类调用该方法的时候同样会产生歧义。
多实现演示:
interface A { void show1(); } interface B { void show2(); } class C implements A,B// 多实现。同时实现多个接口。 { public void show1(){} public void show2(){} }
写到这里,我想到如果某个子类实现了两个接口,而两个接口中又定义了相同的变量,那么多实现是不是也有多继承的弊端呢?
于是我写了如下测试代码:
interface A { public static final int a = 1; } interface B { // public static final int a = 1; } class C implements A,B{ public C(){ System.out.println(a); } }
如果上面代码中接口B中哪一行代码不注释掉的话,Eclipse是报错的。还有要注意的就是,抽象类中定义变量是不一定要用到static final关键字的,而接口中只要定义变量,必然使用这两个修饰符。
定义一个类的时候,既可以继承,又可以实现。
class Fu { public void show(){} } interface Inter { pulbic void show1(); } class Zi extends Fu implements Inter { public void show1() { } }
子类通过继承父类来获取子类的基本功能,如果子类还需要扩展其他的功能,就可以通过接口来实现。
类与类之间是继承(is a)关系,类与接口之间是实现(like a)关系,接口与接口之间是继承关系,而且可以多继承。
interface InterA { void show1(); } interface InterAA { void show11(); } interface InterB extends InterA,InterAA//接口的多继承。 { void show2(); } class Test implements InterB { public void show1(){} public void show2(){} public void show11(){} } class InterfaceDemo { public static void main(String[] args) { DemoImpl d = new DemoImpl(); d.show1(); d.show2(); } }
接口的实现问题:
interface Inter { //定义四种显示功能。 public void show1(); public void show2(); public void show3(); public void show4(); } //定义子类,要使用第一种显示方式。 class InterImpl1 implements Inter { //覆盖show1方法。 public void show1() { System.out.println("show1 run"); } //为了让该类实例化。还需要覆盖其他三个方法,虽然该类用不上。 public void show2(){} public void show3(){} public void show4(){} } //另一个子类需要使用显示3方法。 class InterImpl3 implements Inter { //覆盖show3方法。 public void show3() { System.out.println("show3 run"); } //为了让该类实例化。还需要覆盖其他三个方法,虽然该类用不上。 public void show2(){} public void show1(){} public void show4(){} }
但是:
为了使用接口中的部分方法。而覆盖了全部的方法,而且每一个子类都要这么做,复用性差。
将这些不用的方法都单独抽取到一个独立的类中。
让这个类去实现接口,并覆盖接口中的所有方法。
这个类知道这些方法的具体实现内容吗?不知道。
所以只能为了后期子类创建对象方便,而进行空实现。
而这时,这个类创建对象有意义吗?没有意义。这个类创建对象就不需要,直接将其抽象化。
这就是没有抽象方法的抽象类。
这样就可以在使用其中某一个方法的时候不实现其他无关的方法。
效果如下:
abstract class InterImpl implements Inter { //实现Inter接口中的所有方法。 public void show1(){} public void show2(){} public void show3(){} public void show4(){} } //如果有子类去使用显示1方法。让子类继承InterImpl实现类就可以了。 class InterImpl11 extends InterImpl { public void show1() { System.out.println("show1 run"); } } class InterImpl33 extends InterImpl { public void show3() { System.out.println("show3 run"); } }
多态
多态的体现:父类的引用或者接口的引用指向了自己的子类对象。
Dog d = new Dog();//Dog对象的类型是Dog类型。
Animal a = new Dog();//Dog对象的类型右边是Dog类型,左边Animal类型。
多态是如何提高程序的可扩展性的:
这样的好处是,程序的已知流程在没有派生类的时候就可以写好,以后要有新的功能,只要再写个派生类就可以了。举个例子,在电脑上显示图片,基本上要有读文件,解析文件,显示图形三个步骤,这些步骤可以写在基类中,然后具体的如何读文件,如何解析文件,如何显示,就交给派生类去做。
多态的弊端:
通过父类引用操作子类对象时,只能使用父类中已有的方法,不能操作子类特有的方法。
多态的前提:
1,必须有关系:继承,实现。
2,通常都有重写操作。
那么使用多态创建的对象如何调用子类的方法:
对该对象进行向下转型
Animal a = new Dog();//这一步骤也叫向上转型。
这里Animal是Dog的父类。
向下转型方法:
强制类型转换
Dog d = (Dog)a
但是向下转型是要注意,类型转换是否正确,比如上面的a对象,如果对其进行向下转型应该转成Dog类型,如果进行如下转换就会出ClassCastException异常。
所以为了避免这个问题,需要在向下转型前,做类型的判断。
判断类型用的是关键字 instanceof
if(a instanceof Cat)//a指向的对象的类型是Cat类型。 { //将a转型Cat类型。 Cat c = (Cat)a; } else if(a instanceof Dog) { Dog d = (Dog)a; }
多态中,成员调用的特点。
1,成员变量。
当子父类中出现同名的成员变量时。
多态调用该变量时:
编译时期:参考的是引用型变量所属的类中是否有被调用的成员变量。没有,编译失败。
运行时期:也是调用引用型变量所属的类中的成员变量。
简单记:编译和运行都参考等号的左边。
编译运行看左边。
2,成员函数。
编译,参考左边,如果没有,编译失败。
运行,参考右边的对象所属的类。
编译看左边,运行看右边。
对于成员函数是动态绑定到对象上。
3,静态函数。
编译和运行都参考左边。
静态函数是静态的绑定到类上。
(这些都是可以理解的,没必要死记硬背)
------- android培训、java培训、.net培训、 IOS培训 期待与您交流!
-------
相关文章推荐
- Java基础语法<八> 继承 多态 抽象 反射
- Java基础-四大特性理解(抽象、封装、继承、多态)
- Java基础笔记-抽象,继承,多态
- 黑马程序员——Java基础--继承、多态、抽象、接口联合使用的案例
- 黑马程序员—————Java基础----继承、多态、抽象、接口
- Java基础 对封装、继承、多态、抽象的理解
- Java基础:继承、多态、抽象、接口
- 黑马程序员 ——Java基础之继承、抽象、接口、多态
- java基础知识2--抽象 多态,继承等
- 黑马程序员——Java基础:继承、final关键字、抽象、接口、多态
- 黑马程序员——Java基础---继承,抽象,多态,接口,包,内部类
- java基础——java的三大基本特征(继承、抽象、多态)
- 黑马程序员<java基础<面向对象(封装,继承,多态,抽象)>>
- java基础 继承、封装、多态、抽象详解
- Java基础-->面向对象<继承,内部类,多态,包,抽象与接口>
- Java基础-OOP特性之封装、继承、多态、抽象
- java基础-继承-封装-多态
- [JAVA基础汇总]第十一章 继承和多态
- 黑马程序员——Java基础---继承、抽象、接口
- 黑马程序员--Java基础学习笔记【继承、多态】