一, 什么是内部类
Java中所谓内部类, 就是定义在另1个类内的类.
例如:
class A{
int a;
class B{
int b;
}
}
上面类B就是类A的一个内部类.
但是下面的写法, B只是A的一个成员.
class A{
private int a;
B b;
}
class B{
int bb;
}
因为B的定义体在类A之外, 注意区别.
Java编译器编译后, 还是会把面的代码编译成两个class.
1个是A
另1个是 A$B
对于Jvm来讲,它们实际上还是两个单独的类.
二,内部类的好处.
的确, 内部类的写法的确会破坏Java代码的良好结构以及可读性.
但是内部类也有它存在的意义.
例如一部法拉利跑车类, 它里面包括了1个发动机, 而发动机本身也有很多方法(例如,启动, 变速等). 所以发动机本身也是1个类.
但是法拉利的发动机技术很好, 为了技术机密保护, 法拉利本身不想外部知道发动机的细节, 就可以把发动机作为1个内部类.
也就是说, 外部不能直接实例化1个法拉利发动机, 用在别的车上.
好处:
1. 限制外部世界对内部类的访问.
2. 内部类可以直接访问外部类的成员.
其中第2点很重要, 甚至比第一点还重要.
三,什么情况下需要用到内部类.
这里我举1个具体点的例子.
加入1个聊天窗口Form类.
有很多控件类对象成员(例如输入框, 显示框等).
现在要启动2个线程, 分别用于接受和发送消息.
问题来了, 启动线程必须要重写Thread类或实现Runable接口,总结要写新的类.
那么新的类写在哪里呢.
如果写在外面, 那么新的类必须写入很多控件类的成员.
如果写入在Form类里面, 就可以直接使用Form类的所有成员. 这样就方便很多.
而且这个两个类也不想被其他类使用, 所以内部类就很适合了.
四,内部类的种类.
没错, Java内部类也可以根据定义的位置和访问权限分成下面若干种内部类.
4.1 定义在另1个类的方法体外的内部类.
4.1.1 非私有内部类(非静态)
这种内部i类(B)虽然定义在另1个类(A)的内部, 但实际上它是被第三个类(C)实例化的.
不过C类想实例化类B. 则必须先实例化类A.
public class InternalClass1{
public static void f(){
A a = new A(3);
A.B b = a.new B();
b.print();
A.B b2 = new A(4).new B();
b2.print();
}
}
class A{
private int i;
public A(int i){
this.i = i;
}
class B{
public void print(){
System.out.printf("A.i is %d\n",i);
}
}
}
上面的例子定义了外部类A和内部类B.
可以见到内部类B是无限制地访问A的私有成员的.
反过来, 类A也同样可以无限制的内部类B的私有成员.
而B可以被第三个类(InternalClass1)实例化.
实例化的写法上面写了两种, 但是都必须事先实例化1个A对象.
也就是说Java必须为A的1个对象分配内存, 才能为A的内部类对象分配内存.
4.1.2 私有内部类(非静态)
私有内部类指的是private 修饰的内部类(注意外部类不能被private修饰)
这样的话
内部类B不能被第三方类C直接访问和实例化
但是能通过A的封装方法访问.
例子:
public class InternalClass2{
public static void f(){
//A2.B2 b = new A2(3).new B2(); //error
A2 a = new A2(3);
a.f();
}
}
class A2{
private int i;
public A2(int i){
this.i = i;
}
public void f(){
B2 b = new B2();
System.out.println(i + b.b);
}
private class B2{
private int b =3;
public void print(){
System.out.printf("A.i is %d\n",i);
}
}
}
4.1.3 静态内部类
跟外部类不同, 内部类可以用static关键字来修饰. 代表这个是1个静态内部类.
静态内部类跟非静态内部类有两点不同.
1. 实例化1个非静态内部类之前必须实例化其外部类. 而静态内部类不需要. 可以直接被实例化, 也就是说静态内部类不需要1个外部类对象.
2. 同样地, 内部类只能访问其外部类的静态成员. 因为外部类的非静态成员依赖于外部类对象实例化.
例子:
public class InternalClass3{
public static void f(){
A3.printj(3);
}
}
class A3{
private int i;
private static int j;
public static void printj(int j){
B3 b = new B3(j);
b.print();
}
private static class B3{
public B3(int j){
//A3.i= 3;//error
A3.j = j;
}
public void print(){
System.out.printf("A.j is %d\n",j);
}
}
}
4.1.4 方法体内部类
下面开始恶心了, 就是内部类可以定义在方法体内.
假如B 是 A的方法f(int a)内的1个内部类.
1. B还是无限制访问A的所有成员.
2. A只能在方法f()内访问和使用B.
假如B定义在f() 里面的if() 或 try{}字句内, 那么同样地, A只能在if,try的作用范围内使用B.
3. 在方法体f()内, 代码还是严格按照顺序执行的, 那就是说必须先定义内部体B, 再使用.
4. 假如B定义的内部使用到参数a 那么a必须用Final修饰.
例子:
public class InternalClass4{
public static void f(){
new A4(2).printB(3,6);
}
}
class A4{
private int i;
public A4(int i){
this.i = i;
}
public void printB(final int x, int y){
//class B
class B4{
public int getSum(){
return i + x;
}
}
System.out.println(new B4().getSum() + y);
}
}
例如上面的printB方法, 有两个参数x, y 但是内部类B只访问了x, 所以x必须用final修饰, 而y不用.
可以看出, 这个种内部类已经大大影响java代码的可读性了.
幸好这种内部类不可能被其他类访问. 作用域也只在方法体内. 但是起名字还是需要避免和同包内的其它类冲突.
4.1.5 匿名内部类
所谓匿名内部类就是定义1个内部类时不指定类名字
听起来不合常理啊.
在外部类A内定义1个匿名内部类B(注意B不是类名, 只是1个代号) 有两个必要天剑.
1.内部类B只能定义在A的方法体内.
2.内部类必须是继承自另1个类Bp(Bp是类名). 或者是实现另1个接口Bi(Bi是接口名).
而Bp和Bi必须事先定义.
对于第一种, 一般用于调用1个超类的重写工公共方法.. 例如重写Thread类Run方法, 开启1个新进程.
public class InternalClass5{
public static void f(){
new A5().printB(3);
}
}
class A5{
private int i = 100;
public void printB(final int x){
//a new internal class which inherits Thread
new Thread(){
public void run(){
while(i > 0){
System.out.println(i);
i = i -x;
}
}
}.start();
}
}
对于第2种, 一般是返回1个接口的实现类对象:
例如:
public class InternalClass6{
public static void f(){
Ib b = new A6().GetIB(4);
b.f();
}
}
class A6{
private int i = 100;
public Ib GetIB(final int x){
//a new internal class which implements interface Ib
return new Ib(){
public void f(){
System.out.println("this is a method implemented from a interface!");
System.out.println(i+x);
}
};
}
}
interface Ib{
void f();
}
注意这个接口必须是事先定义的.
匿名内部类的好处是随写随用, 而且无非担心命名冲突.