Java中的内部类
Posted lol-toulan
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java中的内部类相关的知识,希望对你有一定的参考价值。
内部类:顾名思义,定义在内部的类,所以,在Java中,可以将一个类定义在另一个类里面或者一个方法里面,这样的类称为内部类。广泛意义上的内部类一般来说包括这四种:成员内部类、局部内部类、匿名内部类和静态内部类。
为什么要用内部类,普通的创建一个新类,创建一个新对象,也能完成相同的作用,下面举例来说明一下:
这是没有用内部类的实现方法:
abstract class Demo{ //因为Demo类是抽象类,所以把不用实现方法体。谁调用该方法谁重写。 public abstract void eat(); } class Son extends Demo{ //父类中的抽象方法由子类重写。 public void eat(){ System.out.println("show son"); } } public class Test { public static void main(String[] args) { //Son s = new Son(); //s.eat(); Demo d = new Son(); d.eat(); } }
//运行结果:show son
我们可以看到,在上述代码中,Son类继承Demo类之后才能重写Demo类中的抽象方法,但是,如果Son类只用一次,那么将其编写为一个新类岂不是太不方便了,
这时就用到了匿名内部类,代码如下:
abstract class Demo{ //因为Demo类是抽象类,所以把不用实现方法体。谁调用该方法谁重写。 public abstract void eat(); } //class Son extends Demo{ // //父类中的抽象方法由子类重写。 // public void eat(){ // System.out.println("show son"); // } //} public class Test { public static void main(String[] args) { //Son s = new Son(); //s.eat(); Demo d = new Demo() { @Override public void eat() { System.out.println("show son"); } }; d.eat(); } } //运行结果:show son
//
//可以看到,我们直接将抽象类Person中的方法在大括号中实现了
//这样便可以省略一个类的书写
//并且,匿名内部类还能用于接口上
下面就来详细了解一下这四种内部类的用法。
1.成员内部类:成员内部类声明在类中,方法体、代码块之外。和成员变量、成员方法在同一级别。
看下面例子:
//成员内部类 class Demo{ public int num1 = 100; private int num2 = 200; class Inner{ public int num3 = 300; private int num4 = 400; public void test(){ System.out.println(num1); System.out.println(num2); System.out.println(num3); System.out.println(num4); } } public void test2(){ Inner i = new Inner(); i.test(); System.out.println("-------------"); System.out.println(num1); System.out.println(num2); System.out.println(i.num3); System.out.println(i.num4); } } public class Test { public static void main(String[] args) { Demo d = new Demo(); d.test2(); } }
运行结果:
1 100 2 200 3 300 4 400 5 ------------- 6 100 7 200 8 300 9 400
由上述代码我们可以清楚的发现,内部类调用外部类的方法时,可以直接调用(static修饰的内部类除外,因为静态类无法调用非静态变量),而外部类调用内部类时,则需要进行一系列的操作,例如创建对象,才能调用。
静态内部类:
我们知道,以前在学习static时说过,当成员变量或者成员方法被static修饰的,可以通过类名调用,而且,被static修饰的方法不能调用非静态变量,同理,当成员内部类被static修饰时,我们可以把静态内部类当作一个static修饰的变量,或者方法。所以静态内部类也可以通过外部类的类名进行调用。例子如下:
//静态内部类 class Demo { public int num1 = 100; private static int num2 = 200; //静态内部类 public static class Inner { public static int num3 = 300; private int num4 = 400; public void test() { // System.out.println(num1); System.out.println(num2); System.out.println(num3); System.out.println(num4); } //这是由静态修饰的方法,只能调用静态成员变量和方法 public static void test2() { System.out.println(num2); System.out.println(num3); } } } public class Test { public static void main(String[] args) { // Demo.Inner di = new Demo.Inner(); di.test(); System.out.println("-------------"); //因为Inner类是Demo类的静态内部成员方法,所以可以通过,类名.方法名来调用。 di.test2(); System.out.println("----------"); Demo.Inner.test2(); } } // 200 // 300 // 400 // ------------- // 200 // 300 // ---------- // 200 // 300
其实我们可以把成员内部类和静态内部类放在一起来看。此外,我们应把内部类当作一个成员变量或者成员方法来看,这样会方便我们理解成员内部类。
局部内部类:
下面我们先看一道面试题:
局部内部类访问局部变量的注意事项?:
我们通过举例来看结论:
//局部内部类 class Outer { private int num = 100; public void method() { int num2 = 200; class Inner { public void show() { num2 = 200; System.out.println(num); System.out.println(num2); } } Inner i = new Inner(); i.show(); } } public class Test { public static void main(String[] args) { Outer o = new Outer(); o.method(); } } //Error:(11, 17) java: 从内部类引用的本地变量必须是最终变量或实际上的最终变量
我们发现虽然不用final修饰num2,但是当我们尝试改变num2的值时,编译器会告诉我们本地变量必须是最终变量或者实际上的最终变量。所以由此我们可以知道局部内部类访问局部变量是必须用final修饰。
原因是:局部变量随着方法的调用而调用,随着方法的调用结束而消失,而这个时候,局部对象并没有从堆内存中消失,还要用那个变量,为了能让数据继续被使用。所以我们加final修饰。
我们先看一下正确的代码,同时再来看一下Outer类的反编译文件。
package com.ni_ming_nei_bu_lei; //局部内部类 class Outer { private int num = 100; public void method() { int num2 = 200; class Inner { public void show() { System.out.println(num); System.out.println(num2); } } Inner i = new Inner(); i.show(); } } public class Test { public static void main(String[] args) { Outer o = new Outer(); o.method(); } } // Decompiled by Jad v1.5.8e2. Copyright 2001 Pavel Kouznetsov. // Jad home page: http://kpdus.tripod.com/jad.html // Decompiler options: packimports(3) fieldsfirst ansi space // Source File Name: Test.java //反编译文件 //package com.ni_ming_nei_bu_lei; // // import java.io.PrintStream; // //class Outer //{ // // private int num; // // Outer() // { // num = 100; // } // // public void method() // { // final int num2 = 200; // class 1Inner // { // // final int val$num2; // final Outer this$0; // // public void show() // { // System.out.println(num); // System.out.println(num2); // } // // 1Inner() // { // this.this$0 = Outer.this; // num2 = i; // super(); // } // } // // 1Inner i = new 1Inner(); // i.show(); // } // //}
由反编译文件我们可以清晰看到,虽然在JDK1.7之后局部内部类访问局部变量时,虽然我们不需要手动加上final修饰符,但系统自动的将num2转化为final类型的常量。
匿名内部类:
前提: 存在一个接口或者一个类,,这里的类可以是具体类也可以是抽象类。
格式: new 类名或者接口() { 重写方法 };
在本篇第一个代码,笔者就写了一个匿名内部类呦,如果不理解 ,仔细看下去。
匿名对象的本质:是一个继承了该类或者实现该接口的子类匿名对象。
通过一个例子来解释:
//匿名内部类 interface Inter{ public abstract void show(); } class Outer{ public void method (){ new Inter (){ //作为一个javaer,我们知道new出来的是对象,既然是对象,那就能调用方法 public void show(){ System.out.println("show "); }; }.show(); //所以此处本质上是new出来的对象在调用show方法。 } } public class Test { public static void main(String[] args) { Outer outer = new Outer(); outer.method(); } } //结果:show
但是问题又来了,这是接口中只有一个方法,那要是有两个哪?有同学可能会说,那还不简单,new 两次,这次写次数都少,那要是有10个,20个哪?都这样带调用难道不是代码冗余太严重了吗?毕竟我们作为一个优秀Javaer,我们需要用最简单的方法解决复杂的问题,So,Javaer大牛们想了一个方法,既然创建的是对象,哪我能不能象普通的创建对象那样,A a = new B();利用a调用对象,答案是肯定的,
格式是:
A a = new A(){ } //注意,这不是第一眼理解的那样,这一句语句意思是,把子类对象赋给父接口,是多态。这个等式左边是父接口或类名,右边是子类匿名对象。
下面通过代码来解释:
package com.ni_ming_nei_bu_lei; //匿名内部类 interface Inter { public abstract void eat(); public abstract void eat2(); } class Outer { // public void method (){ // new Inter (){ //作为一个javaer,我们知道new出来的是对象,既然是对象,那就能调用方法 // public void eat(){ // System.out.println("show "); // }; // }.eat(); //所以此处本质上是new出来的对象在调用show方法。 // } //这种方法,就算父接口,或者父类中有再多的方法,都可以这样调用无比方便 匿名内部类中必须实现父接口或者父类中的所有方法。 public void method() { Inter i = new Inter() { //作为一个javaer,我们知道new出来的是对象,既然是对象,那就能调用方法 public void eat() { System.out.println("eat "); } public void eat2() { System.out.println("eat2"); } }; //所以此处本质上是new出来的对象在调用show方法。 i.eat(); i.eat2(); } } public class Test { public static void main(String[] args) { Outer outer = new Outer(); outer.method(); } } //eat //eat2
补充:匿名内部类在开发中的作用。
代码:
package com.ni_ming_nei_bu_lei; //匿名内部类开发中的作用 interface Person { public abstract void study(); } class PersonDemo { //接口名作为形式参数 //其实这里要的不是接口,要的是接口实现类对象。 public void method(Person p) { p.study(); } } //实现类接口 class Student implements Person { public void study() { System.out.println("好好学习,天天向上"); } } public class Test { public static void main(String[] args) { //普通 PersonDemo pd = new PersonDemo(); Person p = new Student(); //多态 pd.method(p); System.out.println("----------"); //匿名内部类,简单 pd.method(new Person() { @Override public void study() { System.out.println("好好学习,天天向上"); } }); } } // 好好学习,天天向上 // ---------- // 好好学习,天天向上
以上是关于Java中的内部类的主要内容,如果未能解决你的问题,请参考以下文章