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

封装(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三大特性:封装继承和多态)