外部类通常有方法返回内部类的引用
class Test1{ public class Test{ //... } public Test getTest(){ return new Test(); } }
创建内部类的对象
创建内部类对象时,如果是在外部类里面创建,则直接使用InnerClass来声明引用,如果是是外部类之外,则必须使用OutterClass.InnerClass的形式。
如果是在外部类的静态方法中或其他类的方法中创建,则必须使用"外部类的引用.new InnerClass()"的形式。如果是在外部类的非静态方法中创建,则可以直接使用new InnerClass()的形式,也可以使用"外部类的引用.new InnerClass()"的形式。
class Test1{ public class Test{ //... } public Test getTest(){ return new Test(); } public static void test(){ Test t = new Test1().new Test(); } public void test1(){ Test t = new Test1().new Test(); } public void test2(){ Test t = new Test(); } }内部类可以访问外部类的一切元素
class Test2{ private int i = 10; public class Test3{ public void foo(){ System.out.println(i);//可以访问外部类的私有i } } }
这是因为内部类持有一个外部类的引用,这个引用可以访问外部类的任何元素。创建内部类时要求提供外部类的引用,否则编译出错。
在内部类中产生外部类的引用
如果需要在内部类中产生外部类的引用,则使用"OutterClass.this"的形式
class Test3{ class Test4{ public Test3 getTest3(){ return Test3.this; } } }在其它类创建内部类对象
使用“外部类引用.new InnnerClass()”形式
class Test5{ class Test6{ } } class Test7{ public Test5.Test6 getTest6(){ return new Test5().new Test6(); } }不能使用"new Test5().new Test5.Test6()"的形式,这样会报错。
当我们创建内部类时必须提供外部类的引用,这仅限于内部类。如果是嵌套类(静态内部类)的话,就不需要外部类对象的引用了。
内部类和upcasting
当内部类被upcast成接口或父类时,内部类就真正独立了。如果把内部类通过权限控制符设为私有的或保护的,这样一来内部类就不能被看到,不能被接触了,有利于信息隐藏。我们得到的仅仅是对父类或接口的引用。甚至不知道精确的类型是什么。
interface Customer{ boolean pay(); } class Company{ private class CompanyCustomer implements Customer{ public boolean pay(){ System.out.println("pay"); return true; } } public Customer getCustomer(){ return new CompanyCustomer(); } } public class TestInnerClass { public static void main(String ... args){ Customer c = new Company().getCustomer(); c.pay(); } }内部类的修饰符
对于访问权限控制符,外部类只能用public或默认修饰。内部类可以用public,private,protected,默认修饰,和普通的成员变量可见性一样。要注意:protected也提供包访问权限。
方法和scope中的内部类
内部类可以在方法中或scope中创建,你需要这样做可能有以下两种原因:
1.需要某个方法返回实现某个接口的类的引用,这时候可以将实现接口的类写在方法中,然后返回时new一个引用。
2.需要一个辅助类来解决复杂问题,但是不想让大家公开访问。
以下列出六种可能的场景:
1.在方法中定义的类
public Destination destination(String s){ class PDestination implements Destination{ private String label; private PDestination(String whereTo){ label = whereTo; } public String readLabel(){ return label; } } return new PDestination(s); }
上面这个内部类只能在destination这个方法中访问,不能在方法外访问。
2.在任意scope中的内部类
private void internalTracking(boolean b){ if(b){ class TrackingSlip{ private String id; TrackingSlip(String s){ id = s; } String getSlip(){ return id; } } TrackingSlip ts = new TrackingSlip("Slip"); String s = ts.getSlip(); } }上面的内部类只能在if(b)这个scope中使用,虽然是在if中,这个类还是每次都会编译,不论会不会执行。
3.为了实现某个接口而引入的匿名内部类
interface Contents{ int value(); } public class Parcel7{ public Contents contents(){ return new Contents(){ private int i = 11; public int value(){ return i; } }; } }以上的实现Contents接口的内部类是匿名的,通过new Contents(){实现}的形式来实现,创建了一个实现Contents接口的匿名类的实例,返回的实例被自动upcast成Contents引用。
这个方法使用的是父类默认的无参数构造函数,如果父类只有有参数的构造函数,则是下面的情况。
4.继承某个无默认无参数构造函数的基类的匿名内部类
public class Wrapping{ private int i; public Wrapping(int x){ i = x; } public int value(){ return i; } } public class Parcel8{ public Wrapping wrapping(int x){ return new Wrapping(x){ public int value(){ return super.value()*47; } }; } }
上面的例子,父类Wrapping只有包含参数的构造函数,没有默认的构造函数,这个时候就需要传递参数进去,然后重写父类的方法。
5.执行field initialization的匿名内部类
public class Parcel9{ public Destination destination(final String dest){ return new Destination(){ private String label = dest;//field initializtion public String readLabel(){ return label; } } } }如果匿名内部类想使用匿名内部类外的对象,编译器要求参数引用定义为final的,否则会报错。就像这个例子中的dest一样。
6.使用instance initialization来构建匿名内部类
abstract class Base{ public Base(int i){ System.out.println("Base constructor,i="+i); } public abstract void f(); } public class AnonymousConstructor{ public static Base getBase(int i){ return new Base(i){ {System.out.println("Inside instance initializer");} public void f(){ System.out.println("In anonymous f()"); } }; } }这里的i不一定是final的,因为i是父类中使用的,在匿名内部类中没有直接使用。
public class Parcel10{ public Destination destination(final String dest,final String price){ return new Destination(){ private int cost; //Instance initialization for each object { cost = Math.round(price); if(cost>100){ System.out.println(Over Budget); } } private String label = dest; public String readLabel(){ return label; } }; }这里的参数必须是final的,因为内部类使用到了它们。
如果仅仅通过field初始化,if语句中的内容不能执行,因此instance初始化就相当于内部类的构造函数,当然只能有一个构造函数。
匿名内部类是有限制的,因为只能继承一个类或实现一个接口,不能多个。
使用匿名内部类实现factory method
package test3; public class FactoryMethod { public static void serviceCustomer(ServiceFactory fact){ Service s = fact.getService(); s.method1(); s.method2(); } public static void main(String args[]){ serviceCustomer(Implementation1.factory); serviceCustomer(Implementation2.factory); } } interface Service{ void method1(); void method2(); } interface ServiceFactory{ Service getService(); } class Implementation1 implements Service{ private Implementation1(){} public void method1(){ System.out.println("Implementation1 menthod1"); } public void method2(){ System.out.println("Implementation1 menthod2"); } public static ServiceFactory factory = new ServiceFactory(){ public Service getService(){ return new Implementation1(); } }; } class Implementation2 implements Service{ private Implementation2(){} public void method1(){ System.out.println("Implementation2 menthod1"); } public void method2(){ System.out.println("Implementation2 menthod2"); } public static ServiceFactory factory = new ServiceFactory(){ public Service getService(){ return new Implementation2(); } }; }改进:
1.Service的构造函数成了私有的,只能通过factory来得到实例。
2.Service中的factory对象是static的,因为通常每个工厂只需要一个实例。
嵌套类
静态的内部类又叫嵌套类,嵌套类没有指向外部类实例的引用。
嵌套类意味着:
1.创建嵌套类时不需要外部类的引用
2.不能直接访问外部类对象的非静态变量。
public class TestInnerMain { public void f(){ System.out.println("f()"); } public static void foo(){ System.out.println("foo()"); } public static class TestClass{ public static void main(String args[]){ new TestInnerMain().f(); foo(); } public void test(){ new TestInnerMain().f();//可以间接访问 //f();//不能直接访问 } } }能否包含static内容?
普通的内部类不能有static data,static内部类(嵌套类)可以有static data,包括static fields,static methods,static classes等。
接口中的内部类是嵌套类
接口中定义的类默认是public static的,在这个类中甚至可以实现外面的接口。
interface TestInnerInterface { void foo(); class TestClass implements TestInnerInterface{ @Override public void foo() { // TODO Auto-generated method stub System.out.println("foo()"); } } }如果需要在所有实现接口的类中使用通用的代码,则可以在接口中嵌套类来实现。
嵌套类或内部类生成“OutterClass$InnerClass”形式的class文件,在linux或unix中没有美元符号。
多重嵌套的非静态内部类的访问规则
多重嵌套的非静态内部类,内部的类可以访问所有外部类的成员,通过引用链来调用。
为什么要使用内部类?
内部类可以访问外部类的变量,可以看作外部类的窗口。
每个内部类独立的实现了一些逻辑,内部类可以继承某个类的实现,不管外部类是否继承了某个类的实现。
内部类帮助java实现了多重实现继承,弥补了接口的不足。也就是说,内部类使得我们可以继承多个非接口类。
public class Test0114 { static void takesD(D d){} static void takesE(E e){} public static void main(String args[]){ Z z = new Z(); takesD(z); takesE(z.makeE()); } } class D{} abstract class E{} class Z extends D{ E makeE(){ return new E(){ }; } }这样Z既能当D用,又能当E用。
一个外部类可以有多个内部类,每个内部类有独立的逻辑,这样为外部类提供了多种实现方案。
package test3; interface Selector{ boolean end(); Object current(); void next(); } public class TestMultiInner { public static void main(String args[]){ Sequence sequence = new Sequence(10); for(int i = 0; i < 10; i ++){ sequence.add(Integer.toString(i)); } Selector selector = sequence.selector(); while(!selector.end()){ System.out.println(selector.current() + " "); selector.next(); } Selector selector1 = sequence.reverseSelector(); while(!selector1.end()){ System.out.println(selector1.current()+ " "); selector1.next(); } } } class Sequence{ private Object[] items; private int next = 0; public Sequence(int size){ items = new Object[size]; } public void add(Object x){ if(next<items.length){ items[next++] = x; } } public Selector selector(){ return new SequenceSelector(); } private class SequenceSelector implements Selector{ private int i = 0; @Override public boolean end() { // TODO Auto-generated method stub return i == items.length; } @Override public Object current() { // TODO Auto-generated method stub return items[i]; } @Override public void next() { // TODO Auto-generated method stub if(i<items.length){ i++; } } } private class ReverseSelector implements Selector{ private int i = items.length-1; @Override public boolean end() { // TODO Auto-generated method stub return i == -1; } @Override public Object current() { // TODO Auto-generated method stub return items[i]; } @Override public void next() { // TODO Auto-generated method stub if(i>-1){ i--; } } } public Selector reverseSelector(){ return new ReverseSelector(); } }
闭包和回调
闭包是一个可以访问创建它的作用域的信息的可调用对象。
内部类是一个面向对象的闭包,因为它不仅包含外部类(创建它的作用域)的信息,还自动保留了一个对整个外部类对象的引用,通过这个引用它可以操作外部类的所有成员,包括private的。
java中引入了类似于指针机制的回调。通过回调,给别的对象一些信息来允许别的对象在后面的某个时刻回调最初的对象。