前言
作为一个只擅长javascript的伪程序员,学习java还是挺费劲的,不过没关系,再庞然大物,也能分而治之。
google搜内部类,好多文章都说java有四种内部类。成员内部类,静态内部类、局部内部类、匿名内部类。记性好的可以直接记住,彷佛回到了读书时代。
四大内部类
成员内部类
java是一门强大的面向对象编程语言。在java中,一个类的成员变量,可以是字符串、数字这样的基础数据类型,也可以是数组、Map这样的复杂数据类型,甚至可以是别的类的实例,那为什么不能是一个类呢?
所以把一个类放在java的成员变量位置上,这个类就是成员内部类,放到静态变量位置上,这个类就是静态内部类。
所以很容易写出这样的代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class xxxOuterClass { private String a = "1212"; class Inner{ void log(){ System.out.println(a); } } } class TY{ public static void main(String[] args) { xxxOuterClass n = new xxxOuterClass(); xxxOuterClass.Inner m = n.new Inner(); m.log(); } }
|
java这门语言单从长相上还是挺像typescript的。但是typescript是没有内部类概念的。上面那段代码有几处奇怪的地方,首先是标记1那个孤零零的a变量,实际上java会先找Inner上有没有叫a的成员变量,如果没有,再去找xxxOuterClass。
再来看标记2,内部类是怎么被实例化的。成员内部类依附于它所对应的外部类,所以需要先实例化一个外部类。这不奇怪,想也能想通,奇怪的是new 操作符就然可以那样使用。按照正常的思维,难道不是xxxOuterClass.Inner m = new n.Inner()吗?
既然一个类可以作为另一个类的成员,那么Inner是否能被访问修饰符修饰呢?实际上分别用public、protected、private修饰Inner,并不报错,只不过用private修饰Inner,在TY中不能实例化Inner。那继承呢?
1 2 3 4 5 6 7
| class TY2 extends xxxOuterClass{ public static void main(String[] args) { TY2 ty = new TY2(); TY2.Inner inner = ty.new Inner(); inner.log(); } }
|
看来也是可以的。
静态内部类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class xxxOuterClass { private String a = "1212"; static class Inner{ void log(){ System.out.println("cwwd"); } } }
class TY{ public static void main(String[] args) { xxxOuterClass.Inner m = new xxxOuterClass.Inner(); m.log(); } }
|
静态内部类和静态变量差不多。
局部内部类
所谓局部内部类就是说一个类可以写到某个方法内部。 你用学js的思维去学java就感觉特别烦,什么花花草草都要起个名字。这不是理所当然的吗?
匿名内部类
匿名内部类不是说某个类没有名字,而是说你可以直接继承/实现某个类,然后直接new,不用写那么多繁文缛节。通常配合接口与抽象类来使用。看代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| abstract class xxxFoo{ abstract void log(); }
interface xxxFoo2{ void log2(); }
class TY{ public static void main(String[] args) { xxxFoo foo = new xxxFoo() { @Override void log() { System.out.println("xxxFoo log"); } }; xxxFoo2 foo2 = new xxxFoo2() { @Override public void log2() { System.out.println("xxxFoo2 log"); } }; } }
|
众所周知,在java中,抽象类只能被继承,不能直接实例化,接口只能被实现,不能直接实例化。现在有了特例,除非被用于匿名内部类。
上面的代码按正常思路应该这样写
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
| abstract class xxxFoo{ abstract void log(); }
interface xxxFoo2{ void log2(); }
class TY{ public static void main(String[] args) { class xxxFoo3 extends xxxFoo{ @Override void log() { System.out.println("log1"); } } xxxFoo3 foo = new xxxFoo3(); class xxxFoo4 implements xxxFoo2{
@Override public void log2() { System.out.println("log2"); } } } }
|
原来如此,在这个例子中,所谓匿名,确实省了两个名字。但是转念一想,这不是概念冲突嘛,明明抽象类和接口都不能直接实例化,为啥用于匿名内部类就可以。其实匿名内部类是一种语法糖,java编译器会帮忙补齐缺失的语法。稍微改一下上面的例子。
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
| abstract class xxxFoo{ abstract void log(); }
interface xxxFoo2{ void log2(); }
class TY{ public static void main(String[] args) { xxxFoo foo = new xxxFoo() { @Override void log() { System.out.println("log1"); System.out.println(this.getClass()); } }; xxxFoo2 foo2 = new xxxFoo2() { @Override public void log2() { System.out.println("log2"); System.out.println(this.getClass()); } }; foo.log(); foo2.log2(); } }
|
看到没有,还是会有类,只不过不用开发者操心。
直接继承内部类
我们可以先实例化出外部类,然后再实例化出内部类。我们也可以直接继承一个内部类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| class xxxxOuter{ class xxxInner{ void log(){ System.out.println("log"); } } }
class xxxInner2 extends xxxxOuter.xxxInner{ xxxInner2(xxxxOuter wi){ wi.super(); }
public static void main(String[] args) { xxxInner2 m = new xxxInner2(new xxxxOuter()); m.log(); } }
|
由于成员内部类依赖与外部类,所以xxxInner2的构造函数必须接收一个xxxxOuter的实例,然后调用其super方法。
但是奇怪的是,在我的idea上wi.super标红了,idea认为wi上没有super这个方法?但却可以运行!为啥这里需要调用wi上的super方法,super不是表示父类/父类构造器嘛,xxxxOuter并没有父类,为啥这里不写成wi.constructor()之类的更语义化的api?