内部类就是在一个类的内部定义的类。
1、成员内部类格式如下:
class Outer{
class Inner{}
}
编译上述代码会产生两个文件:
Outer.class 和 Outer$Inner.class。
在外部创建内部类对象
内部类除了可以在外部类中产生实例化对象,也可以在外部类的外部来实例化。
那么,根据内部类生成的*.class文件:Outer$Inner.class。
“ $ ”符号在程序运行时将替换成“ . ”
所以内部类的访问,通过“外部类.内部类”的形式表示。
Outer out = new Outer();//产生外部类实例
Outer.Inner in = null;//声明内部类对象
in = out.new Inner();//实例化内部类对象
示例:(通常情况下,我们不建议像下面这样来实例化内部类对象)
class Demo1{
public static void main(String[] args) {
Outer outer = new Outer();
//在外部创建成员内部类的实例,因为成员内部类需要依赖外部类的对象
Outer.Inner inner = outer.new Inner();
inner.print();
}
}
class Outer{
private String name;
//成员内部类
class Inner{
public void print(){
System.out.println("inner");
}
}
}
应该像这样:
class Demo1{
public static void main(String[] args) {
Outer outer = new Outer();
outer.innerPrint();
}
}
class Outer{
private String name;
//建议在外部类中定义一个方法,对外提供访问内部类的接口
public void innerPrint() {
Inner inner = new Inner();
inner.print();
}
//成员内部类
class Inner{
public void print(){
System.out.println("inner");
}
}
}
2、方法内部类
内部类可以作为一个类的成员外,还可以把类放在方法内定义。
注意:
(1)方法内部类只能在定义该内部类的方法内实例化,不可以在此方法外对其实例化。
(2)方法内部类对象不能使用该内部类所在方法的非final局部变量
格式如下:
class Outer{
public void doSomething() {
class Inner{
public void seeOuter() {}
}
}
}
示例:
class Demo2{
public static void main(String[] args) {
Outer outer = new Outer();
outer.show();
}
}
class Outer{
//-------方法内部类----------
public void show() {
class Inner2{
public void print() {
System.out.println("方法内部类");
}
}
Inner2 inner2 = new Inner2();
inner2.print();
}
}
3、静态内部类
在一个类内部定义一个静态内部类:
静态的含义是该内部类可以像其他静态成员一样,没有外部类对象时,也能够访问它。静态嵌套类仅能访问外部类的静态成员和方法。
例如:
class Outer{
static class Inner{}
}
class Demo{
public static void main(String[] args) {
Outer.Inner n = new Outer.Inner();
}
}
示例:
class Demo3{
public static void main(String[] args) {
Outer.Inner3 inner3 = new Outer.Inner3();
inner3.print();
}
}
class Outer{
//--------静态内部类----------
static class Inner3{
public void print() {
System.out.println("静态内部类");
}
}
}
成员内部类和方法内部类可以调用外部类中的属性,而静态内部类不行。
4、匿名内部类
匿名内部类就是没有名字的内部类。
匿名内部类的三种情况:
(1)继承式的匿名内部类
(2)接口式的匿名内部类
(3)参数式的匿名内部类
在使用匿名内部类时,要记住以下几个原则:
(1)不能有构造方法,只能有一个实例。
(2)不能定义任何静态成员、静态方法。
(3)不能是public, protected, private, static。
(4)一定是在new的后面,用其隐含实现一个接口或实现一个类。
(5)匿名内部类为局部的,所以局部内部类的所有限制都对其生效。
示例1:(继承式匿名内部类)
class Demo4{
public static void main(String[] args) {
Outer outer = new Outer();
outer.print1();
}
}
abstract class Cat{
public abstract void eat();
}
class Outer{
//--------匿名内部类----------
//继承式
public void print1() {
Cat cat = new Cat() {
public void eat() {
System.out.println("eat:继承式匿名内部类");
}
};
cat.eat();
}
}
示例2:(接口式匿名内部类)
class Demo4{
public static void main(String[] args) {
Outer outer = new Outer();
outer.print2();
}
}
interface Eat{
public void eat();
}
class Outer{
//--------匿名内部类----------
//接口式
public void print2() {
Eat eat = new Eat() {
public void eat() {
System.out.println("eat:接口式匿名内部类");
}
};
eat.eat();
}
}
示例3:(参数式匿名内部类)
class Demo4{
public static void main(String[] args) {
Outer outer = new Outer();
//参数式匿名内部类
outer.print3(new Eat() {
public void eat() {
System.out.println("参数式匿名内部类");
}
});
//若有多个参数的话:
/*outer.print3(new Eat() {
public void eat() {
System.out.println("参数式匿名内部类");
}
},10,20,30);*/ //在括号的前面加
}
}
interface Eat{
public void eat();
}
class Outer{
//--------匿名内部类----------
//参数式
public void print3(Eat eat) {//实现部分不是在这里写的,是在调用处写
eat.eat();
}
}
问题:局部内部类访问局部变量必须用final修饰,为什么?
答:当调用这个方法时,局部变量如果没有用final修饰,它的生命周期和方法的生命周期是一样的。当方法被调用时会入栈,方法结束后即弹栈,这个局部变量也会消失。那么如果局部内部类对象还没有马上消失,想要用这个局部变量,显然已无法使用了。如果用final修饰,则会在类加载的时候进入常量池,即使方法弹栈,常量池的常量还在,可以继续使用。
注意,在jdk1.8中取消了在局部内部类中使用的变量必须显式的使用final修饰,编译器默认会为变量加上final。
内部类的作用:
每个内部类都能独立地继承自一个(接口的)实现,所以无论外部类是否已经继承了某个(接口的)实现,对于内部类都没有影响。如果没有内部类提供的可以继承多个具体的或抽象的类的能力,一些设计与编程问题就很难解决。从这个角度看,内部类使得多重继承的解决方案变得完整。接口解决了部分问题,而内部类有效地实现了“多重继承”。
例如:
class A extends B{
class C extends D,E{
//在一个类中可以写多个内部类,每个内部类继承一个类,这样就变相地解决了多继承的问题
}
}
总结:
成员:成员内部类、静态内部类
局部:方法内部类、匿名内部类
成员内部类、方法内部类、匿名内部类都要依赖外部类对象,静态内部类不依赖外部类对象。所以,在项目中优先考虑选择静态内部类(这样不会产生内存泄漏。因为静态内部类不依赖于外部类对象,静态内部类的对象是独立的,外部类对象释放对静态内部类没影响,)。