一个类中重新定义的类,又称为内部类,又称为寄生类,平时编程中很少使用内部类,特别混乱。
但是,内部类也有很大的作用:
提供了更好的封装,将类隐藏在外部类中,使其他类无法调动。
内部类可以使用外部类的所有数据,包括private限定。
在jdk啊,很多地方都在使用内部类。 在集合的源代码中,例如,在ArrayList中,为了实现迭代器接口,使用内部类来实现迭代器接口。
包含内部类的类称为顶级,顶级只能与public在缺省情况下限定。 内部类可以使用四种权限修饰符之一。 我们访问内部类时,从外部类访问内部类,而不能直接访问内部类。
package demo_Inner;
公共类测试{
publicstaticvoidmain (字符串[ ] args ) {
Outer outer=new Outer (;
outer.print (; //外部类
Outer.Inner inner=new Outer ().new Inner );
inner.print (; //内部类
}
}
类outer {
公共类inner {
公共语音打印(
System.out.println (内部类);
}
}
公共语音打印(
系统. out.println (外部类);
}
}上述代码有三个类:测试类和外部类Outer,还有内部类Inner。 用测试类的main方法进行测试,直接创建外部类对象。 当然要访问的是外部类中的方法,打印输出“外部类”,然后创建内部类对象并调用内部类中的方法。 请注意,只能从外部类调用内部类。 因此,内部类的完整类名是Outer.Inner,在创建对象时,它也会从外部类的对象中找到内部类,然后创建内部类对象。 因此,创建上述内部类的写法如下。
Outer.Inner inner=new Outer ().new Inner ) )或:
Outer outer=new Outer (;
outer.inner inner=outer.new inner (;
外部类可以直接创建内部类对象。 要在测试类中创建内部类对象,必须将其作为属性通过外部类
公共inner inner=new inner (;
如果不想让测试类创建内部类对象,可以使用private来限定内部类对象。 但是,这仍然可以在外部类中访问内部类。 请注意,外部类和内部类不能重命名。 否则,将报告错误。
java虚拟机遇到几个类时会生成几个类文件。 内部类也是如此,但内部类和其他类的文件名不匹配。 外部类$内部类. class
内部类分为四类:实例内部类、静态内部类、本地内部类和匿名内部类
一、实例内部类
实例内部类的创建必须依赖于外部类的实例。 上例是实例内部类。 请注意,实例内部类可以访问外部类(包括private )中由任何权限修饰符限定的数据
上面的程序改造:在外部类中添加由各种权限修饰符限定的成员变量和一个方法,并在内部类中调用
package demo_Inner;
公共类测试{
publicstaticvoidmain (字符串[ ] args ) {
//Outer outer=new Outer (;
//outer.inner inner=outer.new inner (;
//outer.print (;
Outer.Inner inner=new Outer ().new Inner );
inner.out (;
}
}
类outer {
私有Int id=1001;
保护性字符串名称='礼貌蜻蜓';
公共字符串sex='男人';
静态int score=77;
公共语音打印(
System.out.println (外部类方法);
}
公共类inner {
公共语音输出
system.out.println(id;
system.out.println(name;
system.out.println(sex;
/p>System.out.println(score);
print();
}
}
}输出:
1001
细心的蜻蜓
男
77
外部类中方法
通过输出可以看到,我们没有创建外部类的对象,就可以直接访问到外部类的成员变量和方法。内部类之所以能访问到内部类的数据,因为当内部类的实例存在的时候,内部类的实例肯定已经存在,内部类自动持有外部类的引用。如果在内部类中还有一个内部类,这个内部类依然可以访问到属于他的两个外部类的数据。
class Outer{
private int id = 1001;
protected String name = "细心的蜻蜓";
public String sex = "男";
static int score = 77;
public void print() {
System.out.println("外部类中方法");
}
public class Inner{
class IInner{
public void a() {
System.out.println(id);
System.out.println(name);
System.out.println(sex);
System.out.println(score);
out();
print();
}
}
public void out() {
print();
}
}
}
利用javap命令反编译内部类的class文件,可以解释为什么内部类可以直接访问到外部类的数据。在内部类编译的时候创建了一个引用这个引用就是外部类的引用(demo_Inner是包名),内部类的构造方法传进去的参数就是这个引用,在最后红框中也注释说明了这个引用是外部类的引用。也就解释了为什么内部类可以直接访问到外部类的数据。
反过来则不行,外部类不能直接去访问内部类的数据,必须要创建内部类对象去访问。一个内部类只会对应一个外部类的实例,但是一个外部类实例可以对应多个内部类实例,
就好比箱子里的人一定知道自己在箱子里,外面的人不知道箱子里有没有人。
内部类中不能定义静态的成员变量和内部类类,但是外部类中可以有一个静态的内部类,这就是静态内部类
class Outer{
private int id = 1001;
protected String name = "细心的蜻蜓";
public String sex = "男";
static int score = 77;
public void print() {
System.out.println("外部类中方法");
}
public class Inner{
static int a;//编译报错
static class IInner{ }//编译报错
public void out() {
print();
}
}
}
二、静态内部类
静态内部类也是定义在外部类中,用一个static修饰静态内部类不会自动持有外部类的引用,对于非静态的成员变量和方法直接调用会报错,如果是静态的成员变量或方法不会,如果想要调用需要创建外部类对象。
class Outer{
private int id = 1001;
protected String name = "细心的蜻蜓";
public String sex = "男";
static int score = 77;
public void print() {
System.out.println("外部类中方法");
}
public static class Inner{
public void out() {
System.out.println(id);//编译报错
System.out.println(name);//编译报错
System.out.println(sex);//编译报错
System.out.println(score);//合法
print();//编译报错
}
}
}我们可以在内部类中创建外部类的对象访问
class Outer{
private int id = 1001;
protected String name = "细心的蜻蜓";
public String sex = "男";
static int score = 77;
public void print() {
System.out.println("外部类中方法");
}
public static class Inner{
public void out() {
Outer outer = new Outer();
System.out.println(outer.id);//编译报错
System.out.println(outer.name);//编译报错
System.out.println(outer.sex);//编译报错
System.out.println(score);//合法
outer.print();//编译报错
}
}
}
在测试类中可以直接通过类名创建对象:
Outer.Inner inner = new Outer.Inner();
三、局部内部类
局部内部类是在方法中的内部类,可以把它看作一个局部变量,只不过这个变量是一个类,当然他的作用范围是当前方法,局部内部类不能用权限修饰符修饰,也不能用static修饰
class Outer{
public void print() {
class Inner{
public void out() {
}
public static void out1() {//编译错误
}
}
Inner inner = new Inner();
inner.out();
}
}
另外局部内部类也是可以访问外部类的数据的,自动持有引用。
四、匿名内部类
匿名内部类没有名字,因为没有名字,所以只能使用一次,但使用匿名内部类还有个前提条件:必须继承一个父类或实现一个接口,这是因为匿名内部类是直接使用new来生成一个对象的引用。当然这个引用是隐式的。
先说一下匿名内类的好处,在实现接口的时候可以节省代码。这是我们最常用的结构,实现一个接口:
package demo_Inner;
interface Animal{
public void run();
}
public class Dog implements Animal{
public static void main(String[] args) {
Dog dog = new Dog();
dog.run();
}
@Override
public void run() {
System.out.println("跑");
}
}接下来用匿名内部类实现:
package demo_Inner;
interface Animal{
public void run();
}
public class Dog {
public static void main(String[] args) {
Animal animal=new Animal() {
@Override
public void run() {
System.out.println("跑");
}
};
animal.run();
}
}也可以这么写:
public static void main(String[] args) {
new Animal() {
@Override
public void run() {
System.out.println("跑");
}
}.run();
}
把接口中的方法直接在创建接口对象的时候在大括号内实现了,不用再去单独拿出一个类实现接口,再创建对象去调用
注意:匿名内部类是没有构造方法的,类中没有任何static修饰的静态变量和方法,内部类中使用形参的时候,该形参必须是final修饰的常量。
public static void main(String[] args) {
final int a = 0;
new Animal() {
@Override
public void run() {
System.out.println("跑");
System.out.println(a);//如果a不用final修饰会报错
}
}.run();
}在这里引用http://blog.csdn.net/chenssy/article/details/13170015的看法:
在内部类中的属性和外部方法的参数两者从外表上看是同一个东西,但实际上却不是,所以他们两者是可以任意变化的,也就是说在内部类中我对属性的改变并不会影响到外部的形参,而然这从程序员的角度来看这是不可行的,毕竟站在程序的角度来看这两个根本就是同一个,如果内部类该变了,而外部方法的形参却没有改变这是难以理解和不可接受的,所以为了保持参数的一致性,就规定使用final来避免形参的不改变。
简单理解就是,拷贝引用,为了避免引用值发生改变,例如被外部类的方法修改等,而导致内部类得到的值不一致,于是用final来让该引用不可改变。
故如果定义了一个匿名内部类,并且希望它使用一个其外部定义的参数,那么编译器会要求该参数引用是final的。