JAVA基础(38) java四大特性理解(封装、继承、多态、抽象)

一、封装

观点一:

属性可用来描述同一类事物的特征,方法可描述一类事物可做的操作。封装就是把属于同一类事物的共性(包括属性与方法)归到一个类中,以方便使用。

概念:
封装也称为信息隐藏,是指利用抽象数据类型将数据和基于数据的操作封装在一起,使其构成一个不可分割的独立实体,数据被保护在抽象数据类型的内部,尽可能地隐藏内部的细节,只保留一些对外接口使之与外部发生联系。
系统的其他部分只有通过包裹在数据外面的被授权的操作来与这个抽象数据类型交流与交互。也就是说,用户无需知道对象内部方法的实现细节,但可以根据对象提供的外部接口(对象名和参数)访问该对象。

好处:
  (1)实现了专业的分工。将能实现某一特定功能的代码封装成一个独立的实体后,各程序员可以在需要的时候调用,从而实现了专业的分工。
  (2)隐藏信息,实现细节。通过控制访问权限可以将可以将不想让客户端程序员看到的信息隐藏起来,如某客户的银行的密码需要保密,只能对该客户开发权限。

观点二:

封装是把过程和数据包围起来,对数据的访问只能通过已定义的接口。
面向对象计算始于这个基本概念,即现实世界可以被描绘成一系列完全自治、封装的对象,这些对象通过一个受保护的接口访问其他对象。
封装是一种信息隐藏技术,在java中通过关键字private实现封装。什么是封装?
封装把对像的所有组成部分组合在一起,封装定义程序如何引用对象的数据,封装实际上使用方法将类的数据隐藏起来,控制用户对类的修改和访问数据的程度。

① 对象的数据封装特性彻底消除了传统结构方法中数据与操作分离所带来的种种问题,提高了程序的可复用性和可维护性,降低了程序员保持数据与操作内容的负担。
②对象的数据封装特性还可以把对象的私有数据和公共数据分离开,保护了私有数据,减少了可能的模块间干扰,达到降低程序复杂性、提高可控性的目的。

封装的理解:
1、封装(Encapsulation)是面向对象的三大特征之一,它指的是将对象的状态信息隐藏在对象内部,
不允许外部程序直接访问对象内部信息,而是通过该类所提供的方法来实现对内部信息的操作和访问。
2、掌握了访问控制符的用法之后,下面通过使用合理的访问控制来定义一个Person类,这个Person类就实现了良好的封装。代码如下:
public class Person {
    //将属性使用private修饰,将这些属性隐藏起来
    private String name;
    private int age;

    //提供方法来操作name属性
    public void setName(String name) {
       //对姓名执行合理的校验
       if(name.length() > 6 || name.length() < 2){
           System.out.println("您的姓名不符合要求");
       }else{
           this.name = name;
       }
    }

    public String getName() {
       return this.name;
    }

    //提供方法来操作age属性
    public void setAge(int age) {
       if(age>100 || age<0){
           System.out.println("您的年龄必须要在0~100之间");
       }else{
           this.age = age;
       }
    }

    public int getAge() {
       return this.age;
    }
    
    public static void main(String[] args) {
       Person p = new Person();
       p.setAge(10);
       System.out.println(p.getAge());
    }
}
运行结果为:10

二、继承

个性对共性的属性与方法的接受,并加入个性特有的属性与方法,继承后子类自动拥有了父类的属性和方法,
但特别注意的是,父类的私有属性和构造方法并不能被继承。
另外子类可以写自己特有的属性和方法,目的是实现功能的扩展,子类也可以复写父类的方法即方法的重写。实际就是多态性!

概念:
一个类继承另一个类,则称继承的类为子类,被继承的类为父类。
子类与父类的关系并不是日常生活中的父子关系,子类与父类而是一种特殊化与一般化的关系,是is-a的关系,子类是父类更加详细的分类。
如class dog extends animal,就可以理解为dog is a animal.注意设计继承的时候,若要让某个类能继承,父类需适当开放访问权限,遵循里氏代换原则,
即向修改关闭对扩展开放,也就是开-闭原则。

好处:
1,提高了代码的复用性。
2,让类与类之间产生了关系,给第三个特征多态提供了前提。
java中支持单继承,一个子类织女呢个有一个直接父类。
java支持多层继承,当要使用一个继承体系时,1,查看该体系中的顶层类,交接该体系的基本功能。
3,创建体系中的最子类对象,完成功能的使用。

什么时候定义继承?
   当类与类之间存在着所属关系的时候就定义继承。
   如:xxx是yyy中的一种,xxx extends yyy  

所属关系:is a
   当本类的成员和局部变量同名用this区分。当子父类中成员变量同名时用super区分父类。
   this代表本类对象的引用,super代表父类的一个空间 

重写:
   当子父类中出现相同方法时,会先运行子类中的方法。
   
重写的特点:
   方法名一样,访问修饰符权限不小于父类,返回类型一致,参数列表一致。

什么时候用重写?
    当对一个类进行子类的扩展时,子类需要保留父类的功能声明,但是要定义子类中该功能的特有内容时,就使用重写操作完成。

子父类中的构造方法的特点:
   在子类构造对象时,发现,访问子类构造函数时,父类也运行了。
原因:
   在子类构造方法中的第一行有一个默认的隐士语句。super();调用父类中的空参数构造函数。

子类的实例化过程:
   子类中的构造方法都会访问父类中的无参构造方法

继承的理解:
1、继承是面向对象的三大特征之一,也是实现代码复用的重要手段。Java的继承具有单继承的特点,每个子类只有一个直接父类。
2、Java的继承通过extends关键字来实现,实现继承的类被称为子类,被继承的类称为父类(有的也称其为基类、超类),父类和子类的关系,是一种一般和特殊的关系。
      就像是水果和苹果的关系,苹果继承了水果,苹果是水果的子类,水果是苹果的父类,则苹果是一种特殊的水果。
3、Java使用extends作为继承的关键字,extends关键字在英文是扩展的意思,而不是继承。为什么国内把extends翻译成继承呢?
     除了与历史原因有关外,把extends翻译成为继承也是有其道理的:子类扩展父类,将可以获得父类的全部属性和方法,这与汉语中得继承(子辈从父辈那里获得一笔财富成为继承)具有很好的类似性。值得指出的是:Java的子类不能获得父类的构造器。
4、实例:
父类:
class BaseClass{
    public double weight;
    public void info() {
       System.out.println("我的体重是"+weight+"千克");
    }
}
子类:
public class ExtendsDemo001 extends BaseClass {
    public static void main(String[] args) {
       //创建ExtendsDemo001对象
       ExtendsDemo001 ed = new ExtendsDemo001();
       //ExtendsDemo001本身没有weight属性,但是ExtendsDemo001的父类有weight属性,也可以访问ExtendsDemo001对象的属性
       ed.weight = 56;
       //调用ExtendsDemo001对象的info()方法
       ed.info();
    }
}
打印结果为:我的体重是56.0千克

5、Java类只能有一个父类。这句话是错误的,应该这样说:Java类只能有一个直接父类,可以有无限多个间接父类,如:
class  Fruit  extends  Plant{…….}
class  Apple  extends  Fruit {…….}

重写父类的方法:
1、大部分的时候,子类总是以父类为基础,额外添加新的属性和方法。但有一种情况例外:子类需要重写父类的方法。
例如鸟类都包含了飞翔的方法,其中鸵鸟是一种特殊的鸟类,因此鸵鸟也是鸟的子类,
  因此它也将从鸟类获得飞翔方法,但这个飞翔方法明显不适合鸵鸟,为此,鸵鸟需要重写鸟类的方法。
class Bird{
   //Bird类的fly()方法
   private void fly(){
      System.out.println("我要在天空中飞翔");
    }
 }
public class OcerrideTest extends Bird{
     //重写Bird类的fly()方法
     public void fly(){
        System.out.println("我只能在地上奔跑");
     }
     public static void main(String[] args) {
       //创建OcerrideTest对象
       OcerrideTest ot = new OcerrideTest();
       ot.fly();
     }
 }
打印结果为:我只能在地上奔跑

这种子类包含父类同名方法的现象被称为方法重写,也被称为方法覆盖(Override)。
方法的重写要遵循“两同两小一大”规则:
⑴ “两同”:方法名相同;形参列表相同。
⑵ “两小”:子类方法之返回类型应比父类方法返回值类型更小或相等;子类方法声明抛出的异常类应比父类方法声明抛出的异常类更小或相等。
⑶ 子类方法的访问权限应比父类方法更大或相等
尤其需要指出的是:覆盖方法和被覆盖方法要么都是类方法,要么都是实例方法,不能一个是类方法,一个是实例方法,例如下面的代码将会有编译错误:
Class  BaseClass{
    public  static  void  test(){…….}
}

Class  SubClass extends BaseClass{
     public void test(){………….}
}
若想调用父类中的fly()方法,则只需在子类中fly()方法中加上如下代码即可:
super.fly();
注意:super和this一样,都不能出现在static的方法中


调用父类构造器
1、看如下程序定义的Basehe Sub类,其中Sub类是Base类的子类,程序在Sub类的构造器中使用super来调用Base构造器里的初始化代码。
class Base{
    public double size;
    public String name;
    public Base(double size, String name){
       this.size=size;
       this.name=name;
    }
}
public class Sub extends Base{
    public String color;
    public Sub(double size, String name, String color){
       //在子类构造器中调用父类构造器,使用super调用来实现
       super(size,name);
       this.color = color;
    }
    public static void main(String[] args) {
       Sub s = new Sub(5.6,"测试对象","红色");
       System.out.println(s.size+"------"+s.name+"------"+s.color);
    }
}
打印结果为:5.6------测试对象------红色

静态初始化块
1、如果定义初始化块时使用了static修饰符,则这个初始化块,就变成了静态初始化块,也被称作为类初始化块。
静态初始化块是类相关的,系统将在类初始化阶段执行静态初始化块,而不是在创建对象时才执行。因此静态初始化块总是比普通初始化块先执行

三、多态

     多态的概念发展出来,是以封装和继承为基础的。
  多态就是在抽象的层面上实施一个统一的行为,到个体(具体)的层面上时,这个统一的行为会因为个体(具体)的形态特征而实施自己的特征行为。
   (针对一个抽象的事,对于内部个体又能找到其自身的行为去执行)

概念:
  相同的事物,调用其相同的方法,参数也相同时,但表现的行为却不同。

理解:
  子类以父类的身份出现,但做事情时还是以自己的方法实现。
    子类以父类的身份出现需要向上转型(upcast),其中向上转型是由JVM自动实现的,是安全的,但向下转型(downcast)是不安全的,需要强制转换。
    子类以父类的身份出现时自己特有的属性和方法将不能使用。

面向对象的三大特性:
    封装、继承、多态。从一定角度来看,封装和继承几乎都是为多态而准备的。这是我们最后一个概念,也是最重要的知识点。
多态的定义:
    指允许不同类的对象对同一消息做出响应。即同一消息可以根据发送对象的不同而采用多种不同的行为方式。(发送消息就是函数调用)
实现多态的技术称为:
    动态绑定(dynamic binding),是指在执行期间判断所引用对象的实际类型,根据其实际的类型调用其相应的方法。
多态的作用:
   消除类型之间的耦合关系。
现实中,关于多态的例子不胜枚举。
    比方说按下 F1 键这个动作,如果当前在 Flash 界面下弹出的就是 AS 3 的帮助文档;
    如果当前在 Word 下弹出的就是 Word 帮助;在 Windows 下弹出的就是 Windows 帮助和支持。
    同一个事件发生在不同的对象上会产生不同的结果。

多态存在的三个必要条件
    一、要有继承;
    二、要有重写;
    三、父类引用指向子类对象。

多态的好处:
1.可替换性(substitutability)。
         多态对已存在代码具有可替换性。例如,多态对圆Circle类工作,对其他任何圆形几何体,如圆环,也同样工作。
2.可扩充性(extensibility)。
         多态对代码具有可扩充性。增加新的子类不影响已存在类的多态性、继承性,以及其他特性的运行和操作。实际上新加子类更容易获得多态功能。
 例如,在实现了圆锥、半圆锥以及半球体的多态基础上,很容易增添球体类的多态性。
3.接口性(interface-ability)。
  多态是超类通过方法签名,向子类提供了一个共同接口,由子类来完善或者覆盖它而实现的。
4.灵活性(flexibility)。
它在应用中体现了灵活多样的操作,提高了使用效率。
5.简化性(simplicity)。
多态简化对应用软件的代码编写和修改过程,尤其在处理大量对象的运算和操作时,这个特点尤为突出和重要。

Java中多态的实现方式:
  接口实现,继承父类进行方法重写,同一个类中进行方法重载。

多态的理解:
1、多态(Polymorphism)是面向对象的三大特征之一。
2、Java引用变量有两个类型:一个是编译时类型,一个是运行时类型。
编译时的类型由声明该变量时使用的类型决定,运行时的类型由实际赋给该变量的对象决定。
如果编译时类型和运行时类型不一致,就会出现所谓的多态(Polymorphism)
先看下面的程序:
class SuperClass{
    public int book = 6;
    public void base() {
       System.out.println("父类的普通方法base()");
    }
    public void test(){
       System.out.println("父类中北覆盖的方法");
    }
}
public class PloymorphismTest001 extends SuperClass{
    //重新定义一个book实例属性,覆盖父类的book实例属性
    public String book = "Java疯狂讲义";
    public void test() {
       System.out.println("子类中覆盖父类的方法");
    }
    private void Dmeo() {
       System.out.println("子类中普通的方法");
    }
    //主方法
    public static void main(String[] args) {
       //下面编译时类型和运行时类型完全一样,因此不存在多态
       SuperClass sc = new SuperClass();
       System.out.println("book1= "+sc.book);//打印结果为:6
       //下面两次调用将执行SuperClass的方法
       sc.base();
       sc.test();
       //下面编译时类型和运行时类型完全一样,因此不存在多态
       PloymorphismTest001 pt = new PloymorphismTest001();
       System.out.println("book2= "+pt.book);//打印结果为:Java疯狂讲义
       //下面调用将执行从父类继承到的base方法
       pt.base();
       //下面调用将执行当前类的test方法
       pt.test();
       //下面编译时类型和运行时类型不一样,多态发生
       SuperClass sscc = new PloymorphismTest001();
       //结果表明访问的是父类属性
       System.out.println("book3= "+sscc.book);//打印结果为:6
        //下面调用将执行从父类继承到得base方法
       sscc.base();
       //下面调用将执行当前类的test方法
       sscc.test();
       //因为sscc的编译类型是SuperClass,SuperClass类没有提供Demo()方法
       //所以下面代码编译时会出现错误
       //sscc.Demo();
    }
}
程序运行结果为:
         book1= 6
         父类的普通方法base()
         父类中北覆盖的方法
         book2= Java疯狂讲义
         父类的普通方法base()
         子类中覆盖父类的方法
          book3= 6
          父类的普通方法base()
          子类中覆盖父类的方法

上面程序的main方法中显示创建而来3个引用变量,对于前两个引用变量sc和pt,它们编译时类型和运行时类型完全相同,因此调用它们的属性和方法非常正常,完全没有问题。但第三个引用变量sscc,则比较特殊,它编译时类型是SuperClass ,而运行时类型是PloymorphismTest001,当调用该引用变量的test方法时,实际上是执行PloymorphismTest001类中覆盖后的test方法,这就是多态。

当把一个子类对象直接赋给父类引用变量,例如上面的SuperClass sscc = new PloymorphismTest001();这个sscc引用变量的编译时类型是SuperClass,而运行时类型是PloymorphismTest001,当运行时调用该引用变量的方法时,其方法行为总是像子类方法的行为,而不是像父类方法行为,这将出现相同类型的变量、执行同一个方法时呈现出不同的行为特征,这就是多态。

特别提醒:
与方法不同的是,对象的属性则不具备多态性,如上面的sscc引用变量,程序中输出它的book属性时,并不是输出PloymorphismTest001类里定义的实例属性,而是输出SuperClass类的实例属性。

注意:
我们通过Object p = new Person()代码定义一个变量p,则这个p只能调用Object类的方法,而不能调用Person类里定义的方法。

四、抽象

象就是有点模糊的意思,还没确定好的意思。
就比方要定义一个方法和类。但还没确定怎么去实现它的具体一点的子方法,那我就可以用抽象类或接口。
具体怎么用,要做什么,我不用关心,由使用的人自己去定义去实现。

既然面向对象设计的重点在于抽象,那Java接口和Java抽象类就有它存在的必然性了。
Java接口和Java抽象类代表的就是抽象类型,就是我们需要提出的抽象层的具体表现。
OOP面向对象的编程,如果要提高程序的复用率,增加程序 的可维护性,可扩展性,就必须是面向接口的编程,面向抽象的编程,
正确地使用接口、抽象类这些太有用的抽象类型做为你结构层次上的顶层。

Java接口和Java抽象类的比较:
1、Java接口和Java抽象类最大的一个区别,就在于Java抽象类可以提供某些方法的部分实现,而Java接口不可以,
这大概就是Java抽象类唯一的优点吧,但这个优点非常有用。
如果向一个抽象类里加入一个新的具体方法时,那么它所有的子类都一下子都得到了这个新方法,而Java接口做不到这一点,如果向一个Java接口里加入一 个新方法,所有实现这个接口的类就无法成功通过编译了,因为你必须让每一个类都再实现这个方法才行,这显然是Java接口的缺点。

2、一个抽象类的实现只能由这个抽象类的子类给出,也就是说,这个实现处在抽象类所定义出的继承的等级结构中,而由于Java语言的单继承性,所以抽象类作为类型定义工具的效能大打折扣。
在这一点上,Java接口的优势就出来了,任何一个实现了一个Java接口所规定的方法的类都可以具有这个接口的类型,而一个类可以实现任意多个Java接口,从而这个类就有了多种类型。

3、从第2点不难看出,Java接口是定义混合类型的理想工具,混合类表明一个类不仅仅具有某个主类型的行为,而且具有其他的次要行为。

4、结合1、2点中抽象类和Java接口的各自优势,具精典的设计模式就出来了:声明类型的工作仍 然由Java接口承担,但是同时给出一个Java抽象类,且实现了这个接口,而其他同属于这个抽象类型的具体类可以选择实现这个Java接口,也可以选择 继承这个抽象类,也就是说在层次结构中,Java接口在最上面,然后紧跟着抽象类,哈,这下两个的最大优点都能发挥到极至了。这个模式就是“缺省适配模 式”。
在Java语言API中用了这种模式,而且全都遵循一定的命名规范:Abstract +接口名。

Java接口和Java抽象类的存在就是为了用于具体类的实现和继承的,如果你准备写一个具体类去继承另一个具体类的话,那你的设计就有很大问题了。
Java抽象类就是为了继承而存在的,它的抽象方法就是为了强制子类必须去实现的。

使用Java接口和抽象Java类进行变量的类型声明、参数是类型声明、方法的返还类型说明,以及数据类型的转换等。
而不要用具体Java类进行变量的类型声明、参数是类型声明、方法的返还类型说明,以及数据类型的转换等。

猜你喜欢

转载自blog.csdn.net/zengdeqing2012/article/details/79543427