Java学习——面对对象的思想入门
本文是看过《head first Java》之后的一点感悟,写点东西帮忙以后回忆,Java目前在我的工作中用到还不多,而我又对面对对象的编程非常的感兴趣。曾经在MFC平台上写过一个比较大的程序,但是看了本书后,发现之前程序中漏洞百出,而且对面对对象的思想理解不深刻,感觉需要重新学习一遍。C++和JAVA的面对对象还是很有差距的,但是他们的基本思想是相同,抓住思想再学习语言会更高效。http://www.cnblogs.com/jsgnadsj
类
面向过程编程语言的局限:
大家最熟悉的应该是C语言编程,它是一个面向过程的编程语言。我曾经做过模型车,用主控芯片去控制四个轮子转动,用C语言的时候,只要控制轮子正反转就能控制小车方向。http://www.cnblogs.com/jsgnadsj
电机正转与反转:
- void motorFrontLeft(int sw)
- {
- switch{
- case 1:clockwise_rotation();
- case 2:counter_clockwise_rotation();
- }
- }
同理其他电机:FrontRight、BackLeft、BackRight。
clockwise_rotation() 函数是低层硬件操作,实质上就是电池正负极连接到电机两端的方式。正接的时候正转,反着接就反着转。
这里补充一点:并不是说C语言一定是面向过程而不能编写面向对象的程序,这是误解,C同样也能做面向对象编程。面向过程和面向对象是编程的一种思想,C++是在C的基础上发展而成,为了更好的利用面向对象的思想。
通过前进函数调用上面的电机函数就能够控制小车前进、后退、左转、右转动作了。
前进动作:
- void RunFront(int sw)
- {
- motorFrontLeft(1);
- motorFrontRight(1);
- motorBackLeft(1);
- motorBackRight(1);
- }
四个轮子同时正转,小车就前进了。
这就是面对过程思想编程,通过编写过程代码,来控制。
如果需要我们增加一些功能,或者修改一些功能的时候,麻烦来了。比如前进动作,希望能够控制其前进速度,那么我们要在RunFront()的函数参数中增加速度这个参数项,不仅如此,还要将此速度值传递给RunFront中调用的motorXX函数。这还没结束,真正控制轮子转速的是电机,所以还要修改motorXX里面代码,为其增加可以控制转速的功能。
四个轮子需要修改四个函数,如果这是一个蜈蚣车(只是说明需要的轮子多),那得修改多少。
面对对象思想的就是希望能够比较好的解决这类问题。
面对对象语言的优势:
面对对象思想主要有三个大的核心思想:封装,继承,多态。
我们可以看到,修改工作量大是因为我们对每个轮子控制函数要进行修改,也就是修改重复代码,这样会增加出现的概率。想想,四个电机其实质是什么?就是电机!除了安放在不同的位置,其他什么都一样的。理想状态下,通过同样的设置就能够得到同样的效果。比如接线方式是一样则正转,电压一样则转速一致。对于这样的有着相同属性和行为的物体,我们可以把他们统称为一个类(Class)。http://www.cnblogs.com/jsgnadsj
- class Motor{
- int rotate;//方向
- int v; //速度
- void run(int rotate){
- //
- switch (dir){
- case 1://正转
- case 2://反转
- }
- }
- void stop(){
- }
- }
Motor就是我们构造出来的类。这就是我们需要的电机的总称。
如何使用类?下面讲对象。
对象
类是一类事物的抽象描述。比如我们说的电机,它不是虚无飘渺凭空出现的事物。我们首先有它的设计图,然后根据设计图创造出它的实物。设计图就是类,创造出来的实物就是对象。这就是类与对象关系。类是抽象的(设计图),对象是具体的(电机实物)。
电动车的四个电机的构造:
- Motor motorFL = new Motor();
- Motor motorFR = new Motor();
- Motor motorBL = new Motor();
- Motor motorBR = new Motor();
这样就创造了四个电机实例对象。具体语法及含义参考java书籍。
变量类型:
首先明确一点,Java中只有两类类型,一种是基本数据类型,另一种是引用类型。
数据类型:char、short、int、float、double、boolean,与C中基本类似。
引用类型:作用于对象的变量
Java中对对象的操作都是通过引用变量来操作的,比如上面代码,Motor motorXX = new Motor(),
motorXX就是引用变量,Motor()是对象,这样就通过了motorXX来访问Motor对象,类比C中指针变量。
明确一点 motorXX 与 Motor() 是不同的两个变量,一个是引用类型:通过 Motor motorXX 来声明;一个是实际对象:通过new Motor()来创造。通过 = 使他们两个之间联系起来,这样就能够通过motorXX来访问Motor()对象。http://www.cnblogs.com/jsgnadsj
"="在这里的意思可以理解为将引用变量指向对象。
上面4句代码会产生如下结果:
我们可以修改motorFL来指向motorFR所指向的对象,如下图:
- motorFL = motorFR;
这样的操作之后,相当于两个遥控器可以控制同一个对象,那么 那个没有遥控器的对象怎么办呢?
我们无论如何都不会再指向它了,所以它会被Java的回收机制在适当的时候删除。(适当这个词好深奥!!!)
这里将引用对象和实际对象非常简单的说了下,这里只是入门而已,具体还是要看书,比如它们存放的区域就不同等等。
封装
我们有电机的设计图,但是我们不希望别人看到这个设计图,这就是封装。之前电机,我们通过类能够快速创造出4个电机对象,说明该类是能够重复利用的;比如我们家里的电视机,我们只需要怎么用,不必关心它是怎么做出来的;还有将设计图中某些东西给隐藏起来,这样的好处就是安全。这些都是封装的好处。
说道封装的安全性,我们就要提到四个关键字:Public、private、defalut、protected。什么是安全,就是某些事物是否有权限。比如:你爸爸可以进入你家,因为你爸爸有权限,而坏人呢,坏人没有进入你家权限,所以他是危险的。刚才提到的四个关键字就是四个等级的权限(具体参考书籍),这样理解就好了。
修改Motor类,使其添加明确的权限功能:http://www.cnblogs.com/jsgnadsj
- class Motor{
- private int rotate;//方向
- private int v; //速度
- public void run(int rotate){
- //
- switch (rotate){
- case 1://正转
- case 2://反转
- }
- }
- public void stop(){
- }
- }
属性rotate用private修饰,而方法run用public修饰。这就是刚才我们提到的电视,不关心里面构造(rotate),而只要知道怎么用(run)。
Static 关键字:
Static的变量:
如果我们希望统计一个类被创造出多少个对象,在C语言中,我们可以这样统计一个方法被调用次数:
- void func()
- {
- static time;
- time++;
- }
所以在Java中也可以用Static来帮助统计对象个数。
在类中定义一个统计个数的变量,然后在构造函数中自增。
- class Motor{
- private int time;
- private int rotate;//方向
- private int v; //速度
- public Motor()
- {
- time++;
- }
- public void run(int rotate){
- //
- switch (rotate){
- case 1://正转
- case 2://反转
- }
- }
- public void stop(){
- }
- }
这样每次创建一个Moter对象的时候,time都会自增,就可以统计对象个数了。
Static的方法:
在Java里面最熟悉的应该是Math里面的静态方法,比如Math().abs()…
对于abs()方法,它是一个静态方法,允许不创建对象而调用静态方法,是Java为了减少程序员调用某些常用方法时的麻烦,而允许程序员按照传统的C语言中使用函数的方式来使用方法。可以这样调用:Math().abs(-1);而不需要先创建Math对象,再调用方法。
Static类:
一般情况下是不可以用static修饰类的。如果一定要用static修饰类的话,通常static修饰的是匿名内部类。 在一个类中创建另外一个类,叫做成员内部类。这个成员内部类可以静态的(利用static关键字修饰),也可以是非静态的。由于静态的内部类在定义、使用的时候会有种种的限制。所以在实际工作中用到的并不多。 在开发过程中,内部类中使用的最多的还是非静态地成员内部类。不过在特定的情况下,静态内部类也能够发挥其独特的作用。 |
具体可以查看相关书籍。
继承
为什么需要继承?
我们以电视机为例,电视机发展过程为:黑白电视机->彩色电视机->液晶电视机->智能电视机…一步一步发展过来的。我们在从黑白电视机发展为彩色电视机的时候,不是完全摒弃了黑白电视机的设计图,因为黑白电视机某些属性和性质(比如电源系统)都还能够继续为彩色电视机使用,不需要再设计,所以仅仅需要修改显示结构即可。
继承就起到这样的作用,可以减少代码重复量,只需要修改或者增加新属性和行为就可以得到一个新的类。http://www.cnblogs.com/jsgnadsj
写一个电视机类:
普通电视机类
- class TeleVision
- {
- private int voltage;//电压
- private int channel;//频道
- public void show(){
- System.out.println("television!");
- }
- public int getVoltage() {
- return voltage;
- }
- public void setVoltage(int voltage) {
- this.voltage = voltage;
- }
- public int getChannel() {
- return channel;
- }
- public void setChannel(int channel) {
- this.channel = channel;
- }
- }
现在我继承这个类,继承后的类是黑白电视机,修改show方法即可。
黑白电视机类:
- class BlackWhiteTV extends TeleVision
- {
- public void show()
- {
- System.out.println("Black White TV!");
- }
- }
BlackWhiteTV 里面包含了 TeleVision 中的方法:
对象bwTV继承了他的父类TeleVision的public方法,但是没有直接访问private属性的权利。private修饰的属性和行为是不允许其他类包括它的继承类访问。
覆盖 override
为什么需要覆盖?
上例中,电视机类是一个总体概述的,通过一种抽象提取出来的,后面还要对它修改使其更符合面对对象的思想,这里暂且认为TeleVision类是一个蓝图,是我们脑海中的。基于这样的条件下,电视机和黑白电视机show是不同的,TeleVision是我们想想出来的,我们希望有个机器能够显示出图像就行,具体是什么样子我们不关心,而BlackWhiteTV则不一样,我们希望它能够显示出黑白的图像。假设之后有彩色电视机了,那么我们不再需要它show出黑白,那么我们直接可以将它的show覆盖,重新改成彩色就可以了。
上个例子中,BlackWhiteTV中的show方法就是覆盖了它的父类show方法。在执行bwTV.show()的时候,会调用BlackWhiteTV中的show方法。
覆盖注意几个问题:http://www.cnblogs.com/jsgnadsj
- 覆盖的方法的标志必须要和被覆盖的方法的标志完全匹配,才能达到覆盖的效果;
- 覆盖的方法的返回值必须和被覆盖的方法的返回一致;
- 覆盖的方法所抛出的异常必须和被覆盖方法的所抛出的异常一致,或者是其子类;
- 被覆盖的方法不能为private,否则在其子类中只是新定义了一个方法,并没有对其进行覆盖。
对于覆盖还有其他许多约束条件。
重载 overload
这里提及一下,这是多态思想。上例中,BlackWhiteTV中的show方法是没有参数传入的,如果此时我希望写一个有一个参数传递的show方法时候,如下:
- class BlackWhiteTV extends TeleVision
- {
- public void show()
- {
- System.out.println("Black White TV!");
- }
- public void show(int i)
- {
- System.out.printf("%d Black White TV!\n",i);
- }
- }
这就是重载,为什么会有这样的设计?
比如我们现在的电视机,希望它既可以显示黑白(比较怀旧),又能够显示彩色,那么我们肯定不希望这样写函数:showBlackWhite(),showColorful()。假如还有什么2D,3D,4D,5D等等,那么我们对于一个对象要写很多个不同函数,使用的时候还要记忆具体函数名,虽然函数名上可以取得比较相似,但是还是有点复杂。再比如我们常见的函数abs求绝对值的函数,java中的数据类型有int、float、double等等,我们肯定不希望函数写成这样:absInt(int i)、absFloat(float f)、absDouble(double d)。我们希望用一个函数名能够实现接收多个不同类型参数,简化代码,所以重载就提供了这样的方法。
我们用同样的函数名,接收不同类型的参数就能够实现一个函数名具有不同的功能,重载的意义重大。
重载需要注意:
- 在使用重载时只能通过不同的参数样式。例如,不同的参数类型,不同的参数个数,不同的参数顺序(当然,同一方法内的几个参数类型必须不一样,例如可以是fun(int,float),但是不能为fun(int,int));
- 不能通过访问权限、返回类型、抛出的异常进行重载;
- 方法的异常类型和数目不会对重载造成影响;
- 对于继承来说,如果某一方法在父类中是访问权限是priavte,那么就不能在子类对其进行重载,如果定义的话,也只是定义了一个新方法,而不会达到重载的效果。
继承时候类中方法调用:
如下继承图
- SmartTV stv = new SmartTV();
- stv.function();
stv.function()调用的是哪个类中的方法?
如果在SmartTelevision中覆盖了function方法,则调用SmartTelevision中的,反之,则调用ColorTelevision中的;若ColorTelevision中也没有覆盖function方法,则调用Television中的方法。
对于ColorTelevision中的function完全按照(返回类型、参数类型、方法名)Television中的function来写,这叫做覆盖。
this 关键字
为什么需要this关键字?
比如一下这种情况:
- class BlackWhiteTV extends TeleVision
- {
- private int i;
- public void show()
- {
- System.out.println("Black White TV!");
- }
- public void show(int i)
- {
- System.out.printf("%d Black White TV!\n",i);
- System.out.printf("%d Black White TV!\n",this.i);
- }
- }
BlackWhiteTV类中有类属性i,同时show方法的参数也是i,此时如何区分这两个i?
当一个对象创建后,Java虚拟机(JVM)就会给这个对象分配一个引用自身的指针,这个指针的名字就是 this。所以我们可以用this.i来指代类中属性i。
如下情况:
- class BlackWhiteTV extends TeleVision
- {
- private int i;
- BlackWhiteTV()
- {
- System.out.println("blackwhiteTV create!");
- }
- BlackWhiteTV(int i)
- {
- this();
- System.out.println("the parament i:"+i);
- }
- public void show()
- {
- System.out.println("Black White TV!");
- }
- public void show(int i)
- {
- System.out.printf("%d Black White TV!\n",i);
- System.out.printf("%d Black White TV!\n",this.i);
- }
- }
如果想要BlackWhiteTV(int i)首先调用BlackWhiteTV()再调用自己的函数,这个时候用this()就表示该类的无参数构造函数,如果带参数this(x),这样就是调用类的含有参数的构造函数。
注意:
- 做区分:函数参数或者函数中的局部变量和成员变量同名的情况下,成员变量被屏蔽,此时要访问成员变量则需要用"this.成员变量名"的方式来引用成员变量。
- 调用自身类某些方法:通过this调用另一个构造方法,用发是this(参数列表),这个仅仅在类的构造方法中,别的地方不能这么用。
- 还有一个地方用到this:在函数中,需要引用该函所属类的当前对象时候,直接用this。
super 关键字
如果我们希望子类方法不是完全覆盖父类方法,而是希望先调用父类方法,再调用自身的函数,
例子如下:
- class TeleVision
- {
- public void show(){
- System.out.println("television!");
- }
- }
- class BlackWhiteTV extends TeleVision
- {
- public void show()
- {
- super.show();
- System.out.println("Black White TV!");
- }
- }
这样会显示:
Television!
Black White TV!
这样子类不是完全的覆盖父类方法,而是保留了父类方法基础上,又有自己的方法。
同理this,super同样对构造函数一样效用。可以通过super(参数)调用父类构造函数。
如果遇到子类中的成员变量或方法与父类中的成员变量或方法同名。因为子类中的成员变量或方法名优先级高,所以子类中的同名成员变量或方法就隐藏了父类的成员变量或方法,但是我们如果想要使用父类中的这个成员变量或方法,就需要用到super。
Super和This区别
- super(参数):调用基类中的某一个构造函数(应该为构造函数中的第一条语句)
- this(参数) :调用本类中另一种形成的构造函数(应该为构造函数中的第一条语句)
- super : 它引用当前对象的直接父类中的成员(用来访问直接父类中被隐藏的父类中成员数据或函数,基类与派生类中有相同成员定义时如:super.变量名 super.成员函数据名(实参)
- this :它代表当前对象名(在程序中易产生二义性之处,应使用this来指明当前对象;如果函数的形参与类中的成员数据同名,这时需用this来指明成员变量名)
- 调用super()必须写在子类构造方法的第一行,否则编译不通过。每个子类构造方法的第一条语句,都是隐含地调用super(),如果父类没有这种形式的构造函数,那么在编译的时候就会报错。
- super()和this()类似,区别是,super()从子类中调用父类的构造方法,this()在同一类内调用其它方法。
- super()和this()均需放在构造方法内第一行。
- 尽管可以用this调用一个构造器,但却不能调用两个。
- this和super不能同时出现在一个构造函数里面,因为this必然会调用其它的构造函数,其它的构造函数必然也会有super语句的存在,所以在同一个构造函数里面有相同的语句,就失去了语句的意义,编译器也不会通过。
- this()和super()都指的是对象,所以,均不可以在static环境中使用。包括:static变量,static方法,static语句块。
- 从本质上讲,this是一个指向本对象的指针, 然而super是一个Java关键字。http://www.cnblogs.com/jsgnadsj
final 关键字
继承有一个缺点:打破了封装特性!我们可以通过继承来覆盖或者重载它的方法,那么我们能不能不然我们的类(或者方法或者属性)被继承呢?——Final关键字就派上用场!
类被修饰为final:
类方法被修饰为final:
类成员被修饰为final:
假设我们知道一个变量不会再被改变时候,我们可以把他定义为常量。用final修饰。比如常数PI,我们就可以用final修饰:
类成员变量被修饰的时候需要注意:
- 显示的初始化,
- 只能初始化一次,
- 在声明或者构造函数中初始化。
对于PI,我们希望能够给所有的类使用,怎么办?
在要使用的类中,创造一个含有PI的类,然后用 类.PI 方式调用。这样的方法不好,因为我们只需要PI一个值,但是创造出来的类其他部分确实多余的。
想想有没其他办法?
前面我们说道过static,我们可以直接通过 类 .static修饰的变量来调用,所以我们可以在一个常用类中定义一个 public final static的PI 就可以了。而这个PI就在我们的Math类中。
- 对于类:将某个类的定义为final 时,该类无法被继承。而且由于final类禁止继承,所以final类中所有的方法都隐式指定为final的,因为无法覆盖它们。
- 对于类的方法:仅将某个类的方法定义为final时,该方法无法继承,其他不受影响。
- 为了确保某个函数的行为在继承过程中保持不变,并且不能被覆盖(overridding),可以使用final方法。
- class中所有的private和static方法自然就是final。类中所有的private方法都隐式地指定是final的。由于无法取用private方法,所以也就无法覆盖它。
- 对于类的属性(成员):由于其不能被改变,所以要显示的初始化,并且之后不能在被修改。
中国地区的电气的电压是220v,所以可以将 voltage定义为final类,但是private是隐式的final,所以直接private即可。
Final 在还有其他注意:比如final 的对象引用等。具体参考书籍。
如何判断继承:
IS-A 方法:如果 x继承了y(即x extends y),那么有y is – a x成立。这条性质有传递性。
Abstract 关键字
- class TeleVision
- {
- private int voltage;//电压
- private int channel;//频道
- public void show(){
- System.out.println("television!");
- }
- public int getVoltage() {
- return voltage;
- }
- public void setVoltage(int voltage) {
- this.voltage = voltage;
- }
- public int getChannel() {
- return channel;
- }
- public void setChannel(int channel) {
- this.channel = channel;
- }
- }
- class BlackWhiteTV extends TeleVision
- {
- public void show()
- {
- System.out.println("Black White CRT TV!");
- }
- }
- class ColorfulTV extends BlackWhiteTV
- {
- public void show()
- {
- System.out.println("Colorful CRT TV!");
- }
- }
我们现在的代码是这样,有没有感觉到TeleVision里面show方法有点多余?因为我们后面的黑白电视,彩色电视都没有用到TeleVision的show()方法,而是直接将其覆盖,实现了它们自己的方法。
然后我们再思考,电压和频道的get和set对TeleVision都没有用,因为我们没有创造出TeleVision的实例,因为它是我们脑子中蓝图,是一个虚拟的,虚拟的电视机我们不需要去获取他的属性数据。
这样我们希望能够有一种方法,只给这个类保留类的方法名称,也就是说继承该类的所以子类都有这样的方法,但是各个子类的实现却不一样,各自为政。这样既可以保证该类确实有这样的方法,同时也能让程序员灵活的实现。
抽象类发挥巨大作用!http://www.cnblogs.com/jsgnadsj
原则:
- 必须用public abstract修饰抽象方法,private ,static 都不能用于修饰
- 包含抽象方法的类必须是抽象类,也就是说,只要类中有抽象方法则该类一定是抽象类
- 抽象类可以不含抽象方法
- abstract class TeleVision
- {
- private int voltage = 220;//电压
- private int channel;//频道
- abstract public void show();
- public int getVoltage() {
- return voltage;
- }
- public int getChannel() {
- return channel;
- }
- public void setChannel(int channel) {
- this.channel = channel;
- }
- }
黑白电视机:
- class BlackWhiteTV extends TeleVision
- {
- public void show()
- {
- System.out.println("Black White CRT TV!");
- }
- }
此类中必须实现TeleVision的show(),否则编译器报错。我们越来越能够抽象电视机这样的事物了,电视机不就是能够放映的事物嘛!只要有show()即可,而具体实现看具体继承的类了。比如:黑白电视的show黑白,彩色电视的show彩色。
我们重新调整一下类的继承关系,因为彩色电视机不是从黑白电视机继承的,彩色电视机也可以从电视机继承来。
对于这张继承图,电器是最顶层的,电视机不就是一个电器设备,这样就将电视机类更加抽象了。
电器类:
- abstract class ElecEequitment
- {
- private final int voltage = 220; //额定电压
- public int getVoltage()
- {
- return this.voltage;
- }
- abstract public void function(); //功能
- abstract public int getCstmPower(); //获取消耗电量
- }
厨房电器类:
- abstract class CookElecEquitments extends ElecEequitment
- {
- abstract public void ResistWater();
- }
在原有的基础上,增加了 防水功能。
客房电器类:
- abstract class RoomElecEquitments extends ElecEequitment
- {
- abstract public void energySavingStandard(int i);
- }
在原有的基础上,增加了 "节能标准"。
继承抽象类的类不仅可以是普通类,同样也能够时抽象类。
多态:
上面的电器家组图的UML图如下:
抽象类代码在上面!
- class MicrowaveOven extends CookElecEquitments
- {
- public void function()
- {
- System.out.println("Microwave Oven!");
- }
- public int getCstmPower()
- {
- return 0;
- }
- public void ResistWater()
- {
- }
- }
- class ElectricCooker extends CookElecEquitments
- {
- public void function()
- {
- System.out.println("Electric Cooker!");
- }
- public int getCstmPower()
- {
- return 0;
- }
- public void ResistWater()
- {
- }
- }
- class Light extends RoomElecEquitments
- {
- public void function()
- {
- System.out.println("Light!");
- }
- public int getCstmPower()
- {
- return 0;
- }
- public void energySavingStandard(int i)
- {
- System.out.println("the standard is"+i);
- }
- }
- class Television extends RoomElecEquitments
- {
- public void function()
- {
- System.out.println("watch TV!");
- }
- public int getCstmPower()
- {
- return 0;
- }
- public void energySavingStandard(int i)
- {
- System.out.println("the standard is"+i);
- }
- }
- class PC extends RoomElecEquitments
- {
- public void function()
- {
- System.out.println("PC!");
- }
- public int getCstmPower()
- {
- return 0;
- }
- public void energySavingStandard(int i)
- {
- System.out.println("the standard is"+i);
- }
- }
- class AirCondition extends RoomElecEquitments
- {
- public void function()
- {
- System.out.println("Air Condition!");
- }
- public int getCstmPower()
- {
- return 0;
- }
- public void energySavingStandard(int i)
- {
- System.out.println("the standard is"+i);
- }
- }
假设这里我们需要频繁的调用最下面的类,那么我们需要有6个不同类型的引用,比如:
我们要声明六个不同类的引用
- MicrowaveOven mo = new MicrowaveOven();
- ElectricCooker ec = new ElectricCooker();
- Light lt = new Light();
- Television tv = new Television();
- PC pc = new PC();
- AirCondition ac = new AirCondition();
然后调用的时候,分别调用,对PC就要用pc,对Television就要用tv,比如他们都有一个function()功能,那么调用的时候需要这样:
- mo.function();
- ec.function();
- lt.function();
- tv.function();
- pc.function();
- ac.function();
有没有感觉很麻烦?它们都有相同的方法,却要一个一个通过不同引用类型调用。
麻烦的原因是:引用类型和对象类型必须相符才能正确调用。
应用1:引用类型是实际类型的父类(或间接父类):
我们看看UML图,最后的6个对象怎么都是有点联系的,既然有联系能不能通过什么方法来解决这样的问题呢?
他们有一个共同的父类(间接),我们能不能利用这个特性?
如下代码:
- ElecEequitment[] eq = new ElecEequitment[6];
- eq[0] = new MicrowaveOven();
- eq[1] = new ElectricCooker();
- eq[2] = new Light();
- eq[3] = new Television();
- eq[4] = new PC();
- eq[5] = new AirCondition();
- int i;
- for(i = 0 ; i < 6 ; i++)
- {
- eq[i].function();
- }
第一句代码:创建6个父类引用!注意这里的是 new ElecEequitment[6] 而不是 new ElecEequitment()[6]。并没有创造6个ElecEequitment对象。
下面的for循环代码中,通过他们共同的父类(间接)的引用来调用,这样方便了很多。
同样值得注意,第一句话中的引用变量类不一定是abstract,也可以是普通类。
同样,这样的方法也可以用于传递函数的参数或者返回类型。http://www.cnblogs.com/jsgnadsj
应用2:参数类型与返回类型的多态
比如我增加一个方法,用于打开电器,如果没有多态,那么我需要对每一个电器都需要写一个打开方法的函数。如果增加了新的电器,那么又要改写代码。——多态就能够轻松解决这样的问题
- class opt
- {
- public void powerOn(XXX xxx)
- {
- }
- }
上述代码中,XXX是已有的电器类(TV、PC…),xxx则是对应的引用。
应用多态就能够很好的解决,XXX定义为父类,这样就很好的解决了。
- class opt
- {
- public void powerOn(ElecEequitment eq)
- {
- System.out.println(eq.getClass().getSimpleName()+"power On");
- }
- }
- public class Elec_equipmentTest {
- public void powerOn(ElecEequitment eq)
- {
- System.out.println(eq.getClass().getSimpleName()+"power On");
- }
- public static void main(String[] args) {
- // TODO Auto-generated method stub
- ElecEequitment[] eq = new ElecEequitment[6];
- eq[0] = new MicrowaveOven();
- eq[1] = new ElectricCooker();
- eq[2] = new Light();
- eq[3] = new Television();
- eq[4] = new PC();
- eq[5] = new AirCondition();
- opt o = new opt();
- for (int j = 0; j < eq.length; j++) {
- o.powerOn(eq[j]);
- }
- }
- }
输出:
- 注意一种情况,父类中如果有static 修饰的方法的时候。由于静态方法只能继承,不能重载,那么如果子类中定义了同名同形式的静态方法,它对父类方法只起到隐藏的作用。调用的时候用谁的引用,则调用谁的版本。http://www.cnblogs.com/jsgnadsj
重载
多态对于方法的作用,之前讲过的重载!
再说关键字 abstract
之前说过,创建引用对象的时候是new ElecEequitment[6] 而不是 new ElecEequitment()[6]。
- 假如我们不小心写成了后面那种样子,没有abstract修饰ElecEequitment()会被创造出来,但是电器类是什么样子?可能是电视机,可能是电脑、可能是微波炉,对它应该是一个变形金刚。想要他是什么,它就应该能够变成什么,所以这里添加了abstract修饰,这样电器类就不会被构造出来。
- Abstract的方法必须在继承具体类中全部实现。如果某个abstract方法没有实现,那么就会有包含这个方法的类就应该是abstract类。
类型转化
- class TV
- {
- private int voltage;
- public int getVoltage()
- {
- return voltage;
- }
- public void function()
- {
- System.out.println("Watch TV");
- }
- public int getCstmPower()
- {
- return 10;
- }
- }
- class ColorTV extends TV
- {
- public void showcolor()
- {
- System.out.println("ColorTV: colorful");
- }
- public void function()
- {
- System.out.println("Watch Color TV!");
- }
- }
- class SmartTV extends ColorTV
- {
- public void onInternet()
- {
- System.out.println("SmartTV: surf the Internet");
- }
- public void function()
- {
- System.out.println("Watch Smart TV");
- }
- }
之前都是用父类做引用类型来使用它的子类的实际类型。即引用类型是TV,用来引用ColorTV、SmartTV。如果我们希望有个集合来存放ColorTV、SmartTV或者其他XXXTV(父类为TV)等等。那么就可以用TV做参数来存放TV及TV的派生类。
我们来设计一个TV类:http://www.cnblogs.com/jsgnadsj
- class TVarylist
- {
- private static int length;
- private TV[] arytv = new TV[10];
- public void add(TV t)
- {
- arytv[length++] = t;
- }
- public TV get(int idx)
- {
- return arytv[idx];
- }
- }
通过这样的调用:
- public static void main(String[] args) {
- // TODO Auto-generated method stub
- SmartTV stv = new SmartTV();
- ColorTV ctv = new ColorTV();
- TVarylist ary = new TVarylist();
- ary.add(ctv);
- ary.add(stv);
- System.out.println(ary.get(0).getClass().getSimpleName());
- System.out.println(ary.get(1).getClass().getSimpleName());
- }
最后显示:
TV集合类的设计如下:
- class TVarylist
- {
- private static int length;
- private TV[] arytv = new TV[10];
- public void add(TV t)
- {
- arytv[length++] = t;
- }
- public TV get(int idx)
- {
- return arytv[idx];
- }
- }
这是一个简单的集合类设计,最大能够存放10个。
做一个大胆的假设,TV类也是我们自己定义的一个类,要是有个类是所有类(包括我们自己定义出来的类)的父类,那么多态的功能将会大大增强。如果这样,我们可以制作一个集合类,可以将所有元素通过总父类引用进去。
Java还真有这样的一个类:Object
Object是所有类的父类,同样也是你自己定义类的父类。(原因可以查书)
Java类中已经有做好的集合类,ArrayList类。
比如我们可以定义一个TV的集合类:ArrayList<TV>,刚才说过,有一个所有类的父类——Object类,那么一定可以用ArrayList<Object>来存放Java中所有的类。
但是需要注意的是,
明明是按SmartTV类型的stv存入,那么我也应该用SmartTV类型的引用类型来获取呀?
对于这样的问题,我们要知道当我们将集合类<Object>以这样的形式声明的时候,无论什么都将用Object类引用,所以当我们get的时候,获取的引用对象也应该是Object。而要想获得原先的类型SmartTV,就应该用原类型强制转化一下。
- public static void main(String[] args) {
- // TODO Auto-generated method stub
- SmartTV stv = new SmartTV();
- ColorTV ctv = new ColorTV();
- ArrayList<Object> aryobj = new ArrayList<Object>();
- aryobj.add(ctv);
- aryobj.add(stv);
- SmartTV rstv = (SmartTV)aryobj.get(1);
- rstv.function();
- }
总结
所谓多态,就是父类型的引用可以指向子类型的对象,或者接口类型的引用可以指向实现该接口的类的实例。
接口
我希望给 Television 和 PC 增加usb功能,如何实现?
可以在RoomElecEquitments类中添加一个usb的抽象方法,在Television 和PC中实现就行了,但是这样Light和AirCondition不是也得实现这个usb抽象方法了,有人给出方案,可以通过写空代码实现。如果对ElectricCooker也添加usb功能,那么是不是就要在ElecEequipment添加一个抽象的usb方法,那不得每一个电器都要去实现这个usb方法!!!不能忍受。http://www.cnblogs.com/jsgnadsj
Java中提供了一个非常好用的方法来解决这个问题,为类专门指定想要的功能,你缺什么就给你添加什么功能,这样的方法叫接口。
这样就可以指定的为类添加指定的功能了。
Interface 和implements关键字
接口可以理解为抽象的抽象类。也就是说,里面的方法只有抽象方法,需要在继承它的类中实现,相当于只告诉我们,这里有个方法需要你实现,而具体归你管,我只管告诉你。
Interface天生就是abstract,不难理解。用interface声明一个接口。接口类中的方法默认就是abstract,所以可以省略不写。
- interface usb
- {
- public void read();
- public void write();
- }
对电器的代码的TV再继承如下:
- class SmartTeleVision extends Television implements usb
- {
- public void function()
- {
- System.out.println("watch Smart TV!");
- }
- @Override
- public void read() {
- // TODO Auto-generated method stub
- System.out.println("usb read");
- }
- @Override
- public void write() {
- // TODO Auto-generated method stub
- System.out.println("usb write");
- }
- }
调用如下:
- public static void main(String[] args) {
- // TODO Auto-generated method stub
- SmartTeleVision smarttv = new SmartTeleVision();
- smarttv.read();
- smarttv.write();
- }
显示如下:
以上基本是Java的面对对象思想的一个缩影,写出来是为了能够更好的接触java,本文不能完全覆盖所有内容。文中错误还请各位看管指出,谢谢。http://www.cnblogs.com/jsgnadsj