zoukankan      html  css  js  c++  java
  • Java学习——面对对象的思想入门

         

    本文是看过《head first Java》之后的一点感悟,写点东西帮忙以后回忆,Java目前在我的工作中用到还不多,而我又对面对对象的编程非常的感兴趣。曾经在MFC平台上写过一个比较大的程序,但是看了本书后,发现之前程序中漏洞百出,而且对面对对象的思想理解不深刻,感觉需要重新学习一遍。C++和JAVA的面对对象还是很有差距的,但是他们的基本思想是相同,抓住思想再学习语言会更高效。http://www.cnblogs.com/jsgnadsj

     

    面向过程编程语言的局限:

     

    大家最熟悉的应该是C语言编程,它是一个面向过程的编程语言。我曾经做过模型车,用主控芯片去控制四个轮子转动,用C语言的时候,只要控制轮子正反转就能控制小车方向。http://www.cnblogs.com/jsgnadsj

    电机正转与反转:

    1. void motorFrontLeft(int sw)
    2. {
    3.    switch{
    4.    case 1:clockwise_rotation();
    5.    case 2:counter_clockwise_rotation();
    6.    }
    7. }

    同理其他电机:FrontRight、BackLeft、BackRight。

    clockwise_rotation() 函数是低层硬件操作,实质上就是电池正负极连接到电机两端的方式。正接的时候正转,反着接就反着转。

     

    这里补充一点:并不是说C语言一定是面向过程而不能编写面向对象的程序,这是误解,C同样也能做面向对象编程。面向过程和面向对象是编程的一种思想,C++是在C的基础上发展而成,为了更好的利用面向对象的思想。

     

    通过前进函数调用上面的电机函数就能够控制小车前进、后退、左转、右转动作了。

    前进动作:

    1. void RunFront(int sw)
    2. {
    3.    motorFrontLeft(1);
    4.    motorFrontRight(1);
    5.    motorBackLeft(1);
    6.    motorBackRight(1);
    7. }

    四个轮子同时正转,小车就前进了。

    这就是面对过程思想编程,通过编写过程代码,来控制。

    如果需要我们增加一些功能,或者修改一些功能的时候,麻烦来了。比如前进动作,希望能够控制其前进速度,那么我们要在RunFront()的函数参数中增加速度这个参数项,不仅如此,还要将此速度值传递给RunFront中调用的motorXX函数。这还没结束,真正控制轮子转速的是电机,所以还要修改motorXX里面代码,为其增加可以控制转速的功能。

    四个轮子需要修改四个函数,如果这是一个蜈蚣车(只是说明需要的轮子多),那得修改多少。

    面对对象思想的就是希望能够比较好的解决这类问题。

     

    面对对象语言的优势:

     

    面对对象思想主要有三个大的核心思想:封装,继承,多态。

    我们可以看到,修改工作量大是因为我们对每个轮子控制函数要进行修改,也就是修改重复代码,这样会增加出现的概率。想想,四个电机其实质是什么?就是电机!除了安放在不同的位置,其他什么都一样的。理想状态下,通过同样的设置就能够得到同样的效果。比如接线方式是一样则正转,电压一样则转速一致。对于这样的有着相同属性和行为的物体,我们可以把他们统称为一个类(Class)。http://www.cnblogs.com/jsgnadsj

    1. class Motor{
    2.    int rotate;//方向
    3.    int v; //速度
    4.    void run(int rotate){
    5.       //
    6.       switch (dir){
    7.  
    8.       case 1://正转
    9.  
    10.       case 2://反转
    11.       }
    12.    }
    13.    void stop(){
    14.  
    15.    }
    16. }

    Motor就是我们构造出来的类。这就是我们需要的电机的总称。

    如何使用类?下面讲对象。

     

    对象

     

    类是一类事物的抽象描述。比如我们说的电机,它不是虚无飘渺凭空出现的事物。我们首先有它的设计图,然后根据设计图创造出它的实物。设计图就是类,创造出来的实物就是对象。这就是类与对象关系。类是抽象的(设计图),对象是具体的(电机实物)。

    电动车的四个电机的构造:

    1. Motor motorFL = new Motor();
    2. Motor motorFR = new Motor();
    3. Motor motorBL = new Motor();
    4. 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所指向的对象,如下图:

    1. motorFL = motorFR;

    这样的操作之后,相当于两个遥控器可以控制同一个对象,那么 那个没有遥控器的对象怎么办呢?

    我们无论如何都不会再指向它了,所以它会被Java的回收机制在适当的时候删除。(适当这个词好深奥!!!)

    这里将引用对象和实际对象非常简单的说了下,这里只是入门而已,具体还是要看书,比如它们存放的区域就不同等等。

     

     

    封装

     

    我们有电机的设计图,但是我们不希望别人看到这个设计图,这就是封装。之前电机,我们通过类能够快速创造出4个电机对象,说明该类是能够重复利用的;比如我们家里的电视机,我们只需要怎么用,不必关心它是怎么做出来的;还有将设计图中某些东西给隐藏起来,这样的好处就是安全。这些都是封装的好处。

    说道封装的安全性,我们就要提到四个关键字:Public、private、defalut、protected。什么是安全,就是某些事物是否有权限。比如:你爸爸可以进入你家,因为你爸爸有权限,而坏人呢,坏人没有进入你家权限,所以他是危险的。刚才提到的四个关键字就是四个等级的权限(具体参考书籍),这样理解就好了。

    修改Motor类,使其添加明确的权限功能:http://www.cnblogs.com/jsgnadsj

    1. class Motor{
    2.    private int rotate;//方向
    3.    private int v; //速度
    4.    public void run(int rotate){
    5.       //
    6.       switch (rotate){
    7.  
    8.       case 1://正转
    9.  
    10.       case 2://反转
    11.       }
    12.    }
    13.    public void stop(){
    14.  
    15.    }
    16. }

    属性rotate用private修饰,而方法run用public修饰。这就是刚才我们提到的电视,不关心里面构造(rotate),而只要知道怎么用(run)。

     

    Static 关键字:

     

    Static的变量:

    如果我们希望统计一个类被创造出多少个对象,在C语言中,我们可以这样统计一个方法被调用次数:

    1. void func()
    2. {
    3.    static time;
    4.    time++;
    5. }

    所以在Java中也可以用Static来帮助统计对象个数。

    在类中定义一个统计个数的变量,然后在构造函数中自增。

    1. class Motor{
    2.    private int time;
    3.    private int rotate;//方向
    4.    private int v; //速度
    5.  
    6.    public Motor()
    7.    {
    8.       time++;
    9.    }
    10.    public void run(int rotate){
    11.       //
    12.       switch (rotate){
    13.  
    14.       case 1://正转
    15.  
    16.       case 2://反转
    17.       }
    18.    }
    19.    public void stop(){
    20.  
    21.    }
    22. }

    这样每次创建一个Moter对象的时候,time都会自增,就可以统计对象个数了。

     

    Static的方法:

    在Java里面最熟悉的应该是Math里面的静态方法,比如Math().abs()…

    对于abs()方法,它是一个静态方法,允许不创建对象而调用静态方法,是Java为了减少程序员调用某些常用方法时的麻烦,而允许程序员按照传统的C语言中使用函数的方式来使用方法。可以这样调用:Math().abs(-1);而不需要先创建Math对象,再调用方法。

     

    Static类:

    一般情况下是不可以用static修饰类的。如果一定要用static修饰类的话,通常static修饰的是匿名内部类。

    在一个类中创建另外一个类,叫做成员内部类。这个成员内部类可以静态的(利用static关键字修饰),也可以是非静态的。由于静态的内部类在定义、使用的时候会有种种的限制。所以在实际工作中用到的并不多。

    在开发过程中,内部类中使用的最多的还是非静态地成员内部类。不过在特定的情况下,静态内部类也能够发挥其独特的作用。

    具体可以查看相关书籍。

     

     

    继承

     

    为什么需要继承?

    我们以电视机为例,电视机发展过程为:黑白电视机->彩色电视机->液晶电视机->智能电视机…一步一步发展过来的。我们在从黑白电视机发展为彩色电视机的时候,不是完全摒弃了黑白电视机的设计图,因为黑白电视机某些属性和性质(比如电源系统)都还能够继续为彩色电视机使用,不需要再设计,所以仅仅需要修改显示结构即可。

    继承就起到这样的作用,可以减少代码重复量,只需要修改或者增加新属性和行为就可以得到一个新的类。http://www.cnblogs.com/jsgnadsj

     

     

    写一个电视机类:

    普通电视机类

    1. class TeleVision
    2. {
    3.    private int voltage;//电压
    4.    private int channel;//频道
    5.  
    6.    public void show(){
    7.       System.out.println("television!");
    8.    }
    9.  
    10.    public int getVoltage() {
    11.       return voltage;
    12.    }
    13.    public void setVoltage(int voltage) {
    14.       this.voltage = voltage;
    15.    }
    16.    public int getChannel() {
    17.       return channel;
    18.    }
    19.    public void setChannel(int channel) {
    20.       this.channel = channel;
    21.    }
    22. }

    现在我继承这个类,继承后的类是黑白电视机,修改show方法即可。

    黑白电视机类:

    1. class BlackWhiteTV extends TeleVision
    2. {
    3.    public void show()
    4.    {
    5.       System.out.println("Black White TV!");
    6.    }
    7.  
    8. }

    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方法时候,如下:

    1. class BlackWhiteTV extends TeleVision
    2. {
    3.    public void show()
    4.    {
    5.       System.out.println("Black White TV!");
    6.    }
    7.    public void show(int i)
    8.    {
    9.       System.out.printf("%d Black White TV! ",i);
    10.    }
    11.  
    12. }

    这就是重载,为什么会有这样的设计?

    比如我们现在的电视机,希望它既可以显示黑白(比较怀旧),又能够显示彩色,那么我们肯定不希望这样写函数:showBlackWhite(),showColorful()。假如还有什么2D,3D,4D,5D等等,那么我们对于一个对象要写很多个不同函数,使用的时候还要记忆具体函数名,虽然函数名上可以取得比较相似,但是还是有点复杂。再比如我们常见的函数abs求绝对值的函数,java中的数据类型有int、float、double等等,我们肯定不希望函数写成这样:absInt(int i)、absFloat(float f)、absDouble(double d)。我们希望用一个函数名能够实现接收多个不同类型参数,简化代码,所以重载就提供了这样的方法。

    我们用同样的函数名,接收不同类型的参数就能够实现一个函数名具有不同的功能,重载的意义重大。

    重载需要注意:

    1. 在使用重载时只能通过不同的参数样式。例如,不同的参数类型,不同的参数个数,不同的参数顺序(当然,同一方法内的几个参数类型必须不一样,例如可以是fun(int,float),但是不能为fun(int,int));
    2. 不能通过访问权限、返回类型、抛出的异常进行重载;
    3. 方法的异常类型和数目不会对重载造成影响;
    4. 对于继承来说,如果某一方法在父类中是访问权限是priavte,那么就不能在子类对其进行重载,如果定义的话,也只是定义了一个新方法,而不会达到重载的效果。

     

    继承时候类中方法调用:

    如下继承图

    1. SmartTV stv = new SmartTV();
    2. stv.function();

    stv.function()调用的是哪个类中的方法?

    如果在SmartTelevision中覆盖了function方法,则调用SmartTelevision中的,反之,则调用ColorTelevision中的;若ColorTelevision中也没有覆盖function方法,则调用Television中的方法。

    对于ColorTelevision中的function完全按照(返回类型、参数类型、方法名)Television中的function来写,这叫做覆盖

     

     

    this 关键字

    为什么需要this关键字?

    比如一下这种情况:

    1. class BlackWhiteTV extends TeleVision
    2. {
    3.    private int i;
    4.    public void show()
    5.    {
    6.       System.out.println("Black White TV!");
    7.    }
    8.    public void show(int i)
    9.    {
    10.       System.out.printf("%d Black White TV! ",i);
    11.       System.out.printf("%d Black White TV! ",this.i);
    12.    }
    13.  
    14. }

    BlackWhiteTV类中有类属性i,同时show方法的参数也是i,此时如何区分这两个i?

    当一个对象创建后,Java虚拟机(JVM)就会给这个对象分配一个引用自身的指针,这个指针的名字就是 this。所以我们可以用this.i来指代类中属性i。

     

    如下情况:

    1. class BlackWhiteTV extends TeleVision
    2. {
    3.    private int i;
    4.    BlackWhiteTV()
    5.    {
    6.       System.out.println("blackwhiteTV create!");
    7.    }
    8.    BlackWhiteTV(int i)
    9.    {
    10.       this();
    11.       System.out.println("the parament i:"+i);
    12.    }
    13.    public void show()
    14.    {
    15.       System.out.println("Black White TV!");
    16.    }
    17.    public void show(int i)
    18.    {
    19.       System.out.printf("%d Black White TV! ",i);
    20.       System.out.printf("%d Black White TV! ",this.i);
    21.    }
    22.  
    23. }

     

    如果想要BlackWhiteTV(int i)首先调用BlackWhiteTV()再调用自己的函数,这个时候用this()就表示该类的无参数构造函数,如果带参数this(x),这样就是调用类的含有参数的构造函数。

     

    注意:

    • 做区分:函数参数或者函数中的局部变量和成员变量同名的情况下,成员变量被屏蔽,此时要访问成员变量则需要用"this.成员变量名"的方式来引用成员变量。
    • 调用自身类某些方法:通过this调用另一个构造方法,用发是this(参数列表),这个仅仅在类的构造方法中,别的地方不能这么用。
    • 还有一个地方用到this:在函数中,需要引用该函所属类的当前对象时候,直接用this。

     

    super 关键字

     

    如果我们希望子类方法不是完全覆盖父类方法,而是希望先调用父类方法,再调用自身的函数,

    例子如下:

    1. class TeleVision
    2. {
    3.    public void show(){
    4.       System.out.println("television!");
    5.    }
    6. }
    7.  
    8. class BlackWhiteTV extends TeleVision
    9. {
    10.    public void show()
    11.    {
    12.       super.show();
    13.       System.out.println("Black White TV!");
    14.    }
    15.  
    16. }

    这样会显示:

    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修饰:

    类成员变量被修饰的时候需要注意:

    1. 显示的初始化,
    2. 只能初始化一次,
    3. 在声明或者构造函数中初始化。

     

    对于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 关键字

     

    1. class TeleVision
    2. {
    3.    private int voltage;//电压
    4.    private int channel;//频道
    5.  
    6.    public void show(){
    7.       System.out.println("television!");
    8.    }
    9.  
    10.    public int getVoltage() {
    11.       return voltage;
    12.    }
    13.    public void setVoltage(int voltage) {
    14.       this.voltage = voltage;
    15.    }
    16.    public int getChannel() {
    17.       return channel;
    18.    }
    19.    public void setChannel(int channel) {
    20.       this.channel = channel;
    21.    }
    22. }
    23.  
    24. class BlackWhiteTV extends TeleVision
    25. {
    26.    public void show()
    27.    {
    28.       System.out.println("Black White CRT TV!");
    29.    }
    30. }
    31.  
    32. class ColorfulTV extends BlackWhiteTV
    33. {
    34.    public void show()
    35.    {
    36.       System.out.println("Colorful CRT TV!");
    37.    }
    38. }

    我们现在的代码是这样,有没有感觉到TeleVision里面show方法有点多余?因为我们后面的黑白电视,彩色电视都没有用到TeleVision的show()方法,而是直接将其覆盖,实现了它们自己的方法。

    然后我们再思考,电压和频道的get和set对TeleVision都没有用,因为我们没有创造出TeleVision的实例,因为它是我们脑子中蓝图,是一个虚拟的,虚拟的电视机我们不需要去获取他的属性数据。

    这样我们希望能够有一种方法,只给这个类保留类的方法名称,也就是说继承该类的所以子类都有这样的方法,但是各个子类的实现却不一样,各自为政。这样既可以保证该类确实有这样的方法,同时也能让程序员灵活的实现。

    抽象类发挥巨大作用!http://www.cnblogs.com/jsgnadsj

    原则:

    • 必须用public abstract修饰抽象方法,private ,static 都不能用于修饰
    • 包含抽象方法的类必须是抽象类,也就是说,只要类中有抽象方法则该类一定是抽象类
    • 抽象类可以不含抽象方法

     

    1. abstract class TeleVision
    2. {
    3.    private int voltage = 220;//电压
    4.    private int channel;//频道
    5.  
    6.    abstract public void show();
    7.  
    8.    public int getVoltage() {
    9.       return voltage;
    10.    }
    11.    public int getChannel() {
    12.       return channel;
    13.    }
    14.    public void setChannel(int channel) {
    15.       this.channel = channel;
    16.    }
    17. }

     

    黑白电视机:

    1. class BlackWhiteTV extends TeleVision
    2. {
    3.    public void show()
    4.    {
    5.       System.out.println("Black White CRT TV!");
    6.    }
    7. }

    此类中必须实现TeleVision的show(),否则编译器报错。我们越来越能够抽象电视机这样的事物了,电视机不就是能够放映的事物嘛!只要有show()即可,而具体实现看具体继承的类了。比如:黑白电视的show黑白,彩色电视的show彩色。

     

    我们重新调整一下类的继承关系,因为彩色电视机不是从黑白电视机继承的,彩色电视机也可以从电视机继承来。

     

    对于这张继承图,电器是最顶层的,电视机不就是一个电器设备,这样就将电视机类更加抽象了。

    电器类:

    1. abstract class ElecEequitment
    2. {
    3.    private final int voltage = 220; //额定电压
    4.  
    5.    public int getVoltage()
    6.    {
    7.       return this.voltage;
    8.    }
    9.  
    10.    abstract public void function(); //功能
    11.    abstract public int getCstmPower(); //获取消耗电量
    12. }

     

    厨房电器类:

    1. abstract class CookElecEquitments extends ElecEequitment
    2. {
    3.    abstract public void ResistWater();
    4. }

    在原有的基础上,增加了 防水功能。

     

    客房电器类:

    1. abstract class RoomElecEquitments extends ElecEequitment
    2. {
    3.    abstract public void energySavingStandard(int i);
    4. }

    在原有的基础上,增加了 "节能标准"。

     

    继承抽象类的类不仅可以是普通类,同样也能够时抽象类。

     

     

    多态:

     

    上面的电器家组图的UML图如下:

    抽象类代码在上面!

    1. class MicrowaveOven extends CookElecEquitments
    2. {
    3.    public void function()
    4.    {
    5.       System.out.println("Microwave Oven!");
    6.    }
    7.    public int getCstmPower()
    8.    {
    9.       return 0;
    10.    }
    11.  
    12.    public void ResistWater()
    13.    {
    14.  
    15.    }
    16. }
    17. class ElectricCooker extends CookElecEquitments
    18. {
    19.    public void function()
    20.    {
    21.       System.out.println("Electric Cooker!");
    22.    }
    23.    public int getCstmPower()
    24.    {
    25.       return 0;
    26.    }
    27.  
    28.    public void ResistWater()
    29.    {
    30.  
    31.    }
    32. }
    33. class Light extends RoomElecEquitments
    34. {
    35.    public void function()
    36.    {
    37.       System.out.println("Light!");
    38.    }
    39.    public int getCstmPower()
    40.    {
    41.       return 0;
    42.    }
    43.  
    44.    public void energySavingStandard(int i)
    45.    {
    46.       System.out.println("the standard is"+i);
    47.    }
    48. }
    49. class Television extends RoomElecEquitments
    50. {
    51.    public void function()
    52.    {
    53.       System.out.println("watch TV!");
    54.    }
    55.    public int getCstmPower()
    56.    {
    57.       return 0;
    58.    }
    59.  
    60.    public void energySavingStandard(int i)
    61.    {
    62.       System.out.println("the standard is"+i);
    63.    }
    64. }
    65.  
    66. class PC extends RoomElecEquitments
    67. {
    68.    public void function()
    69.    {
    70.       System.out.println("PC!");
    71.    }
    72.    public int getCstmPower()
    73.    {
    74.       return 0;
    75.    }
    76.  
    77.    public void energySavingStandard(int i)
    78.    {
    79.       System.out.println("the standard is"+i);
    80.    }
    81. }
    82.  
    83. class AirCondition extends RoomElecEquitments
    84. {
    85.    public void function()
    86.    {
    87.       System.out.println("Air Condition!");
    88.    }
    89.    public int getCstmPower()
    90.    {
    91.       return 0;
    92.    }
    93.  
    94.    public void energySavingStandard(int i)
    95.    {
    96.       System.out.println("the standard is"+i);
    97.    }
    98. }

     

     

    假设这里我们需要频繁的调用最下面的类,那么我们需要有6个不同类型的引用,比如:

    我们要声明六个不同类的引用

    1. MicrowaveOven mo = new MicrowaveOven();
    2. ElectricCooker ec = new ElectricCooker();
    3. Light lt = new Light();
    4. Television tv = new Television();
    5. PC pc = new PC();
    6. AirCondition ac = new AirCondition();

    然后调用的时候,分别调用,对PC就要用pc,对Television就要用tv,比如他们都有一个function()功能,那么调用的时候需要这样:

    1. mo.function();
    2. ec.function();
    3. lt.function();
    4. tv.function();
    5. pc.function();
    6. ac.function();

    有没有感觉很麻烦?它们都有相同的方法,却要一个一个通过不同引用类型调用。

    麻烦的原因是:引用类型和对象类型必须相符才能正确调用。

     

    应用1:引用类型是实际类型的父类(或间接父类):

    我们看看UML图,最后的6个对象怎么都是有点联系的,既然有联系能不能通过什么方法来解决这样的问题呢?

    他们有一个共同的父类(间接),我们能不能利用这个特性?

    如下代码:

    1. ElecEequitment[] eq = new ElecEequitment[6];
    2.  
    3. eq[0] = new MicrowaveOven();
    4. eq[1] = new ElectricCooker();
    5. eq[2] = new Light();
    6. eq[3] = new Television();
    7. eq[4] = new PC();
    8. eq[5] = new AirCondition();
    9.  
    10. int i;
    11. for(i = 0 ; i < 6 ; i++)
    12. {
    13.    eq[i].function();
    14. }

    第一句代码:创建6个父类引用!注意这里的是 new ElecEequitment[6] 而不是 new ElecEequitment()[6]。并没有创造6个ElecEequitment对象。

    下面的for循环代码中,通过他们共同的父类(间接)的引用来调用,这样方便了很多。

    同样值得注意,第一句话中的引用变量类不一定是abstract,也可以是普通类。

     

    同样,这样的方法也可以用于传递函数的参数或者返回类型。http://www.cnblogs.com/jsgnadsj

     

    应用2:参数类型与返回类型的多态

     

    比如我增加一个方法,用于打开电器,如果没有多态,那么我需要对每一个电器都需要写一个打开方法的函数。如果增加了新的电器,那么又要改写代码。——多态就能够轻松解决这样的问题

    1. class opt
    2. {
    3.    public void powerOn(XXX xxx)
    4.    {
    5.  
    6.    }
    7. }

    上述代码中,XXX是已有的电器类(TV、PC…),xxx则是对应的引用。

    应用多态就能够很好的解决,XXX定义为父类,这样就很好的解决了。

    1. class opt
    2. {
    3.    public void powerOn(ElecEequitment eq)
    4.    {
    5.       System.out.println(eq.getClass().getSimpleName()+"power On");
    6.    }
    7. }
    8. public class Elec_equipmentTest {
    9.    public void powerOn(ElecEequitment eq)
    10.    {
    11.       System.out.println(eq.getClass().getSimpleName()+"power On");
    12.    }
    13.    public static void main(String[] args) {
    14.       // TODO Auto-generated method stub
    15.       ElecEequitment[] eq = new ElecEequitment[6];
    16.  
    17.       eq[0] = new MicrowaveOven();
    18.       eq[1] = new ElectricCooker();
    19.       eq[2] = new Light();
    20.       eq[3] = new Television();
    21.       eq[4] = new PC();
    22.       eq[5] = new AirCondition();
    23.  
    24.       opt o = new opt();
    25.       for (int j = 0; j < eq.length; j++) {
    26.          o.powerOn(eq[j]);
    27.       }
    28.    }
    29.  
    30. }

    输出:

     

    • 注意一种情况,父类中如果有static 修饰的方法的时候。由于静态方法只能继承,不能重载,那么如果子类中定义了同名同形式的静态方法,它对父类方法只起到隐藏的作用。调用的时候用谁的引用,则调用谁的版本。http://www.cnblogs.com/jsgnadsj

     

    重载

    多态对于方法的作用,之前讲过的重载!

     

    再说关键字 abstract

    之前说过,创建引用对象的时候是new ElecEequitment[6] 而不是 new ElecEequitment()[6]。

    1. 假如我们不小心写成了后面那种样子,没有abstract修饰ElecEequitment()会被创造出来,但是电器类是什么样子?可能是电视机,可能是电脑、可能是微波炉,对它应该是一个变形金刚。想要他是什么,它就应该能够变成什么,所以这里添加了abstract修饰,这样电器类就不会被构造出来。
    2. Abstract的方法必须在继承具体类中全部实现。如果某个abstract方法没有实现,那么就会有包含这个方法的类就应该是abstract类。

     

    类型转化

    1. class TV
    2. {
    3.    private int voltage;
    4.    public int getVoltage()
    5.    {
    6.       return voltage;
    7.    }
    8.    public void function()
    9.    {
    10.       System.out.println("Watch TV");
    11.    }
    12.    public int getCstmPower()
    13.    {
    14.       return 10;
    15.    }
    16. }
    17.  
    18. class ColorTV extends TV
    19. {
    20.    public void showcolor()
    21.    {
    22.       System.out.println("ColorTV: colorful");
    23.    }
    24.    public void function()
    25.    {
    26.       System.out.println("Watch Color TV!");
    27.    }
    28. }
    29.  
    30. class SmartTV extends ColorTV
    31. {
    32.    public void onInternet()
    33.    {
    34.       System.out.println("SmartTV: surf the Internet");
    35.    }
    36.    public void function()
    37.    {
    38.       System.out.println("Watch Smart TV");
    39.    }
    40. }

     

    之前都是用父类做引用类型来使用它的子类的实际类型。即引用类型是TV,用来引用ColorTV、SmartTV。如果我们希望有个集合来存放ColorTV、SmartTV或者其他XXXTV(父类为TV)等等。那么就可以用TV做参数来存放TV及TV的派生类。

    我们来设计一个TV类:http://www.cnblogs.com/jsgnadsj

    1. class TVarylist
    2. {
    3.    private static int length;
    4.    private TV[] arytv = new TV[10];
    5.  
    6.    public void add(TV t)
    7.    {
    8.       arytv[length++] = t;
    9.    }
    10.    public TV get(int idx)
    11.    {
    12.       return arytv[idx];
    13.    }
    14. }

    通过这样的调用:

    1. public static void main(String[] args) {
    2.       // TODO Auto-generated method stub
    3.       SmartTV stv = new SmartTV();
    4.       ColorTV ctv = new ColorTV();
    5.  
    6.       TVarylist ary = new TVarylist();
    7.       ary.add(ctv);
    8.       ary.add(stv);
    9.       System.out.println(ary.get(0).getClass().getSimpleName());
    10.       System.out.println(ary.get(1).getClass().getSimpleName());
    11.  
    12.    }

    最后显示:

    TV集合类的设计如下:

    1. class TVarylist
    2. {
    3.    private static int length;
    4.    private TV[] arytv = new TV[10];
    5.  
    6.    public void add(TV t)
    7.    {
    8.       arytv[length++] = t;
    9.    }
    10.    public TV get(int idx)
    11.    {
    12.       return arytv[idx];
    13.    }
    14. }

    这是一个简单的集合类设计,最大能够存放10个。

    做一个大胆的假设,TV类也是我们自己定义的一个类,要是有个类是所有类(包括我们自己定义出来的类)的父类,那么多态的功能将会大大增强。如果这样,我们可以制作一个集合类,可以将所有元素通过总父类引用进去。

    Java还真有这样的一个类:Object

    Object是所有类的父类,同样也是你自己定义类的父类。(原因可以查书)

    Java类中已经有做好的集合类,ArrayList类。

    比如我们可以定义一个TV的集合类:ArrayList<TV>,刚才说过,有一个所有类的父类——Object类,那么一定可以用ArrayList<Object>来存放Java中所有的类。

    但是需要注意的是,

    明明是按SmartTV类型的stv存入,那么我也应该用SmartTV类型的引用类型来获取呀?

    对于这样的问题,我们要知道当我们将集合类<Object>以这样的形式声明的时候,无论什么都将用Object类引用,所以当我们get的时候,获取的引用对象也应该是Object。而要想获得原先的类型SmartTV,就应该用原类型强制转化一下。

    1. public static void main(String[] args) {
    2.       // TODO Auto-generated method stub
    3.       SmartTV stv = new SmartTV();
    4.       ColorTV ctv = new ColorTV();
    5.  
    6.  
    7.       ArrayList<Object> aryobj = new ArrayList<Object>();
    8.       aryobj.add(ctv);
    9.       aryobj.add(stv);
    10.  
    11.       SmartTV rstv = (SmartTV)aryobj.get(1);
    12.       rstv.function();
    13.    }

     

    总结

    所谓多态,就是父类型的引用可以指向子类型的对象,或者接口类型的引用可以指向实现该接口的类的实例。

     

    接口

    我希望给 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,所以可以省略不写。

    1. interface usb
    2. {
    3.    public void read();
    4.    public void write();
    5. }

     

    对电器的代码的TV再继承如下:

    1. class SmartTeleVision extends Television implements usb
    2. {
    3.    public void function()
    4.    {
    5.       System.out.println("watch Smart TV!");
    6.    }
    7.  
    8.    @Override
    9.    public void read() {
    10.       // TODO Auto-generated method stub
    11.       System.out.println("usb read");
    12.    }
    13.  
    14.    @Override
    15.    public void write() {
    16.       // TODO Auto-generated method stub
    17.       System.out.println("usb write");
    18.    }
    19.  
    20. }

    调用如下:

    1. public static void main(String[] args) {
    2.    // TODO Auto-generated method stub
    3.    SmartTeleVision smarttv = new SmartTeleVision();
    4.  
    5.    smarttv.read();
    6.    smarttv.write();
    7. }

    显示如下:

     

    以上基本是Java的面对对象思想的一个缩影,写出来是为了能够更好的接触java,本文不能完全覆盖所有内容。文中错误还请各位看管指出,谢谢。http://www.cnblogs.com/jsgnadsj

  • 相关阅读:
    python运算符
    CocoChina开发者大会
    iphone模拟器上不能安装从itunes下载的app
    Objective-C学习--源文件结构
    Objective-C学习笔记--复合
    Objective-C学习笔记--继承
    Objective-C学习笔记--实例化对象
    Objective-C学习笔记--@implementation
    Objective-C学习笔记--@interface
    C语言函数原型
  • 原文地址:https://www.cnblogs.com/jsgnadsj/p/3493724.html
Copyright © 2011-2022 走看看