JAVA基础——面向对象三大特性:封装、继承、多态

看到一篇整理很是全的文章,摘抄下来!

目录

JAVA面向对象三大特性详解

回到顶部html

1、封装

  一、概念:java

    将类的某些信息隐藏在类内部,不容许外部程序直接访问,而是经过该类提供的方法来实现对隐藏信息的操做和访问。算法

  二、好处:安全

  •    只能经过规定的方法访问数据。
  •       隐藏类的实例细节,方便修改和实现。 

  三、封装的实现步骤ide

     

    须要注意:对封装的属性不必定要经过get/set方法,其余方法也能够对封装的属性进行操做。固然最好使用get/set方法,比较标准。函数


 

A、访问修饰符学习

 

    从表格能够看出从上到下封装性愈来愈差测试

B、this关键字this

 1.this关键字表明当前对象设计

  this.属性 操做当前对象的属性

  this.方法 调用当前对象的方法。

 2.封装对象的属性的时候,常常会使用this关键字。

 3.当getter和setter函数参数名和成员函数名重合的时候,可使用this区别。如:

  

C、Java 中的内部类

 内部类( Inner Class )就是定义在另一个类里面的类。与之对应,包含内部类的类被称为外部类。

 那么问题来了:那为何要将一个类定义在另外一个类里面呢?清清爽爽的独立的一个类多好啊!!

 答:内部类的主要做用以下:

  1. 内部类提供了更好的封装,能够把内部类隐藏在外部类以内,不容许同一个包中的其余类访问该类。

  2. 内部类的方法能够直接访问外部类的全部数据,包括私有的数据

  3. 内部类所实现的功能使用外部类一样能够实现,只是有时使用内部类更方便。

  内部类可分为如下几种: 

    • 成员内部类
    • 静态内部类
    • 方法内部类
    • 匿名内部类  

  各个内部类的具体使用请转移到另外一篇随文:http://www.cnblogs.com/hysum/p/7101974.html

回到顶部

2、继承

一、继承的概念

 继承是类与类的一种关系,是一种“is a”的关系。好比“狗”继承“动物”,这里动物类是狗类的父类或者基类,狗类是动物类的子类或者派生类。以下图所示:

   

 注:java中的继承是单继承,即一个类只有一个父类。

二、继承的好处

 子类拥有父类的全部属性和方法(除了private修饰的属性不能拥有)从而实现了实现代码的复用; 

三、语法规则,只要在子类加上extends关键字继承相应的父类就能够了:

  


 A、方法的重写

 子类若是对继承的父类的方法不满意(不适合),能够本身编写继承的方法,这种方式就称为方法的重写。当调用方法时会优先调用子类的方法。

 重写要注意:

     a、返回值类型

  b、方法名

  c、参数类型及个数

 都要与父类继承的方法相同,才叫方法的重写。

 重载和重写的区别:

  方法重载:在同一个类中处理不一样数据的多个相同方法名的多态手段。

  方法重写:相对继承而言,子类中对父类已经存在的方法进行区别化的修改。


 B、继承的初始化顺序

  一、初始化父类再初始化子类

  二、先执行初始化对象中属性,再执行构造方法中的初始化。

 基于上面两点,咱们就知道实例化一个子类,java程序的执行顺序是:

 父类对象属性初始化---->父类对象构造方法---->子类对象属性初始化--->子类对象构造方法   

 下面有个形象的图:

   


 C、final关键字

 使用final关键字作标识有“最终的”含义。

  1. final 修饰类,则该类不容许被继承。

  2. final 修饰方法,则该方法不容许被覆盖(重写)

  3. final 修饰属性,则该类的该属性不会进行隐式的初始化,因此 该final 属性的初始化属性必须有值,或在构造方法中赋值(但只能选其一,且必须选其一,由于没有默认值!),且初始化以后就不能改了,只能赋值一次

  4. final 修饰变量,则该变量的值只能赋一次值,在声明变量的时候才能赋值,即变为常量


 D、super关键字

 在对象的内部使用,能够表明父类对象。

  一、访问父类的属性:super.age

   二、访问父类的方法:super.eat()

 super的应用:

 首先咱们知道子类的构造的过程中必须调用父类的构造方法。其实这个过程已经隐式地使用了咱们的super关键字。

 这是由于若是子类的构造方法中没有显示调用父类的构造方法,则系统默认调用父类无参的构造方法。

 那么若是本身用super关键字在子类里调用父类的构造方法,则必须在子类的构造方法中的第一行

 要注意的是:若是子类构造方法中既没有显示调用父类的构造方法,而父类没有无参的构造方法,则编译出错。

(补充说明,虽然没有显示声明父类的无参的构造方法,系统会自动默认生成一个无参构造方法,可是,若是你声明了一个有参的构造方法,而没有声明无参的构造方法,这时系统不会动默认生成一个无参构造方法,此时称为父类有没有无参的构造方法。)


 E、Object类

 Object类是全部类的父类,若是一个类没有使用extends关键字明确标识继承另外一个类,那么这个类默认继承Object类。

 Object类中的方法,适合全部子类!!!

 那么Object类中有什么主要的方法呢?

 一、toString()

  a. 在Object类里面定义toString()方法的时候返回的对象的哈希code码(对象地址字符串)。

  咱们能够发现,若是咱们直接用System.out.print(对象)输出一个对象,则运行结果输出的是对象的对象地址字符串,也称为哈希code码。如:

  哈希码是经过哈希算法生成的一个字符串,它是用来惟一区分咱们对象的地址码,就像咱们的身份证同样。  

  b. 能够经过重写toString()方法表示出对象的属性。

   若是咱们但愿输出一个对象的时候,不是它的哈希码,而是它的各个属性值,那咱们能够经过重写toString()方法表示出对象的属性。

  二、equals()

  a、equals()----返回值是布尔类型。

  b、默认的状况下,比较的是对象的引用是否指向同一块内存地址-------对象实例化时,即给对象分配内存空间,该内存空间的地址就是内存地址。使用方法如:dog.equals(dog2);

  c、 若是是两个对象,但想判断两个对象的属性是否相同,则重写equals()方法。

 以Dog类为例,重写后的equals()方法以下(固然你能够根据本身想比较的属性来重写,这里我以age属性是否相同来重写equals()方法):

    

 上面有四个判断,它们的含义分别是:

  1.判断地址是否相同----if (this == obj),相同则返回true

  2.判断对象是否为空----if (obj == null),为空则返回false

  3.getClass()能够获得类对象,判断类型是否同样-----if (getClass() != obj.getClass()),不同则返回false

  4.判断属性值是否同样----if (age != other.age),不同返回false

  5.若是地址相同,对象不为空,类型同样,属性值同样则返回true

 这里要注意的是,理解obj.getClass()获得的类对象和类的对象的区别,如下用图形表示:
      

 能够看到,对于类对象咱们关心它属于哪一个类,拥有什么属性和方法,好比我和你都是属于“人”这个类对象;而类的对象则是一个类的实例化的具体的一个对象。好比我和你是两个不一样的人。

回到顶部

3、多态

 面向对象的最后一个特性就是多态,那么什么是多态呢?多态就是对象的多种形态。

 java里的多态主要表如今两个方面:

1.引用多态   

  父类的引用能够指向本类的对象;

  父类的引用能够指向子类的对象;

  这两句话是什么意思呢,让咱们用代码来体验一下,首先咱们建立一个父类Animal和一个子类Dog,在主函数里以下所示:

  

  注意:咱们不能使用一个子类的引用来指向父类的对象,如:

  这里咱们必须深入理解引用多态的意义,才能更好记忆这种多态的特性。为何子类的引用不能用来指向父类的对象呢?我在这里通俗给你们讲解一下:就以上面的例子来讲,咱们能说“狗是一种动物”,可是不能说“动物是一种狗”,狗和动物是父类和子类的继承关系,它们的从属是不能颠倒的。当父类的引用指向子类的对象时,该对象将只是当作一种特殊的父类(里面有重写的方法和属性),反之,一个子类的引用来指向父类的对象是不可行的!!

2.方法多态

  根据上述建立的两个对象:本类对象和子类对象,一样都是父类的引用,当咱们指向不一样的对象时,它们调用的方法也是多态的。

  建立本类对象时,调用的方法为本类方法;

  建立子类对象时,调用的方法为子类重写的方法或者继承的方法;

  使用多态的时候要注意:若是咱们在子类中编写一个独有的方法(没有继承父类的方法),此时就不能经过父类的引用建立的子类对象来调用该方法!!!

  注意: 继承是多态的基础。


 A、引用类型转换 

 了解了多态的含义后,咱们在平常使用多态的特性时常常须要进行引用类型转换。

 引用类型转换:

 1. 向上类型转换(隐式/自动类型转换),是小类型转换到大类型。

  就以上述的父类Animal和一个子类Dog来讲明,当父类的引用能够指向子类的对象时,就是向上类型转换。如:

    

 2. 向下类型转换(强制类型转换),是大类型转换到小类型(有风险,可能出现数据溢出)。

  将上述代码再加上一行,咱们再次将父类转换为子类引用,那么会出现错误,编译器不容许咱们直接这么作虽然咱们知道这个父类引用指向的就是子类对象,可是编译器认为这种转换是存在风险的如:

    

  那么咱们该怎么解决这个问题呢,咱们能够在animal前加上(Dog)来强制类型转换。如:

  可是若是父类引用没有指向该子类的对象,则不能向下类型转换,虽然编译器不会报错,可是运行的时候程序会出错,如:

  其实这就是上面所说的子类的引用指向父类的对象,而强制转换类型也不能转换!!

  还有一种状况是父类的引用指向其余子类的对象,则不能经过强制转为该子类的对象。如:

    

  这是由于咱们在编译的时候进行了强制类型转换,编译时的类型是咱们强制转换的类型,因此编译器不会报错,而当咱们运行的时候,程序给animal开辟的是Dog类型的内存空间,这与Cat类型内存空间不匹配,因此没法正常转换。这两种状况出错的本质是同样的,因此咱们在使用强制类型转换的时候要特别注意这两种错误!!下面有个更安全的方式来实现向下类型转换。。。。

     3. instanceof运算符,来解决引用对象的类型,避免类型转换的安全性问题。

  instanceof是Java的一个二元操做符,和==,>,<是同一类东东。因为它是由字母组成的,因此也是Java的保留关键字。它的做用是测试它左边的对象是不是它右边的类的实例,返回boolean类型的数据。

  咱们来使用instanceof运算符来规避上面的错误,代码修改以下:

  

  利用if语句和instanceof运算符来判断两个对象的类型是否一致。

  补充说明:在比较一个对象是否和另外一个对象属于同一个类实例的时候,咱们一般能够采用instanceof和getClass两种方法经过二者是否相等来判断,可是二者在判断上面是有差异的。Instanceof进行类型检查规则是:你属于该类吗?或者你属于该类的派生类吗?而经过getClass得到类型信息采用==来进行检查是否相等的操做是严格的判断,不会存在继承方面的考虑

  总结:在写程序的时候,若是要进行类型转换,咱们最好使用instanceof运算符来判断它左边的对象是不是它右边的类的实例,再进行强制转换。


 B、抽象类

 定义:抽象类前使用abstract关键字修饰,则该类为抽象类。

 使用抽象类要注意如下几点:

  1. 抽象类是约束子类必须有什么方法,而并不关注子类如何实现这些方法。

  2. 抽象类应用场景:

   a. 在某些状况下,某个父类只是知道其子类应该包含怎样的方法,但没法准确知道这些子类如何实现这些方法(可实现动态多态)。

   b. 从多个具备相同特征的类中抽象出一个抽象类,以这个抽象类做为子类的模板,从而避免子类设计的随意性。

  3. 抽象类定义抽象方法,只有声明,不须要实现。抽象方法没有方法体以分号结束,抽象方法必须用abstract关键字来修饰。如:

   

  四、包含抽象方法的类是抽象类。抽象类中能够包含普通的方法,也能够没有抽象方法。如:

    

  五、抽象类不能直接建立,能够定义引用变量来指向子类对象,来实现抽象方法。以上述的Telephone抽象类为例:     

1 public abstract class Telephone {
2     public abstract void call();//抽象方法,方法体以分号结束,只有声明,不须要实现
3     public void message(){
4         System.out.println("我是抽象类的普通方法");
5     }//抽象类中包含普通的方法
6 }

 

1 public class Phone extends Telephone {
2 
3     public void call() {//继承抽象类的子类必须重写抽象方法
4         // TODO Auto-generated method stub
5         System.out.println("我重写了抽象类的方法");
6     }
7     
8 }

 

 以上是Telephone抽象类和子类Phone的定义,下面咱们看main函数里:

    

    运行结果(排错以后):

    


 C、接口

 一、概念

  接口能够理解为一种特殊的类,由全局常量和公共的抽象方法所组成。也可理解为一个特殊的抽象类,由于它含有抽象方法。

   若是说类是一种具体实现体,而接口定义了某一批类所须要遵照的规范,接口不关心这些类的内部数据,也不关心这些类里方法的实现细节,它只规定这些类里必须提供的某些方法。(这里与抽象类类似)

 2.接口定义的基本语法

      [修饰符] [abstract] interface 接口名 [extends父接口1,2....](多继承){

        0…n常量 (public static final)                                          

        0…n 抽象方法(public abstract)                                      

      }                                                                                             

  其中[ ]里的内容表示可选项,能够写也能够不写;接口中的属性都是常量,即便定义时不添加public static final 修饰符,系统也会自动加上;接口中的方法都是抽象方法,即便定义时不添加public abstract修饰符,系统也会自动加上。

 3.使用接口

  一个类能够实现一个或多个接口,实现接口使用implements关键字。java中一个类只能继承一个父类,是不够灵活的,经过实现多个接口能够补充。

  继承父类实现接口的语法为:

     [修饰符] class 类名 extends 父类 implements 接口1,接口2...{

       类体部分//若是继承了抽象类,须要实现继承的抽象方法;要实现接口中的抽象方法

     }

  注意:若是要继承父类,继承父类必须在实现接口以前,即extends关键字必须在implements关键字前

  补充说明:一般咱们在命名一个接口时,常常以I开头,用来区分普通的类。如:IPlayGame

  如下咱们来补充在上述抽象类中的例子,咱们以前已经定义了一个抽象类Telephone和子类Phone,这里咱们再建立一个IPlayGame的接口,而后在原来定义的两个类稍做修改,代码以下:

1 public interface IPlayGame {
2     public void paly();//abstract 关键字能够省略,系统会自动加上
3     public String name="游戏名字";//static final关键字能够省略,系统会自动加上
4 }

 

1 public class Phone extends Telephone implements IPlayGame{
 2 
 3     public void call() {//继承抽象类的子类必须重写抽象方法
 4         // TODO Auto-generated method stub
 5         System.out.println("我重写了抽象类的方法");
 6     }
 7 
 8     @Override
 9     public void paly() {
10         // TODO Auto-generated method stub
11         System.out.println("我重写了接口的方法");
12     }
13     
14 }

 

 

1 public class train {
 2     
 3 
 4     public static void main(String[] args) {
 5         // TODO Auto-generated method stub
 6         IPlayGame i=new Phone();//用接口的引用指向子类的对象
 7         i.paly();//调用接口的方法
 8         System.out.println(i.name);//输出接口的常量
 9     
10         
11 
12 }
13 }

 

 运行结果:

    

 4.接口和匿名内部类配合使用

  接口在使用过程当中还常常和匿名内部类配合使用。匿名内部类就是没有没名字的内部类,多用于关注实现而不关注实现类的名称。

  语法格式:

1 Interface i =new interface(){
2     Public void method{
3         System.out.println(“利用匿名内部类实现接口1”);
4 }
5 };
6 i.method();

  还有一种写法:(直接把方法的调用写在匿名内部类的最后)

1 Interface i =new interface(){
2 Public void method{
3         System.out.println(“利用匿名内部类实现接口1”);
4 }
5 }.method();

回到顶部

4、抽象类和接口的区别

   咱们在多态的学习过程当中认识到抽象类和接口都是实现java多态特性的关键部分,二者都包含抽象方法,只关注方法的声明而不关注方法的具体实现,那么这二者又有什么区别呢??咱们在编写java程序的时候又该如何抉择呢?

  参考博文:http://www.cnblogs.com/felixzh/p/5938544.html

(1)语法层面上的区别

 1.一个类只能继承一个抽象类,而一个类却能够实现多个接口。
 2.抽象类中的成员变量能够是各类类型的,而接口中的成员变量只能是public static final类型的;且必须给其初值,因此实现类中不能从新定义,也不能改变其值;抽象类中的变量默认是 friendly 型,其值能够在子类中从新定义,也能够从新赋值。 
 3.抽象类中能够有非抽象方法,接口中则不能有非抽象方法。
 4.接口能够省略abstract 关键字,抽象类不能。
 5.接口中不能含有静态代码块以及静态方法,而抽象类能够有静态代码块和静态方法;

(2)设计层面上的区别

  1)抽象类是对一种事物的抽象,即对类抽象,而接口是对行为的抽象。抽象类是对整个类总体进行抽象,包括属性、行为,可是接口倒是对类局部(行为)进行抽象。举个简单的例子,飞机和鸟是不一样类的事物,可是它们都有一个共性,就是都会飞。那么在设计的时候,能够将飞机设计为一个类Airplane,将鸟设计为一个类Bird,可是不能将 飞行 这个特性也设计为类,所以它只是一个行为特性,并非对一类事物的抽象描述。此时能够将 飞行 设计为一个接口Fly,包含方法fly( ),而后Airplane和Bird分别根据本身的须要实现Fly这个接口。而后至于有不一样种类的飞机,好比战斗机、民用飞机等直接继承Airplane便可,对于鸟也是相似的,不一样种类的鸟直接继承Bird类便可。从这里能够看出,继承是一个 "是否是"的关系,而 接口 实现则是 "有没有"的关系。若是一个类继承了某个抽象类,则子类一定是抽象类的种类,而接口实现则是有没有、具有不具有的关系,好比鸟是否能飞(或者是否具有飞行这个特色),能飞行则能够实现这个接口,不能飞行就不实现这个接口。

  2)设计层面不一样,抽象类做为不少子类的父类,它是一种模板式设计。而接口是一种行为规范,它是一种辐射式设计。什么是模板式设计?最简单例子,你们都用过ppt里面的模板,若是用模板A设计了ppt B和ppt C,ppt B和ppt C公共的部分就是模板A了,若是它们的公共部分须要改动,则只须要改动模板A就能够了,不须要从新对ppt B和ppt C进行改动。而辐射式设计,好比某个电梯都装了某种报警器,一旦要更新报警器,就必须所有更新。也就是说对于抽象类,若是须要添加新的方法,能够直接在抽象类中添加具体的实现,子类能够不进行变动;而对于接口则不行,若是接口进行了变动,则全部实现这个接口的类都必须进行相应的改动。

       下面看一个网上流传最普遍的例子:门和警报的例子:门都有open( )和close( )两个动做,此时咱们能够定义经过抽象类和接口来定义这个抽象概念:

1 abstract class Door {
2     public abstract void open();
3     public abstract void close();
4 }

    或者:

1 interface Door {
2     public abstract void open();
3     public abstract void close();
4 }

  可是如今若是咱们须要门具备报警alarm( )的功能,那么该如何实现?下面提供两种思路:

  1)将这三个功能都放在抽象类里面,可是这样一来全部继承于这个抽象类的子类都具有了报警功能,可是有的门并不必定具有报警功能;

  2)将这三个功能都放在接口里面,须要用到报警功能的类就须要实现这个接口中的open( )和close( ),也许这个类根本就不具有open( )和close( )这两个功能,好比火灾报警器。

  从这里能够看出, Door的open() 、close()和alarm()根本就属于两个不一样范畴内的行为,open()和close()属于门自己固有的行为特性,而alarm()属于延伸的附加行为。所以最好的解决办法是单独将报警设计为一个接口,包含alarm()行为,Door设计为单独的一个抽象类,包含open和close两种行为。再设计一个报警门继承Door类和实现Alarm接口。

 

1 interface Alram {
 2     void alarm();
 3 }
 4  
 5 abstract class Door {
 6     void open();
 7     void close();
 8 }
 9  
10 class AlarmDoor extends Door implements Alarm {
11     void oepn() {
12       //....
13     }
14     void close() {
15       //....
16     }
17     void alarm() {
18       //....
19     }
20 }