一、什么是内部类?
内部类(inner class)是定义在另一个类中的类
为什么使用内部类?
1)内部类方法可以访问该类定义所在的作用域中的数据,包括私有数据
2)内部类可以对同一个包中的其他类隐藏起来
3)当想要定义一个回调函数且不想编写大量代码时,使用匿名内部类比较便捷
二、内部类有几种?
内部类分为:成员内部类、局部内部类、静态内部类、匿名内部类
2.1、成员内部类
成员内部类是最普通的内部类,我们先从它开始说起:
package com.my.po; /** * description:{description} * author:jyy * date:2018-02-08 16:53 * modify:{modify} */ public class Outer { private String id; private String name; public class Inner { public void print() { id = "1001"; name = "李明"; System.out.println(id); System.out.println(name); } } public void start() { Inner inner = new Inner(); inner.print(); } }
执行结果:
1001
李明
Inner是一个内部类,内部类可以访问外部类定义的所有属性和方法,包括私有属性
内部类的对象总有一个隐式的引用,它指向创建它的外部类对象,也是通过这个隐式引用,内部类可以访问外部类的属性和方法,图示如下:
所以System.out.println(name)等价于System.out.println(Outer.this.name),格式为:外部类.this.属性/方法名,下面一个例子,可以加深我们的理解
package com.my.po; /** * description:{description} * author:jyy * date:2018-02-08 16:53 * modify:{modify} */ public class Outer { private String id = "1"; private String name = "张三"; public class Inner { private String name = "李四"; public void print() { String name = "王五"; System.out.println(id); System.out.println(name); System.out.println(this.name); System.out.println(Outer.this.name); } } public void start() { Inner inner = new Inner(); inner.print(); } }
执行结果:
1
王五
李四
张三
内部类可以拥有private、protected、public、包访问权限(默认),而外部类只有public和包访问权限(protected)
注意:成员内部类不能含有static修饰的变量和方法,因为成员内部类需要先创建外部类,才能创建自己
2.2、局部内部类
局部内部类是定义在一个方法或者作用域中的类
package com.my.po; /** * description:{description} * author:jyy * date:2018-02-08 16:53 * modify:{modify} */ public class Outer { public void print(final String name) { class Inner { public void innerPrint() { System.out.println(name); } } Inner inner = new Inner(); inner.innerPrint(); } }
如果需要从外部类的print方法传参到内部类中,形参必须为final类型。因为在方法print执行结束以后,变量会被释放,然而内部类的对象可能仍然在使用这个变量,只有声明为final常量,才不会导致程序报错。所以传入的参数,不可以修改值
局部内部类不可以使用public或private访问修饰符
2.3、静态内部类
静态内部类,修饰符为static的内部类
package com.my.po; /** * description:{description} * author:jyy * date:2018-02-08 16:53 * modify:{modify} */ public class Outer { private static String name = "李明"; public static class Inner { public void print() { System.out.println(name); } } }
可以直接使用Outer.Inner的方式进行调用,静态类中只可以访问外部类的静态属性和方法
2.4、匿名内部类
以上三种内部类,我们在平常编程中使用较少。但是匿名内部类我们会经常遇到,在各种框架中也经常出现它的身影。同时后面的篇章中会说到的lambda表达式,很大的一个作用就是为了简便匿名内部类的写法。所以我们要着重讲一下匿名内部类,上面三种内部类,有所了解即可
在2.2中我们说到了局部内部类,如果我们再深入一步。假如,在方法中内部类,我们只创建这个类的一个对象,我们就可以不用声明了,这种类被称为匿名内部类。前提是这个内部类要有父类或者实现某个接口
package com.my.po; /** * description:{description} * author:jyy * date:2018-02-09 15:03 * modify:{modify} */ public interface Printable { void print(); }
package com.my.po; /** * description:{description} * author:jyy * date:2018-02-08 16:53 * modify:{modify} */ public class Outer { public void start() { class Inner implements Printable { @Override public void print() { System.out.println("实现接口的局部内部类"); } } Inner inner = new Inner(); inner.print(); System.out.println("======================"); Printable printable = new Printable() { @Override public void print() { System.out.println("匿名内部类,重写了接口的方法"); } }; printable.print(); } }
package com.my.controller; import com.my.po.Outer; import junit.framework.TestCase; import org.junit.Test; /** * description:{description} * author:jyy * date:2018-01-09 16:43 * modify:{modify} */ public class AppTest extends TestCase { @Test public void test() { Outer outer = new Outer(); outer.start(); } }
执行结果:
实现接口的局部内部类 ====================== 匿名内部类,重写了接口的方法
package com.my.controller; import com.my.po.Outer; import junit.framework.TestCase; import org.junit.Test; /** * description:{description} * author:jyy * date:2018-01-09 16:43 * modify:{modify} */ public class AppTest extends TestCase { @Test public void test() { Object obj1 = new Object(); System.out.println(obj1.toString()); Object obj2 = new Object() { public String toString() { return "ok"; } }; System.out.println(obj2.toString()); } }
执行结果:
java.lang.Object@306a30c7
ok
匿名内部类同样不可以使用public或private访问修饰符,同样也不可以被static修饰
匿名内部类是唯一一个没有构造器的类,因为构造器的名字必须和类名相同,而匿名内部类连类名都没有
同样父类方法start的传参也是默认final修饰的,也就是说匿名内部类不能修改传入参数的值
三、匿名内部类的使用
匿名内部类的主要使用场景:1、事件监听2、回调
请看下面的示例
try { /** * 代码 */ } catch (Exception e) { logger.error("This is error message.Exception:" + e); e.printStackTrace(); } finally { System.out.println("退出"); }
上面这种格式,相信大家都很熟悉,当我们需要监控代码异常的时候,会在程序大量使用这种代码。然而这段代码中只有try中的部分是需要经常变动的,其他的部分基本不变,我们试着用匿名内部类的方式,重写这部分代码
首先定义一个接口
package com.my.controller; /** * description:{description} * author:jyy * date:2018-02-09 15:42 * modify:{modify} */ public interface CatchExceptionable { void catchException(); }
再定义一个异常处理的模板类
package com.my.controller; import org.apache.log4j.Logger; /** * description:{description} * author:jyy * date:2018-02-09 15:40 * modify:{modify} */ public class ExceptionTemplate { private static Logger logger = Logger.getLogger(ExceptionTemplate.class); public void execute(CatchExceptionable catchExceptionable) { try { catchExceptionable.catchException(); } catch (Exception e) { logger.error("This is error message.Exception:" + e); e.printStackTrace(); } finally { System.out.println("退出"); } } }
当我们写代码的时候,可以像如下方式进行调用,这样我们就不必重复的写try catch这类代码了,提高了复用性
new ExceptionTemplate().execute(new CatchExceptionable() { @Override public void catchException() { /** * 代码 */ } });