java学习面向对象之匿名内部类
之前我们提到“匿名”这个字眼的时候,是在学习new对象的时候,创建匿名对象的时候用到的,之所以说是匿名,是因为直接创建对象,而没有把这个对象赋值给某个值,才称之为匿名。
匿名对象回顾:
class NoObject { void method() { System.out.println("Hello NoNameObj"); } } class NoName { public static void main(String[] args) { new NoObject().method();//这里创建完对象之后,并没有把对象直接赋值给某个值,而是创建完对象之后,直接调用对象,之后这个对象就不能被其他对象调用了
//因为要调用一个对象必须知道他的名字吧,没有名字怎么调用,这个就是匿名的概念。完事之后就成垃圾了不能再调用了。 } }
因为在使用匿名对象的时候,那个对象只需要调用一次就好了,为了简写我门采用了匿名的写法。那么这里的匿名内部类也不例外,这里的匿名内部类也是内部类的一个简写,因为只需要调用一次就好了。那我们来看下日常内部类都是怎么调用的。
1 class Outer 2 { 3 4 int num = 10; 5 6 class Inner 7 { 8 9 void method() 10 { 11 12 System.out.println("The Outer Num is "+num); 13 14 } 15 16 } 17 18 void sayInner() 19 { 20 21 new Inner().method(); 22 23 } 24 25 } 26 27 28 class InnerClassDemo2 29 { 30 31 public static void main(String[] args) { 32 33 new Outer().sayInner(); 34 35 } 36 37 }
我们正常调用内部类是这个样子调用的,如果是匿名内部类又是怎样的呢,我们按照匿名对象的思维来思考一下:
匿名对象没有把创建的值赋给指定类型的引用变量,还有就是这个对象用一次之后就不能用第二次了。相应的:
匿名类,第一个就是没有类名,第二个就是这个类用完一次之后,不能被实例化第二次,也就是匿名应用的时候采用,但是这个类不是真实存在的。
但是这里就存在一个问题了,一个类没有名字我们如何去实例化这个类?所以说要使用匿名内部类是有条件的:
使用匿名对象的条件:
内部类必须继承或者实现一个外部类接口,满足这个条件我们才能使用内部类。代码示例:
1 abstract class AbsDemo 2 { 3 4 abstract void demo(); 5 6 } 7 8 class Outer 9 { 10 11 int num = 10; 12 13 14 void sayInner() 15 { 16 /** 17 *这个地方我们直接new了一个父类或者接口类,但是这里有一点需要注意,那就是在new完了父类或者实现的接口之后, 18 *后面要加上大括号,加上大括号的意义是在于表示,这是一个类,并且是new关键字后面的类或者接口的子类或者实现 19 *之后我们在大括号后直接覆盖或者实现了类或者接口的方法 20 */ 21 new AbsDemo() 22 { 23 24 void demo() 25 { 26 27 System.out.println("This is a NoNameInnerClass Demo"); 28 29 } 30 31 }.demo();//这个地方是非常容易出错的,往往我们new abs(){}之后就忘了调用,因为我们只是单纯的new abs(){}这个样子是没有 32 //任何意义的,为什么因为这个是个匿名的类,也就是说没有类名,你匿名定义之后完了,就不能再引用了。只能立即调用 33 //这个方法后才会变的有意义。还有一点就是这个匿名类定义调用完之后,不要忘了后面的分号结束语句,因为new之后就是 34 //一个语句了,而不是class声明了。 35 36 } 37 38 } 39 40 41 class InnerClassDemo2 42 { 43 44 public static void main(String[] args) { 45 46 new Outer().sayInner(); 47 48 } 49 50 }
匿名内部类通俗来说就是:就是一个匿名子类对象。定义的方法是:new 父类or接口(){子类内容}
匿名内部类的应用:
场景一:当函数参数是接口类型时,且接口方法不超过3个,此时我们可以采用匿名内部类来当参数传递。比如:
1 interface InterfaceDemo 2 { 3 4 public void show1(); 5 public void show2(); 6 7 } 8 9 class InnerClassDemo2 10 { 11 12 public static void main(String[] args) { 13 14 method(new InterfaceDemo(){ 15 16 public void show1() 17 { 18 19 System.out.println("Show 1"); 20 21 } 22 23 public void show2() 24 { 25 26 System.out.println("Show 2"); 27 28 } 29 30 }); 31 32 } 33 34 static void method(InterfaceDemo demo) 35 { 36 37 demo.show1(); 38 demo.show2(); 39 } 40 41 }
这里method()方法需要一个接口类型的对象传入,为了简便期间,我们直接用了匿名内部类对象进行了传递,这样是不是很方便呢?但是这里还有一个地方需要注意,就是假如有现在这样的情况:
1 class InnerClassDemo2 2 { 3 4 class Inner 5 { 6 7 8 } 9 10 public static void main(String[] args) { 11 12 new Inner(); 13 14 } 15 16 17 }
如果我们此时这么运行的话就会报下面的错误:
InnerClassDemo2.java:21: 错误: 无法从静态上下文中引用非静态 变量 this
new Inner();
^
1 个错误
为什么呢?因为我们知道main函数是静态的,他无法调用静态上下文当中的非静态成员,此时class Inner{}在这个类当中就相当于一个成员,但是是非静态的自然而然会报错的。
在这里还有一个多态的问题需要我们注意:比如
1 interface InterfaceDemo 2 { 3 4 public void show1(); 5 public void show2(); 6 7 } 8 9 class DuoTaiFalse 10 { 11 12 void method() 13 { 14 15 /** 16 *这个地方发生了向上转型。即多态 17 */ 18 InterfaceDemo inface = new InterfaceDemo(){ 19 20 public void show1() 21 { 22 23 System.out.println("Show1"); 24 25 } 26 public void show2() 27 { 28 29 System.out.println("Show2"); 30 31 } 32 33 public void show3() 34 { 35 36 System.out.println("Show3"); 37 38 } 39 40 }; 41 42 inface.show1(); 43 inface.show2(); 44 /**这个地方就是错误的,因为InterfaceDemo inface = new InterfaceDemo(){}当进行到这个地方的时候,就已经发生了 45 *向上转型,也就是说这个时候,不能够调用除父类当中已经有的其他的方法。所以调用inface.show3()会提示错误 46 */ 47 //inface.show3(); 48 49 } 50 51 52 } 53 54 class InnerClassDemo2 55 { 56 57 public static void main(String[] args) { 58 59 new DuoTaiFalse().method(); 60 61 } 62 63 }
待续.....