1. 概念
内部类,就是在一个类内部再定义一个类。java中允许一个类的定义位于另一个类的内部,外层的称为外部类,内层的称为内部类,内部类是外部类的成员。
内部类分为:
- 成员内部类(static成员内部类,非static成员内部类);
- 局部内部类;
- 匿名内部类;
2. 成员内部类
1. 内部类可以直接防问外部类的成员,外部类要访问内部类的成员必须要建立内部类的对象。
内部类之所以能访问外部类的成员,是因为内部类持有了外部类的引用,外部类名.this
public class InnerClassDemo {
public static void main(String[] args) {
Outer outer = new Outer();
outer.method();
// Inner inner = new Inner();
/* 直接创建内部类对象,编译报错:
No enclosing instance of type Outer is accessible.
Must qualify the allocation with an enclosing instance of type Outer
(e.g. x.new A() where x is an instance of Outer).
*/
// 创建内部类的对象的正确方法
Outer.Inner inner = new Outer().new Inner();
inner.show();
}
}
class Outer{ // 定义外部类
private int num = 100;
class Inner{ // 定义内部类
void show() { // 内部类直接访问外部类成员
System.out.println("show run . . . " + num);
// 通过外部类引用访问外部类属型
System.out.println("Outer.this.num = " + Outer.this.num);
}
}
public void method() { // 外部类里面创建内部类对象,访问内部类成员方法
Inner inner = new Inner();
inner.show();
}
}
2. 如果内部类是静态的,且成员方法是静态的,则访问该静态方法的方式可以为:Outer.Inner.function();
3. 如果内部类是静态的,相当于一个外部类,被内部类访问外部类的属性也需要为静态的
4. 如果内部类中定义了静态成员,该内部类也必须是静态的
public class InnerClassDemo {
public static void main(String[] args) {
// 创建静态内部类的对象的正确方法
Outer.Inner inner = new Outer.Inner();
inner.show();
// 直接调用静态内部类的静态成员方法
Outer.Inner.function();
}
}
class Outer{
private static int num = 100; // 如果内部类是静态的,则只能访问外部类的静态成员
static class Inner{ // 内部类里面含有静态方法,该内部类必须声明为静态内部类
public static void function() { // 内部类内部定义的静态成员方法
System.out.println("function run . . . " + num);
}
public void show() { // 访问外部类里面的静态成员
System.out.println("show run . . . " + num);
System.out.println("Outer.this.num = " + Outer.num);
}
}
}
3. 成员内部类
1. 局部内部类和成员内部类一样可以访问外部类成员属性,访问方式也一样。
2. 局部内部类访问局部变量的时候只能访问被 final 修饰的局部变量。(JDK1.8之后有变化,不用显式的声明 final )
public class InnerClassDemo {
public static void main(String[] args) {
new Outer().show();
}
}
class Outer{
private int num = 100;
public void show () {
final int temp = 200; // 局部内部类要访问的局部变量,需要用 final 修饰
class Inner{ // 成员内部类
public void method() {
// 访问外部类的成员属性
System.out.println("show run . . . " + num);
// 访问局部变量
System.out.println(temp);
}
}
// 创建成员内部类对象
Inner inner = new Inner();
inner.method(); // 调用成员内部类的方法
}
}
注意:对于 “ 局部内部类访问局部变量的时候只能访问被 final 修饰的局部变量 ” 这个结论JDK1.8之前和之后略有不同。JDK1.8的时候发布了一些新特性,局部内部类访问外部的局部变量的时候,可以不用加 final 修饰词,但是系统还是会把该局部变量当作 final 类型变量处理,当试图修改该局部变量的时候仍然会有和之前版本一样的编译错误。
下面两种情况说明局部内部类访问的外部局部变量还是属于final类型的,只不过JDK1.8之后不用显式的声明出来了,但是和之前版本相比使用起来还是一样要注意这个问题。
4. 匿名内部类
匿名内部类的使用前提:内部类必须继承或者实现一个外部类或者接口
匿名内部类:其实就是一个匿名子类对象。
格式:new 父类 or 接口(){子类内容}
public class InnerClassDemo {
public static void main(String[] args) {
Outer outer = new Outer();
outer.func1("张三");
outer.func2();
}
}
abstract class AbstractClass{ // 抽象类
public abstract void method(String name);
}
interface InterfaceDemo{ // 接口
public abstract void show();
}
class Outer{
public void func1(String name) { // 使用匿名内部类方式一样可以传参
new AbstractClass() { // 将抽象类作为参数
@Override
public void method(String name) {
System.out.println("func1 . . . method . . . " + name);
}
}.method(name); // 调用方法
}
public void func2() {
new InterfaceDemo() { // 将接口作为参数
@Override
public void show() {
System.out.println("func2 . . . show");
}
}.show(); // 调用方法
}
}
匿名内部类在JDK1.8同样也有新的方式来实现,引入Lambad表达式来简化匿名内部类的写法
public static void main(String[] args) {
// 实现对该列表的排序
List<String> names = Arrays.asList("peter", "anna", "mike", "xenia");
// 匿名内部类方式
// Collections.sort(names, new Comparator<String>() {
// @Override
// public int compare(String a, String b) {
// return b.compareTo(a);
// }
// });
// lambda 方式
Collections.sort(names, (String a, String b) -> {
return b.compareTo(a);
});
// 输出比较结果
for (String string : names) {
System.out.println(string);
}
}