为何使用内部类
- 内部类提供了更好的封装,只有外部类能访问内部类
- 内部类可以独立继承一个接口,不受外部类是否继承接口影响
- 内部类中的属性和方法即使是外部类也不能直接访问,相反内部类可以直接访问外部类的属性和方法,即使private
- 利于回调函数的编写
一个内部类的例子:
public class OuterClass {
private String outerName;
private int outerAge;
public class InnerClass{
private String innerName;
private int innerAge;
}
}
内部类是一个编译时概念,编译后外部类及其内部类会生成两个独立的class文件:
Java 内部类 分四种:成员内部类、局部内部类、静态内部类和匿名内部类。
1.成员内部类
成员内部类是最普通的内部类,它的定义为位于另一个类的内部;
class Outter {
private int age = 12;
class Inner {
private int age = 13;
public void print() {
int age = 14;
System.out.println("局部变量:" + age);
System.out.println("内部类变量:" + this.age);
System.out.println("外部类变量:" + Outter.this.age);
}
}
}
public class test1 {
public static void main(String[] args) {
Outter out = new Outter();
Outter.Inner in = out.new Inner();
in.print();
}
}
运行结果:
局部变量:14
内部类变量:13
外部类变量:12
从本例可以看出:成员内部类,就是作为外部类的成员,可以直接使用外部类的所有成员和方法,即使是private的。虽然成员内部类可以无条件地访问外部类的成员,而外部类想访问成员内部类的成员却不是这么随心所欲了。成员内部类不能含有static的变量和方法。因为成员内部类需要先创建了外部类,才能创建它自己的
2.局部内部类
局部内部类是定义在一个方法或者一个作用域里面的类,它和成员内部类的区别在于局部内部类的访问仅限于方法内或者该作用域内。局部内部类是定义在一个方法或者一个作用域里面的类,它和成员内部类的区别在于局部内部类的访问仅限于方法内或者该作用域内。
class Outter {
private int age = 12;
public void Print(final int x) { //这里局部变量x必须设置为final类型!
class Inner {
public void inPrint() {
System.out.println(x);
System.out.println(age);
}
}
new Inner().inPrint();
}
}
public class test1 {
public static void main(String[] args) {
Outter out = new Outter();
out.Print(10);
}
}
运行结果:
10
12
本例中我们将内部类移到了外部类的方法中,然后在外部类的方法中再生成一个内部类对象去调用内部类方法。如果此时我们需要往外部类的方法中传入参数,那么外部类的方法形参必须使用final定义。
换句话说,在方法中定义的内部类只能访问方法中final类型的局部变量,这是因为在方法中定义的局部变量相当于一个常量,它的生命周期超出方法运行的生命周期,由于局部变量被设置为final,所以不能再内部类中改变局部变量的值。
3.静态嵌套类
又叫静态局部类、嵌套内部类,就是修饰为static的内部类。声明为static的内部类,不需要内部类对象和外部类对象之间的联系,就是说我们可以直接引用outer.inner,即不需要创建外部类,也不需要创建内部类。
class Outter {
private static int age = 12;
static class Inner {
public void print() {
System.out.println(age);
}
}
}
public class test1 {
public static void main(String[] args) {
Outter.Inner in = new Outter.Inner();
in.print();
}
}
运行结果:
12
可以看到,如果用static 将内部内静态化,那么内部类就只能访问外部类的静态成员变量,具有局限性。
其次,因为内部类被静态化,因此Outter.Inner可以当做一个整体看,可以直接new 出内部类的对象(通过类名访问static,生不生成外部类对象都没关系)
4.匿名内部类
匿名内部类由于没有名字,所以它的创建方式有点儿奇怪。创建格式如下:
new 父类构造器(参数列表)|实现接口()
{
//匿名内部类的类体部分
}
在这里我们看到使用匿名内部类我们必须要继承一个父类或者实现一个接口,当然也仅能只继承一个父类或者实现一个接口。同时它也是没有class关键字,这是因为匿名内部类是直接使用new来生成一个对象的引用。当然这个引用是隐式的。
public abstract class Bird {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public abstract int fly();
}
public class Test {
public void test(Bird bird){
System.out.println(bird.getName() + "能够飞 " + bird.fly() + "米");
}
public static void main(String[] args) {
Test test = new Test();
test.test(new Bird() {
public int fly() {
return 10000;
}
public String getName() {
return "大雁";
}
});
}
}
------------------
Output:
大雁能够飞 10000米
在Test类中,test()方法接受一个Bird类型的参数,同时我们知道一个抽象类是没有办法直接new的,我们必须要先有实现类才能new出来它的实现类实例。所以在mian方法中直接使用匿名内部类来创建一个Bird实例。
由于匿名内部类不能是抽象类,所以它必须要实现它的抽象父类或者接口里面所有的抽象方法。
对于这段匿名内部类代码其实是可以拆分为如下形式:
public class WildGoose extends Bird{
public int fly() {
return 10000;
}
public String getName() {
return "大雁";
}
}
WildGoose wildGoose = new WildGoose();
test.test(wildGoose);
在这里系统会创建一个继承自Bird类的匿名类的对象,该对象转型为对Bird类型的引用。
对于匿名内部类的使用它是存在一个缺陷的,就是它仅能被使用一次,创建匿名内部类时它会立即创建一个该类的实例,该类的定义会立即消失,所以匿名内部类是不能够被重复使用。对于上面的实例,如果我们需要对test()方法里面内部类进行多次使用,建议重新定义类,而不是使用匿名内部类。