——内部类

内部类有两种情况:
(1) 在类中定义一个类(私有内部类,静态内部类)
(2) 在方法中定义一个类(局部内部类,匿名内部类)

1)内部类概念

如果一个类定义在另一个类的内部,这个内部类就叫做内部类。注意此时这个内部类是一个独立的类,它不属于外部类,更不能通过外部类的对象去调用内部类。外部类对内部类没有任何优越的访问权限。

即说:内部类就是外部类的友元类。注意友元类的定义,内部类可以通过外部类的对象参数来访问外部类中的所有成员。但是外部类不是内部类的友元。

2)内部类可以定义在外部类的public、protected、private都是可以的。

如果内部类定义在public,则可通过 外部类名::内部类名 来定义内部类的对象。
如果定义在private,则外部不可定义内部类的对象,这可实现“实现一个不能被继承的类”问题。

3)内部类可以直接访问外部类中的static、枚举成员,不需要外部类的对象/类名。

class A
{
private: static int k;
               int h;
public: class B{
                     void foo(){
                           cout<<k<<endl;//OK
                           //cout<<h<<endl;//ERROR
                     }
              };
};
int A::k=3;

这里cout<<h<<endl;是一个非常常见的错误。因为内部类是一个独立的类,不属于外部类,所以此时还没有外部类的对象,显然也不存在h。而k就不同了,不需要外部类的对象就已存在,所以这里k是OK的。
这和友元类的使用也是同样的道理。“想要使用另一个类的成员,必须要存在这个类的对象”。

class A
{
private: static int k;
               int h;
public: class B{
                     void foo(A a){
                           cout<<k<<endl;//OK
                           cout<<a.h<<endl;//OK
                     }
              };
};
int A::k=3;

这样就没问题了。

4)在堆中创建内部类对象:

class A
{
public: class B{};
};
int _tmain(int argc, _TCHAR* argv[])
{
       A::B*b=new A::B();
       return 0;
}

5)内部类可以在外部类中声明,然后在外部类外定义:

class A
{
private: static int i;
public: class B;
};
class A::B{
public:void foo(){cout<<i<<endl;}//!!!这里也不需要加A::i.
};
int A::i=3;

这形式上就更像友元类了。注意这里和友元类,不要混淆了。

6)sizeof(外部类)=外部类,和内部类没有任何关系。

class A
{
public:
       class B{int o;};
};
int _tmain(int argc, _TCHAR* argv[])
{
       cout<<sizeof(A)<<endl;//1
       return 0;
}

~私有内部类 —— 在方法之间定义的内部类,非静态

首先看看类中内部类的两个特点:
(1)在外部类的作用范围内可以任意创建内部类对象,即使内部类是私有的(私有内部类)。即内部类对包围它的外部类可见

//代码1:内部类对外部类可见   
class Outer{   
     //创建私有内部类对象   
     public Inner in=new Inner();   
     //私有内部类   
     private class Inner{   
          ...   
     }   
}  

(2) 在内部类中可以访问其外部类的所有域,即使是私有域。即外部类对内部类可见。

//代码2:外部类对内部类可见   
class Outer{   
       //外部类私有数据域   
       private int data=0;   
       //内部类   
       class Inner{   
           void print(){   
                 //内部类访问外部私有数据域   
                 System.out.println(data);   
           }    
       }   
}  

(1)问题来了:上面两个特点到底如何办到的呢?内部类的"内部"到底发生了什么?

对内部类进行编译后发现有两个class文件:Outer.class 和OuterInner.class),而不是Outer类的某一个域。 虚拟机运行的时候,也是把Inner作为一种常规类来处理的。

(2)但问题又来了,即然是两个常规类,为什么他们之间可以互相访问私有域那(最开始提到的两个内部类特点)?这就要问问编译器到底把这两个类编译成什么东西了。

我们利用reflect反射机制来探查了一下内部类编译后的情况(关于探查类内部机制的代码提供在下面的附件里Reflect.java)。

<1>编译代码1生成 OuterInner") 对内部类Inner进行反射。运行结果 发现了三个隐含的成分:

//反编译1  
class Outer$Inner   
{   
        Outer$Inner(Outer,Outer$Inner);  //包可见构造器   
        private Outer$Inner(Outer);   //私有构造器将设置this$0域   
        final Outer this$0;   //外部类实例域this$0  
}  

现在我们可以解释上面的第一个内部类特点了:为什么外部类可以创建内部类的对象?并且内部类能够方便的引用到外部类对象

首先编译器将外、内部类编译后放在同一个包中。在内部类中附加一个包可见构造器。这样, 虚拟机运行Outer类中Inner in=new Inner(); 实际上调用的是包可见构造: new Outer$Inner(this,null)。因此即使是private内部类,也会通过隐含的包可见构造器成功的获得私有内部类的构造权限。

再者,Outer0,那么通过这个引用就可以方便的得到外部类对象中可见成员。但是Outer类中的private成员是如何访问到的呢?这就要看看下面Outer.class文件中的秘密了。