一、面向对象的三大特性(深入理解)

Java编程中,面向对象是最基础的编程思想,面向对象的三大特性!——封装性、继承性、多态性


注意:为了让更多人理解,故提高深入部分放在每节的问题中,高手直接看问题!

1)面向对象

定义

面向对象是指以对象的方式对现实世界的理解和抽象的方法。

官方解释确实实在难以理解,其实简单来,面对象就是对物体的抽象。举个栗子:我现在要去水果店买一袋苹果。那么面向对象与之不同的就是将对象抽取出来,在例子中我们的对象就是 我、水果店、苹果。我们的业务是 去水果店 和 买 两个动作,最终我们得出的例子是这样的:

实例

我:
package com.itaem;

/**
 * 我
 *
 * @author wu-shangsen
 * @version 1.0, 2016/9/22
 */
public class My {
}
水果店:
package com.itaem;

/**
 * 水果店
 *
 * @author wu-shangsen
 * @version 1.0, 2016/9/22
 */
public class Shop {
}
苹果:
package com.itaem;

/**
 * 苹果
 *
 * @author wu-shangsen
 * @version 1.0, 2016/9/22
 */
public class Apple {
}
逛街服务:
package com.itaem;

/**
 * 逛街服务
 *
 * @author wu-shangsen
 * @version 1.0, 2016/9/22
 */
public class ShoppingSevrice {

    /**
     * 去商店
     */
    public void goToShop() {
        //具体逻辑
    }

    /**
     * 买水果
     */
    public void buyApple() {
        //具体逻辑
    }

    public static void main(String[] args) {
        //初始化逛街服务
        ShoppingSevrice shoppingService = new ShoppingSevrice();
        //去水果店
        shoppingService.goToShop();
        //买苹果
        shoppingService.buyApple();
    }
}
从对象的角度分析。对于我去买苹果这样一个场景,将每个对象抽象成一个类,如 我(My),苹果(Apple),水果店(Shop)。其实面向对象就是这么简单,大家都懂了吧?基于这个基础,下面就开始三大特性了。




2)封装性

定义

封装性即意味这对象封装其内部的数据成员,使其对外不可见,以保证数据的安全性。

好处

提高数据安全性,有利于代码管理。

其实如果理解了封装性的话,那么就会觉得上面这句话很精辟很容易理解,说出来很专业。还是不多说废话,直接上代码(对着代码看上面封装性的定义):


实例

我:
package com.itaem;

/**
 * 我
 *
 * @author wu-shangsen
 * @version 1.0, 2016/9/22
 */
public class My {

    private String name ; //姓名(数据成员)

    private int age ; //年龄(数据成员)

    /**
     * 想获取name的值只能通过getName方法获取
     * @return
     */
    public String getName() {
        return name;
    }

    /**
     * 想改变name的值只能通过setName方法改变
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    //与getName相同
    public int getAge() {
        return age;
    }

    //与setName相同
    public void setAge(int age) {
        this.age = age;
    }
}
服务:
package com.itaem;

/**
 * 逛街服务
 *
 * @author wu-shangsen
 * @version 1.0, 2016/9/22
 */
public class ShoppingSevrice {

    public static void main(String[] args) {
        //封装性
        My my = new My() ;
        System.out.println(my.name) ; //编译不通过,不能直接调用数据成员name

        my.setName("AAA"); //改变name
        System.out.println(my.getName()) ; //获取name

        my.setName("BBB"); //改变name
        System.out.println(my.getName()); //再次获取name
    }
}

以上代码的流程:

①当改变name的值,都只是调用了my里面的setName()方法,没有直接改变name的值;

②而是把"AAA"值传入setName()而已,

③而真正直接改变name的值是在My类的setName()方法的this.name=name。

getName()原理相同。也就是说,ShoppingService不知道my里面有name这个数据成员,只是调用方法间接改变My的数据成员值而已。回归定义——封装性即意味这对象封装其内部的数据成员,使其对外不可见,以保证数据的安全性。是不是觉得这句话很精辟!好了,想必大家理解了封装性,那么通过问题来加深理解和深入提高了。



问题一:为什么要实现setter?

解答:封装的概念就是 自己的数据成员仅对自己可见,只有自己才能改变其值。
           所以setter方法就是为了改变数据成员而诞生的。

问题二:为什么要实现getter?

解答:因为数据成员用了private进行对外不可见,所以用getter。


问题三:getter方法使用的注意点?

解答:getter方法返回对象引用,返回可变对象的引用则破环了封装性。

方案:返回引用可变对象可采用克隆。 如:日期(Date),数组(String[]),集合(List),键值对(Map)等

3)继承性 

定义

继承是从已有的类中派生出新的类,新的类能吸收已有类的数据属性和行为,并能扩展新的能力。


好处

提高代码复用。


使用形式

向上转型,向下转型(向下转型一般不会使用也不要去使用)


继承衍生了两个名词,一个叫父类(也叫超类),一个叫子类,父类就是要被继承的类,子类就是使用继承的类。 继承就是获取上一代的东西,那么在代码里面也一样。下面举个栗子,我有名字和年龄,儿子继承了我,而且多了一招哭的技能:


实例

我:
package com.itaem;

/**
 * 我
 *
 * @author wu-shangsen
 * @version 1.0, 2016/9/22
 */
public class My {

    private String name ; //姓名(数据成员)

    private int age ; //年龄(数据成员)

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    void nothingName() {
        System.out.println("包域"); //只能在该包com.itaem中调用
    }

    protected void protectedName() {
        System.out.println("protected域"); //只能在父类和子类中调用
    }

    private void privateName() {
        System.out.println("private域"); //只能在本类中调用
    }
}


儿子:
package com.itaem;

/**
 * 儿子
 *
 * @author wu-shangsen
 * @version 1.0, 2016/9/22
 */
public class Children extends My {

    private String cry = "我要哭";

    public String getCry() {
        return cry;
    }

    public void setCry(String cry) {
        this.cry = cry;
    }
}

服务:
package com.itaem;

/**
 * 逛街服务
 *
 * @author wu-shangsen
 * @version 1.0, 2016/9/22
 */
public class ShoppingSevrice {

    public static void main(String[] args) {
        //封装性
        My my = new My() ;
        System.out.println(my.getName());
        System.out.println(my.getAge());
        System.out.println(my.getCry()); //编译时错误,因为我没有哭这招技能


        Children child = new Children() ;
        System.out.println(child.getName());
        System.out.println(child.getAge());
        System.out.println(child.getCry());
        child.nothingName(); //只能在该包com.itaem中被继承
        child.protectedName(); //只能在父类和子类中被继承
        child.privateName(); //编译时错误,private标示符标示的字段或方法是继承不了的
    }
}
简单来说,继承是子类拥有父类的属性、函数等。但是要注意几点,private和final标示的字段或方法是不能被继承调用的,这个要谨记!上面只是增加大家对继承的理解,后面就又到深入提高了。

问题一:什么叫向上转型?向上转型的方法调用?

原理: 子类创建的一个对象引用赋值给其父类变量,叫做向上转型。
①通过父类对象不能操作子类新增的成员变量,不能调用子类新增的方法;
②通过父类对象调用子类重写父类的方法时,调用的是子类重写后的方法;
实例:
package com.itaem;

/**
 * 服务
 *
 * @author wu-shangsen
 * @version 1.0, 2016/9/22
 */
public class ShoppingSevrice {

    public static void main(String[] args) {
        //继承性
        My my = new Children() ; //向下转型  子类对象传给my变量
        System.out.println(my.getName());
        System.out.println(my.getAge());
        System.out.println(my.getCry()); //编译时错误,因为我没有哭这招技能
    }
}
子类创建的一个对象引用赋值给其父类变量,叫做向上转型。这个大家看了代码都很好理解,但是为什么原理(1)中说通过父类对象不能操作子类新增的成员变量,不能调用子类新增的方法呢?我们将引用堆进行讲解:


解释虽然堆中Children对象存在getCry()的信息,但是我们程序调用的是虚拟机栈的my指针,因为my指针中没有存储getCry()方法的引用信息,所以在程序在编译时期调用my.getCry()就会因为找不到getCry()信息报错。所以向下转型的最重要之一就是不能调用子类的新增方法。
另外还有一点是父类对象调用子类重写父类的方法时,调用的是子类重写后的方法。这点可以参照上图,只是虚拟机栈的my变量多了一个getCry()信息,但是实际引用的还是我们堆中的方法Children.getCry()。




问题二什么叫向下转型?向下转型的方法调用?(向下转型一般不要使用

原理指向对应子类对象的引用的父类 强制转换为对应子类后 将该引用赋值给其子类变量,叫做向下转型。
   ①若将父类对象在强制转换为相应子类的对象,该子类对象又具备了子类所有的属性和方法;

实例

package com.itaem;

/**
 * 服务
 *
 * @author wu-shangsen
 * @version 1.0, 2016/9/22
 */
public class ShoppingSevrice {

    public static void main(String[] args) {
        
        /*
         * 错误示范
         */
        My my = new My() ;
        Children children = (Children)my ;             //不安全的向下转型,编译无错但会运行会出错,抛出 java.lang.ClassCastException

        /*
         * 一般示范
         */
        My my2 = new Children() ;  //向上转型
        Children children2 = (Children)my2 ;       //向下转型,编译和运行皆不会出错

        /*
         * 正确师范
         */
        My my3 = new Children() ; //向上转型
        if(my3 instanceof Children) {
            Children children3 = (Children) my3;       //安全的向下转型
        }
    }
}

看得是不是有点似懂非懂的感觉?没关系,我们继续借助虚拟机堆栈分析,上图:


解释原理其实跟向下转型差不多,也就是说在虚拟机栈Children栈帧中调用getCry()引用时,发现堆中对象my没有getCry()信息,所以会抛出运行时错误。


问题三为什么子类引用不能指向父类对象?

原理:java的机制。堆中父类对象不存在子类的某些方法以及变量,故导致转换缺失,抛出java.lang.ClassCastException(类型转换错误)。




4)多态性

定义

多态性即多种形态。多态性的实现与静态联编、动态联编有关。

分类

静态联编和动态联编

①静态联编/静态绑定 支持的多态性称为编译时的多态性,也称静态多态性,它是通过重载实现的。——重载
②动态联编/动态绑定 支持的多态性称为运行时的多态性,也称动态多态性,它是通过继承和虚函数实现的。——覆盖+对象变量多态

表现形式

①重载  ②覆盖  ③对象变量是多态的(因为父子类)。


上面都是非常专业的解释,太深奥了!来个简单的例子解释一下为什么是多态性(高手直接跳到下面问题)


实例

苹果:
package com.itaem;

/**
 * 苹果
 *
 * @author wu-shangsen
 * @version 1.0, 2016/9/22
 */
public class Apple {

    public void eat() {
        System.out.println("吃苹果");
    }

    public void eat2() {
        System.out.println("吃1个苹果");
    }
}


梨子:
package com.itaem;

/**
 * 梨子
 *
 * @author wu-shangsen
 * @version 1.0, 2016/9/23
 */
public class Pear extends Apple{

    /**
     * 覆写    覆写了苹果的eat()方法
     */
    @Override
    public void eat() {
        System.out.println("吃雪梨");
    }

    /**
     * 重载
     * @param number
     */
    public void eat2(int number) {
        System.out.println("吃"+number+"个雪梨");
    }
}

执行:
package com.itaem;

/**
 * 服务
 *
 * @author wu-shangsen
 * @version 1.0, 2016/9/22
 */
public class ShoppingSevrice {

    public static void main(String[] args) {
        Pear pear = new Pear() ;
        pear.eat();   //吃雪梨
        pear.eat2();  //吃一个苹果
        pear.eat2(5);  //吃5个雪梨
    }
}

打印结果:
吃雪梨
吃1个苹果
吃5个雪梨

Process finished with exit code 0
简单来说,子类写了一个和父类一样名称的方法,就是应用了多态性! 够简单了吧?那么回顾上面定义的什么是静态多态性和动态多态性吧!

问题一:什么叫静态多态性?

定义:静态联编是指在编译时,系统就能决定调用哪个函数。

特征:相同方法名称,根据参数类型或个数的不同调用对应的方法。

范围:private方法、static方法、final方法、构造器(不参与动态联编)
条件:重载,非继承
优点:方便调用,速度快。


问题二:什么叫动态多态性?

定义:动态联编是指在运行时,虚拟机调用  x所引用对象的实际类型 的  最合适的方法。

特征:首先会查找这个类实际引用,然后在查找对应的方法,如果方法在该类已经定义了则直接调用,否则则在该类的超类中查找对应方法,以此类推。

范围:Java中除了 private方法、static方法、final方法、构造器 外,其他的所有方法都是动态绑定的。
特例:如果调用super编译器会自动对超类方法进行搜索。(动态联编虚拟机优化方案:每次调用方法都要进行搜索,时间开销相当大。因此,虚拟机预先为每个类创建了一个方法表,其中列出了所有动态联编法的签名和实际调用的方法)
条件:覆写,继承,向上转型。
优点:无需对现存的代码进行修改,就可以对程序进行扩展。(直接将引用改为新增类即可)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值