Java编程思想——细话Java内部类(下)

一、匿名内部类

      像下边这样虽然返回的是B的对象,但是其后却带有B的{},括号中可以是类的成员和方法。这种类的形式由于对外是隐藏的,且没有直接的类名,所以称为匿名内部类。

public class A{
    public B getB(){
       return new B(){};
    }
}

这里需要注意的一点是,如果在匿名内部类中使用外部定义的对象,那么编译器要求其参数引用是final的(如果不使用就不需要)。

public class A{
    public B getB(final String name){
       return new B(){
           private String st=name;//name必须是final
           public String getStr(){
               return st;
           }
       };
    }
}

因为匿名内部类是没有名字的,所以它是不可能有命名构造器的,但是可以通过实例初始化达到为匿名内部类创建一个构造器的效果。

interface A{
   public void f();
}
public class UseA{
   public static A getA(final int i){
      //因内部类要使用i,所以应为final
      return new A(){
       public void f(){
          System.out.print("this num="+i);
       }
     };//通过实例初始化创建构造器效果
   }
   public static void main(String[] args){
      A a=getA(10);
      a.f();
   }
}
out:
this num=10

但是要注意的是这种方式创建的构造器有一个限制——实力初始化方法不能被重载,所以只能有一个这样的构造器。

注:匿名内部类与正规的继承相比有些受限,因为匿名内部类既可以扩展类,也可以实现接口,但是不能俩者兼备。而且如果实现接口,也只能实现一个接口。

看一下反编译后内部类的效果

final class UseA$1 implements A
{
  UseA$1(int paramInt) {}
  
  public void f()
  {
    System.out.print("this num=" + this.val$i);
  }
}

二、嵌套类

      带有static的内部类被称为嵌套类(要知道普通的内部类对象隐式地保存了一个引用,指向创建它的外围对象)。而此时的内部类会有俩个特点:

  • 创建嵌套类对象不需要外围类的对象。
  • 不能从嵌套类的对象中访问非静态的外围类对象。

注意:嵌套类没有this引用。

嵌套类可以作为接口(接口中除了一些声明外本不能存放任何代码)的一部分,放到接口中的任何类自动地是public和static,且可以在内部类中实现外部接口。

public interface A{
   void f();
   class B implements A{
      public void f(){
         System.out.print("Hello!");
      }
      public static void main(String[] args){
        new B().f();//可以直接通过new进行对象创建(static让其变成了独立的公共类)
     }
   }
}

上边的例子经过编译不会出现任何问题,但是在执行时会报错:


原因:

如果在执行时直接使用"java A",那当然会报错,因为嵌套类本就不属于接口A(所以此时接口A中实际上并没有其他代码),看一下编译后嵌套类的效果:

public class A$B implements A
{
  public void f()
  {
    System.out.print("Hello!");
  }
  
  public static void main(String[] paramArrayOfString)
  {
    new B().f();
  }
}
很显然main方法在A$B类(B类此时在接口A的命名空间中)中,所以要使用”java A$B“来执行代码,此时A$B实际是一个独立的类(Linux中需要将$进行转义)。

三、为什么要使用内部类?

       一般来说内部类继承自某个类或者实现某个接口,内部类的代码操作创建它的外围类的对象。所以可以认为内部类提供了某种进入其外围类的窗口。

*最吸引人的原因:

     每个内部类都能独立地继承自一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响。

解释:

     也就是对于一个普通类来说在不考虑内部类来说,要实现多继承机制只能通过”继承一个类+实现多个接口“或”只实现多个接口“。但是如果没有接口而只有类或抽象类呢?那要想实现多继承就只能通过内部类实现,见如下实例:

class B{
	void show(){
		System.out.println("This is B!");
	}
}
abstract  class C{
	public int i=10;
	abstract void abShow();
	public void showI(){}
}
public class  A extends B{  //主类继承B
    class Inner extends C{  //内部类继承C
		public void abShow(){
		    System.out.println("This is C!");
		}
		C makeC(){
			return new C(){ //匿名内部类继承C并重写C中方法
			   public void abShow(){
			   System.out.println("This is makeC!");
		        }
			   public void showI(){
			     System.out.println("i="+i);
		        }
			};
		}
	}

	public static void main(String[] args){
		A a=new A();
		A.Inner in=a.new Inner();
		C c=in.makeC();
		a.show();
		in.abShow();
		c.abShow();
		c.showI();

	}
}
out:
This is B!
This is C!
This is makeC!
i=10

*内部类的一些特性:

  • 内部类可以有多个实例,每个实例都有自己的状态信息,并且与其外围类对象的信息相互独立。
  • 在单个外围类中,可以让多个内部类以不同的方式实现同一个接口,或继承同一个类(就如上边的例子)。
  • 创建内部类对象的时刻并不依赖于外围类对象的创建。
  • 内部类并没有令人迷惑的”is-a“关系;它就是一个独立的实体。

*闭包与回调(callback):

  • 闭包——是一个可调用的对象,它记录了一些信息,这些信息来自于创建它的作用域。
  • 得出结论——内部类就是一个面向对象的闭包(不仅包含外围类对象的信息还自动有一个指向此外围类对象的引用)。
  • 回调——通过回调,对象可以携带一些信息,这些信息允许它在稍后的某个时刻调用初始化的对象。它的价值在于它的灵活性,可以在运行时动态地决定需要调用什么方法。

*内部类在控制框架中的使用:

控制框架由一个或一组类构成,用以将变化的事物与不变的事物相互分离。

  • 控制框架的完整实现是由单个的类创建的,从而使得实现的细节被封装。内部类用来表示解决问题所必需的各种不同的action()。
  • 内部类可以很容易地访问外围类的任意成员,所以可以避免这种实现变得笨拙。如果没有这种能力,代码将变得令人讨厌,以至于你肯定会选择别的方法。

使用内部类可以在单一的类中产生对同一个基类Event的多种导出版本。

*内部类标识符:

前边的反编译.class文件中显示了每个对象的全部信息,内部类在编译时同样会生成一个独立的.class文件。可以看出这些文件的命名有严格的规则:外围类的名字,加上”$“,再加上内部类的名字就是一个内部类的.class文件名:

class A$Inner$1  extends C
{
  A$Inner$1(A.Inner paramInner) {}
  
  public void abShow()
  {
    System.out.println("This is makeC!");
  }
  
  public void showI()
  {
    System.out.println("i=" + this.i);
  }
}
在名字中可以看到一个数字”1“,这个代表匿名内部类,由于匿名内部类没有名字,所以编译器会简单的产生一个数字作为其标识符。如果内部类是嵌套在别的内部类中,需要在最内层的类名前再加一个”$“。

----------------------------------------------------------------------------------------------

       接口和内部类的产生使得Java的多继承机制更加完善,尤其对于内部类来说,其允许继承多个非接口类型(类或抽象类),并且比C++中多继承更好理解和使用。但是在实际开发中什么时候应该用这些技术,应该在设计阶段考虑。


猜你喜欢

转载自blog.csdn.net/goodli199309/article/details/79981739
今日推荐