抽象类的实际应用--模板设计(抽象类是约束子类必须有的方法,具体怎么实现的不管)
接口的实际应用--制定标准
在开发中,一个类永远不要去继承一个已经实现好的类,要么继承抽象类,要么实现接口,如果两个类同时都
可以使用的话,优秀使用接口,避免单继承的局限。
class A{}
class B extends A{}
|
1、抽象类实际应用
abstract class A{
public abstract void fun() ;
};
class B extends A{
public void fun(){
System.out.println("Hello World!!!") ;
}
};
public class Demo32{
public static void main(String args[]){
A a = new B() ;
a.fun() ;
}
};
|
违纪卡
|
|||
姓名:
|
李兴华
|
班级:
|
小班1006
|
时间:
|
1990-1-2
|
事由:
|
上课吃饭
|
abstract class Person{
private String name ;
private int age ;
public Person(String name,int age){
this.name = name ;
this.age = age ;
}
public void say(){
System.out.println(this.getContent()) ;
}
public String getName(){
return this.name ;
}
public int getAge(){
return this.age ;
}
public abstract String getContent() ;
};
class Worker extends Person{
private float salary ;
public Worker(String name,int age,float salary){
super(name,age) ;
this.salary = salary ;
}
public String getContent(){
return "工人说 --> 姓名:"+this.getName()+",年龄:"+this.getAge()+",工资:"+this.salary ;
}
};
class Student extends Person{
private float score ;
public Student(String name,int age,float score){
super(name,age) ;
this.score = score ;
}
public String getContent(){
return "学生说 --> 姓名:"+this.getName()+",年龄:"+this.getAge()+",成绩:"+this.score ;
}
};
public class Demo33{
public static void main(String args[]){
Person p = null ;
// p = new Student("张三",20,99.0f) ;
p = new Worker("张三",20,999.0f) ;
p.say() ;
}
};
|
abstract class Person{
private String name ;
public Person(String name){
this.name = name ;
}
public void call(){
System.out.println(this.getContent()) ;
}
public String getName(){
return this.name ;
}
public abstract String getContent() ;
};
class Teacher extends Person{
public Teacher(String name){
super(name) ;
}
public String getContent(){
return "同学们好,姓名:"+this.getName() ;
}
};
class Student extends Person{
public Student(String name){
super(name) ;
}
public String getContent(){
return "报告,姓名:"+this.getName() ;
}
};
class Class_{
public void comeIn(Person p){
p.call() ;
}
};
public class Demo34{
public static void main(String args[]){
Class_ c = new Class_() ;
c.comeIn(new Student("张三")) ;
c.comeIn(new Teacher("李四")) ;
}
};
|
2、接口的实际应用
interface A{
public void print() ;
}
class B implements A{
public void print(){
System.out.println("Hello World!!!") ;
}
};
public class Demo35{
public static void main(String args[]){
A a = new B() ;
a.print() ;
}
};
|
interface USB{
public void start() ;
public void stop() ;
}
class MainBoard{
public void plugIn(USB usb){
usb.start() ;
usb.stop() ;
}
};
class Mp3 implements USB{
public void start(){
System.out.println("MP3开始工作。") ;
}
public void stop(){
System.out.println("MP3停止工作。") ;
}
};
class Print implements USB{
public void start(){
System.out.println("打印机开始工作。") ;
}
public void stop(){
System.out.println("打印机停止工作。") ;
}
};
public class Demo36{
public static void main(String args[]){
MainBoard mb = new MainBoard() ;
mb.plugIn(new Mp3()) ;
mb.plugIn(new Print()) ;
}
};
|
2.1 工厂设计模式(接口应用)
工厂设计模式是在java开发中最常使用的一种设计模式。
interface Fruit{ // 定义一个水果接口 public void eat() ; // 吃水果 } class Apple implements Fruit{ public void eat(){ System.out.println("** 吃苹果。") ; } }; class Orange implements Fruit{ public void eat(){ System.out.println("** 吃橘子。") ; } }; public class InterfaceCaseDemo03{ public static void main(String args[]){ Fruit f = new Apple() ; // 实例化接口 f.eat() ; } };
运行结果:
吃苹果。
这样的代码可以使吗?有问题吗?
分析:
主方法:就应该表示客户端。主方法的代码越少越好。此时,直接在主方法中指定了要操作的子类,如果要更换子类,肯定要修改客户端。
就表示跟特定的子类耦合在一起了。
JVM工作原理:程序-》JVM(相当于客户端)-》操作系统。
问题的解决:客户端通过过渡端,得到特定子类的接口实例,返回接口实例给客户端,接口实例调用接口中的方法。
、
此过渡端,在程序中就称为工厂设计(工厂类)。
interface Fruit{ // 定义一个水果接口 public void eat() ; // 吃水果 } class Apple implements Fruit{ public void eat(){ System.out.println("** 吃苹果。") ; } }; class Orange implements Fruit{ public void eat(){ System.out.println("** 吃橘子。") ; } }; class Factory{ // 定义工厂类 public static Fruit getInstance(String className){ //注意这里的方法是static修饰的,因为在主方法中是Factory调用 Fruit f = null ; if("apple".equals(className)){ // 判断是否要的是苹果的子类 f = new Apple() ; } if("orange".equals(className)){ // 判断是否要的是橘子的子类 f = new Orange() ; } return f ; } }; public class InterfaceCaseDemo04{ public static void main(String args[]){ Fruit f = Factory.getInstance(”apple“) ; // 实例化接口 f.eat() ; } };
运行结果:
**吃苹果
流程是:客户端(主方法)通过工厂类的getInstance()方法,通过传入的参数判断,该获取实例化哪个子类的实例,
然后把获取的实例通过getInstance()方法返回(return)该实例给主方法中的接口实例,最后通过接口实例调用所需方法。
2.2、 代理设计模式(接口应用)
假设现在有以下这种情况:
1)张三借了李四500块钱。
2)李四不换,张三生气。
3)张三找到王五,王五是讨债公司。
4)王五准备了刀枪
5)把李四欠的钱还回来了。
也就是张三找李四借钱,王五代理了张三。
代理设计:也是在java中应用较多的设计模式,所谓的代理设计就是指一个代理主题来操作真实主题,真实主题执行具体的业务操作,
而代理主题负责其他相关业务。就好比生活中经常用到的代理上网那样,客户通过网络代理连接网络,由代理服务器完成用户权限,上网操作相关操作。
分析结果:不管代理操作也好,真实操作也好,其共同目的就是上网,所以用户关心的是如何上网,至于如何操作的,用户不关心。
interface Network{ public void browse() ; // 浏览 } class Real implements Network{ //真实上网操作类 public void browse(){ System.out.println("上网浏览信息") ; } }; class Proxy implements Network{ //代理类。 private Network network ; // 代理对象 public Proxy(Network network){ //初始化,把真实对象传给代理对象,向上转型操作。 this.network = network ; } public void check(){ System.out.println("检查用户是否合法。") ; } public void browse(){ this.check() ; this.network.browse() ; // 调用真实的主题操作,这里调用的是真实类里的对象。 } }; public class ProxyDemo{ public static void main(String args[]){ Network net = null ; net = new Proxy(new Real()) ;// 指定代理操作,这里两次向上转型操作,第一次向上是实例化代理类,
第二次向上转型是括号中,把真实类对象传入,以便在代理类中调用真实类中的方法。
net.browse() ; // 客户只关心上网浏览一个操作 } };
运行结果:
检查用户是否合法
上网浏览信息
2.3、适配器模式
此设计,在日后学习java 的图形界面用的非常多。
在Java中,一个子类实现了一个接口,则肯定在子类中覆写此接口中全部抽象方法。那么这样一来,
如果一个接口中提供的抽象方法过多,而且没有必要全部实现的话,肯定很浪费,此时就需要一个中间过渡,
但是此过渡又不希望被直接调用,所以将此过渡器定义成抽象类更合适,即:一个接口首先被一个抽象类(此抽象类成为适配器类)继承,
并在此抽象类中实现若干方法(方法体为空),则以后的子类直接继承此抽象类(适配器),就可以有选择的覆写所需方法了。
一个抽象类可以实现一个接口,那么对于抽象类的子类,则必须覆写抽象类和接口的全部(未实现的)抽象方法。(即如果接口中的某类方法在抽象类中实现了(覆写了),那么在抽象类的子类中就可以不用覆写了。因为继承,在父类中的方法,被子类继承了,相当于子类覆写了,子类照样可以调用,覆写该方法。)
interface Window{ // 定义Window接口,表示窗口操作 public void open() ; // 打开 public void close() ; // 关闭 public void activated() ; // 窗口活动 public void iconified() ; // 窗口最小化 public void deiconified();// 窗口恢复大小 } abstract class WindowAdapter implements Window{ public void open(){} ; // 打开 public void close(){} ; // 关闭 public void activated(){} ; // 窗口活动 public void iconified(){} ; // 窗口最小化 public void deiconified(){};// 窗口恢复大小 }; class WindowImpl extends WindowAdapter{ public void open(){ System.out.println("窗口打开。") ; } public void close(){ System.out.println("窗口关闭。") ; } }; public class AdapterDemo{ public static void main(String args[]){ Window win = new WindowImpl() ; win.open() ; win.close() ; } };
运行结果:
窗口打开。
窗口关闭。
此种设计思路,在java的图形界面编程上使用的非常多。但是在javaee上使用不常见。
2.4、 内部类的扩展
之前已经讲过内部类的概念,实际上在一个抽象类中也可以包含一个接口。
abstract class A{ // 定义抽象类 public abstract void printA() ; // 抽象方法 interface B{ // 定义内部接口 public void printB() ; // 定义抽象方法 } }; class X extends A{ // 继承抽象类 public void printA(){ System.out.println("HELLO --> A") ; } class Y implements B{ // 定义内部类实现内部接口 public void printB(){ System.out.println("HELLO --> B") ; } }; }; public class InnerExtDemo01{ public static void main(String args[]){ A.B b = new X().new Y() ; //参考前面内部类的知识,实现内部类实例的方法:外部类.内部类 内部类对象= 外部类对象.new 内部类 b.printB() ; } };
运行结果:
HELLO-->B
含有内部接口的抽象类的子类,必须也要定义内部类继承该接口。
反之,在一个接口中也可以定义一个抽象类。
interface A{ // 定义接口 public void printA() ; // 抽象方法 abstract class B{ // 定义内部抽象类 public abstract void printB() ; // 定义抽象方法 } }; class X implements A{ // 实现接口 public void printA(){ System.out.println("HELLO --> A") ; } class Y extends B{ // 继承抽象类 public void printB(){ System.out.println("HELLO --> B") ; } }; }; public class InnerExtDemo02{ public static void main(String args[]){ A.B b = new X().new Y() ; b.printB() ; } };
含有内部抽象类的接口,对于继承他的子类来说,除了要覆写接口中的抽象方法,还要专门定义一个内部类继承抽象类,并覆写全部抽象类。
但是,从个人开发来说,此种设计不太常见,因为代码有些混乱。
其实,无论对于接口,或者是抽象类,都是要求子类将本类中定义的方法实现,区别也仅仅是接口要求全部实现,抽象类中的非抽象方法不一定要重写。对于接口使用远超过抽象类的问题,网上有很多的解释。为了代码的重用?我觉得不是重点。为了更好的扩展性,太抽象了,和抽象类的定义一样让人无从捉摸。因为接口可以多重继承,这个解释太通俗了,很多刚入门的开发者,不能完全明白。
我的看法,接口使用多于抽象类,确实是因为接口可以多重继承,便于项目后期的维护和扩展。
贴一段网上搜的很好的知识点:
抽象类和接口之间的区别:
共性:它们都是不断抽取出来的抽象非概念
区别:
1、抽象类只能被单继承、接口可以被多实现,避免了单继承的局限性。
2、抽象类中可以定义抽象方法,和非抽象方法,它可以用于定义体系的基本共性的内容。接口中只能定义抽象方法,它主要用于对象的功能的扩展。
3、抽象类是继承关系,是is a关系,接口是实现关系是like a关系。
4、抽象类中的成员修饰符都是自定义的,接口中的修饰符都是固定的。
记住:不要把接口狭义的理解为interface,应该理解广义些,就是对外提供的规则,凡是对外暴露的都可以是接口。
下面比较一下两者的具体语法区别:
1.抽象类可以有构造方法,接口中不能有构造方法。
2.抽象类中可以有普通成员变量,接口中没有普通成员变量
3.抽象类中可以包含非抽象的普通方法,接口中的所有方法必须都是抽象的,不能有非抽象的普通方法。
4. 抽象类中的抽象方法的访问类型可以是public,protected和(默认类型,虽然eclipse下不报错,但应该也不行),但接口中的抽象方法只能是public类型的,并且默认即为public abstract类型。
5. 抽象类中可以包含静态方法,接口中不能包含静态方法
6. 抽象类和接口中都可以包含静态成员变量,抽象类中的静态成员变量的访问类型可以任意,但接口中定义的变量只能是public static final类型,并且默认即为public static final类型。
7. 一个类可以实现多个接口,但只能继承一个抽象类。
下面接着再说说两者在应用上的区别:
接口更多的是在系统架构设计方法发挥作用,主要用于定义模块之间的通信契约。而抽象类在代码实现方面发挥作用,可以实现代码的重用,例如,模板方法设计模式是抽象类的一个典型应用,假设某个项目的所有Servlet类都要用相同的方式进行权限判断、记录访问日志和处理异常,那么就可以定义一个抽象的基类,让所有的Servlet都继承这个抽象基类,在抽象基类的service方法中完成权限判断、记录访问日志和处理异常的代码,在各个子类中只是完成各自的业务逻辑代码
(这应该是最全最细的答案了)
比如公司系统中,有系统管理员,普通员工,主管,组长,经理等
这些角色都有登陆,修改密码这些共同的行为,这些行为规范可以被统一的命名,如Login和UpdatePassword,
那么,我们可以将这些方法抽到抽象类中,作为抽象方法,等待子类具体实现。当然各角色的身份不一样,所以实际上执行这些行为的步骤可能不一样,例如数据表的不同,则需要不同的子类去实现不同的代码,这就是抽象类比普通类作为基类被继承的其中一个优点。而实际上login操作,对于各类角色是一样的,那么,可以作为非抽象方法被继承,这就体现了抽象类在代码实现方面的优点,可以实现代码的重用。
实际上你也可以不用抽象类,直接在每个子类定义相应的行为也可以,如MemberLogin、AdminLogin等,但如果是在中大型软件里,基于命名规范化,后续扩展和维护考虑,可能用抽象类好些,统一用Login,这样后面的人看到这个就基本了解情况了,对吧。
再来说接口,打比方说,现在有A查看公司人员,B查看公司销售数据,C查看员工工资,这些行为对于普通员工,组长,经理,主管来说,并不是共有的行为。那么,抽象类就难以实现各个角色的不同行为,因为类的单继承性。而如果,A、B、C、分别是三个接口的话,那么经理继承ABC,员工继承A,主管继承AC,这样子处理的话,不仅易于理解,而且架构更加清晰,是吧。
更重要的一点是,假如公司的组织进行了调整,增加了一个职位。对于接口,只要这个角色去继承相应的接口即可,不会对于之前已经良好运行的代码做任何修改。事实上,对于已有代码的修改,是越少越好,因为代价很大,属于重复劳动,也可能出现意想不到的错误。这就是所谓的良好的扩展性,和可维护性