面向对象主要有三大特性:继承和多态、封装。java
在了解抽象类以前,先来了解一下抽象方法。抽象方法是一种特殊的方法:它只有声明,而没有具体的实现。抽象方法的声明格式为:编程
abstract void fun();
抽象方法必须用abstract关键字进行修饰。若是一个类含有抽象方法,则称这个类为抽象类,抽象类必须在类前用abstract关键字修饰。由于抽象类中含有无具体实现的方法,因此不能用抽象类建立对象。抽象类的声明格式以下:安全
public abstract class ClassName { abstract void fun(); }
下面要注意一个问题:在《JAVA编程思想》一书中,将抽象类定义为“包含抽象方法的类”,可是后面发现若是一个类不包含抽象方法,只是用abstract修饰的话也是抽象类。也就是说抽象类不必定必须含有抽象方法。我的以为这个属于钻牛角尖的问题吧,由于若是一个抽象类不包含任何抽象方法,为什么还要设计为抽象类?因此暂且记住这个概念吧,没必要去深究为何。函数
在面向对象领域因为抽象的概念在问题领域没有对应的具体概念,因此用以表征抽象概念的抽象类是不能实例化的。同时,抽象类体现了数据抽象的思想,是实现多态的一种机制。它定义了一组抽象的方法,至于这组抽象方法的具体表现形式由派生类来实现。同时抽象类提供了继承的概念,它的出发点就是为了继承,不然它没有存在的任何意义。对于一个父类,若是它的某个方法在父类中实现出来没有任何意义,必须根据子类的实际需求来进行不一样的实现,那么就能够将这个方法声明为abstract方法,此时这个类也就成为abstract类了。this
使用抽象类时应注意一下几点:spa
一、包含抽象方法的类称为抽象类,但并不意味着抽象类中只能有抽象方法,它和普通类同样,一样能够拥有成员变量和普通的成员方法.net
二、若是一个非抽象类继承了抽象类,则非抽象类必须实现抽象父类的全部抽象方法设计
三、子类中的抽象方法不能与父类的抽象方法同名3d
四、抽象类不能建立实体,由于抽象类存在抽象方法,而抽象方法没有实体,建立对象后,抽象对象调用抽象方法是没有意义的code
五、抽象类中必定有构造函数。主要为了初始化抽象类中的属性。一般由子类实现
六、final和abstract是否能够同时修饰一个方法,由于用final修饰后,修饰类表明不能够继承,修饰方法不可重写,abstract修饰类就是用来被继承的,修饰方法就是用来被重写的
abstract不能与private修饰同一个方法,由于privte成员对外是不可见的,只能在本类中使用,这样子类就没法重写抽象方法
abstract不能与static修饰同一个方法,static修饰的方法能够用类名调用,而对于abstract修饰的方法没有具体的方法实现,全部不能直接调用
接口,英文称做interface,在软件工程中,接口泛指供别人调用的方法或者函数。从这里,咱们能够体会到Java语言设计者的初衷,它是对行为的抽象,而没有具体的实现,接口自己不是类。同时实现该接口的实现类必需要实现该接口的全部方法,经过使用implements关键字,他表示该类在遵循某个或某组特定的接口,同时也表示着“interface只是它的外貌,可是如今须要声明它是如何工做的”。
接口是抽象类的延伸,java为了了保证数据安全是不能多重继承的,也就是说继承只能存在一个父类,可是接口不一样,一个类能够同时实现多个接口,无论这些接口之间有没有关系,因此接口弥补了抽象类不能多重继承的缺陷,可是推荐继承和接口共同使用,由于这样既能够保证数据安全性又能够实现多重继承。接口声明形式以下:
public interface InterfaceName { }
在使用接口过程当中须要注意以下几个问题:
一、一个Interface的方全部法访问权限自动被声明为public。确切的说只能为public,固然你能够显示的声明为protected、private,可是编译会出错!
二、接口中定义的全部变量默认是public static final的,即静态常量既然是常量,那么定义的时候必须赋值,能够经过接口名直接访问:ImplementClass.name。
三、接口中定义的方法不能有方法体。接口中定义的方法默认添加public abstract
四、有抽象函数的不必定是抽象类,也能够是接口类。
五、因为接口中的方法默认都是抽象的,因此接口不能被实例化。
六、类实现接口经过implements实现,实现接口的非抽象类必需要实现该接口的全部方法,抽象类能够不用实现。
七、若是实现类要访问接口中的成员,不能使用super关键字。由于二者之间没有显示的继承关系,何况接口中的成员成员属性是静态的
八、接口没有构造方法。
九、不能使用new操做符实例化一个接口,但能够声明一个接口变量,该变量必须引用(refer to)一个实现该接口的类的对象。可使用 instanceof 检查一个对象是否实现了某个特定的接口。
例如:if(anObject instanceof Comparable){}。
十、在实现多接口的时候必定要避免方法名的重复。
一、语法层面上的区别
1)抽象类能够提供成员方法的实现细节(即普通方法),而接口中只能存在public abstract 方法;
2)抽象类中的成员变量能够是各类类型的,而接口中的成员变量只能是public static final类型的;
3)接口中不能含有静态代码块以及静态方法,而抽象类能够有静态代码块和静态方法;
4)一个类只能继承一个抽象类,而一个类却能够实现多个接口,Java是单继承,多实现。
二、设计层面上的区别
1)抽象类是对一种事物的抽象,即对类抽象,而接口是对行为的抽象。抽象类是对整个类总体进行抽象,包括属性、行为,可是接口倒是对类局部(行为)进行抽象。
2)抽象类所体现的是一种继承关系,而继承是一个 "is-a"的关系,而 接口 实现则是 "has-a"的关系。若是一个类继承了某个抽象类,则子类一定是抽象类的种类,而接口实现则是有没有、具有不具有的关系。好比:将鸟设计为一个类Bird,可是不能将 飞行 这个特性也设计为类,所以它只是一个行为特性,并非对一类事物的抽象描述。此时能够将 飞行 设计为一个接口Fly,包含方法fly( ),对于不一样种类的鸟直接继承Bird类便可,而鸟是否能飞(或者是否具有飞行这个特色),能飞行则能够实现这个接口,不能飞行就不实现这个接口。
3)设计层面不一样,抽象类做为不少子类的父类,它是一种模板式设计。而接口是一种行为规范,它是一种辐射式设计。对于抽象类,若是须要添加新的方法,能够直接在抽象类中添加具体的实现,子类能够不进行变动;而对于接口则不行,若是接口进行了变动,则全部实现这个接口的类都必须进行相应的改动。
继承是使用已存在的类的定义做为基础创建新类的技术,新类的定义能够增长新的数据或新的功能,也能够用父类的功能,但不能选择性地继承父类。经过使用继承咱们可以很是方便地复用之前的代码,可以大大的提升开发的效率。
继承的特色:
一、子类拥有父类非private的属性和方法
二、子类能够拥有本身属性和方法,即子类能够对父类进行扩展
三、子类能够用本身的方式实现父类的方法(方法重写)
四、构造函数不能被继承
五、继承使用extends关键字实现
子类重写父类的函数的时候,返回值类型必须是父类函数的返回值类型或该返回值类型的子类,不能返回比父类更大的数据类型;
若是子类的对象调用方法,默认先使用this进行查找,若是当前对象没有找到属性或方法,找当前对象中维护的super关键字指向的对象,若是尚未找到编译报错,找到直接调用。
全部的重载函数必须在同一个类中
多态存在的三个必要条件
多态弊端: 提升扩展性,可是只能使用父类引用指向父类成员。
注意:
在多态的状况下,字符类存在同名的成员(成员变量和成员函数)时,访问的是父类的成员,只有是同名的非静态成员函数时,才访问子类的成员函数;
多态用于形参类型时,能够接受多个类型的数据;
多态用于返回类型时,能够返回多个类型的数据,使用了多态的方法,定义的变量类型要与返回的类型一致。
如下面例子来分析多态
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
首先咱们先看一句话:当超类对象引用变量引用子类对象时,被引用对象的类型而不是引用变量的类型决定了调用谁的成员方法,可是这个被调用的方法必须是在超类中定义过的,也就是说被调用的方法必须是被子类重写的方法。这句话对多态进行了一个归纳。其实在继承链中对象方法的调用存在一个优先级:this.show(O)、super.show(O)、this.show((super)O)、super.show((super)O)。
对于前半句的意思就是:当父类变量引用子类对象时,在调用成员函数时,应该调用向子类的成员函数,但前提是此函数时被子类重写的函数。
A B C D的继承关系以下:
分析:
对于1和2,B和C属于A的子类,调用a1.show(b),a1.show(b),能够找到A.show(A boj),由于多态状况下,父类作形参时,能够接受其子类的实参。
对于3,直接就能够找到A.show(D odj)。
对于4,原本因为a2引用的是其子类B的一个对象,所以调用的成员函数应为B.show(B obj),可是因为B.show(B obj)不是重写的函数,所以不会调用B.show(B obj)。故将按照优先级,先看this.show(O),而类A里面没有找到show(B obj)方法,因而到A的super(超类)找,而A没有超类,所以转到第三优先级this.show((super)O),this仍然是a2,这里O为B,(super)O即(super)B即A,所以它到类A里面找show(A obj)的方法,类A有这个方法,可是因为a2引用的是类B的一个对象,且B覆盖了A的show(A obj)方法,所以最终锁定到类B的show(A obj),输出为"B and A”。
对于5,一样将按照优先级,先看this.show(O),而类A里面没有找到show(C obj)方法,因而到A的super(超类)找,而A没有超类,所以转到第三优先级this.show((super)O),this仍然是a2,这里O为C,因为A是C的超类,所以它到类A里面找show(A obj)的方法,类A有这个方法,可是因为a2引用的是类B的一个对象,且B覆盖了A的show(A obj)方法,所以最终锁定到类B的show(A obj),输出为"B and A”。
对于6,一样将按照优先级,先看this.show(O),而类A里面恰好找到了show(D obj)方法,输出为"D and A”.
对于7,能够直接调用this.show(O)。
对于8,一样将按照优先级,先看this.show(O),而类B里面没有找到show(C obj)方法,因而到B的super(超类)找,而类A里面没有找到show(C obj)方法,所以转到第三优先级this.show((super)O),this仍然是b,这里O为C,因为B是C的超类,所以它到类B里面找show(B obj)的方法,所以输出为"B and B”。
对于9,一样将按照优先级,先看this.show(O),而类B里面没有找到show(D obj)方法,因而到B的super(超类)找,而类A里面找到了show(D obj)方法,所以输出为"A and D”。
封装是指利用抽象数据类型将数据和基于数据的操做封装在一块儿,使其构成一个不可分割的独立实体,数据被保护在抽象数据类型的内部,尽量地隐藏内部的细节,只保留一些对外接口使之与外部发生联系。系统的其余对象只能经过包裹在数据外面的已经受权的操做来与这个封装的对象进行交流和交互。也就是说用户是无需知道对象内部的细节(固然也无从知道),但能够经过该对象对外的提供的接口来访问该对象。
使用封装有四大好处:
一、良好的封装可以减小耦合。
二、类内部的结构能够自由修改。
三、能够对成员进行更精确的控制。
四、隐藏信息,实现细节。