天天看点

java的三大特性:封装,继承和多态

java有三大特性:封装,继承和多态。

一、 封装

1. 封装就是将类的信息隐藏在类内部,不允许外部程序直接访问,而是通过该类的方法实现对隐藏信息的操作和访问。

2. 封装是怎么实现的呢?

   a. 需要修改属性的访问控制符(修改为private);

   b. 创建getter/setter方法(用于属性的读写);

   c. 在getter/setter方法中加入属性控制语句(用于判断属性值的合法性);

public class Student{
    private float score;
    public void setScore(float score){
        this.score = score;
    }
    public float getScore(){
        return score;
    }
}
           

二、 继承

继承是类与类的一种关系,比较像集合中的从属于关系。比如说,狗属于动物。就可以看成狗类继承了动物类,那么狗类就是动物类的子类(派生类),动物类就是狗类的父类(基类)。在Java中是单继承的,也就是说一个子类只有一个父类。

//父类:动物类
public class Animal{
    public int age;
    public String name;
    public void eat(){
        System.out.println("动物具有吃东西的本领!");
    }
}
//子类:狗类
public class Dog extends Animal {
    
}
           

三、 多态

多态指的是对象的多种形态。多态有两种:引用多态和方法多态。继承是多态的实现基础。

1.引用多态

父类的引用可以指向本类的对象;父类的引用可以指向子类的对象。

//父类:动物类
public class Animal{
    public int age;
    public String name;
    public void eat(){
        System.out.println("动物具有吃东西的本领!");
    }
}
//子类:狗类
public class Dog extends Animal {
    
}
//测试类
public class Test{
    public static void main(String[] args) {
    //父类的引用可以指向本类的对象
    Animal ani1 = new Animal();
    //父类的引用可以指向子类的对象
    Animal ani2 = new Dog();    
    }
}
           

2. 方法多态

创建父类对象时,调用的方法为父类方法;

创建子类对象时,调用的方法是子类重写的方法或继承自父类的方法;

//父类:动物类
public class Animal{
    public int age;
    public String name;
    public void eat(){
        System.out.println("动物具有吃东西的本领!");
    }
}
//子类:狗类
public class Dog extends Animal {
    public void eat(){
        System.out.println("狗是吃肉的。");
    }
}
//测试类
public class Test{
    public static void main(String[] args) {
    //父类的引用可以指向本类的对象
    Animal ani1 = new Animal();
    //父类的引用可以指向子类的对象
    Animal ani2 = new Dog();
    //输出动物具有吃的本领
    ani1.eat();    
    //输出狗是吃肉的
    ani2.eat();
    }
}
           
运行结果:
动物具有吃的本领  
狗是吃肉的
           
public class Wine {
    public void fun1(){
        System.out.println("Wine 的Fun.....");
        fun2();
    }
    
    public void fun2(){
        System.out.println("Wine 的Fun2...");
    }
}

public class JNC extends Wine{
    /**
     * @desc 子类重载父类方法
     *        父类中不存在该方法,向上转型后,父类是不能引用该方法的
     * @param a
     * @return void
     */
    public void fun1(String a){
        System.out.println("JNC 的 Fun1...");
        fun2();
    }
    
    /**
     * 子类重写父类方法
     * 指向子类的父类引用调用fun2时,必定是调用该方法
     */
    public void fun2(){
        System.out.println("JNC 的Fun2...");
    }
}

public class Test {
    public static void main(String[] args) {
        //向上转型
        Wine a = new JNC();
        a.fun1();
    }
}
           
输出结果:
Wine 的Fun.....
JNC 的Fun2...
           

 从程序的运行结果中我们发现,a.fun1()首先是运行父类Wine中的fun1().然后再运行子类JNC中的fun2()。

      分析:在这个程序中子类JNC重载了父类Wine的方法fun1(),重写fun2(),而且重载后的fun1(String a)与 fun1()不是同一个方法,由于父类中没有该方法,向上转型后会丢失该方法,所以执行JNC的Wine类型引用是不能引用fun1(String a)方法。而子类JNC重写了fun2() ,那么指向JNC的Wine引用会调用JNC中fun2()方法。

      所以对于多态我们可以总结如下:

      指向子类的父类引用由于向上转型了,它只能访问父类中拥有的方法和属性,而对于子类中存在而父类中不存在的方法,该引用是不能使用的,尽管是重载该方法。若子类重写了父类中的某些方法,在调用该些方法的时候,必定是使用子类中定义的这些方法(动态连接、动态调用)。

 3.多态实现条件

  Java实现多态有三个必要条件:继承、重写、向上转型。

         继承:在多态中必须存在有继承关系的子类和父类。

         重写:子类对父类中某些方法进行重新定义,在调用这些方法时就会调用子类的方法。

         向上转型:在多态中需要将子类的引用赋给父类对象,只有这样该引用才能够具备技能调用父类的方法和子类的方法。

 只有满足了上述三个条件,我们才能够在同一个继承结构中使用统一的逻辑实现代码处理不同的对象,从而达到执行不同的行为。

      对于Java而言,它多态的实现机制遵循一个原则:当超类对象引用变量引用子类对象时,被引用对象的类型而不是引用变量的类型决定了调用谁的成员方法,但是这个被调用的方法必须是在超类中定义过的,也就是说被子类覆盖的方法。

在Java中有两种形式可以实现多态。继承和接口。

经典实例

public class A {
    public String show(D obj) {
        return ("A and D");
    }

    public String show(A obj) {
        return ("A and A");
    }
}

public class B extends A{
    public String show(B obj){
        return ("B and B");
    }
    
    public String show(A obj){
        return ("B and A");
    }
}

public class C extends B{

}

public class D extends B{

}

public class Test {
    public static void main(String[] args) {
        A a1 = new A();
        A a2 = new B();
        B b = new B();
        C c = new C();
        D d = new D();
        
        System.out.println("1--" + a1.show(b));
        System.out.println("2--" + a1.show(c));
        System.out.println("3--" + a1.show(d));
        System.out.println("4--" + a2.show(b));
        System.out.println("5--" + a2.show(c));
        System.out.println("6--" + a2.show(d));
        System.out.println("7--" + b.show(b));
        System.out.println("8--" + b.show(c));
        System.out.println("9--" + b.show(d));      
    }
}
           
运行结果:
1--A and A
2--A and A
3--A and D
4--B and A
5--B and A
6--A and D
7--B and B
8--B and B
9--A and D
           

 在这里看结果1、2、3还好理解,从4开始就开始糊涂了,对于4来说为什么输出不是“B and B”呢?

      首先我们先看一句话:当超类对象引用变量引用子类对象时,被引用对象的类型而不是引用变量的类型决定了调用谁的成员方法,但是这个被调用的方法必须是在超类中定义过的,也就是说被子类覆盖的方法。这句话对多态进行了一个概括。其实在继承链中对象方法的调用存在一个优先级:this.show(O)、super.show(O)、this.show((super)O)、super.show((super)O)。

      分析:

     从上面的程序中我们可以看出A、B、C、D存在如下关系。

java的三大特性:封装,继承和多态

      首先我们分析5,a2.show(c),a2是A类型的引用变量,所以this就代表了A,a2.show(c),它在A类中找发现没有找到,于是到A的超类中找(super),由于A没有超类(Object除外),所以跳到第三级(C类开始查找),也就是this.show((super)O),C的超类有B、A,所以(super)O为B、A,this同样是A,这里在A中找到了show(A obj),同时由于a2是B类的一个引用且B类重写了show(A obj),因此最终会调用子类B类的show(A obj)方法,结果也就是B and A。

      按照同样的方法我也可以确认其他的答案。

      方法已经找到了但是我们这里还是存在一点疑问,我们还是来看这句话:当超类对象引用变量引用子类对象时,被引用对象的类型而不是引用变量的类型决定了调用谁的成员方法,但是这个被调用的方法必须是在超类中定义过的,也就是说被子类覆盖的方法。这我们用一个例子来说明这句话所代表的含义:a2.show(b);

      这里a2是引用变量,为A类型,它引用的是B对象,因此按照上面那句话的意思是说有B来决定调用谁的方法,所以a2.show(b)应该要调用B中的show(B obj),产生的结果应该是“B and B”,但是为什么会与前面的运行结果产生差异呢?这里我们忽略了后面那句话“但是这儿被调用的方法必须是在超类中定义过的”,那么show(B obj)在A类中存在吗?根本就不存在!所以这句话在这里不适用?那么难道是这句话错误了?非也!其实这句话还隐含这这句话:它仍然要按照继承链中调用方法的优先级来确认。所以它才会在A类中找到show(A obj),同时由于B重写了该方法所以才会调用B类中的方法,否则就会调用A类中的方法。

      所以多态机制遵循的原则概括为:当超类对象引用变量引用子类对象时,被引用对象的类型决定了调用谁的成员方法(而不是引用变量的类型决定),但是这个被调用的方法必须是在超类中定义过的,也就是说被子类覆盖的方法,但是它仍然要根据继承链中方法调用的优先级来确认方法,该优先级为:this.show(O)、super.show(O)、this.show((super)O)、super.show((super)O)。

      参考资料:http://blog.csdn.net/thinkGhoster/archive/2008/04/19/2307001.aspx。

      百度文库:http://wenku.baidu.com/view/73f66f92daef5ef7ba0d3c03.html