Java内部类
在一个类的内部再定义一个类,所以称为内部类。
内部类主要有几种:成员内部类、局部内部类、静态内部类、匿名内部类。
成员内部类
一个类中直接定义的内部类,可以理解为类的普通成员。
而作为类的成员,成员内部类可以无限制地访问外部类的所有成员(属性 + 方法)。
但有一点特别的是,成员内部类的实例不能单独存在,必须依附于一个外部类的实例。
即要实例化一个成员内部类,必须首先创建一个外部类的实例,然后调用外部类实例的 new 来创建内部类实例 outer.new Inner()
。
这其实也很好理解,因为类的成员就是要依附于具体的实例的。
// inner class
public class Main {
public static void main(String[] args) {
Outer outer = new Outer("Nested"); // 实例化一个Outer
Outer.Inner inner = outer.new Inner(); // 实例化一个Inner
inner.hello();
}
}
class Outer {
private String name;
Outer(String name) {
this.name = name;
}
class Inner {
void hello() {
System.out.println("Hello, " + Outer.this.name);
}
}
}
静态内部类(嵌套类)
与成员内部类类似、也是定义在类中,只是多了 static 修饰。
因此与成员内部类所不同的是,静态内部类不再依附于外部类的实例,可以独立地实例化,是一个完全独立的类。
但是,它只可以访问外部类 静态的属性和方法。
// Static Nested Class
public class Main {
public static void main(String[] args) {
Outer.StaticNested sn = new Outer.StaticNested();
sn.hello();
}
}
class Outer {
private static String NAME = "OUTER";
private String name;
Outer(String name) {
this.name = name;
}
static class StaticNested {
void hello() {
System.out.println("Hello, " + Outer.NAME);
}
}
}
局部内部类
在方法中定义的内部类称为局部内部类。
与局部变量类似,局部内部类不能有访问说明符,因为它不是外围类的一部分。
但是它可以访问当前代码块内的常量,和外部类所有的成员。
局部内部类的生命周期只限于定义该内部类的方法,只能在此方法内实例化,不可以在此方法外对其实例化。
public class Outer {
private static int number = 100;
private int j = 20;
private String name = "Java";
//定义外部类方法
public void outer_funOne(int k){
final int number = 100;
int j = 50;
//方法内部的类(局部内部类)
class Demo{
public Demo(int k){
demo_funOne(k);
}
int number = 300; //可以定义与外部类同名的变量
// static int j = 10; //不可以定义静态变量
//内部类的方法
public void demo_funOne(int k){
System.out.println("内部类方法:demo_funOne");
//访问外部类的变量,如果没有与内部类同名的变量,则可直接用变量名
System.out.println(name);
//访问外部类与内部类同名的变量
System.out.println(Outer.this.number);
System.out.println("内部类方法传入的参数是:"+k);
}
}
new Demo(k);
}
public static void main(String[] args) {
//访问内部类必须要先有外部类对象
Outer out = new Outer();
out.outer_funOne(11);
}
}
匿名内部类
匿名内部类 是定义在方法中的,其语法为 new 某个接口或者类、并在后面加上类的定义。
其实是 首先定义了一个匿名类、该类实现某个接口或继承某个类,并在定义后马上实例化了这个匿名类。
与局部内部类一样,可以访问当前代码块内的常量,和外部类所有的成员。
class Outer {
private String name;
Outer(String name) {
this.name = name;
}
void asyncHello() {
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("Hello, " + Outer.this.name);
}
};
new Thread(r).start();
}
}
日常开发用的最多的场景就是,用匿名内部类来实现函数式接口,并且通常是使用Lambda 表达式
来简化代码。
如以上例子的匿名内部类就是实现了函数式接口 Runnable。
函数式接口:只有一个抽象方法的接口。
匿名类也可以继承自普通类
import java.util.HashMap;
public class Main {
public static void main(String[] args) {
HashMap<String, String> map1 = new HashMap<>();
HashMap<String, String> map2 = new HashMap<>() {};
HashMap<String, String> map3 = new HashMap<>() {
{
put("A", "1");
put("B", "2");
}
};
System.out.println(map3.get("A"));
}
}
map1 是一个普通的 HashMap 实例。
map2 是一个匿名类实例,只是该匿名类继承自 HashMap,并且类定义为空。
map3 也是一个继承自 HashMap 的匿名类实例,并且添加了构造代码块来初始化数据。
构造代码块:构造函数之前执行,并且每次实例化都会执行。