文档章节

java 内部类 InnerClass

远方__
 远方__
发布于 2016/12/23 12:35
字数 12467
阅读 3
收藏 0
提起Java内部类(Inner Class)可能很多人不太熟悉,实际上类似的概念在C++里也有,那就是嵌套类(Nested Class),关于这两者的区别与联系,在下文中会有对比。内部类从表面上看,就是在类中又定义了一个类(下文会看到,内部类可以在很多地方定义),而实 际上并没有那么简单,乍看上去内部类似乎有些多余,它的用处对于初学者来说可能并不是那么显著,但是随着对它的深入了解,你会发现Java的设计者在内部 类身上的确是用心良苦。学会使用内部类,是掌握Java高级编程的一部分,它可以让你更优雅地设计你的程序结构。下面从以下几个方面来介绍:
第一次见面
  1. public   interface  Contents {  
  2.     int  value();  
  3. }  
  4. public   interface  Destination {  
  5.     String readLabel();  
  6. }  
  7. public   class  Goods {  
  8.     private   class  Content  implements  Contents {  
  9.         private   int  i =  11 ;  
  10.         public   int  value() {  
  11.             return  i;  
  12.         }  
  13.     }  
  14.     protected   class  GDestination  implements  Destination {  
  15.         private  String label;  
  16.         private  GDestination(String whereTo) {  
  17.             label = whereTo;  
  18.         }  
  19.         public  String readLabel() {  
  20.             return  label;  
  21.         }  
  22.     }  
  23.     public  Destination dest(String s) {  
  24.         return   new  GDestination(s);  
  25.     }  
  26.     public  Contents cont() {  
  27.         return   new  Content();  
  28.     }  
  29. }  
  30. class  TestGoods {  
  31.     public   static   void  main(String[] args) {  
  32.         Goods p = new  Goods();  
  33.         Contents c = p.cont();  
  34.         Destination d = p.dest("Beijing" );  
  35.     }  
  36. }  
Java代码 
  1. public interface Contents {  
  2. int value();  
  3. }  
  4. public interface Destination {  
  5. String readLabel();  
  6. }  
  7. public class Goods {  
  8. private class Content implements Contents {  
  9. private int i = 11;  
  10. public int value() {  
  11. return i;  
  12. }  
  13. }  
  14. protected class GDestination implements Destination {  
  15. private String label;  
  16. private GDestination(String whereTo) {  
  17. label = whereTo;  
  18. }  
  19. public String readLabel() {  
  20. return label;  
  21. }  
  22. }  
  23. public Destination dest(String s) {  
  24. return new GDestination(s);  
  25. }  
  26. public Contents cont() {  
  27. return new Content();  
  28. }  
  29. }  
  30. class TestGoods {  
  31. public static void main(String[] args) {  
  32. Goods p = new Goods();  
  33. Contents c = p.cont();  
  34. Destination d = p.dest("Beijing");  
  35. }  
  36. }  
 
在这个例子里类Content和GDestination被定义在了类Goods内部,并且分别有着 protected和private修饰符来控制访问级别。Content代表着Goods的内容,而GDestination代表着Goods的目的 地。它们分别实现了两个接口Content和 Destination。在后面的main方法里,直接用 Contents c和Destination d进行操作,你甚至连这两个内部类的名字都没有看见!这样,内部类的第一个好处就体现出来了 隐藏你不想让别人知道的操作,也即封装性。
同时,我们也发现了在外部类作用范围之外得到内部类对象的第一个方法,那就是利用其外部类的方法创建并返回。上 例中的cont()和dest()方法就是这么做的。那么还有没有别的方法呢?当然有,其语法格式如下:
outerObject=new outerClass(Constructor Parameters);
outerClass.innerClass innerObject=outerObject.new InnerClass(Constructor Parameters);
注意在创建非静态内部类对象时,一定要先创建起相应的外部类对象。至于原因,也就引出了我们下一个话题 非静态内部类对象有着指向其外部类对象的引用,对刚才的例子稍作修改:
  1. public   class  Goods {  
  2.     private   int  valueRate =  2 ;  
  3.     private   class  Content  implements  Contents {  
  4.         private   int  i =  11  * valueRate;  
  5.         public   int  value() {  
  6.             return  i;  
  7.         }  
  8.     }  
  9.     protected   class  GDestination  implements  Destination {  
  10.         private  String label;  
  11.         private  GDestination(String whereTo) {  
  12.             label = whereTo;  
  13.         }  
  14.         public  String readLabel() {  
  15.             return  label;  
  16.         }  
  17.     }  
  18.     public  Destination dest(String s) {  
  19.         return   new  GDestination(s);  
  20.     }  
  21.     public  Contents cont() {  
  22.         return   new  Content();  
  23.     }  
  24. }  
Java代码 
  1. public class Goods {  
  2. private int valueRate = 2;  
  3. private class Content implements Contents {  
  4. private int i = 11 * valueRate;  
  5. public int value() {  
  6. return i;  
  7. }  
  8. }  
  9. protected class GDestination implements Destination {  
  10. private String label;  
  11. private GDestination(String whereTo) {  
  12. label = whereTo;  
  13. }  
  14. public String readLabel() {  
  15. return label;  
  16. }  
  17. }  
  18. public Destination dest(String s) {  
  19. return new GDestination(s);  
  20. }  
  21. public Contents cont() {  
  22. return new Content();  
  23. }  
  24. }  

在这里我们给Goods类增加了一个private成员变量valueRate,意义是货物的价值系数,在内部 类Content的方法value()计算价值时把它乘上。我们发现,value()可以访问valueRate,这也是内部类的第二个好处一个内部类对 象可以访问创建它的外部类对象的内容,甚至包括私有变量!这是一个非常有用的特性,为我们在设计时提供了更多的思路和捷径。要想实现这个功能,内部类对象 就必须有指向外部类对象的引用。Java编译器在创建内部类对象时,隐式的把其外部类对象的引用也传了进去并一直保存着。这样就使得内部类对象始终可以访 问其外部类对象,同时这也是为什么在外部类作用范围之外向要创建内部类对象必须先创建其外部类对象的原因。
有人会问,如果内部类里的一个成员变量与外部类的一个成员变量同名,也即外部类的同名成员变量被屏蔽了,怎么 办?没事,Java里用如下格式表达外部类的引用:
outerClass.this
有了它,我们就不怕这种屏蔽的情况了。
静态内部类
和普通的类一样,内部类也可以有静态的。不过和非静态内部类相比,区别就在于静态内部类没有了指向外部的引用。 这实际上和C++中的嵌套类很相像了,Java内部类与C++嵌套类最大的不同就在于是否有指向外部的引用这一点上,当然从设计的角度以及以它一些细节来 讲还有区别。
除此之外,在任何非静态内部类中,都不能有静态数据,静态方法或者又一个静态内部类(内部类的嵌套可以不止一 层)。不过静态内部类中却可以拥有这一切。这也算是两者的第二个区别吧。
局部内部类
是的,Java内部类也可以是局部的,它可以定义在一个方法甚至一个代码块之内。
  1. public   class  Goods1 {  
  2.     public  Destination dest(String s) {  
  3.         class  GDestination  implements  Destination {  
  4.             private  String label;  
  5.             private  GDestination(String whereTo) {  
  6.                 label = whereTo;  
  7.             }  
  8.             public  String readLabel() {  
  9.                 return  label;  
  10.             }  
  11.         }  
  12.         return   new  GDestination(s);  
  13.     }  
  14.     public   static   void  main(String[] args) {  
  15.         Goods1 g = new  Goods1();  
  16.         Destination d = g.dest("Beijing" );  
  17.     }  
  18. }  
Java代码 
  1. public class Goods1 {  
  2. public Destination dest(String s) {  
  3. class GDestination implements Destination {  
  4. private String label;  
  5. private GDestination(String whereTo) {  
  6. label = whereTo;  
  7. }  
  8. public String readLabel() {  
  9. return label;  
  10. }  
  11. }  
  12. return new GDestination(s);  
  13. }  
  14. public static void main(String[] args) {  
  15. Goods1 g = new Goods1();  
  16. Destination d = g.dest("Beijing");  
  17. }  
  18. }  

上面就是这样一个例子。在方法dest中我们定义了一个内部类,最后由这个方法返回这个内部类的对象。如果我们 在用一个内部类的时候仅需要创建它的一个对象并创给外部,就可以这样做。当然,定义在方法中的内部类可以使设计多样化,用途绝不仅仅在这一点。
下面有一个更怪的例子:
  1. public   class  Goods2 {  
  2.     private   void  internalTracking( boolean  b) {  
  3.         if  (b) {  
  4.             class  TrackingSlip {  
  5.                 private  String id;  
  6.                 TrackingSlip(String s) {  
  7.                     id = s;  
  8.                 }  
  9.                 String getSlip() {  
  10.                     return  id;  
  11.                 }  
  12.             }  
  13.             TrackingSlip ts = new  TrackingSlip( "slip" );  
  14.             String s = ts.getSlip();  
  15.         }  
  16.     }  
  17.     public   void  track() {  
  18.         internalTracking(true );  
  19.     }  
  20.     public   static   void  main(String[] args) {  
  21.         Goods2 g = new  Goods2();  
  22.         g.track();  
  23.     }  
  24. }  
Java代码 
  1. public class Goods2 {  
  2. private void internalTracking(boolean b) {  
  3. if (b) {  
  4. class TrackingSlip {  
  5. private String id;  
  6. TrackingSlip(String s) {  
  7. id = s;  
  8. }  
  9. String getSlip() {  
  10. return id;  
  11. }  
  12. }  
  13. TrackingSlip ts = new TrackingSlip("slip");  
  14. String s = ts.getSlip();  
  15. }  
  16. }  
  17. public void track() {  
  18. internalTracking(true);  
  19. }  
  20. public static void main(String[] args) {  
  21. Goods2 g = new Goods2();  
  22. g.track();  
  23. }  
  24. }  

你不能在if之外创建这个内部类的对象,因为这已经超出了它的作用域。不过在编译的时候,内部类 TrackingSlip和其他类一样同时被编译,只不过它由它自己的作用域,超出了这个范围就无效,除此之外它和其他内部类并没有区别。
匿名内部类
java的匿名内部类的语法规则看上去有些古怪,不过如同匿名数组一样,当你只需要创建一个类的对象而且用不上 它的名字时,使用内部类可以使代码看上去简洁清楚。它的语法规则是这样的:
new interfacename(){......}; 或 new superclassname(){......};
下面接着前面继续举例子:
  1. public   class  Goods3 {  
  2.     public  Contents cont() {  
  3.         return   new  Contents() {  
  4.             private   int  i =  11 ;  
  5.             public   int  value() {  
  6.                 return  i;  
  7.             }  
  8.         };  
  9.     }  
  10. }  
Java代码 
  1. public class Goods3 {  
  2. public Contents cont() {  
  3. return new Contents() {  
  4. private int i = 11;  
  5. public int value() {  
  6. return i;  
  7. }  
  8. };  
  9. }  
  10. }  

这里方法cont()使用匿名内部类直接返回了一个实现了接口Contents的类的对象,看上去的确十分简 洁。
在java的事件处理的匿名适配器中,匿名内部类被大量的使用。例如在想关闭窗口时加上这样一句代码:
  1. frame.addWindowListener( new  WindowAdapter(){  
  2.     public   void  windowClosing(WindowEvent e){  
  3.        System.exit(0 );  
  4.     }  
  5. });   
Java代码 
  1. frame.addWindowListener(new WindowAdapter(){  
  2. public void windowClosing(WindowEvent e){  
  3. System.exit(0);  
  4. }  
  5. });   

有一点需要注意的是,匿名内部类由于没有名字,所以它没有构造函数(但是如果这个匿名内部类继承了一个只含有带 参数构造函数的父类,创建它的时候必须带上这些参数,并在实现的过程中使用super关键字调用相应的内容)。如果你想要初始化它的成员变量,有下面几种 方法:
如果是在一个方法的匿名内部类,可以利用这个方法传进你想要的参数,不过记住,这些参数必须被声明为 final。
将匿名内部类改造成有名字的局部内部类,这样它就可以拥有构造函数了。
在这个匿名内部类中使用初始化代码块。
为什么需要内部类?
java内部类有什么好处?为什么需要内部类?
首先举一个简单的例子,如果你想实现一个接口,但是这个接口中的一个方法和你构想的这个类中的 一个方法的名称,参数相同,你应该怎么办?这时候,你可以建一个内部类实现这个接口。由于内部类对外部类的所有内容都是可访问的,所以这样做可以完成所有 你直接实现这个接口的功能。
不过你可能要质疑,更改一下方法的不就行了吗?
的确,以此作为设计内部类的理由,实在没有说服力。
真正的原因是这样的,java中的内部类和接口加在一起,可以的解决常被C++程序员抱怨 java中存在的一个问题 没有多继承。实际上,C++的多继承设计起来很复杂,而java通过内部类加上接口,可以很好的实现多继承的效果。

四个 应用场景:
场景一:当某个类除了它的外部类,不再被其他的类使用时
我们说这个内部类依附于它的外部类而存在,可能的原因有:1、不可能为其他的类使用;2、 出于某种原因,不能被其他类引用,可能会引起错误。等等。这个场景是我们使用内部类比较多的一个场景。下面我们以一个大家熟悉的例子来说明。
在我 们的企业级Java项目开发过程中,数据库连接池是一个我们经常要用到的概念。虽然在很多时候,我们都是用的第三方的数据库连接池,不需要我们亲自来做这 个数据库连接池。但是,作为我们Java内部类使用的第一个场景,这个数据库连接池是一个很好的例子。为了简单起见,以下我们就来简单的模拟一下数据库连 接池,在我们的例子中,我们只实现数据库连接池的一些简单的功能。如果想完全实现它,大家不妨自己试一试。
首先,我们定义一个接口,将数据库连接 池的功能先定义出来,如下:
public interface Pool extends TimerListener
{
        // 初始化连接池
        public boolean init();
        //销毁连接池
        public void destory();
        // 取得一个连接
        public Connection getConn();
        //还有一些其他的功能,这里 不再列出
        ……
}
有了这个功能接口,我们就可以在它的基础上实现数据库连接池的部分功能了。我们首先想到这个数据 库连接池类的操作对象应该是由Connection对象组成的一个数组,既然是数组,我们的池在取得Connection的时候,就要对数组元素进行遍 历,看看Connection对象是否已经被使用,所以数组里每一个Connection对象都要有一个使用标志。我们再对连接池的功能进行分析,会发现 每一个Connection对象还要一个上次访问时间和使用次数。
通过上面的分析,我们可以得出,连接池里的数组的元素应该是由对象组成,该对象 的类可能如下:
public class PoolConn
{
        private Connection conn;
        private boolean isUse;
        private long lastAccess;
        private int useCount;
        ……
}
下 面的省略号省掉的是关于四个属性的一些get和set方法。我们可以看到这个类的核心就是Connection,其他的一些属性都是Connection 的一些标志。可以说这个类只有在连接池这个类里有用,其他地方用不到。这时候,我们就该考虑是不是可以把这个类作为一个内部类呢?而且我们把它作为一个内 部类以后,可以把它定义成一个私有类,然后将它的属性公开,这样省掉了那些无谓的get和set方法。下面我们就试试看:
public class ConnectPool implements Pool
{
        // 存在Connection的数组
        private PoolConn[] poolConns;
        //连接 池的最小连接数
        private int min;
        //连接池的最大连接数
        private int max;
        // 一个连接的最大使用次数
        private int maxUseCount;
        //一个连接的最大空闲时间
        private long maxTimeout;
        // 同一时间的Connection最大使用个数
        private int maxConns;
        //定时器
        private Timer timer;
        public boolean init()
        {
               try
               {
                      ……
                      this.poolConns = new PoolConn[this.min];
                      for(int i=0;i<this.min;i++)
                      {
                             PoolConn poolConn = new PoolConn();
                             poolConn.conn = ConnectionManager.getConnection();
                             poolConn.isUse = false;
                             poolConn.lastAccess = new Date().getTime();
                             poolConn.useCount = 0;
                             this.poolConns[i] = poolConn;
}
……
return true;
               }
               catch(Exception e)
               {
                      return false;
}
}
……
private class PoolConn
{
       public Connection conn;
       public boolean isUse;
public long lastAccess;
       public int useCount;
}
}
因 为本文不是专题来讲述数据库连接池的,所以在上面的代码中绝大部分的内容被省略掉了。PoolConn类不大可能被除了ConnectionPool类的 其他类使用到,把它作为ConnectionPool的私有内部类不会影响到其他类。同时,我们可以看到,使用了内部类,使得我们可以将该内部类的数据公 开,ConnectionPool类可以直接操作PoolConn类的数据成员,避免了因set和get方法带来的麻烦。
上面的一个例子,是使用 内部类使得你的代码得到简化和方便。还有些情况下,你可能要避免你的类被除了它的外部类以外的类使用到,这时候你却不得不使用内部类来解决问题。
 
场 景二:解决一些非面向对象的语句块
这些语句块包括if…else if…else语句,case语句,等等。这些语句都不是面向对象的,给我们造 成了系统的扩展上的麻烦。我们可以看看,在模式中,有多少模式是用来解决由if语句带来的扩展性的问题。
Java编程中还有一个困扰我们的问题, 那就是try…catch…问题,特别是在JDBC编程过程中。请看下面的代码:
……
try
         {
                String[] divisionData = null;
                conn = manager.getInstance().getConnection();
                stmt = (OracleCallableStatement)conn.prepareCall("{ Call PM_GET_PRODUCT.HEADER_DIVISION(?, ?) }");
                stmt.setLong(1 ,productId.longValue() );
                stmt.registerOutParameter(2, oracle.jdbc.OracleTypes.CURSOR); ;
                stmt.execute();
                ResultSet rs = stmt.getCursor(2);
                int i = 0 ;
                String strDivision = "";
                while( rs.next() )
                {
                             strDivision += rs.getString("DIVISION_ID") + "," ;
                  }
                  int length = strDivision.length() ;
                  if(length != 0 )
                  {
                         strDivision = strDivision.substring(0,length - 1);
                  }
                  divisionData = StringUtil.split(strDivision, ",") ;
                  map.put("Division", strDivision ) ;
                  LoggerAgent.debug("GetHeaderProcess","getDivisionData","getValue + " + strDivision +" " + productId) ;
       }catch(Exception e)
        {
                       LoggerAgent.error("GetHeaderData", "getDivisionData",
                                                     "SQLException: " + e);
                       e.printStackTrace() ;
 
       }finally
        {
                       manager.close(stmt);
                       manager.releaseConnection(conn);
        }
这 是我们最最常用的一个JDBC编程的代码示例。一个系统有很多这样的查询方法,这段代码一般分作三段:try关键字括起来的那段是用来做查询操作 的,catch关键字括起来的那段需要做两件事,记录出错的原因和事务回滚(如果需要的话),finally关键字括起来的那段用来释放数据库连接。
我 们的烦恼是:try关键字括起来的那段是变化的,每个方法的一般都不一样。而catch和finally关键字括起来的那两段却一般都是不变的,每个方法 的那两段都是一样的。既然后面那两段是一样的,我们就非常希望将它们提取出来,做一个单独的方法,然后让每一个使用到它们的方法调用。但 是,try…catch…finally…是一个完整的语句段,不能把它们分开。这样的结果,使得我们不得不在每一个数据层方法里重复的写相同的 catch…finally…这两段语句。
既然不能将那些讨厌的try…catch…finally…作为一个公用方法提出去,那么我们还是需要 想其他的办法来解决这个问题。不然我们老是写那么重复代码,真是既繁琐,又不容易维护。
我们容易想到,既然catch…finally…这两段代 码不能提出来,那么我们能不能将try…里面的代码提出去呢?唉哟,try…里面的代码是可变的呢。怎么办?
既然try…里面的代码是可变的,这 意味着这些代码是可扩展的,是应该由用户来实现的,对于这样的可扩展内容,我们很容易想到用接口来定义它们,然后由用户去实现。这样以来我们首先定义一个 接口:
public interface DataManager
{
        public void manageData();
}
我 们需要用户在manageData()方法中实现他们对数据层访问的代码,也就是try…里面的代码。
然后我们使用一个模板类来实现所有的 try…catch…finally…语句的功能,如下:
public class DataTemplate
{
        public void execute(DataManager dm)
        {
               try
               {
                      dm.manageData();
}
catch(Exception e)
{
       LoggerAgent.error("GetHeaderData", "getDivisionData",
                        "SQLException: " + e);
       e.printStackTrace() ;
 
}finally
{
       manager.close(stmt);
       manager.releaseConnection(conn);
}
}
}
这 样,一个模板类就完成了。我们也通过这个模板类将catch…finally…两段代码提出来了。我们来看看使用了这个模板类的数据层方法是怎么实现的:
new DataTemplate().execute(new DataManager()
{
        public void manageData()
        {
                String[] divisionData = null;
                conn = manager.getInstance().getConnection();
                stmt = (OracleCallableStatement)conn.prepareCall("{ Call PM_GET_PRODUCT.HEADER_DIVISION(?, ?) }");
                stmt.setLong(1 ,productId.longValue() );
                stmt.registerOutParameter(2, oracle.jdbc.OracleTypes.CURSOR); ;
                stmt.execute();
                ResultSet rs = stmt.getCursor(2);
                int i = 0 ;
                String strDivision = "";
                while( rs.next() )
                {
                             strDivision += rs.getString("DIVISION_ID") + "," ;
}
                  int length = strDivision.length() ;
                  if(length != 0 )
                  {
                         strDivision = strDivision.substring(0,length - 1);
                  }
                  divisionData = StringUtil.split(strDivision, ",") ;
                  map.put("Division", strDivision ) ;
                  LoggerAgent.debug("GetHeaderProcess","getDivisionData","getValue + " + strDivision +" " + productId) ;
}
});
注 意:本段代码仅供思路上的参考,没有经过上机测试。
我们可以看到,正是这个实现了DataManager接口得匿名内部类的使用,才使得我们解决 了对try…catch…finally…语句的改造。这样,第一为我们解决了令人痛苦的重复代码;第二也让我们在数据层方法的编码中,直接关注对数据的 操作,不用关心那些必需的但是与数据操作无关的东西。
我们现在来回想一下Spring框架的数据层,是不是正是使用了这种方法呢?
 
 
场 景之三:一些多算法场合
假如我们有这样一个需求:我们的一个方法用来对数组排序并且依次打印各元素,对数组排序方法有很多种,用哪种方法排序交给 用户自己确定。
对于这样一个需求,我们很容易解决。我们决定给哪些排序算法定义一个接口,具体的算法实现由用户自己完成,只要求他实现我们的接口 就行。
public interface SortAlgor
{
        public void sort(int[] is);
}
这 样,我们再在方法里实现先排序后打印,代码如下:
public void printSortedArray(int[] is,SortAlgor sa)
{
        ……
       sa.sort(is);
        for(int i=0;i<is.length;i++)
        {
               System.out.print(is[i]+” “);
}
System.out.println();
}
客 户端对上面方法的使用如下:
int[] is = new int[]{3,1,4,9,2};
printSortedArray(is,new SortAlgor()
{
        public void sort(is)
        {
               int k = 0;
               for(int i=0;i<is.length;i++)
               {
                     for(int j=i+1;j<is.length;j++)
                      {
                             if(is[i]>is[j])
                             {
                                    k = is[i];
                                    is[i] = is[j];
                                    is[j] = k;
                             }
                      }
               }
}
});
这 样的用法很多,我们都或多或少的被动的使用过。如在Swing编程中,我们经常需要对组件增加监听器对象,如下所示:
spinner2.addChangeListener(new ChangeListener()
{
public void stateChanged(ChangeEvent e) 
{
System.out.println("Source: " + e.getSource());
}
}
);
在 Arrays包里,对元素为对象的数组的排序:
Arrays.sort(emps,new Comparator(){
        Public int compare(Object o1,Object o2)
        {
               return ((Employee)o1).getServedYears()-((Employee)o2).getServedYears();
}
});
这 样的例子还有很多,JDK教会了我们很多使用内部类的方法。随时我们都可以看一看API,看看还会在什么地方使用到内部类呢?
 
 
 
场 景之四:适当使用内部类,使得代码更加灵活和富有扩展性
适当的使用内部类,可以使得你的代码更加灵活和富有扩展性。当然,在这里头起作用的还是一 些模式的运行,但如果不配以内部类的使用,这些方法的使用效果就差远了。不信?请看下面的例子:
我们记得简单工厂模式的作用就是将客户对各个对象 的依赖转移到了工厂类里。很显然,简单工厂模式并没有消除那些依赖,只是简单的将它们转移到了工厂类里。如果有新的对象增加进来,则我们需要修改工厂类。 所以我们需要对工厂类做进一步的改造,进一步消除它对具体类的依赖。以前我们提供过一个使用反射来消除依赖的方法;这里,我们将提供另外一种方法。
这 种方法是将工厂进一步抽象,而将具体的工厂类交由具体类的创建者来实现,这样,工厂类和具体类的依赖的问题就得到了解决。而工厂的使用者则调用抽象的工厂 来获得具体类的对象。如下。
我们以一个生产形体的工厂为例,下面是这些形体的接口:
package polyFactory;
 
public interface Shape {
public void draw();
public void erase();
 
}
 
通 过上面的描述,大家都可能已经猜到,这个抽象的工厂肯定使用的是模板方法模式。如下:
package polyFactory;
 
import java.util.HashMap;
import java.util.Map;
 
 
public abstract class ShapeFactory {
protected abstract Shape create();
private static Map factories = new HashMap();
public static void addFactory(String id,ShapeFactory f)
{
       factories.put(id,f);
}
public static final Shape createShape(String id)
{
       if(!factories.containsKey(id))
        {
               try
               {
                      Class.forName("polyFactory."+id);
               }
               catch(ClassNotFoundException e)
               {
                      throw new RuntimeException("Bad shape creation : "+id);
               }
        }
        return ((ShapeFactory)factories.get(id)).create();
}
}
不 错,正是模板方法模式的运用。这个类蛮简单的:首先是一个create()方法,用来产生具体类的对象,留交各具体工厂实现去实现。然后是一个Map类型 的静态变量,用来存放具体工厂的实现以及他们的ID号。接着的一个方法使用来增加一个具体工厂的实现。最后一个静态方法是用来获取具体对象,里面的那个 Class.forName……的作用是调用以ID号为类名的类的一些静态的东西。
下面,我们来看具体的类的实现:
package polyFactory;
 
public class Circle implements Shape {
 
 
public void draw() {
        // TODO Auto-generated method stub
       System.out.println("the circle is drawing...");
}
 
 
public void erase() {
        // TODO Auto-generated method stub
       System.out.println("the circle is erasing...");
}
private static class Factory extends ShapeFactory
{
       protected Shape create()
        {
               return new Circle();
        }
}
static {ShapeFactory.addFactory("Circle",new Factory());}
 
}
这 个类的其他的地方也平常得很。但就是后面的那个内部类Factory用得好。第一呢,这个类只做一件事,就是产生一个Circle对象,与其他类无关,就 这一个条也就满足了使用内部类的条件。第二呢,这个Factory类需要是静态的,这也得要求它被使用内部类,不然,下面的 ShapeFacotry.addFactory就没办法add了。而最后的那个静态的语句块是用来将具体的工厂类添加到抽象的工厂里面去。在抽象工厂里 调用Class.forName就会执行这个静态的语句块了。
下面仍然是一个具体类:
package polyFactory;
 
 
public class Square implements Shape {
 
public void draw() {
        // TODO Auto-generated method stub
       System.out.println("the square is drawing...");
}
 
public void erase() {
        // TODO Auto-generated method stub
       System.out.println("the square is erasing...");
}
private static class Factory extends ShapeFactory
{
       protected Shape create()
        {
               return new Square();
        }
}
static {ShapeFactory.addFactory("Square",new Factory());}
 
}
最 后,我们来测试一下:
String[] ids = new String[]{"Circle","Square","Square","Circle"};
        for(int i=0;i<ids.length;i++)
        {
               Shape shape = ShapeFactory.createShape(ids[i]);
               shape.draw();
               shape.erase();
        }
测 试结果为:
the circle is drawing...
the circle is erasing...
the square is drawing...
the square is erasing...
the square is drawing...
the square is erasing...
the circle is drawing...
the circle is erasing...
       这 个方法是巧妙地使用了内部类,将具体类的实现和它的具体工厂类绑定起来,由具体类的实现者在这个内部类的具体工厂里去产生一个具体类的对象,这当然容易得 多。虽然需要每一个具体类都创建一个具体工厂类,但由于具体工厂类是一个内部类,这样也不会随着具体类的增加而不断增加新的工厂类,使得代码看起来很臃 肿,这也是本方法不得不使用内部类的一个原因吧。 提起Java内部类(Inner Class)可能很多人不太熟悉,实际上类似的概念在C++里也有,那就是嵌套类(Nested Class),关于这两者的区别与联系,在下文中会有对比。内部类从表面上看,就是在类中又定义了一个类(下文会看到,内部类可以在很多地方定义),而实 际上并没有那么简单,乍看上去内部类似乎有些多余,它的用处对于初学者来说可能并不是那么显著,但是随着对它的深入了解,你会发现Java的设计者在内部 类身上的确是用心良苦。学会使用内部类,是掌握Java高级编程的一部分,它可以让你更优雅地设计你的程序结构。下面从以下几个方面来介绍:
第一次见面
  1. public   interface  Contents {  
  2.     int  value();  
  3. }  
  4. public   interface  Destination {  
  5.     String readLabel();  
  6. }  
  7. public   class  Goods {  
  8.     private   class  Content  implements  Contents {  
  9.         private   int  i =  11 ;  
  10.         public   int  value() {  
  11.             return  i;  
  12.         }  
  13.     }  
  14.     protected   class  GDestination  implements  Destination {  
  15.         private  String label;  
  16.         private  GDestination(String whereTo) {  
  17.             label = whereTo;  
  18.         }  
  19.         public  String readLabel() {  
  20.             return  label;  
  21.         }  
  22.     }  
  23.     public  Destination dest(String s) {  
  24.         return   new  GDestination(s);  
  25.     }  
  26.     public  Contents cont() {  
  27.         return   new  Content();  
  28.     }  
  29. }  
  30. class  TestGoods {  
  31.     public   static   void  main(String[] args) {  
  32.         Goods p = new  Goods();  
  33.         Contents c = p.cont();  
  34.         Destination d = p.dest("Beijing" );  
  35.     }  
  36. }  
Java代码 
  1. public interface Contents {  
  2. int value();  
  3. }  
  4. public interface Destination {  
  5. String readLabel();  
  6. }  
  7. public class Goods {  
  8. private class Content implements Contents {  
  9. private int i = 11;  
  10. public int value() {  
  11. return i;  
  12. }  
  13. }  
  14. protected class GDestination implements Destination {  
  15. private String label;  
  16. private GDestination(String whereTo) {  
  17. label = whereTo;  
  18. }  
  19. public String readLabel() {  
  20. return label;  
  21. }  
  22. }  
  23. public Destination dest(String s) {  
  24. return new GDestination(s);  
  25. }  
  26. public Contents cont() {  
  27. return new Content();  
  28. }  
  29. }  
  30. class TestGoods {  
  31. public static void main(String[] args) {  
  32. Goods p = new Goods();  
  33. Contents c = p.cont();  
  34. Destination d = p.dest("Beijing");  
  35. }  
  36. }  
 
在这个例子里类Content和GDestination被定义在了类Goods内部,并且分别有着 protected和private修饰符来控制访问级别。Content代表着Goods的内容,而GDestination代表着Goods的目的 地。它们分别实现了两个接口Content和 Destination。在后面的main方法里,直接用 Contents c和Destination d进行操作,你甚至连这两个内部类的名字都没有看见!这样,内部类的第一个好处就体现出来了 隐藏你不想让别人知道的操作,也即封装性。
同时,我们也发现了在外部类作用范围之外得到内部类对象的第一个方法,那就是利用其外部类的方法创建并返回。上 例中的cont()和dest()方法就是这么做的。那么还有没有别的方法呢?当然有,其语法格式如下:
outerObject=new outerClass(Constructor Parameters);
outerClass.innerClass innerObject=outerObject.new InnerClass(Constructor Parameters);
注意在创建非静态内部类对象时,一定要先创建起相应的外部类对象。至于原因,也就引出了我们下一个话题 非静态内部类对象有着指向其外部类对象的引用,对刚才的例子稍作修改:
  1. public   class  Goods {  
  2.     private   int  valueRate =  2 ;  
  3.     private   class  Content  implements  Contents {  
  4.         private   int  i =  11  * valueRate;  
  5.         public   int  value() {  
  6.             return  i;  
  7.         }  
  8.     }  
  9.     protected   class  GDestination  implements  Destination {  
  10.         private  String label;  
  11.         private  GDestination(String whereTo) {  
  12.             label = whereTo;  
  13.         }  
  14.         public  String readLabel() {  
  15.             return  label;  
  16.         }  
  17.     }  
  18.     public  Destination dest(String s) {  
  19.         return   new  GDestination(s);  
  20.     }  
  21.     public  Contents cont() {  
  22.         return   new  Content();  
  23.     }  
  24. }  
Java代码 
  1. public class Goods {  
  2. private int valueRate = 2;  
  3. private class Content implements Contents {  
  4. private int i = 11 * valueRate;  
  5. public int value() {  
  6. return i;  
  7. }  
  8. }  
  9. protected class GDestination implements Destination {  
  10. private String label;  
  11. private GDestination(String whereTo) {  
  12. label = whereTo;  
  13. }  
  14. public String readLabel() {  
  15. return label;  
  16. }  
  17. }  
  18. public Destination dest(String s) {  
  19. return new GDestination(s);  
  20. }  
  21. public Contents cont() {  
  22. return new Content();  
  23. }  
  24. }  

在这里我们给Goods类增加了一个private成员变量valueRate,意义是货物的价值系数,在内部 类Content的方法value()计算价值时把它乘上。我们发现,value()可以访问valueRate,这也是内部类的第二个好处一个内部类对 象可以访问创建它的外部类对象的内容,甚至包括私有变量!这是一个非常有用的特性,为我们在设计时提供了更多的思路和捷径。要想实现这个功能,内部类对象 就必须有指向外部类对象的引用。Java编译器在创建内部类对象时,隐式的把其外部类对象的引用也传了进去并一直保存着。这样就使得内部类对象始终可以访 问其外部类对象,同时这也是为什么在外部类作用范围之外向要创建内部类对象必须先创建其外部类对象的原因。
有人会问,如果内部类里的一个成员变量与外部类的一个成员变量同名,也即外部类的同名成员变量被屏蔽了,怎么 办?没事,Java里用如下格式表达外部类的引用:
outerClass.this
有了它,我们就不怕这种屏蔽的情况了。
静态内部类
和普通的类一样,内部类也可以有静态的。不过和非静态内部类相比,区别就在于静态内部类没有了指向外部的引用。 这实际上和C++中的嵌套类很相像了,Java内部类与C++嵌套类最大的不同就在于是否有指向外部的引用这一点上,当然从设计的角度以及以它一些细节来 讲还有区别。
除此之外,在任何非静态内部类中,都不能有静态数据,静态方法或者又一个静态内部类(内部类的嵌套可以不止一 层)。不过静态内部类中却可以拥有这一切。这也算是两者的第二个区别吧。
局部内部类
是的,Java内部类也可以是局部的,它可以定义在一个方法甚至一个代码块之内。
  1. public   class  Goods1 {  
  2.     public  Destination dest(String s) {  
  3.         class  GDestination  implements  Destination {  
  4.             private  String label;  
  5.             private  GDestination(String whereTo) {  
  6.                 label = whereTo;  
  7.             }  
  8.             public  String readLabel() {  
  9.                 return  label;  
  10.             }  
  11.         }  
  12.         return   new  GDestination(s);  
  13.     }  
  14.     public   static   void  main(String[] args) {  
  15.         Goods1 g = new  Goods1();  
  16.         Destination d = g.dest("Beijing" );  
  17.     }  
  18. }  
Java代码 
  1. public class Goods1 {  
  2. public Destination dest(String s) {  
  3. class GDestination implements Destination {  
  4. private String label;  
  5. private GDestination(String whereTo) {  
  6. label = whereTo;  
  7. }  
  8. public String readLabel() {  
  9. return label;  
  10. }  
  11. }  
  12. return new GDestination(s);  
  13. }  
  14. public static void main(String[] args) {  
  15. Goods1 g = new Goods1();  
  16. Destination d = g.dest("Beijing");  
  17. }  
  18. }  

上面就是这样一个例子。在方法dest中我们定义了一个内部类,最后由这个方法返回这个内部类的对象。如果我们 在用一个内部类的时候仅需要创建它的一个对象并创给外部,就可以这样做。当然,定义在方法中的内部类可以使设计多样化,用途绝不仅仅在这一点。
下面有一个更怪的例子:
  1. public   class  Goods2 {  
  2.     private   void  internalTracking( boolean  b) {  
  3.         if  (b) {  
  4.             class  TrackingSlip {  
  5.                 private  String id;  
  6.                 TrackingSlip(String s) {  
  7.                     id = s;  
  8.                 }  
  9.                 String getSlip() {  
  10.                     return  id;  
  11.                 }  
  12.             }  
  13.             TrackingSlip ts = new  TrackingSlip( "slip" );  
  14.             String s = ts.getSlip();  
  15.         }  
  16.     }  
  17.     public   void  track() {  
  18.         internalTracking(true );  
  19.     }  
  20.     public   static   void  main(String[] args) {  
  21.         Goods2 g = new  Goods2();  
  22.         g.track();  
  23.     }  
  24. }  
Java代码 
  1. public class Goods2 {  
  2. private void internalTracking(boolean b) {  
  3. if (b) {  
  4. class TrackingSlip {  
  5. private String id;  
  6. TrackingSlip(String s) {  
  7. id = s;  
  8. }  
  9. String getSlip() {  
  10. return id;  
  11. }  
  12. }  
  13. TrackingSlip ts = new TrackingSlip("slip");  
  14. String s = ts.getSlip();  
  15. }  
  16. }  
  17. public void track() {  
  18. internalTracking(true);  
  19. }  
  20. public static void main(String[] args) {  
  21. Goods2 g = new Goods2();  
  22. g.track();  
  23. }  
  24. }  

你不能在if之外创建这个内部类的对象,因为这已经超出了它的作用域。不过在编译的时候,内部类 TrackingSlip和其他类一样同时被编译,只不过它由它自己的作用域,超出了这个范围就无效,除此之外它和其他内部类并没有区别。
匿名内部类
java的匿名内部类的语法规则看上去有些古怪,不过如同匿名数组一样,当你只需要创建一个类的对象而且用不上 它的名字时,使用内部类可以使代码看上去简洁清楚。它的语法规则是这样的:
new interfacename(){......}; 或 new superclassname(){......};
下面接着前面继续举例子:
  1. public   class  Goods3 {  
  2.     public  Contents cont() {  
  3.         return   new  Contents() {  
  4.             private   int  i =  11 ;  
  5.             public   int  value() {  
  6.                 return  i;  
  7.             }  
  8.         };  
  9.     }  
  10. }  
Java代码 
  1. public class Goods3 {  
  2. public Contents cont() {  
  3. return new Contents() {  
  4. private int i = 11;  
  5. public int value() {  
  6. return i;  
  7. }  
  8. };  
  9. }  
  10. }  

这里方法cont()使用匿名内部类直接返回了一个实现了接口Contents的类的对象,看上去的确十分简 洁。
在java的事件处理的匿名适配器中,匿名内部类被大量的使用。例如在想关闭窗口时加上这样一句代码:
  1. frame.addWindowListener( new  WindowAdapter(){  
  2.     public   void  windowClosing(WindowEvent e){  
  3.        System.exit(0 );  
  4.     }  
  5. });   
Java代码 
  1. frame.addWindowListener(new WindowAdapter(){  
  2. public void windowClosing(WindowEvent e){  
  3. System.exit(0);  
  4. }  
  5. });   

有一点需要注意的是,匿名内部类由于没有名字,所以它没有构造函数(但是如果这个匿名内部类继承了一个只含有带 参数构造函数的父类,创建它的时候必须带上这些参数,并在实现的过程中使用super关键字调用相应的内容)。如果你想要初始化它的成员变量,有下面几种 方法:
如果是在一个方法的匿名内部类,可以利用这个方法传进你想要的参数,不过记住,这些参数必须被声明为 final。
将匿名内部类改造成有名字的局部内部类,这样它就可以拥有构造函数了。
在这个匿名内部类中使用初始化代码块。
为什么需要内部类?
java内部类有什么好处?为什么需要内部类?
首先举一个简单的例子,如果你想实现一个接口,但是这个接口中的一个方法和你构想的这个类中的 一个方法的名称,参数相同,你应该怎么办?这时候,你可以建一个内部类实现这个接口。由于内部类对外部类的所有内容都是可访问的,所以这样做可以完成所有 你直接实现这个接口的功能。
不过你可能要质疑,更改一下方法的不就行了吗?
的确,以此作为设计内部类的理由,实在没有说服力。
真正的原因是这样的,java中的内部类和接口加在一起,可以的解决常被C++程序员抱怨 java中存在的一个问题 没有多继承。实际上,C++的多继承设计起来很复杂,而java通过内部类加上接口,可以很好的实现多继承的效果。

四个 应用场景:
场景一:当某个类除了它的外部类,不再被其他的类使用时
我们说这个内部类依附于它的外部类而存在,可能的原因有:1、不可能为其他的类使用;2、 出于某种原因,不能被其他类引用,可能会引起错误。等等。这个场景是我们使用内部类比较多的一个场景。下面我们以一个大家熟悉的例子来说明。
在我 们的企业级Java项目开发过程中,数据库连接池是一个我们经常要用到的概念。虽然在很多时候,我们都是用的第三方的数据库连接池,不需要我们亲自来做这 个数据库连接池。但是,作为我们Java内部类使用的第一个场景,这个数据库连接池是一个很好的例子。为了简单起见,以下我们就来简单的模拟一下数据库连 接池,在我们的例子中,我们只实现数据库连接池的一些简单的功能。如果想完全实现它,大家不妨自己试一试。
首先,我们定义一个接口,将数据库连接 池的功能先定义出来,如下:
public interface Pool extends TimerListener
{
        // 初始化连接池
        public boolean init();
        //销毁连接池
        public void destory();
        // 取得一个连接
        public Connection getConn();
        //还有一些其他的功能,这里 不再列出
        ……
}
有了这个功能接口,我们就可以在它的基础上实现数据库连接池的部分功能了。我们首先想到这个数据 库连接池类的操作对象应该是由Connection对象组成的一个数组,既然是数组,我们的池在取得Connection的时候,就要对数组元素进行遍 历,看看Connection对象是否已经被使用,所以数组里每一个Connection对象都要有一个使用标志。我们再对连接池的功能进行分析,会发现 每一个Connection对象还要一个上次访问时间和使用次数。
通过上面的分析,我们可以得出,连接池里的数组的元素应该是由对象组成,该对象 的类可能如下:
public class PoolConn
{
        private Connection conn;
        private boolean isUse;
        private long lastAccess;
        private int useCount;
        ……
}
下 面的省略号省掉的是关于四个属性的一些get和set方法。我们可以看到这个类的核心就是Connection,其他的一些属性都是Connection 的一些标志。可以说这个类只有在连接池这个类里有用,其他地方用不到。这时候,我们就该考虑是不是可以把这个类作为一个内部类呢?而且我们把它作为一个内 部类以后,可以把它定义成一个私有类,然后将它的属性公开,这样省掉了那些无谓的get和set方法。下面我们就试试看:
public class ConnectPool implements Pool
{
        // 存在Connection的数组
        private PoolConn[] poolConns;
        //连接 池的最小连接数
        private int min;
        //连接池的最大连接数
        private int max;
        // 一个连接的最大使用次数
        private int maxUseCount;
        //一个连接的最大空闲时间
        private long maxTimeout;
        // 同一时间的Connection最大使用个数
        private int maxConns;
        //定时器
        private Timer timer;
        public boolean init()
        {
               try
               {
                      ……
                      this.poolConns = new PoolConn[this.min];
                      for(int i=0;i<this.min;i++)
                      {
                             PoolConn poolConn = new PoolConn();
                             poolConn.conn = ConnectionManager.getConnection();
                             poolConn.isUse = false;
                             poolConn.lastAccess = new Date().getTime();
                             poolConn.useCount = 0;
                             this.poolConns[i] = poolConn;
}
……
return true;
               }
               catch(Exception e)
               {
                      return false;
}
}
……
private class PoolConn
{
       public Connection conn;
       public boolean isUse;
public long lastAccess;
       public int useCount;
}
}
因 为本文不是专题来讲述数据库连接池的,所以在上面的代码中绝大部分的内容被省略掉了。PoolConn类不大可能被除了ConnectionPool类的 其他类使用到,把它作为ConnectionPool的私有内部类不会影响到其他类。同时,我们可以看到,使用了内部类,使得我们可以将该内部类的数据公 开,ConnectionPool类可以直接操作PoolConn类的数据成员,避免了因set和get方法带来的麻烦。
上面的一个例子,是使用 内部类使得你的代码得到简化和方便。还有些情况下,你可能要避免你的类被除了它的外部类以外的类使用到,这时候你却不得不使用内部类来解决问题。
 
场 景二:解决一些非面向对象的语句块
这些语句块包括if…else if…else语句,case语句,等等。这些语句都不是面向对象的,给我们造 成了系统的扩展上的麻烦。我们可以看看,在模式中,有多少模式是用来解决由if语句带来的扩展性的问题。
Java编程中还有一个困扰我们的问题, 那就是try…catch…问题,特别是在JDBC编程过程中。请看下面的代码:
……
try
         {
                String[] divisionData = null;
                conn = manager.getInstance().getConnection();
                stmt = (OracleCallableStatement)conn.prepareCall("{ Call PM_GET_PRODUCT.HEADER_DIVISION(?, ?) }");
                stmt.setLong(1 ,productId.longValue() );
                stmt.registerOutParameter(2, oracle.jdbc.OracleTypes.CURSOR); ;
                stmt.execute();
                ResultSet rs = stmt.getCursor(2);
                int i = 0 ;
                String strDivision = "";
                while( rs.next() )
                {
                             strDivision += rs.getString("DIVISION_ID") + "," ;
                  }
                  int length = strDivision.length() ;
                  if(length != 0 )
                  {
                         strDivision = strDivision.substring(0,length - 1);
                  }
                  divisionData = StringUtil.split(strDivision, ",") ;
                  map.put("Division", strDivision ) ;
                  LoggerAgent.debug("GetHeaderProcess","getDivisionData","getValue + " + strDivision +" " + productId) ;
       }catch(Exception e)
        {
                       LoggerAgent.error("GetHeaderData", "getDivisionData",
                                                     "SQLException: " + e);
                       e.printStackTrace() ;
 
       }finally
        {
                       manager.close(stmt);
                       manager.releaseConnection(conn);
        }
这 是我们最最常用的一个JDBC编程的代码示例。一个系统有很多这样的查询方法,这段代码一般分作三段:try关键字括起来的那段是用来做查询操作 的,catch关键字括起来的那段需要做两件事,记录出错的原因和事务回滚(如果需要的话),finally关键字括起来的那段用来释放数据库连接。
我 们的烦恼是:try关键字括起来的那段是变化的,每个方法的一般都不一样。而catch和finally关键字括起来的那两段却一般都是不变的,每个方法 的那两段都是一样的。既然后面那两段是一样的,我们就非常希望将它们提取出来,做一个单独的方法,然后让每一个使用到它们的方法调用。但 是,try…catch…finally…是一个完整的语句段,不能把它们分开。这样的结果,使得我们不得不在每一个数据层方法里重复的写相同的 catch…finally…这两段语句。
既然不能将那些讨厌的try…catch…finally…作为一个公用方法提出去,那么我们还是需要 想其他的办法来解决这个问题。不然我们老是写那么重复代码,真是既繁琐,又不容易维护。
我们容易想到,既然catch…finally…这两段代 码不能提出来,那么我们能不能将try…里面的代码提出去呢?唉哟,try…里面的代码是可变的呢。怎么办?
既然try…里面的代码是可变的,这 意味着这些代码是可扩展的,是应该由用户来实现的,对于这样的可扩展内容,我们很容易想到用接口来定义它们,然后由用户去实现。这样以来我们首先定义一个 接口:
public interface DataManager
{
        public void manageData();
}
我 们需要用户在manageData()方法中实现他们对数据层访问的代码,也就是try…里面的代码。
然后我们使用一个模板类来实现所有的 try…catch…finally…语句的功能,如下:
public class DataTemplate
{
        public void execute(DataManager dm)
        {
               try
               {
                      dm.manageData();
}
catch(Exception e)
{
       LoggerAgent.error("GetHeaderData", "getDivisionData",
                        "SQLException: " + e);
       e.printStackTrace() ;
 
}finally
{
       manager.close(stmt);
       manager.releaseConnection(conn);
}
}
}
这 样,一个模板类就完成了。我们也通过这个模板类将catch…finally…两段代码提出来了。我们来看看使用了这个模板类的数据层方法是怎么实现的:
new DataTemplate().execute(new DataManager()
{
        public void manageData()
        {
                String[] divisionData = null;
                conn = manager.getInstance().getConnection();
                stmt = (OracleCallableStatement)conn.prepareCall("{ Call PM_GET_PRODUCT.HEADER_DIVISION(?, ?) }");
                stmt.setLong(1 ,productId.longValue() );
                stmt.registerOutParameter(2, oracle.jdbc.OracleTypes.CURSOR); ;
                stmt.execute();
                ResultSet rs = stmt.getCursor(2);
                int i = 0 ;
                String strDivision = "";
                while( rs.next() )
                {
                             strDivision += rs.getString("DIVISION_ID") + "," ;
}
                  int length = strDivision.length() ;
                  if(length != 0 )
                  {
                         strDivision = strDivision.substring(0,length - 1);
                  }
                  divisionData = StringUtil.split(strDivision, ",") ;
                  map.put("Division", strDivision ) ;
                  LoggerAgent.debug("GetHeaderProcess","getDivisionData","getValue + " + strDivision +" " + productId) ;
}
});
注 意:本段代码仅供思路上的参考,没有经过上机测试。
我们可以看到,正是这个实现了DataManager接口得匿名内部类的使用,才使得我们解决 了对try…catch…finally…语句的改造。这样,第一为我们解决了令人痛苦的重复代码;第二也让我们在数据层方法的编码中,直接关注对数据的 操作,不用关心那些必需的但是与数据操作无关的东西。
我们现在来回想一下Spring框架的数据层,是不是正是使用了这种方法呢?
 
 
场 景之三:一些多算法场合
假如我们有这样一个需求:我们的一个方法用来对数组排序并且依次打印各元素,对数组排序方法有很多种,用哪种方法排序交给 用户自己确定。
对于这样一个需求,我们很容易解决。我们决定给哪些排序算法定义一个接口,具体的算法实现由用户自己完成,只要求他实现我们的接口 就行。
public interface SortAlgor
{
        public void sort(int[] is);
}
这 样,我们再在方法里实现先排序后打印,代码如下:
public void printSortedArray(int[] is,SortAlgor sa)
{
        ……
       sa.sort(is);
        for(int i=0;i<is.length;i++)
        {
               System.out.print(is[i]+” “);
}
System.out.println();
}
客 户端对上面方法的使用如下:
int[] is = new int[]{3,1,4,9,2};
printSortedArray(is,new SortAlgor()
{
        public void sort(is)
        {
               int k = 0;
               for(int i=0;i<is.length;i++)
               {
                     for(int j=i+1;j<is.length;j++)
                      {
                             if(is[i]>is[j])
                             {
                                    k = is[i];
                                    is[i] = is[j];
                                    is[j] = k;
                             }
                      }
               }
}
});
这 样的用法很多,我们都或多或少的被动的使用过。如在Swing编程中,我们经常需要对组件增加监听器对象,如下所示:
spinner2.addChangeListener(new ChangeListener()
{
public void stateChanged(ChangeEvent e) 
{
System.out.println("Source: " + e.getSource());
}
}
);
在 Arrays包里,对元素为对象的数组的排序:
Arrays.sort(emps,new Comparator(){
        Public int compare(Object o1,Object o2)
        {
               return ((Employee)o1).getServedYears()-((Employee)o2).getServedYears();
}
});
这 样的例子还有很多,JDK教会了我们很多使用内部类的方法。随时我们都可以看一看API,看看还会在什么地方使用到内部类呢?
 
 
 
场 景之四:适当使用内部类,使得代码更加灵活和富有扩展性
适当的使用内部类,可以使得你的代码更加灵活和富有扩展性。当然,在这里头起作用的还是一 些模式的运行,但如果不配以内部类的使用,这些方法的使用效果就差远了。不信?请看下面的例子:
我们记得简单工厂模式的作用就是将客户对各个对象 的依赖转移到了工厂类里。很显然,简单工厂模式并没有消除那些依赖,只是简单的将它们转移到了工厂类里。如果有新的对象增加进来,则我们需要修改工厂类。 所以我们需要对工厂类做进一步的改造,进一步消除它对具体类的依赖。以前我们提供过一个使用反射来消除依赖的方法;这里,我们将提供另外一种方法。
这 种方法是将工厂进一步抽象,而将具体的工厂类交由具体类的创建者来实现,这样,工厂类和具体类的依赖的问题就得到了解决。而工厂的使用者则调用抽象的工厂 来获得具体类的对象。如下。
我们以一个生产形体的工厂为例,下面是这些形体的接口:
package polyFactory;
 
public interface Shape {
public void draw();
public void erase();
 
}
 
通 过上面的描述,大家都可能已经猜到,这个抽象的工厂肯定使用的是模板方法模式。如下:
package polyFactory;
 
import java.util.HashMap;
import java.util.Map;
 
 
public abstract class ShapeFactory {
protected abstract Shape create();
private static Map factories = new HashMap();
public static void addFactory(String id,ShapeFactory f)
{
       factories.put(id,f);
}
public static final Shape createShape(String id)
{
       if(!factories.containsKey(id))
        {
               try
               {
                      Class.forName("polyFactory."+id);
               }
               catch(ClassNotFoundException e)
               {
                      throw new RuntimeException("Bad shape creation : "+id);
               }
        }
        return ((ShapeFactory)factories.get(id)).create();
}
}
不 错,正是模板方法模式的运用。这个类蛮简单的:首先是一个create()方法,用来产生具体类的对象,留交各具体工厂实现去实现。然后是一个Map类型 的静态变量,用来存放具体工厂的实现以及他们的ID号。接着的一个方法使用来增加一个具体工厂的实现。最后一个静态方法是用来获取具体对象,里面的那个 Class.forName……的作用是调用以ID号为类名的类的一些静态的东西。
下面,我们来看具体的类的实现:
package polyFactory;
 
public class Circle implements Shape {
 
 
public void draw() {
        // TODO Auto-generated method stub
       System.out.println("the circle is drawing...");
}
 
 
public void erase() {
        // TODO Auto-generated method stub
       System.out.println("the circle is erasing...");
}
private static class Factory extends ShapeFactory
{
       protected Shape create()
        {
               return new Circle();
        }
}
static {ShapeFactory.addFactory("Circle",new Factory());}
 
}
这 个类的其他的地方也平常得很。但就是后面的那个内部类Factory用得好。第一呢,这个类只做一件事,就是产生一个Circle对象,与其他类无关,就 这一个条也就满足了使用内部类的条件。第二呢,这个Factory类需要是静态的,这也得要求它被使用内部类,不然,下面的 ShapeFacotry.addFactory就没办法add了。而最后的那个静态的语句块是用来将具体的工厂类添加到抽象的工厂里面去。在抽象工厂里 调用Class.forName就会执行这个静态的语句块了。
下面仍然是一个具体类:
package polyFactory;
 
 
public class Square implements Shape {
 
public void draw() {
        // TODO Auto-generated method stub
       System.out.println("the square is drawing...");
}
 
public void erase() {
        // TODO Auto-generated method stub
       System.out.println("the square is erasing...");
}
private static class Factory extends ShapeFactory
{
       protected Shape create()
        {
               return new Square();
        }
}
static {ShapeFactory.addFactory("Square",new Factory());}
 
}
最 后,我们来测试一下:
String[] ids = new String[]{"Circle","Square","Square","Circle"};
        for(int i=0;i<ids.length;i++)
        {
               Shape shape = ShapeFactory.createShape(ids[i]);
               shape.draw();
               shape.erase();
        }
测 试结果为:
the circle is drawing...
the circle is erasing...
the square is drawing...
the square is erasing...
the square is drawing...
the square is erasing...
the circle is drawing...
the circle is erasing...
       这 个方法是巧妙地使用了内部类,将具体类的实现和它的具体工厂类绑定起来,由具体类的实现者在这个内部类的具体工厂里去产生一个具体类的对象,这当然容易得 多。虽然需要每一个具体类都创建一个具体工厂类,但由于具体工厂类是一个内部类,这样也不会随着具体类的增加而不断增加新的工厂类,使得代码看起来很臃 肿,这也是本方法不得不使用内部类的一个原因吧。

本文转载自:http://blog.csdn.net/sinat_27615265/article/details/50619761

远方__
粉丝 0
博文 82
码字总数 0
作品 0
丰台
程序员
私信 提问
Spring源码阅读ClassUtils里的几个常量值【有趣的】

看aop部分的代码的时候,看到spring判断调用的方法判断中,有个判断类名的方法: 然后就看了一眼常量值,非常有趣。 内部类的间隔符 ==>

洛水
2018/12/29
15
0
关于Java内部类字段和方法不能使用static修饰的原因

昨天的文章中,遗留了一个问题就是,为什么Java内部类字段和方法不能使用static修饰。 先下下面一段代码: 上面的内部类的成员变量和方法,只要加上了static修饰,就会出现编译错误。 原因:...

九劫散仙
2018/06/02
27
0
【目录导航】JAVA零基础进阶之路

【JAVA零基础入门系列】(已完结)导航目录 Day1 开发环境搭建 Day2 Java集成开发环境IDEA Day3 Java基本数据类型 Day4 变量与常量 Day5 Java中的运算符 Day6 Java字符串 Day7 Java输入与输出...

MFrank
2018/06/21
0
0
我来告诉你,一个草根程序员如何进入BAT

本文将从心态、学习和素养三个方面手把手的告诉你,LZ作为一个非科班毕业,出身于三流大学的草根,是如何凭借自己的努力,进入到国内一流互联网公司的。 心态 看到这个标题,有的同学可能会说...

Java架构
2018/07/11
0
0
Java中的内部类实例初始化匿名函数(JLS7 8.6:Instance Initializers)

偶尔发现了一段好玩的java代码,关于Java内部类的,觉得很有意思,但是想不明白这段代码是java语法的特性哪?还是别的什么。最后发现还是对java了解的不够深啊。先上代码: import java.util...

qalong
2013/09/02
3.4K
0

没有更多内容

加载失败,请刷新页面

加载更多

《Designing.Data-Intensive.Applications》笔记 四

第九章 一致性与共识 分布式系统最重要的的抽象之一是共识(consensus):让所有的节点对某件事达成一致。 最终一致性(eventual consistency)只提供较弱的保证,需要探索更高的一致性保证(stro...

丰田破产标志
今天
6
0
docker 使用mysql

1, 进入容器 比如 myslq1 里面进行操作 docker exec -it mysql1 /bin/bash 2. 退出 容器 交互: exit 3. mysql 启动在容器里面,并且 可以本地连接mysql docker run --name mysql1 --env MY...

之渊
今天
7
0
python数据结构

1、字符串及其方法(案例来自Python-100-Days) def main(): str1 = 'hello, world!' # 通过len函数计算字符串的长度 print(len(str1)) # 13 # 获得字符串首字母大写的...

huijue
今天
5
0
OSChina 周日乱弹 —— 我,小小编辑,食人族酋长

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @宇辰OSC :分享娃娃的单曲《飘洋过海来看你》: #今日歌曲推荐# 《飘洋过海来看你》- 娃娃 手机党少年们想听歌,请使劲儿戳(这里) @宇辰OSC...

小小编辑
今天
1K
11
MongoDB系列-- SpringBoot 中对 MongoDB 的 基本操作

SpringBoot 中对 MongoDB 的 基本操作 Database 库的创建 首先 在MongoDB 操作客户端 Robo 3T 中 创建数据库: 增加用户User: 创建 Collections 集合(类似mysql 中的 表): 后面我们大部分都...

TcWong
今天
40
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部