1、 抽象类
看我们以前示例中的Person、Student和Employee,从我们使用的角度来看主要对Student和Employee进行实例化,Person中主要包含了一些公共的属性和方法,而Person我们通常不会实例化,所以我们可以把它定义成抽象的:
- 在java中采用abstract关键字定义的类就是抽象类,采用abstract关键字定义的方法就是抽象方法
 - 抽象的方法只需在抽象类中,提供声明,不需要实现
 - 如果一个类中含有抽象方法,那么这个类必须定义成抽象类
 - 如果这个类是抽象的,那么这个类被子类继承,抽象方法必须被重写。如果在子类中不复写该抽象方法,那么必须将此类再次声明为抽象类
 - 抽象的类是不能实例化的,就像现实世界中人其实是抽象的,张三、李四才是具体的
 - 抽象类不能被final修饰(它是需要被继承的)
 - 抽象方法不能被final修饰,因为抽象方法就是被子类实现的
 
抽象类中可以包含方法实现,可以将一些公共的代码放到抽象类中,另外在抽象类中可以定义一些抽象的方法,这样就会存在一个约束,而子类必须实现我们定义的方法,如:teacher必须实现printInfo方法,Student也必须实现printInfo方法,方法名称不能修改,必须为printInfo,这样就能实现多态的机制,有了多态的机制,我们在运行期就可以动态的调用子类的方法。所以在运行期可以灵活的互换实现。

1、采用abstract声明抽象类
public class AbstractTest01 {public static void main(String[] args) {//不能实例化抽象类//抽象类是不存在,抽象类必须有子类继承Person p = new Person();//以下使用是正确的,因为我们new的是具体类Person p1 = new Employee();p1.setName("张三");System.out.println(p1.getName());}}//采用abstract定义抽象类//在抽象类中可以定义一些子类公共的方法或属性//这样子类就可以直接继承下来使用了,而不需要每个//子类重复定义abstract class Person {private String name;public void setName(String name) {this.name = name;}public String getName() {return name;}//此方法各个子类都可以使用public void commonMethod1() {System.out.println("---------commonMethod1-------");}}class Employee extends Person {}class Student extends Person {}
2、抽象的方法只需在抽象类中,提供声明,不需要实现,起到了一个强制的约束作用,要求子类必须实现
public class AbstractTest02 {public static void main(String[] args) {//Person p = new Employee();//Person p = new Student();//Person p = new Person();p.setName("张三");p.printInfo();}}abstract class Person {private String name;public void setName(String name) {this.name = name;}public String getName() {return name;}//此方法各个子类都可以使用public void commonMethod1() {System.out.println("---------commonMethod1-------");}//public void printInfo() {// System.out.println("------Person.printInfo()--------");//}//采用abstract定义抽象方法//如果有一个方法为抽象的,那么此类必须为抽象的//如果一个类是抽象的,并不要求具有抽象的方法public abstract void printInfo();}class Employee extends Person {//必须实现抽象的方法public void printInfo() {System.out.println("Employee.printInfo()");}}class Student extends Person {//必须实现抽象的方法public void printInfo() {System.out.println("Student.printInfo()");}}
3、如果这个类是抽象的,那么这个类被子类继承,抽象方法必须被覆盖。如果在子类中不覆盖该抽象方法,那么必须将此方法再次声明为抽象方法
public class AbstractTest03 {public static void main(String[] args) {//此时不能再new Employee了Person p = new Employee();}}abstract class Person {private String name;public void setName(String name) {this.name = name;}public String getName() {return name;}//此方法各个子类都可以使用public void commonMethod1() {System.out.println("---------commonMethod1-------");}//采用abstract定义抽象方法public abstract void printInfo();}abstract class Employee extends Person {//再次声明该方法为抽象的public abstract void printInfo();}class Student extends Person {//实现抽象的方法public void printInfo() {System.out.println("Student.printInfo()");}}
4、抽象类不能被final修饰
public class AbstractTest04 {public static void main(String[] args) {}}//不能采用final修改抽象类//两个关键字是矛盾的final abstract class Person {private String name;public void setName(String name) {this.name = name;}public String getName() {return name;}//此方法各个子类都可以使用public void commonMethod1() {System.out.println("---------commonMethod1-------");}//采用abstract定义抽象方法public abstract void printInfo();}class Employee extends Person {//实现抽象的方法public void printInfo() {System.out.println("Student.printInfo()");}}class Student extends Person {//实现抽象的方法public void printInfo() {System.out.println("Student.printInfo()");}}
5、抽象方法不能被final修饰
public class AbstractTest05 {public static void main(String[] args) {}}abstract class Person {private String name;public void setName(String name) {this.name = name;}public String getName() {return name;}//此方法各个子类都可以使用public void commonMethod1() {System.out.println("---------commonMethod1-------");}//不能采用final修饰抽象的方法//这两个关键字存在矛盾public final abstract void printInfo();}class Employee extends Person {//实现抽象的方法public void printInfo() {System.out.println("Student.printInfo()");}}class Student extends Person {//实现抽象的方法public void printInfo() {System.out.println("Student.printInfo()");}}
6、抽象类中可以没有抽象方法(参见1.16.1)
2、 接口(行为)
接口我们可以看作是抽象类的一种特殊情况,在接口中只能定义抽象的方法和常量
1) 在java中接口采用interface声明
2) 接口中的方法默认都是public abstract的,不能更改
3) 接口中的变量默认都是public static final类型的,不能更改,所以必须显示的初始化
4) 接口不能被实例化,接口中没有构造函数的概念
5) 接口之间可以继承,但接口之间不能实现
6) 接口中的方法只能通过类来实现,通过implements关键字
7) 如果一个类实现了接口,那么接口中所有的方法必须实现
8) 一类可以实现多个接口
9) 接口可以继承多个接口
1、接口中的方法默认都是public abstract的,不能更改
public class InterfaceTest01 {public static void main(String[] args) {}}//采用interface定义接口//定义功能,没有实现//实现委托给类实现interface StudentManager {//正确,默认public abstract 等同public abstract void addStudent(int id, String name);public void addStudent(int id, String name);//正确//public abstract void addStudent(int id, String name);//正确,可以加入public修饰符,此种写法较多public void delStudent(int id);//正确,可以加入abstract,这种写法比较少public abstract void modifyStudent(int id, String name);//编译错误,因为接口就是让其他人实现//采用private就和接口原本的定义产生矛盾了private String findStudentById(int id);}
2、接口中的变量是public static final类型的,不能更改,所以必须显示的初始化
public class InterfaceTest02 {public static void main(String[] args) {//不能修改,因为final的//StudentManager.YES = "abc";System.out.println(StudentManager.YES);}}interface StudentManager {//正确,默认加入public static finalString YES = "yes";//正确, 开发中一般就按照下面的方式进行声明public static final String NO = "no";//错误,必须赋值,因为是final的//int ON;//错误,不能采用private声明private static final int OFF = -1;}
3、接口不能被实例化,接口中没有构造函数的概念
public class InterfaceTest03 {public static void main(String[] args) {//接口是抽象类的一种特例,只能定义方法和变量,没有实现//所以不能实例化StudentManager studentManager = new StudentManager();}}interface StudentManager {public void addStudent(int id, String name);}
4、接口之间可以[多]继承,但接口之间不能实现
public class InterfaceTest04 {public static void main(String[] args) {}}interface inter1 {public void method1();public void method2();}interface inter2 {public void method3();}//接口可以继承interface inter3 extends inter1 {public void method4();}//接口不能实现接口//接口只能被类实现interface inter4 implements inter2 {public void method3();}
5、如果一个类实现了接口,那么接口中所有的方法必须实现
public class InterfaceTest05 {public static void main(String[] args) {//Iter1Impl实现了Inter1接口//所以它是Inter1类型的产品//所以可以赋值Inter1 iter1 = new Iter1Impl();iter1.method1();//Iter1Impl123实现了Inter1接口//所以它是Inter1类型的产品//所以可以赋值iter1 = new Iter1Impl123();iter1.method1();//可以直接采用Iter1Impl来声明类型//这种方式存在问题//不利于互换,因为面向具体编程了Iter1Impl iter1Impl = new Iter1Impl();iter1Impl.method1();//不能直接赋值给iter1Impl//因为Iter1Impl123不是Iter1Impl类型//iter1Impl = new Iter1Impl123();//iter1Impl.method1();}}//接口中的方法必须全部实现class Iter1Impl implements Inter1 {public void method1() {System.out.println("method1");}public void method2() {System.out.println("method2");}public void method3() {System.out.println("method3");}}class Iter1Impl123 implements Inter1 {public void method1() {System.out.println("method1_123");}public void method2() {System.out.println("method2_123");}public void method3() {System.out.println("method3_123");}}abstract class Iter1Impl456 implements Inter1 {public void method1() {System.out.println("method1_123");}public void method2() {System.out.println("method2_123");}//再次声明成抽象方法public abstract void method3();}//定义接口interface Inter1 {public void method1();public void method2();public void method3();}
6、一类可以实现多个接口
public class InterfaceTest06 {public static void main(String[] args) {//可以采用Inter1定义Inter1 inter1 = new InterImpl();inter1.method1();//可以采用Inter1定义Inter2 inter2 = new InterImpl();inter2.method2();//可以采用Inter1定义Inter3 inter3 = new InterImpl();inter3.method3();}}//实现多个接口,采用逗号隔开//这样这个类就拥有了多种类型//等同于现实中的多继承//所以采用java中的接口可以实现多继承//把接口粒度划分细了,主要使功能定义的含义更明确//可以采用一个大的接口定义所有功能,替代多个小的接口,//但这样定义功能不明确,粒度太粗了class InterImpl implements Inter1, Inter2, Inter3 {public void method1() {System.out.println("----method1-------");}public void method2() {System.out.println("----method2-------");}public void method3() {System.out.println("----method3-------");}}interface Inter1 {public void method1();}interface Inter2 {public void method2();}interface Inter3 {public void method3();}/*interface Inter {public void method1();public void method2();public void method3();}*/
7、一个接口可以继承多个几口
//声明接口:interfaceinterface A{public static final int CONST = 1;void a();//接口中的方法默认都是public abstract的}interface K{void k();}//接口可以继承多个接口interface B extends A,K{void b();int d();}
3、 接口的进一步应用
在java中接口其实描述了类需要做的事情,类要遵循接口的定义来做事,使用接口到底有什么本质的好处?可以归纳为两点:(提供了标准、规范)
- 采用接口明确的声明了它所能提供的服务
 - 解决了Java单继承的问题(一个类可以实现多个接口,一个接口可以继承多个接口)
 - 实现了可接插性(重要)
 
示例:完成学生信息的增删改操作,系统要求适用于多个数据库,如:适用于Oracle和MySQL;
第一种方案,不使用接口,每个数据库实现一个类:
//Oracle的实现public class StudentOracleImpl {public void add(int id, String name) {System.out.println("StudentOracleImpl.add()");}public void del(int id) {System.out.println("StudentOracleImpl.del()");}public void modify(int id, String name) {System.out.println("StudentOracleImpl.modify()");}}
需求发生变化了,客户需要将数据移植Mysql上
////Mysql的实现public class StudentMysqlImpl {public void addStudent(int id, String name) {System.out.println("StudentMysqlImpl.addStudent()");}public void deleteStudent(int id) {System.out.println("StudentMysqlImpl.deleteStudent()");}public void udpateStudent(int id, String name) {System.out.println("StudentMysqlImpl.udpateStudent()");}}
调用以上两个类完成向Oracle数据库和Mysql数据存储数据
public class StudentManager {public static void main(String[] args) {//对Oracle数据库的支持/*StudentOracleImpl studentOracleImpl = new StudentOracleImpl();studentOracleImpl.add(1, "张三");studentOracleImpl.del(1);studentOracleImpl.modify(1, "张三");*///需要支持Mysql数据库StudentMysqlImpl studentMysqlImpl = new StudentMysqlImpl();studentMysqlImpl.addStudent(1, "张三");studentMysqlImpl.deleteStudent(1);studentMysqlImpl.udpateStudent(1, "张三");}}
以上代码不能灵活的适应需求,当需求发生改变需要改动的代码量太大,这样可能会导致代码的冗余,另外可能会导致项目的失败,为什么会导致这个问题,在开发中没有考虑到程序的扩展性,就是一味的实现,这样做程序是不行的,所以大的项目比较追求程序扩展性,有了扩展性才可以更好的适应需求。
第二种方案,使用接口
UML,统一建模语言
public class Student4OracleImpl implements IStudent {public void add(int id, String name) {System.out.println("Student4OracleImpl.add()");}public void del(int id) {System.out.println("Student4OracleImpl.del()");}public void modify(int id, String name) {System.out.println("Student4OracleImpl.modify()");}}public class Student4MysqlImpl implements IStudent {public void add(int id, String name) {System.out.println("Student4MysqlImpl.add()");}public void del(int id) {System.out.println("Student4MysqlImpl.del()");}public void modify(int id, String name) {System.out.println("Student4MysqlImpl.modify()");}}public class StudentService {public static void main(String[] args) {/*IStudent istudent = new Student4OracleImpl();IStudent istudent = new Student4MysqlImpl();istudent.add(1, "张三");istudent.del(1);istudent.modify(1, "张三");*///IStudent istudent = new Student4OracleImpl();//IStudent istudent = new Student4MysqlImpl();//doCrud(istudent);//doCrud(new Student4OracleImpl());//doCrud(new Student4MysqlImpl());//doCrud(new Student4OracleImpl());doCrud(new Student4MysqlImpl());}//此种写法没有依赖具体的实现//而只依赖的抽象,就像你的手机电池一样:你的手机只依赖电池(电池是一个抽象的事物),//而不依赖某个厂家的电池(某个厂家的电池就是具体的事物了)//因为你依赖了抽象的事物,每个抽象的事物都有不同的实现//这样你就可以利用多态的机制完成动态绑定,进行互换,是程序具有较高的灵活//我们尽量遵循面向接口(抽象)编程,而不要面向实现编程public static void doCrud(IStudent istudent) {istudent.add(1, "张三");istudent.del(1);istudent.modify(1, "张三");}//以下写法不具有扩展性//因为它依赖了具体的实现//建议不要采用此种方法,此种方案是面向实现编程,就依赖于具体的东西了/*public static void doCrud(Student4OracleImpl istudent) {istudent.add(1, "张三");istudent.del(1);istudent.modify(1, "张三");}*/}
