为什么使用java内部类?
使用内部类的一个主要原因:每个内部类都能独立的继承一个类(接口),无论外围类是否已经继承某个类,对内部类没有任何的影响。
使用内部类的优点:它能非常好的解决多重继承的问题,提供了更好的封装,除了外围类,其他的类都不能访问。
内部类的使用:
内部类的使用方式有四种:成员内部类,静态内部类,局部内部类,匿名内部类。接下来分别看一下,内部类的使用方式。
1.成员内部类
public class Outer{ public String name; public class Inner{
public int age;
public int info(){
System.out.println("我的年龄是:"+age);
}
} }
注意:
- 成员内部类定义在外部类的内部,相当于外部类的一个成员变量的位置,内部类可以像类属性那样被访问控制符修饰。
- 内部类可以访问外部类的成员,而不受任何访问控制符的限制,包括直接访问外部类的私有属性。
- 创建内部类对象,外部类.内部类 对象名=外部类对象.new 内部类();
- 内部类不能存在任何static的变量和方法,可以定义常量。因为静态变量或方法依赖类,不依赖于实例变量,但是内部类是依赖于外部类的实例对象的,这样的话就会产生一个矛盾,编译器不会编译的。
- 如果内部类和外部类具有相同的成员变量和方法,内部类默认访问自己的成员变量或方法,如果访问外部类的成员变量,使用这种方式:外部类.this.name。
问题:为什么成员内部类可以直接访问外部类的属性或方法呢?
java文件被编译之后,内部类会持有一个外部类的隐式引用,这个引用是编译过程中添加进去的。编译的时候,编译器会给内部类的构造器添加一个外部类的引用,无论使用有参构造器或是无参构造器,这样内部类就可以访问外部类的属性或者方法。这也从侧面说明成员内部类是依赖于外部类的。
2.静态内部类
public class Outer{ public String name; public static class Inner{ public int age; public int info(){ System.out.println("我的年龄是:"+age); } } }
- 静态内部类不能直接访问外部类的非静态成员,但是可以通过new 外部类().成员的方式访问
- 如果外部类的静态成员与内部类的成员名称相同,可通过类名.静态成员访问外部类的静态成员;如果外部类的静态成员与内部类的成员名称不相同,则可通过成员名直接调用外部类的静态成员。
- 创建静态内部类的对象时,不需要外部类的对象,可以直接创建:内部类 对象名 = new 内部类()
3.局部内部类
public class Outer{ public String name; public void showinfo(){ class Inner{ public int age; public int info(){ System.out.println("我的年龄是:"+age); } } } }
- 局部内部类就像是方法里面的一个局部变量一样,是不能有 public、protected、private 以及 static 修饰符的
- 局部内部类只能访问局部final变量,尽管jdk1.8不需要final显式的修饰局部变量,但是编译器仍然是按final变量进行编译的,这点可以通过反编译验证。这个在编译内部类的时候,如果局部变量的值在编译期间可以确定,则会在内部类创建一个拷贝(基本数据类型),如果局部变量的值是无法在编译期间确定,则通过构造器传参的方式来对拷贝进行初始化赋值。使用的形参为什么要用final呢?在内部类中的属性和外部方法的参数两者从外表上看使用一种东西,但是内部类只是持有的一个变量的拷贝而已,内部类属性的改变不会影响到外部形参,但是从程序员的角度看,这就是同一个变量,所以为了保持参数的一致性,就规定使用final来避免形参的不改变。
4.匿名内部类
public class Outer{ public String name; public getInner(){ return new Innter(){ public int getNumber(){ return 123; } }; } interface Inner{ int getNumber(); } }
- 匿名内部类它仅能被使用一次,创建匿名内部类时他会立即船舰一个该类的实例,该类的定义会立即消失。
- 使用匿名内部类我们必须继承一个类或实现一个接口,且只能继承一个类或实现一个接口。
- 匿名内部类是不能定义构造函数的,匿名内部类中不能存在任何的静态成员变量和静态方法。
- 匿名类初始化:使用构造器代码块,利用构造代码块能够达到为匿名内部类创建一个构造器的效果。