美文网首页
Java三大特性:封装继承和多态

Java三大特性:封装继承和多态

作者: 阿斯巴甜不太甜 | 来源:发表于2019-11-19 20:19 被阅读0次

    封装(Encapsulation)

    1. definition

    封装是指将类的数据或者过程隐藏起来的一个过程。在面向对象程序设计中,一个类以及其行为和属性都应该被视作一个整体,所以是不应该让其他类随意修改的。封装的目的就是将这些属性和方法通过访问域关键字隐藏后,提供一个简单的访问接口给其他类。

    public class Person{
        private String name;
        private int age;
    
        public int getAge(){
          return age;
        }
    
        public String getName(){
          return name;
        }
    
        public void setAge(int age){
          this.age = age;
        }
    
        public void setName(String name){
          this.name = name;
        }
    }
    

    2. 为什么不直接把属性设为public,而用getter和setter方法?

    如果直接访问,那么任何程序都可以随意更改这个属性的值,即使把他置空或者置为非法值

    这里不光是指语法错误,可能是语义错误。比如规定x>0,直接设为了x<=0)。

    通过间接访问的方式,可以通过setter方法来对其赋值过程进行一个过滤和加工。虽然通常并没有过滤的这个过程,但是一旦出错,我们可以直接在setter中实现。

    另外,getter方法可以决定是返回其真实值还是克隆抑或是其他任意安全的值。

    继承(inheritance)

    1. definition

    继承是面向对象中复用代码的一种手段,一个类可以衍生出子类,而子类通常是在功能或者行为上与父类相似的但却更具体的类。

    class 父类 {
    }
     
    class 子类 extends 父类 {
    }
    

    Java中支持多重继承(纵向),但并不能同时继承多个(横向)

    final 关键字声明类可以把类定义为不能继承的,即最终类;或者用于修饰方法,该方法不能被子类重写

    2. 构造方法

    当调用子类的构造函数时,子类会(隐式或显式)调用父类的构造函数。

    如果父类的构造器带有参数,则必须在子类的构造器中显式地通过 super 关键字调用父类的构造器并配以适当的参数列表。

    如果父类构造器没有参数,则在子类的构造器中不需要使用 super 关键字调用父类构造器,系统会自动调用父类的无参构造器。

    class A{
        A(){System.out.println("A");}
        A(int i){System.out.println("A:"+i);}
    }
    class Aa extends A{
        Aa(int i){}
    }
    

    当直接调用new Aa(i)时,会隐式调用super()即A(),这个时候会输出A;而当A不存在第一种构造函数时,则必须在Aa(int i)中显式的调用super(int i)。

    隐式的调用一定是无参的构造函数

    接口(Interface)和抽象类(Abstract Class)

    1. 抽象类

    不可以被实例化的一种特殊的类,但是可以被继承。

    抽象方法

    public abstract class Employee
    {
       private String name;
       private String address;
       private int number;
       
       public abstract double computePay();
       
       //其余代码
    }
    

    抽象方法就是一个并没有实现的方法,有抽象方法的类一定是一个抽象类,但是抽象类并不一定含有抽象方法

    子类如果继承抽象类的话,如果该抽象类中存在抽象方法,那么子类必须实现该抽象方法才能被实例化;否则必须也声明为抽象类。

    显然一个没有被实现的方法如果被实例化是不符合逻辑的,因为根本不能调用

    2. 接口

    接口应该可以被理解为一种行为方式的规范,当实体类需要做某些事的时候,需要按照这些规范去实现,而怎么实现可以不必关心。

    interface Animal {
       public void eat();
       public void travel();
    }
    

    与抽象类的区别:

    1. 接口中的所有方法必须都是公共的抽象方法。在实现上是隐式的,也就是所有方法前面默认加上了public abstract
    2. 接口中的所有属性都隐式的加上了static final,也就是说是静态不可修改的,同时这种情况下必须在定义的时候就对变量进行初始化。
    3. 一个类可以同时实现多个接口

    多态(Polymorphism)

    1. definition

    多态就是指同一个行为可以通过多种形态进行表现,具体到代码中就是一个方法可以由多种途径实现(重载和重写)。

    2. 实现

    class A{
        void s() {};
    }
    class Aa extends A{
        void s() {System.out.println(1111);};
        void s(int i) {System.out.println(i);};
    }
    

    Aa中第一种s的实现叫做重写(override)即把父类的代码覆盖了,第二种叫对s()的重载(overload)即重新定义了一种新的名为s的方法的形态。

    向上/下 转型

    1. 向上转型(从具体到抽象)

    向上转型: 用子类的对象去实例化父类,父类可以调用被覆盖的方法

    比如

    class A {
             public void print() {
                      System.out.println("A:print");
             }
    }
    
    class B extends A {
            public void print() {
            System.out.println("B:print");
            }
             
    }
    
    public class Test{
             public static void main(String args[])
             {
                      A a = new B();          //通过子类去实例化父类
                      a.print();
             }
    }
    
    

    最终打印的结果是"B:print"

    这是因为我们通过子类B去实例化的,所以父类A的print方法已经被子类B的print方法覆盖了。

    另外要注意:

    向上转型父类引用只能调用父类中原本存在而被子类覆盖的方法,子类单独的方法不可调用

    2. 向下转型(从抽象到具体)

    父类对象强制转换成子类对象

    比如:

    class A {
             public void print() {
                      System.out.println("A:print");
             }
    }
    
    class B extends A {
             public void print() {        
                      System.out.println("B:print");
             }
             public void funcB(){
                      System.out.println("funcB");
             }
    }
    
    class C extends A {
             public void print() {        
                      System.out.println("C:print");
             }
             public void funcC(){
                      System.out.println("funcC");
             }
    }
    
    public class Test{
             public static void func(A a)
             {
                      a.print();
                      if(a instanceof B)
                      {
                              B b = (B)a;   //向下转型,通过父类实例化子类
                              b.funcB();    //调用B类独有的方法
                      }
                      else if(a instanceof C)
                      {
                              C c = (C)a;  //向下转型,通过父类实例化子类
                              c.funcC();   //调用C类独有的方法
                      }
             }
    
             public static void main(String args[])
             {
                      func(new A());   
                      func(new B());
                      func(new C());
             }
    }
    

    很容易想到结果,这里就不给出了

    通过向下转型可以单独调用子类其特有的方法

    3. 结论

    向上向下转型的好处在于编写函数的时候,只需将父类(接口)定义为参数即可,因为所有子类都可以看成是特殊的父类;而向下转型感觉一般用的比较少

    相关文章

      网友评论

          本文标题:Java三大特性:封装继承和多态

          本文链接:https://www.haomeiwen.com/subject/pcbgictx.html