java内部类

概念与分类

如果一个事务的内部包含另一个事务,那么这就是一个类内部包含另一个类。

例如:身体和心脏的关系。又如:汽车和发动机的关系。

分类:

  1. 成员内部类
  2. 局部内部类(包含匿名内部类)

成员内部类

定义格式

1
2
3
4
5
6
修饰符 class 外部类名称{
修饰符 class 内部类名称{
// ...
}
// ...
}

注意:内用外,随意访问;外用内,需要内部类对象。

1
2
3
4
5
6
7
8
9
10
public class Body {
public class Heart{ // 成员内部类
public void beat(){
System.out.println("蹦蹦蹦!");
System.out.println("我叫:" + name); // 正确
}
}

private String name = "阿华"; // 外部类的成员变量
}
1
2
3
4
5
6
public class Main1 {
public static void main(String[] args) {
Body body = new Body();
body.new Heart().beat();
}
}

如何使用成员内部类 ?有两种方式

  1. 间接方式:在外部类的方法当中,使用内部类;然后main只是调用外部类的方法。

  2. 直接方式:公式:

    类名称 对象名 = new 类名称();

    外部类名称.内部类名称 对象名 = new 外部类名称().new 内部类名称();

1
2
3
4
5
6
7
8
9
10
11
12
public class Body {
public class Heart{ // 成员内部类
public void beat(){
System.out.println("蹦蹦蹦!");
System.out.println("我叫:" + name); // 正确
}
}
public void beat(){ // 外部类使用内部类的方法
new Heart().beat();
}
private String name = "阿华"; // 外部类的成员变量
}
1
2
3
4
5
6
7
public class Main1 {
public static void main(String[] args) {
Body body = new Body();
body.new Heart().beat();
body.beat();
}
}

局部内部类

如果一个类是定义在一个方法内部的,那么这就是一个局部内部类。

“局部”:只有当前所属的方法才能使用它,出了这个方法外面就不能用了

定义格式:

1
2
3
4
5
6
7
修饰符 class 外部类名称{
修饰符 返回值类型 外部类方法名称(参数列表){
class 局部内部类名称{
// ...
}
}
}

提及类的权限修饰符:

我们知道权限的大小:

public > protected > (default) > private

定义一个类的时候,权限修饰符规则:

  1. 外部类:public / (default)
  2. 成员内部类:public / protected / (default) / private
  3. 局部内部类:什么都不能写

局部内部类使用方法变量final问题

局部内部类,如果希望访问所在方法的局部变量,那么这个局部变量必须是【有效final的】

备注: 从java 8+ 开始,只要局部变量事实不变,那么final关键字可以省略。

原因:

  1. new出来的对象在堆内存当中。
  2. 局部变量是跟着方法走的,在栈内存当中。
  3. 方法运行结束之后,立即出栈,局部变量就会立即消失。
  4. 但是new出来的对象会在堆内存当中持续存在,直到垃圾回收消失

造成局部内部类使用的外部局部变量是常量的问题是,生命周期的不同。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Main3 {
public static void main(String[] args) {
methodOuter();
}
public static void methodOuter(){
int num ;
num = 10;// 所在方法的局部变量,不能再变了
//num = 100; //错
// Variable 'num' is accessed from within inner class, needs to be final or effectively final
class MyInner{
public void methodInner(){
System.out.println(num);
}
}
new MyInner().methodInner();
}
}

匿名内部类

如果接口的实现类(或者父类的子类)只需要使用唯一的一次。

那么这种情况下就可以省略掉该类的定义,而改为使用【匿名内部类】

定义格式(以接口为例子):

1
2
3
接口名称 对象名 = new 接口名称(){
//覆盖重写所有抽象方法
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
abstract class Animal {
public abstract void eat();
}
public class Test {
public static void main(String[] args) {
Animal pet = new Animal() { //匿名内部类,定义出来立即创建对象
@Override
public void eat() {
System.out.println("我是只不知名的动物!吃空气吧");
}
};
pet.eat();
}
}

注意事项

对匿名内部类的格式:

  1. new代表创建对象的动作
  2. 接口名称或者类的名称就是匿名内部类需要实现哪个接口或者继承哪个类
  3. {…}这才是匿名内部类的内容
  4. 匿名内部类 的对象不能向下转型,因为没有名字。所以匿名内部类中重写父类的或者接口中的方法才是有意义的,增加新的方法没有作用。

另外还需要注意的是:

  1. 匿名内部类,在【创建对象】的时候,只能使用唯一一次。使用多次则单独定义对象。

  2. 匿名对象,在【调用方法的时候】,只能调用唯一的一次。

    如果希望同一个对象,调用多次方法,必须给对象起个名字。

  3. 匿名内部类是省略了【实现类/子类名称】,但是匿名对象是省略了【对象名称】

  4. 注意区别匿名内部类和匿名对象。


java内部类
https://blog.wangxk.cc/2020/08/20/java内部类/
作者
Mike
发布于
2020年8月20日
许可协议