1. 内部类
概念:在一个类的内部再定义一个完整的类
内部类分为 4 类:成员内部类、静态内部类、局部内部类、匿名内部类
1.1 特点
- 编译之后可生成独立的字节码文件
- 内部类可直接访问外部类的私有成员,而不破坏封装
- 可为外部类提供必要的内部功能组件
内部类的作用:
1.可以无条件地访问外部类的所有元素
2.实现隐藏:外部类即普通的类不能使用 private, protected 访问权限符来修饰的,而内部类则可以使用 private 和 protected 来修饰
3.可以实现多重继承(多个内部类分别继承别的类)
4.通过匿名内部类来优化简单的接口实现
1.2 补充
- 编译后,生成各自的class文件,内部类通过 this 访问外部类的成员
- 外部类与内部类之间使用
$
符号分隔,示例Outer.class, Outer$Inner.class
- 匿名内部类使用
数字
进行编号,示例Outer.class, Outer$1.class
- 成员内部类是依附外部类而存在的,静态内部类不依附外部类
2. 成员内部类
- 成员内部类与实例变量、实例方法同级别
- 成员内部类是依附外部类而存在的,创建成员内部类对象时,必须依赖外部类对象
- 当外部类与内部类的属性重名时,优先访问内部类属性
- 成员内部类不能定义静态成员,但可以包含静态常量 final
public class Outer {
private String name = "Bob";
// 默认作用域:包内可见
public class Inner {
private String name = "Alice";
// 4. 静态常量 final
private static final String address = "...";
public void show() {
System.out.println(name); // 等价 this.name
// 3. 属性相同时,外部类使用 Outer.this
System.out.println(Outer.this.name);
}
}
}
// 2. 实例化成员内部类
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
// or
Outer.Inner inner = new Outer().new Inner();
2.1 接口中的成员内部类
可以有静态属性
public interface OuterInterface {
class Inner {
private static String address = "";
public void show() {
}
}
}
// demo
OuterInterface.Inner inner = new OuterInterface.Inner();
inner.show();
3. 静态内部类
- 不依赖外部类对象,可通过类名访问
public class Outer {
private String name = "Bob";
public static class Inner {
private String name = "Alice";
// 静态成员
private static String address = "...";
public void show() {
// 调用外部类的属性,先创建外部类对象
Outer outer = new Outer();
System.out.println(outer.name);
// 调用静态内部类的属性
System.out.println(name);
// 调用静态内部类的静态属性
System.out.println(Inner.address);
}
}
}
// demo
Outer.Inner inner = new Outer.Inner();
inner.show();
4. 局部内部类
- 定义在外部类方法中,作用范围和创建对象的范围仅限于当前方法
- 局部内部类访问外部类当前方法中的局部变量时,因无法保障变量的生命周期与自身相同,所以该局部变量必须修饰为 final
- 局部内部类也不能定义静态成员,但可以包含静态常量 final
public class Outer {
private String name = "Bob";
public void show() {
String address = "...";
// 局部内部类在 方法 里声明
class Inner {
private Integer age;
public void innerShow() {
System.out.println(Outer.this.name);
// 局部内部类里访问局部变量,JDK1.7 要求变量必须是常量final,JDK1.8 自动添加final
System.out.println(address);
// Variable 'address' is accessed from within inner class, needs to be final or effectively final
// address = "111";
}
}
Inner inner = new Inner();
inner.innerShow();
}
}
// demo
Outer outer = new Outer();
outer.show();
address 为什么是 final
address 若是一个普通属性,当 show() 方法执行完之后,局部变量 address 就会消失
show() 里面的 Inner inner = new Inner();
new Inner() 在堆里,不会立即消失,innerShow() 属于类 Inner 也不会立即消失
变量 address 没有了,但类还在,不合理,所以是 final 类型
5. 匿名内部类(Anonymous Classes)
- 没有类名的局部内部类(一切特征都与局部内部类相同)
- 必须继承一个父类或者实现一个接口(用来创建接口或抽象类的实例)
- 匿名内部类是唯一一种没有构造器的类,大部分匿名内部类用于接口回调
语法 new 父类() { 子类内容 };
匿名类是一个表达式,因此在定义的最后用分号;
结束
5.1 示例
// 继承类
Thread thread = new Thread() {
@Override
public void run() { // ... }
};
thread.start();
// 实现接口
Runnable runnable = new Runnable() {
@Override
public void run() { // ... }
};
5.2 局部内部类改为匿名内部类
public static void main(String[] args) {
// 局部内部类(定义在方法中的类)
class Mouse implements Usb {
@Override
public void service() {
System.out.println("Mouse run....");
}
}
Usb usb = new Mouse();
usb.service();
}
局部内部类使用一次后就不再使用,优化方式:改为匿名内部类
public static void main(String[] args) {
// 相当于创建了一个局部内部类
Usb usb = new Usb() {
@Override
public void service() {
System.out.println("run...");
}
};
usb.service();
}
5.3 为什么局部变量需要 final修饰
public static void main(String[] args) {
int num = 0; // JDK1.7前,必须加上 final
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println(num++); // 报错,num 不能操作
}
};
}
因为局部变量和匿名内部类的生命周期不同
匿名内部类是创建后是存储在堆中的,而方法中的局部变量是存储在Java栈中,当方法执行完毕后,就进行退栈,同时局部变量也会消失
5.4 其他示例
public class Demo {
public static void main(String[] args) {
// 匿名内部类 Demo$1
List<String> list = new ArrayList<String>() {{
add("111");
add("222");
}};
}
}
public void test() {
// 创建接口实例
Comparator<Integer> comparator = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return Integer.compare(o1, o2);
}
};
// 匿名类的实例作为参数传递 TreeSet(Comparator<? super E> comparator) {}
TreeSet<Integer> ts = new TreeSet<>(comparator);
// 或者
TreeSet<Integer> ts = new TreeSet<>(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return Integer.compare(o1, o2);
}
});
}
6. 非公有类
- 非公有类不能使用public关键字
- 内部类和非共有类,可以与其他类共用一个文件
- 一个源文件中可以包含多个非公有类或者内部类;
网友评论