• java内部类技术提炼


    时间: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本身不支持多继承。用内部类不知算什么……



  • 相关阅读:
    【POJ】【2420】A Star not a Tree?
    【BZOJ】【2818】Gcd
    【BZOJ】【2190】【SDOI2008】仪仗队
    【Vijos】【1164】曹冲养猪
    【BZOJ】【1430】小猴打架
    【BZOJ】【3611】【HEOI2014】大工程
    【转载】完全图的生成树
    【BZOJ】【2286】【SDOI2011】消耗战
    【POJ】【1061】/【BZOJ】【1477】青蛙的约会
    Codeforces VK Cup Finals #424 Div.1 A. Office Keys(DP)
  • 原文地址:https://www.cnblogs.com/rixiang/p/5719824.html
Copyright © 2020-2023  润新知