抽象类
当描述一批对象的时候,我们通常会将一些共有的方法和属性进行向上抽取,抽取出它们的父类,通过继承达到代码复用的目的。
但是,每个子类的方法某些方法业务都是独一无二的,它们都重写了父类中定义的该方法。而这个时候,父类就没有必要提供该方法的具体实现了,只需要将该方法定义为抽象方法,而且该类的存在也仅仅是为了让子类继承,实现代码的复用,那么我们就可以将该类定义为抽象类。
抽象类必须使用abstract修饰符来修饰类名,有抽象方法的类只能被定义成抽象类,抽象类里可以没有抽象方法,也可以有具体实现的方法。
规则:
1、 抽象类必须使用abstract修饰符来修饰类,抽象方法也必须使用abstract来修饰,抽象方法不能有方法体。
2、 抽象类不能被实例化,不能使用new关键字来调用抽象类的构造器创建抽象类的实例。即使抽象类里不包含抽象方法,也不能被创建实例。
3、 抽象类可以包含成员变量、方法(普通方法和抽象方法都可以)、构造器、初始化块、内部类5种成分。抽象类的构造器不能用来创建实例,主要是用于被其子类调用。
abstract class Fu
{
publicFu(String str){
System.out.println(str);
}
}
class Zi1 extends Fu
{
publicZi1(){
super("abcd");
}
}
4、含有抽象方法的类只能被定义为抽象类。
定义抽象方法只需要在普通方法上增加abstract修饰符,并且把方法体去掉,在后面加分号即可。
注意:抽象方法和空方法完全不是同一个概念。一个是抽象方法,一个是普通方法,只不过普通方法没有具体代码。
定义抽象类
只需要在普通类上增加abstract修饰符即可。甚至一个普通类(没有抽象方法)增加abstract修饰符后也将变成抽象类。
//定义Shape抽象类和实现类三角形,圆形求周长方法。获取形状方法
class AbstractTest
{
//使用抽象类,进行解耦操作
static XingZhuang xz;
public static void main(String[] args){
if(true){
xz= new Circle(12.12 ,"红色");
}else{
xz= new Rect(12,12,"黑色");
}
double zc= xz.zhouChang();
String info = xz.getInfo();
}
}
abstract class XingZhuang //形状
{
abstract double zhouChang();
abstract String getInfo();
String color;
}
class Circle extends XingZhuang
{
public Circle(double radius , String color){
this.radius= radius;
this.color= color;
}
double radius;
double zc= 0;
double zhouChang(){
zc =Math.PI * 2 * radius;
return zc;
}
String getInfo(){
return color + "圆形的周长是:"+ zc;
}
}
class Rect extends XingZhuang{
double chang;
double kuang;
double zc= 0;
public Rect(double chang , double kuang,String color){
this.chang= chang;
this.kuang= kuang;
this.color= color;
}
double zhouChang(){
zc = (chang + kuang) * 2;
return zc;
}
String getInfo(){
return color + "矩形的周长是:"+ zc;
}
}
抽象类的作用
抽象类不能被创建实例,只能当成父类被继承。普通类描述对象是对某一类事物的抽象,而抽象类则是更高层次的抽象。从多个具有相同特征的类中抽象出一个抽象类,以这个抽象类作为其子类的模板,从而避免了子类设计的随意性。
抽象类体现的就是一种模板的设计,抽象类作为多个子类的模板,子类建立在抽象类的基础上进行扩展,改造,但子类总体上大致会保留这些抽象类的行为。
如果编写一个抽象类,父类可以提供多个子类通用的抽象方法,并把具体实现推迟到子类去实现。当然,父类也可以提供通用的普通方法,这些普通方法可以作为辅助方法用来辅助子类。
接口
抽象类是从多个类中抽象出来的模板,如果将这种抽象进行的更加彻底,则可以提炼出一种更加特殊的抽象类--接口(interface),接口里不能包含普通方法,接口里所有的方法都是抽象方法。
接口的概念:
在计算机主板上,有N多个接口插槽,供我们拓展计算机的功能,如显卡、内存、硬盘、声卡等。主板只是定义了一下接口规则,这些生产显卡、内存等厂家生产的产品必须实现该接口的规则,才可以将它们的产品插到主板上。
那么,在java中,接口就是定义了一种规范,接口定义的某一批类所需要遵守的规范,接口并不关心这些类的内部状态数据,也不关心这些类里的方法实现的细节,它只是规定了这批类里必须提供某些方法,提供这些方法的类就可以满足实际需要。
可见,接口是从多个类中抽象出来的规范,接口不提供任何实现。接口体现的是规范和实现分离的设计哲学。
class JieKou
{
public static void main(String[] args){
DianNao dn = new DianNao();
//调用方法,传入实现Input接口的实现类
dn.getInput(new JianPan());
//调用方法,传入实现Input接口的实现类
dn.getInput(new ShouXie());
}
}
class DianNao
{
//定义了获取信息需要的组件,只需要遵循Input接口就OK
void getInput(Input input){
System.out.println("输入了" + input. inputMethod());
}
}
//定义了输入设备的规范
interface Input
{
String inputMethod();
}
//遵循了输入设备的规范的键盘
class JianPan implements Input
{
publicString inputMethod(){
return"键盘";
}
}
//遵循了输入设备的规范的手写板
class ShouXie implements Input
{
public String input Method(){
return "手写";
}
}
让规范和实现分离正是接口的好处,弱化了各个组件之间的依赖性,即松耦合设计。例如主机上的各种插槽,只要显卡遵守规范,就可以与主机进行通信,至于显卡是如何设计,内部怎么实现,主板无需关心。
类似的,软件系统之间的各个模块之间也应该采用这种面向接口的耦合,从而降低各个模块之间的耦合,为系统提供了更好的可扩展性和可维护性。
因此,接口定义的是多个类共同的公共行为的规范,这些行为是与外部交流的通道,这就意味着接口里通常是定义一组公用方法。
定义规则:
interface 接口名 extends 父接口1、父接口2
{
0到多个抽象方法的定义
0到多个内部类、接口、枚举
0到多个默认方法或类方法
}
详细说明:
1、命名规范与类名一致。大驼峰。
2、接口可以有多个直接父接口,但是接口只能继承接口,不可以继承类。
由于接口定义的是一种规范,因此接口里不能包含构造器和初始化块。接口可以包含成员变量(只能是静态常量)、方法(只能是抽象实例方法、类方法或默认方法)、内部类(包括内部接口、枚举)定义。
对比接口和类的定义方式,不难发现接口的成员比类里的成员少了两种,而且接口里的成员变量只能是静态常量,接口里的方法只能是抽象方法。
class JieKou
{
public static void main(String[] args){
DaoDian daoDian = new DaoDianImpl();
String[] cusInfo = daoDian.cusInfo();
String mrs = daoDian.mrs(cusInfo[0]);
System.out.println(mrs);
daoDian.zhixing(mrs, cusInfo[1]);
}
}
//顾客到店服务
interface DaoDian{
//获取预约顾客的信息
String[] cusInfo();
//安排顾客预约的美容师
String mrs(String name);
//执行预约的服务
void zhixing(String name , String fuwu);
}
class DaoDianImpl implements DaoDian
{
//获取预约顾客的信息
public String[] cusInfo(){
String[] arr = new String[]{"顾客小红" , "Spa"};
retur narr;
}
//安排顾客预约的美容师
public String mrs(String name){
if(name.equals("顾客小红")){
return "小兰";
}
return "小明";
}
//执行预约的服务
public void zhixing(String name , String fuwu){
System.out.println(name+"美容师执行了" +fuwu+"项目");
}
}
前面已经说了,接口里定义的是多个类共同的公共行为规范,因此接口里的所有成员,包括常量、方法、内部类都是public访问权限。定义接口成员时,可以省略访问控制修饰符,如果指定访问控制修饰符,则只能使用public访问控制修饰符。
对于接口里的定义的静态常量而言,他们是接口相关的,因此系统会自动为这些成员变量增加static和final两个修饰符。也就是说,在接口中定义成员变量时,不管是否使用public static final修饰符,接口里的成员变量总是使用者3个修饰符来修饰,而且接口里没有构造器和初始化块,因此接口里定义的成员变量只能是在定义时指定默认值。
如下两行代码的结果是完全一样的。
int MAX_SIZE = 50;
public static final int MAX_SIZE = 50;
接口里定义的方法只能是抽象方法,因此系统会将自动为普通方法增加public abstract修饰符。
1、 属性一律使用静态修饰符:
如下,接口中定义属性没有使用static修饰,但是在测试类中,直接使用接口.name就可以访问该属性,说明该属性是静态的。
属性权限修饰符是:public
如下,属性是默认修饰符,但是在不同包下的类中,也可以访问。
属性默认使用final修饰:
2、 方法默认使用abstract、public
在接口中定义方法,使用默认权限修饰,子类也使用默认权限修饰,报错,说接口的该方法是public,证明了,接口方法默认使用public修饰。
接口的继承:
接口的继承和类的继承不一样,接口是完全支持多继承的,即一个接口可以有多个直接父接口。和类继承想死,接口扩展某个父接口,将会获得负借口定义的所有抽象方法、常量。
一个接口继承多个父接口时,多个父接口排在extends关键字之后,多个父接口之间以英文逗号隔开。
//TODO
class JieKou implements JieKouC,JieKouB,JieKouA
{
public void methodc(){}
public void methodb(){}
public void methoda(){}
}
interface JieKouA
{
void methoda();
}
interface JieKouB
{
void methodb();
}
interface JieKouC
{
void methodc();
}
接口的多层继承,实现接口,如果接口还有父类接口,那么实现类必须实现包括接口和其父接口的所有抽象方法:
class JieKou implements JieKouC
{
public void methodc(){}
public void methodb(){}
public void methoda(){}
}
interface JieKouA
{
void methoda();
}
interface JieKouB extends JieKouA
{
void methodb();
}
interface JieKouC extends JieKouB
{
void methodc();
}
使用接口
接口不能用于创建实例,但接口可以用于声明引用类型变量。当使用接口来声明引用类型变量时,这个引用类型变量必须引用到其实现类的对象,这也是属于多态特性。
class JieKou
{
public static void main(String[] args){
Person person = new Man();
}
}
interface Person{
}
class Man implements Person
{
}
接口的主要用途就是被实现类实现,除此之外,,接口主要用途如下:
1、 定义变量,也可以用于进行强制类型转换。
class JieKou
{
public static void main(String[] args){
Manman = new Man();
Person person = (Person)man;
}
}
2、 调用接口中定义的常量。
3、被其他类实现,实现功能的扩展
一个类可以实现一个或多个接口,继承只能使用extends关键字实现单继承,实现接口使用implements关键字多实现。
//TODO
class JieKou
{
public static void main(String[] args){
Man man = new Man();
Person person = (Person)man;
}
}
class Person{
}
class Man extends Person implements Working,ShanYang
{
void zhengqian(){}
void xiwan(){}
void shanYang(){}
}
class WoMan extends Person implements ShanYang
{
void shanYang(){}
}
interface Working
{
void zhengqian();
void xiwan();
}
interface ShanYang
{
void shanYang();
}
实现接口与继承父类相似,一样可以获得所实现接口里定义的常量(成员变量)、方法(包括抽象方法和默认方法)
让类实现接口需要定义后增加implements部分,当需要实现多个接口时,使用逗号隔开,一个类可以继承一个父类,并且同时实现多个接口。implements必须放在extends之后。
一个类实现一个或多个接口后,这个类必须实现这些接口里所定义的全部抽象方法(也就是重写),否则就定义为抽象类。
一个类实现某个接口,就会拥有该接口的定义的常量、方法等。因此也可以将实现接口理解为一种特殊的继承。相当于一个更加彻底的抽象类。
//TODO实现两个接口,两个接口都可以接受该对象
class JieKou
{
public static void main(String[] args){
Working working = new Man();
ShanYang shanYang = (ShanYang)working;
}
}
class Person{
}
class Man extends Person implements Working,ShanYang
{
public void zhengqian(){}
public void xiwan(){}
public void shanYang(){}
}
interface Working
{
void zhengqian();
void xiwan();
}
interface ShanYang
{
void shanYang();
}
接口不能继承任何类,但是所有接口类型的变量都可以直接赋值给Object类型的引用变量。因为既然是存在的对象,那么肯定是Object对象的子类对象。
class JieKou
{
public static void main(String[] args)
{
Working working = new Man();
Object obj =working;
}
}
接口和抽象类区别
接口和抽象类很相似,都具有如下特征:
1、接口和抽象类都不能被实例化,他们都需要被继承或实现。
2、接口和抽象类都可以包含抽象方法,实现或继承他们的普通类必须重写抽象方法。
但接口和抽象差别也是非常大,这种差别主要体现在二者的设计目的上。
接口作为系统与外界的交互的窗口,接口体现的是一种规范,对于接口的实现者而言,接口规定了实现者必须向外提供哪些服务,对于接口调用者而言,接口规定了调用者可以调用哪些服务,以及如何调用。当一个程序中使用了接口时,接口是多个模块间的耦合标准。当多个程序之间使用了接口,那么该接口就是多个程序之间的通讯标准。
抽象类则不一样,抽象类作为多个子类共同父类,他所体现的是一种模板设计。抽象类作为多个子类的抽象父类,可以被当成系统实现过程中的中间产品,必须有更进一步的完善。
除此之外,在使用用法上也有差别:
1、接口里只能包含抽象方法,抽象类则完全可以包含普通方法。
2、接口不能定义静态方法,抽象类可以。
3、接口只能定义静态常量,不能定义普通成员变量,抽象类则可以定义静态常量,也可以定义普通成员变量。
4、接口不含构造器,抽象类可以包含构造器和构造代码块。
抽象类是可以被实例化,只不过是子类对象创建的时候,创建抽象类父类对象。所以,抽象类是可以有静态代码块,构造代码块,构造方法,抽象类就既可以有实例变量、也可以有静态变量。
接口没有构造器、构造代码块,所以,接口不能被实例化,也不可能有实例变量。
接口没有静态代码块,那么,就无法为接口定义的常量赋值,那么接口就规定:定义接口属性是需要在声明时直接赋值。
4、 一个类最多只能有一个直接父类,包括抽象类;但一个类可以多个实现接口,通过实现多个接口可以弥补java单继承的不足。
当新增功能时,发现,该功能并不属于该类的父类,就需要使用接口,来扩展一下该功能。
面向接口编程:
接口体现的是一种规范和实现分离的设计哲学,充分利用接口可以降低各个模块之间的耦合性,从而提高系统的扩展性和维护性。
面向接口编程设计在这里不做太多解释,这个不属于现阶段内容,如果有兴趣可以参阅:
基于这种原则,很多软件架构设计都倡导“面向接口”编程。而不是面向实现类编程。希望通过面向接口编程来降低程序的耦合。
工厂模式
工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
介绍
意图:定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。
主要解决:主要解决接口选择的问题。
何时使用:我们明确地计划不同条件下创建不同实例时。
如何解决:让其子类实现工厂接口,返回的也是一个抽象的产品。
关键代码:创建过程在其子类执行。
应用实例: 1、您需要一辆汽车,可以直接从工厂里面提货,而不用去管这辆汽车是怎么做出来的,以及这个汽车里面的具体实现。 2、Hibernate 换数据库只需换方言和驱动就可以。
优点: 1、一个调用者想创建一个对象,只要知道其名称就可以了。 2、扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。 3、屏蔽产品的具体实现,调用者只关心产品的接口。
缺点:每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事。
使用场景: 1、日志记录器:记录可能记录到本地硬盘、系统事件、远程服务器等,用户可以选择记录日志到什么地方。 2、数据库访问,当用户不知道最后系统采用哪一类数据库,以及数据库可能有变化时。 3、设计一个连接服务器的框架,需要三个协议,"POP3"、"IMAP"、"HTTP",可以把这三个作为产品类,共同实现一个接口。
注意事项:作为一种创建类模式,在任何需要生成复杂对象的地方,都可以使用工厂方法模式。有一点需要注意的地方就是复杂对象适合使用工厂模式,而简单对象,特别是只需要通过 new 就可以完成创建的对象,无需使用工厂模式。如果使用工厂模式,就需要引入一个工厂类,会增加系统的复杂度。
http://www.runoob.com/design-pattern/factory-pattern.html
//TODO
class Test
{
public static void main(String[] args){
//调用工厂类获取对象方法,传递对象信息
HuaTu huatu = Factory.getHuaTu("Rect");
//调用返回对象的画图方法
huatu.huaTu();
}
}
interface HuaTu
{
void huaTu();
}
class Circle implements HuaTu
{
public void huaTu(){
System.out.println("绘制圆形");
}
}
class Rect implements HuaTu
{
public void huaTu(){
System.out.println("绘制矩形");
}
}
class Factory
{
//接收传入信息
static HuaTu getHuaTu(String str){
//根据传入信息判断返回什么类型对象
if(str.equals("Circle")){
return new Circle();
}elseif(str.equals("Rect")){
return new Rect();
}
return null;
}
}