0%

Java内部类

前言

最近看《Java编程思想》,看了内部类的讲解,发现自己好多都没听过或者没注意到。

指向外部类的指针

每个内部类都有一个隐藏的外部类的this指针,可以通过Outer.this获得

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Main {
private String message = "hello";

public String getMessage() {
return message;
}

class Inner {
public Inner() {
System.out.println(Main.this.getMessage());
System.out.println(getMessage());
}
}

public static void main(String[] args) {
Main main = new Main();
Main.Inner inner = main.new Inner();
}
}

这个例子看其实意义不大,因为函数getMessage()没有歧义。
如果Inner中也有个getMessage(),那么调用的就是Inner中的,这里this指针就派上用场了。

题外话,在《Java并发编程实战》33页页提到了这个隐藏指针,如果把对象逸出,那么可能出现为构造未完全就调用了方法。比如下面(抱歉写的有点臃肿)。。。

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
37
38
interface Escape{
void doSome(Some some);
}
class Some {
protected Demo demo;

public void printI() {
demo.printI();
}
}
class Demo {
private int i = 0;
public Demo(Escape escape) {
escape.doSome(new Some() {
{
this.demo = Demo.this;
}
});

i = 1;
}

public void printI() {
System.out.println(i);
}

}
public class Main {
public static void main(String[] args) {
Escape escape = new Escape() {
@Override
public void doSome(Some some) {
some.printI();
}
};
Demo demo = new Demo(escape);
}
}

Demo构造完应该i = 1
但是这里调用过之后却是输出了0
这就是内部类是对象隐藏指针逸出导致得到了未构造完全的对象。

返回private内部类

正常类的来说,只能是public或者缺省的,不可以是private的。
但是内部类可以是public,priavte, protected或者缺省的。

1
2
3
4
5
6
7
8
9
10
11
12
13
class Demo {
private class Innter {}
public Innter getInnerInstence() {
return new Innter();
}
}
public class Main {
public static void main(String[] args) {
Demo demo = new Demo();
//下面是不可以的,因为Inner是private的
//Demo.Inner inner = demo.getInnerInstence();
}
}

作为一种折中的方法,我们可以申明一个接口来解决这个问题

1
2
3
4
5
6
7
8
9
10
11
12
13
interface ForTest{}
class Demo {
private class Innter implements ForTest {}
public Innter getInnerInstence() {
return new Innter();
}
}
public class Main {
public static void main(String[] args) {
Demo demo = new Demo();
ForTest inner = demo.getInnerInstence();
}
}

普通内部类不能有static方法和实例变量

1
2
3
4
5
6
7
public class Main {

class Inner {
//Inner classes cannot have static declarations
//public static String message = "hello";
}
}

静态内部类

如果你想要内部类可以用static,那么可以使用静态内部类。
但是静态内部类没有对外部类的引用了。就像是完全的独立的一个类。
创建静态内部类的时候也不需要先创建外部类了。

1
2
3
4
5
6
7
8
9
10
11
12
public class Main {

private String name = "hello";

static class Inner {
public static String message = "hello";
public Inner() {
//Non-static field 'name' cannot be referenced from a static context
//System.out.println(name);
}
}
}

匿名类的构造

匿名类因为没有类名,所以构造函数没法重新定义,不过可以用{}包裹起来作为构造函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Main {

private String name = "hello";

public Main(String name) {
this.name = name;
}

public Main getMain(String name) {
return new Main(name){
//报错
//public Main(){}
{
System.out.println("name is" + name);
}
};
}
}

内部类的构造参数

如果想要在内部类中使用其他的对象,必须是final的或者effectively final的,也就是说不允许内部类修改外界的对象。
什么是effectively final呢,就是如果你在内部类内部没有修改这个参数,那么jvm就默认这个是final的,不需要显式的加final关键词了。
是在Java8中引入的,有点像语法糖。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Main {

private String name = "hello";

public Main(String name) {
this.name = name;
}

public Main getMain(String name) {
return new Main(name){
{
//variable 'name' is accessed from within inner class, need to be final or effectively final
name = "lll";
}
};
}
}

显然我们想知道为什么呢?
stackoverflow的答案

接口中的内部类

接口中定义的方法默认是public的,
接口中定义的类默认是static的。

静态内部类作为测试

一般我们会为每个类做一个main方法进行一些简单的测试。
但是这样编译过之后,class文件中还是有main方法的,占用了空间。
我们可以使用静态内部类作为测试,因为编译过后静态内部类会生成一个单独的class文件,
生产环境中可以不把它包含进去。

我觉得没啥用,因为不止静态内部类,普通内部类和匿名类也会单独生成一个class文件。
生产环境中一个一个删除不是傻逼吗。。。

为什么要引入内部类

作为不能多继承的补充
好有道理

继承内部类

因为内部类拥有外部类的引用,所以继承的时候要有点技巧。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Demo {
class Inner{}
}

public class Main extends Demo.Inner{

//构造函数只能这么写。。。
public Main(Demo demo) {
demo.super();
}

public static void main(String[] args) {

}
}

内部类重写

内部类可以向方法一样重写吗并进行多态吗
不行