目录

  • 成员内部类
  • 定义
  • 成员内部类特点
  • 如何使用成员内部类
  • 静态内部类
  • 定义
  • 静态内部类的特点
  • 如何使用静态内部类
  • 局部内部类
  • 定义
  • 局部内部类的特点
  • 如何使用局部内部类
  • 匿名内部类
  • 定义
  • 前提
  • 格式:
  • 匿名内部类的特点
  • 如何使用匿名内部类

成员内部类

定义

  • 成员内部类 :定义在外部类中方法外的类

定义格式:

[修饰符] class 外部类名 [extends 外部类的父类] [implements 外部类的父接口们] {
    [修饰符] class 内部类名 [extends 非静态内部类自己的父类] [implements 非静态内部类的父接口们] {
        静态内部类的成员列表;
	}
	// 外部类的其他成员列表
}

外部类、内部类的父类、父接口没有关系,各是各的

成员内部类特点

  • 成员内部类仍然是一个独立的类,在编译之后内部类会被编译成独立的.class文件,但是前面冠以外部类的类名和$符号。例如:Outer$Inner.class
  • 成员内部类可以使用修饰符public,protected,缺省,private
  • 成员内部类还可以使用final或abstract修饰
  • 成员内部类中不可以声明静态成员
  • 成员内部类可以直接使用外部类的所有成员,包括私有的。如果成员内部类有与外部类的非静态属性重名时,可以通过“外部类名.this.属性”进行区别,如果与外部类的静态属性重名时,可以通过“外部类名.类变量”进行区别
  • 外部类要访问内部类的成员,必须要建立内部类的对象。

如何使用成员内部类

  • 在外部类中使用成员内部类:
  • 在外部类的静态成员中不能使用非静态的成员内部类
  • 在外部类的非静态成员中,直接创建内部类的对象来访问内部类的属性与方法。此时把它当做一个普通的类即可
  • 在外部类的外面使用成员内部类:
  • 要么通过外部类的对象,去创建内部类的对象
外部类名 外部对象名 = new 外部类型();
内部类名 对象名 = 外部对象名.new 外部类型();
//可合并为:
外部类名.内部类名 对象名 = new 外部类型().new 内部类型();
  • 要么通过外部类的对象,去获取内部类的对象
Outer out = new Outer();
Outer.Inner in  = out.getInner();

代码演示:

public class MemberClassTest {

    public static void main(String[] args) {
        Outer out = new Outer();
        out.outerMethod();
		//通过外部类的对象,去创建内部类的对象
        Outer.Inner oi = out.new Inner();
        oi.innerMethod();
		//通过外部类的对象,去获取内部类的对象
        Outer.Inner obj = out.getInner();
        obj.innerMethod();
    }

}
class Outer{
    private int a = 1;
    private static int b = 2;
    public static void outerStaticMethod(){
		/*Inner in = new Inner();//错误,静态成员中不能使用非静态成员内部类
		in.innerMethod();*/
    }
    public void outerMethod(){
        System.out.println("外部类的方法");
		/*Inner in = new Inner();//可以
		in.innerMethod();*/
    }
    class Inner{
        private int a = 3;
        private int b = 4;
        public void innerMethod(){
            System.out.println("内部类的方法");
            System.out.println("内部类的方法访问外部类的私有非静态属性:"+Outer.this.a);
            System.out.println("内部类的方法访问外部类的私有静态属性:"+Outer.b);
        }
    }
    //通过外部类的某个方法返回内部类的对象
    public Inner getInner(){
        return new Inner();
    }
}

外部类的方法
内部类的方法
内部类的方法访问外部类的私有非静态属性:1
内部类的方法访问外部类的私有静态属性:2
内部类的方法
内部类的方法访问外部类的私有非静态属性:1
内部类的方法访问外部类的私有静态属性:2

静态内部类

定义

静态内部类:定义在外部类中方法外,使用 static 修饰的类

定义格式:

[修饰符] class 外部类名 [extends 外部类的父类] [implements 外部类的父接口们] {
	[修饰符] static class 内部类名 [extends 静态内部类自己的父类] [implements 静态内部类的父接口们] {
        静态内部类的成员列表;
	}   
	// 外部类的其他成员列表
}

静态内部类的特点

  • 静态内部类仍然是一个独立的类,在编译之后内部类会被编译成独立的.class文件,但是前面冠以外部类的类名和$符号。例如:Outer$Inner.class
  • 静态内部类可以使用修饰符public,protected,default,private
  • 静态内部类还可以使用final或abstract修饰
  • 静态内部类中可以声明静态成员
  • 静态内部类可以直接使用外部类的静态成员,包括私有的。但不能使用外部类的非静态成员
  • 静态内部类中有与外部类的静态属性重名时,如果要使用外部类的属性,那么用“外部类名.属性”

如何使用静态内部类

  • 在外部类中使用静态内部类:就和使用其他普通类一样
  • 在外部类的外面使用成员内部类:
  • 使用静态内部类的静态成员:外部类名.静态内部类名.静态内部类的静态成员
  • 使用静态内部类的非静态成员:外部类名.静态内部类名 obj = new 外部类名.静态内部类名(); obj.静态内部类的非静态成员

代码演示:

public class StaticInnerClassTest {

    public static void main(String[] args) {
        Outer.StaticInnerClass.innerStaticMethod();
        Outer.StaticInnerClass os = new Outer.StaticInnerClass();
        os.innerMethod();
    }

}
class Outer{
    public static void outerStaticMethod(){
        StaticInnerClass.innerStaticMethod();
        StaticInnerClass si = new StaticInnerClass();
        si.innerMethod();
    }
    public static void outerMethod(){
        StaticInnerClass.innerStaticMethod();
        StaticInnerClass si = new StaticInnerClass();
        si.innerMethod();
    }
    static class StaticInnerClass{
        public static void innerStaticMethod(){
            System.out.println("内部类的静态方法");
        }
        public void innerMethod(){
            System.out.println("内部类的非静态方法");
        }
    }
}

局部内部类

定义

  • 局部内部类:定义在方法内、代码块内、构造器内

定义格式:

[修饰符] class 外部类名 [extends 外部类的父类] [implements 外部类的父接口们] {
	[修饰符] 返回值类型  方法名([形参列表]){
        [修饰符] class 内部类名 [extends 局部内部类自己的父类] [implements 局部内部类的父接口们] {
        	局部内部类的成员列表;
		}
	}	
	// 外部类的其他成员列表
}

局部内部类的特点

  • 局部内部类仍然是一个独立的类,在编译之后内部类会被编译成独立的.class文件:“外部类的类名$数字编号内部类名.class”。
  • 局部内部类和局部变量地位类似,不能使用 public,protected,private,static 这些成员修饰符,但是可以使用abstract或final
  • 局部内部类不能使用static修饰,因此也不能包含静态成员。
  • 只能在声明它的方法或代码块中使用,而且是先声明后使用。除此之外的任何地方都不能使用该类,因为作用域的问题。
  • 局部内部类可以使用外部类的成员,包括私有的。但是是否可以使用外部类的非静态成员要看所在方法是否是非静态的。
  • 局部内部类可以使用包含它的外部方法中的局部变量

如何使用局部内部类

只能在声明它的方法或代码块中使用,而且是先声明后使用。除此之外的任何地方都不能使用该类。

但是它的对象可以通过包含此局部内部类的外部方法的返回值返回使用,返回值类型只能是局部内部类的父类或父接口类型。

代码演示:

public class LocalInnerClassTest {

    public static void main(String[] args) {
        Outer out = new Outer();
        Object obj = out.outerMethod();
        System.out.println(obj);
        System.out.println();
    }

}
class Outer{
    int value = 5;
    public Object outerMethod(){
        final int localValue = 10;
        class LocalInnerClass {
            public void innerMethod(){
                System.out.println("局部内部类的方法");
                System.out.println("局部内部类的方法可以使用外部类的成员变量:"+value);
                System.out.println("局部内部类的方法可以使用外部类的局部变量:"+localValue);
            }
        }
        //先声明后使用
        LocalInnerClass li = new LocalInnerClass();
        li.innerMethod();
        return li;
    }
}
局部内部类的方法
局部内部类的方法可以使用外部类的成员变量:5
局部内部类的方法可以使用外部类的局部变量:10
demo3.Outer$1LocalInnerClass@6b2fad11

匿名内部类

定义

  • 匿名内部类本质是一个带具体实现的 父类或者父接口的 匿名的 子类对象

如果接口的实现类(或者是父类的子类)只需要使用唯一的一次,那么这种情况下就可以省略该类的定义,而改为使用【匿名内部类】。

特征:声明匿名内部类的同时创建该类的对象

前提

存在一个或者接口,这里的类可以是抽象类也可以是普通类

格式:

继承父类:

new 父类名称([实参列表]){
    // 类的成员列表
};

说明:如果子类调用的是父类的无参构造,那么()中实参列表不用写,如果子类调用的是父类的有参构造,那么就在()中传入实际参数

实现接口:

new 接口名称(){
    // 类的成员列表
};

接口不是不能实例化创建对象吗,new关键字的后面为什么跟着接口名?

对格式“new 接口名称() {...}”进行解析:

  1. new代表创建对象的动作
  2. 接口名称表示匿名内部类需要实现哪个接口
  3. {...}是匿名内部类的内容

匿名内部类的特点

  • 匿名内部类仍然是一个独立的类,在编译之后内部类会被编译成独立的.class文件::外部类名$数字编号.class
  • 匿名内部类必须继承父类或实现接口
  • 匿名内部类只能有一个对象
  • 匿名内部类对象只能使用多态形式引用
  • 匿名内部类是特殊的局部内部类,局部内部类的所有限制对它都适用

如何使用匿名内部类

以接口举例,当你使用一个接口时,似乎得做如下几步操作:

  1. 定义接口实现类
  2. 重写接口中的方法
  3. 创建子类对象
  4. 调用重写后的方法

我们的目的,最终只是为了调用方法,那么能不能简化一下,把以上四步合成一步呢?

案例需求:

定义笔记本电脑类,电脑有USB接口,通过插入鼠标、键盘等不同设备,实现各自不同功能。

传统方式:

定义接口:

public interface USB {
    // 使用USB设备的方法
    public void use();
}

定义笔记本类,定义鼠标类、键盘类分别实现USB接口:

class Mouse implements USB {
    @Override
    public void use() {
        System.out.println("点击鼠标操控电脑...");
    }
}

class KeyBoard implements USB {
    @Override
    public void use() {
        System.out.println("键盘输入数据...");
    }
}

public class NoteBook {
    // USB设备插入笔记本电脑的方法,要求传递的对象要实现USB接口
    public void plugIn(USB usb) {
        usb.use();
    }
}

测试类:

public class AnonymousClassTest {
    public static void main(String[] args) {
        NoteBook noteBook = new NoteBook();

        USB mouse = new Mouse();
        noteBook.plugIn(mouse);

        USB keyBoard = new KeyBoard();
        noteBook.plugIn(keyBoard);
    }
}

匿名内部类方式:

定义接口:

public interface USB {
    public void use();
}

定义笔记本电脑类:

public class NoteBook {
    // 要求传递的对象要实现USB接口
    public void plugIn(USB usb) {
        usb.use();
    }
}

测试类:

public class AnonymousClassTest {
    public static void main(String[] args) {
        
        NoteBook noteBook = new NoteBook();

        // 下面这段代码,声明了实现USB接口的匿名内部类,也创建了它的对象
        // 但是这个对象既没有赋值给一个变量,也没有直接调用方法,无法直接使用
        new USB() {
            @Override
            public void use() {
                System.out.println("实现了USB接口");
            }
        };

        // 使用方式一:
        // 把创建的对象赋值给一个变量,然后调用重写的use方法
        // 多态引用
        USB mouse = new USB() {
            @Override
            public void use() {
                System.out.println("点击鼠标操控电脑...");
            }
        };
        mouse.use();

        // 使用方式二:
        // 用创建的对象直接调用重写的use方法
        new USB() {
            @Override
            public void use() {
                System.out.println("键盘输入数据...");
            }
        }.use();

        // 使用方式三:
        // 把创建的对象作为方法实参
        noteBook.plugIn(new USB() {
            @Override
            public void use() {
                System.out.println("U盘传输数据...");
            }
        });
    }
}

测试结果:

点击鼠标操控电脑...
键盘输入数据...
U盘传输数据...