时间:2016.07.28,2016.07.29
参考书籍:《Thinking in Java》、《Effective Java》
备注:这篇文章并不打算介绍内部类相关的一切技术细节,在《Thinking in Java》里已经介绍的很详细了,我只想重点谈谈一些我所了解的内部类的应用。顺便提纲挈领的对《Thinking in Java》相应章节作一定总结。
一些提炼:
一.真正的内部类
1.内部类拥有其外部类的所有元素(方法和字段)的访问权(原理在于Java默认做到了:当某个外围类的对象创建了一个内部类对象时,此内部类对象必定会秘密地捕获一个指向那个外围类对象的引用。)
2.内部类的创建:
(1)首先,最直接创建内部类的地方只能是在外部类的方法里。
(2)外部类可以写一个工厂方法来返回创建的内部类。
一种形式是这样:OuterClass.InnerClass innerClass = outerClass.getInner();
package cn.j; /** * @ClassName: OutClass * @Description: thinking in java * @author 无名 * @date 2016-7-28 下午9:37:59 * @version 1.0 */ public class OutClass { private int outValue = 1; InnerClass getInner() { return new InnerClass(); } public void outSay(){ System.out.println("from out function."); } class InnerClass { public void innerSay() { System.out.println("I am inner,and out value = " + outValue); outSay(); } } public static void main(String[] args) { OutClass outClass = new OutClass(); OutClass.InnerClass innerClass = outClass.getInner(); innerClass.innerSay(); } }
(3)如果内部类实现了某个接口Inter,则可以 Inter innerClass = outerClass.getInner();
这便是内部类的向上造型
package cn.j; interface Inter{ public void innerSay(); } /** * @ClassName: OutClass * @Description: thinking in java * @author 无名 * @date 2016-7-28 下午9:37:59 * @version 1.0 */ public class OutClass { private int outValue = 1; InnerClass getInner() { return new InnerClass(); } public void outSay(){ System.out.println("from out function."); } class InnerClass implements Inter{ @Override public void innerSay() { System.out.println("I am inner,and out value = " + outValue); outSay(); } } public static void main(String[] args) { OutClass outClass = new OutClass(); Inter innerClass = outClass.getInner(); innerClass.innerSay(); } }
3.在方法内的内部类,局部内部类:
局部内部类与一般内部类有很多相似之处,在于其对外部类的访问权限上。不同之处,在于对于局部内部类的创建,只能在包含该类的方法之上。
package cn.j; public class TestLocalInnerClass { public int int00 = 1; public void outFunc(){ System.out.println("I am Outter class"); } public void func1(){ class InnerLocalClass{ public void say(){ System.out.println("I am InnerLocalClass value:" + int00); outFunc(); } } InnerLocalClass ilc = new InnerLocalClass(); ilc.say(); } public static void main(String[] args){ TestLocalInnerClass tlic = new TestLocalInnerClass(); tlic.func1(); } }
4.匿名内部类:
要我来做匿名内部类的示例的话,非常简单:
package cn.j; /** * @ClassName: MyAnonymousInterface * @Description: Thinking in Java * @author 无名 * @date 2016-7-29 下午8:26:52 * @version 1.0 */ class MyInnerClass { private String msg = "A"; public void say(){ System.out.println("I am MyInnerClass" + msg); } } public class AnonymousClassTest { public static void main(String[] args){ new MyInnerClass().say(); } }
package cn.j; /** * @ClassName: MyAnonymousInterface * @Description: Thinking in Java * @author 无名 * @date 2016-7-29 下午8:26:52 * @version 1.0 */ interface MyAnonymousInterface{ public void say(); } public class AnonymousClassTest { public static void main(String[] args){ new MyAnonymousInterface(){ private String msg = "A"; public void say(){ System.out.println("I am MyAnonymousInterface" + msg); } }.say(); } }
先看看第一个示例,再看看第二个示例,很容易看出其中玄机。匿名内部类其实都是,通过new表达式来直接实现一个接口。在这个过程中插入一个类的定义后立刻便new出这个类并使用它。
之前看到类似这样的创建线程的写法,当时还觉得很nb,其实如果熟悉匿名内部类了,再看真的很easy。这种写法可能就是为了装b吧。具体原理在于Thread的创建可以是以一个实现了Runnable接口的类作为参数的,而这个实现了Runnable接口的类在这里以匿名内部类来实现。
总体来讲,配合上面示例1,然后示例2,再加上对Thread创建方式的理解,便可以完全了解下面的代码:
new Thread(new Runnable() { public void run() { while (true) { System.out.println("fu波多野结衣ck"); } } }).start();
有一点要补充一 下:局部内部类和匿名内部类能且只能访问局部final变量:
类似这样的效果:
package cn.j; public class TestLocalInnerClass { public void func1(){ final int localV = 3; class InnerLocalClass{ public void say(){ System.out.println("I am InnerLocalClass value:" + localV); } } InnerLocalClass ilc = new InnerLocalClass(); ilc.say(); } public static void main(String[] args){ TestLocalInnerClass tlic = new TestLocalInnerClass(); tlic.func1(); } }
5.嵌套类(static修饰的内部类):
(1)You don't need an outer-class object in order to create an object of a nested class.
(2)You can't access a non-static outer-class object from an object of a nested class.
看不懂Thinking in java的英文,看下面的代码也懂了。
package cn.j; /** * @ClassName: NestedClassTest * @Description: Thinking in Java * @author 无名 * @date 2016-7-29 下午9:33:28 * @version 1.0 */ public class NestedClassTest { private int outV00 = 1; public static int outV01 = 1; public void outSay00(){ System.out.println("out say"); } public static void outSay01(){ System.out.println("out static say"); } public static class NestedC00 { private int intV00; public void innerFunc(){ intV00 = outV01; outSay01(); } } public static void main(String[] args){ NestedC00 nc00 = new NestedC00(); nc00.innerFunc(); } }
6.接口中的类
接口中的类类似于嵌套类的情况,本身和接口没有本质联系(默认public static),只是处于其命名空间之下
package cn.j; import cn.j.ClassInInterface.Test; interface ClassInInterface { void func00(); class Test implements ClassInInterface { @Override public void func00() { System.out.println("Howdy"); } } } /** * @ClassName: ClassInInterfaceTest * @Description: Thinking in Java * @author 无名 * @date 2016-7-29 下午9:45:08 * @version 1.0 */ public class ClassInInterfaceTest { public static void main(String[] args) { Test test = new Test(); test.func00(); } }
我所知道的内部类的几个应用:
1.《Effective Java》第二章第2条,讲到使用嵌套类完成构建器设计模式。
package cn.j; public class NutritionFacts { private final int servingSize; private final int servings; private final int calories; private final int fat; private final int sodium; private final int carbonhydrate; public static class Builder { private final int servingSize; private final int servings; private int calories = 0; private int fat = 0; private int sodium = 0; private int carbonhydrate = 0; public Builder(int servingSize,int serving){ this.servingSize = servingSize; this.servings = serving; } public Builder calories(int val){ this.calories = val; return this; } public Builder fat(int val){ this.fat = val; return this; } public Builder carbonhyate(int val){ this.carbonhydrate = val; return this; } public Builder sodium(int val){ this.sodium = val; return this; } public NutritionFacts build(){ return new NutritionFacts(this); } } private NutritionFacts(Builder builder){ servingSize = builder.servingSize; servings = builder.servings; calories = builder.calories; fat = builder.fat; sodium = builder.sodium; carbonhydrate = builder.carbonhydrate; } public static void main(String[] args) { NutritionFacts cocacola = new NutritionFacts.Builder(240,80).calories(100).sodium(35).carbonhyate(27).build(); } }
结合代码来看,如果单纯用构造函数来初始化那些参数的话,会比较麻烦,而难以阅读,使用易于出错。
例如new Test(1,2,3,4,5,6,7,8,9);这种写法。
上述代码的形式,NutritionFacts cocacola = new NutritionFacts.Builder(240,80).calories(100).sodium(35).carbonhyate(27).build();可读性非常强。builder模式模拟了具名的可选参数。
2. 项目中遇到的,自定义注解实现前后台参数校验,用到了内部类:
package sonn.sonnannotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import javax.validation.Constraint; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; import javax.validation.Payload; import sonn.util.StringUtill; /** * @ClassName: IsValidString * @Description: 自定义注解实现前后台参数校验,判断是否包含非法字符 * @author 无名 * @date 2016-7-25 下午8:22:58 * @version 1.0 */ @Target({ElementType.FIELD, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Constraint(validatedBy = IsValidString.ValidStringChecker.class) @Documented public @interface IsValidString { String message() default "The string is invalid."; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default{}; class ValidStringChecker implements ConstraintValidator<IsValidString,String> { @Override public void initialize(IsValidString arg0) { } @Override public boolean isValid(String strValue, ConstraintValidatorContext context) { if(StringUtill.isStringEmpty(strValue)) { return true; } if(strValue.contains("<")) { return false; } return true; } } }
具体内容可以参考我的博文:http://blog.csdn.net/sonnadolf/article/details/52040017
3.之前提到的,匿名内部类创建线程的写法。其实很多时候都可以采用匿名内部类方式实现接口,直接new一个类来使用。只是大家不熟悉这个用法罢了。
spring源码实例化singleton bean的代码:
// Create bean instance. if (mbd.isSingleton()) { sharedInstance = getSingleton(beanName, new ObjectFactory() { public Object getObject() throws BeansException { try { return createBean(beanName, mbd, args); } catch (BeansException ex) { // Explicitly remove instance from singleton cache: It might have been put there // eagerly by the creation process, to allow for circular reference resolution. // Also remove any beans that received a temporary reference to the bean. destroySingleton(beanName); throw ex; } } });
4.java jdk的线程池的Executors类,用到了嵌套类。
类似这样:
public class Executors { .......... static class DefaultThreadFactory implements ThreadFactory { .......... } .......... }
5.非常非常重要的一点,内部类是tmd可以弥补java无法多继承缺点的啊。虽然我觉得有点扯淡。因为java本身不支持多继承。用内部类不知算什么……