Java中的 内部类(吐血总结)

1. 内部类的作用

内部类是一个独立的实体,可以用来实现闭包;能与外部类通信;内部类与接口使得多继承更完整

 

2. 内部类的分类

1)普通内部类

类的实例相关,可以看成是一个实例变量。内部类的类名由 “外部类.内部类” 确定。

普通内部类不能声明 static相关的变量或方法。内部类可以直接访问外部类的所有成员(包括 private成员),隐式或显式(外部类.this)。而外部类可以 new 内部类,实例相关的可以直接 new,static 相关(类相关)需要使用实例的引用去 引用.new。内部类 class 前可以添加 private 与 protected 表示只对其外部类或其子类有访问权限。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
final class Demo {
    private int m = 4;
 
    private Demo() {
        System.out.println("Demo initial");
    }
 
    private class Inner {
        private int i = 23;
 
        Inner() {
            System.out.println("Inner initial");
        }
        private void f() {
            System.out.println("f method" + " i:" + this.i + " m:" + Demo.this.m);
        }
    }
 
    public static void main(String[] args) {
        Demo demo = new Demo();
        System.out.println("Dispatch Inner class");
        Demo.Inner inn = demo.new Inner();
        inn.f();
    }
 
}
/* out:
Demo initial
Dispatch Inner class
Inner initial
f method i:23 m:4
 
*///~

 

* 关于闭包

Java 中通过 内部类指向顶级类的指针(Demo.this) 访问顶级类中的(又称内部类所在词法作用域)变量。其中 Demo.this ,表示的是顶级类的上下文;而内部类中的 this (可省略)表示的是内部类上下文。

Js 中只有一个 this,它既可以访问函数体外部又可以访问函数体内部。先在内部寻找,如果找不到则去外部寻找。而去外部找就是所谓的闭包引用。闭包又称为词法闭包。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var myObject = {
    foo: "bar",
    func: function() {
        var self = this;
        console.log("outer func:" + this);
        console.log("outer func:  this.foo = " + this.foo);
        console.log("outer func:  self.foo = " + self.foo);
        (function() {
            console.log("inner func:" + this)
            console.log("inner func:  this.foo = " + this.foo);
            console.log("inner func:  self.foo = " + self.foo);
        }());
    }
};
 
myObject.func();
 
//output:
outer func:[object Object]
outer func: this.foo = bar
outer func: self.foo = bar
inner func:[object Window]
inner func: this.foo = undefined
inner func: self.foo = bar

 * 注意:

在原型继承的情况下,如果函数无法找到其所有者/调用者(sush as  IIFE),那么在非严格模式下函数内的 this 指向 window 对象。关于变量,一般情况下 this 先访问该函数内部的变量,再去函数所在词法作用域寻找,如果还找不到则去 window 作用域寻找。而去 window 里找变量通常是危险的,这时就需要使用参数或其它方式保存变量。从 关于 lambda 表达式中的 this 一文中,可以得出:lambda 去上一个调用者/window 对象从 深入浅出 JavaScript 中的 this,而对于使用 new Foo() 构造函数调用,this 指向到新的对象,普通变量不受影响。

 

2)局部内部类

方法和作用域中的内部类称为局部内部类,只在其词法作用域中可被访问。可以重载构造器。

 

3)匿名内部类

匿名内部类引用外部类方法中的局部变量必须是 final的原因:对于外部类方法中的局部变量,在该方法调用完成后从栈中清除( remove from stack) ,这时匿名内部类对象将变得无法访问它们。如果将这些变量声明为 final,他们实际上将不再是一个变量而变成了常量,编译器将在编译时把它们替换为具体的常量。(也有观点认为局部变量整体复制,final 保证一致性。)

匿名内部类没有具名构造器,new Wrapper(x) {} 将值传递给基类的构造器。可以被匿名内部类直接引用,不需要 final。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
abstract class Base {
    public Base(int i) {
        System.out.println("Base constructor, i=" + i + "\n");
    }
 
    public abstract void f();
}
 
class AnonymousConstructor {
    private int m = 10000;
    public Base getBase(int i, final int outer) {
        return new Base(i) { //这里引用 局部变量 i,是被匿名内部类的基类使用,不需要声明 final。
            {
                System.out.println("AnonymousInnerClass instance initializer\n");
            }
            @Override
            public void f() {
                // 局部变量 outer 也是被匿名内部类直接使用,需要声明为 fianl
                System.out.println("This variable must declare final first: " + outer + "\n");
 
                // 不同于前面对局部变量 i 的使用,这里是匿名直接使用,则需要声明 final。否则报错!(java 8 或以上除外)
                //System.out.println("This variable must declare final first: " + i + "\n");
 
                // 对非局部变量 m 的使用,不需要声明 final。m 等同与 AnonymousConstructor.this.m
                System.out.println(m);
 
            }
        };
    }
 
    public static void main(String[] args) {
        AnonymousConstructor ac = new AnonymousConstructor();
        Base base = ac.getBase(3, 9);
        base.f();
    }
}

 

4)嵌套类

如果不需要内部类对象与其外部类对象之间有联系,那么可以将内部类声明为 static,称之为静态内部类或嵌套类。特点是:创建嵌套类的实例不需要其外围类的实例;不能从嵌套类的实例中访问非静态的外围类的实例(没有 this 引用);嵌套类不会随外部类加载而初始化。接口内部的类自动是 public static 的,可以将嵌套类放入接口中,从而使得他们可以被该接口的不同实现所共用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
final class Demo {
    private int m = 4;
    private static int n = 6;
 
    private Demo() {
        System.out.println("Demo initial");
    }
 
    static class Inner {
        private int i = 23;
 
        Inner() {
            System.out.println("Inner initial");
        }
        private void f() {
            System.out.println("f method" + " i:" + this.i + " n:" + Demo.n);
        }
    }
 
    public static void main(String[] args) {
//        Demo.Inner inn = new Inner(); // Inner initial
//        inn.f(); // f method i:23 n:6
        new Demo(); // Demo initial
    }
 
}
/* out:
Demo initial
 
*///~

*  关于 java 中的 interface:

正如 接口内部的类自动是 public static 的,类中 嵌套的接口 (nested interface)也是自动 "static" 的,并且能够被移除。同样的,interface  方法上的 "public" 和域名上的 "public final" 也可以被移除。嵌套 interface 示例:

1
2
3
4
5
6
7
8
9
10
public class Foo {
    public interface Bar {
        void callback();
    }
    public static void registerCallback(Bar bar) {...}
}
// ...elsewhere...
Foo.registerCallback(new Foo.Bar() {
    public void callback() {...}
});

 

posted on   Lemo_wd  阅读(219)  评论(0编辑  收藏  举报

编辑推荐:
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
阅读排行:
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

导航

统计

点击右上角即可分享
微信分享提示