精华内容
下载资源
问答
  • 内部类是一个非常有用的特性但又比较难理解使用的特性(鄙人到现在都没有怎么使用内部类,对内部类也只是略知一二)。  内部类我们从外面看是非常容易理解的,无非就是在一个类的内部在定义一个类。 ...

     

     

     

      不多说,直接上干货!

     

    可以将一个类的定义放在另一个类的定义内部,这就是内部类

           内部类是一个非常有用的特性但又比较难理解使用的特性(鄙人到现在都没有怎么使用过内部类,对内部类也只是略知一二)。

     

     

     

      内部类我们从外面看是非常容易理解的,无非就是在一个类的内部在定义一个类

    public class OuterClass {  
        private String name ;  
        private int age;  
      
        public String getName() {  
            return name;  
        }  
      
        public void setName(String name) {  
            this.name = name;  
        }  
      
        public int getAge() {  
            return age;  
        }  
      
        public void setAge(int age) {  
            this.age = age;  
        }  
          
        class InnerClass{  
            public InnerClass(){  
                name = "chenssy";  
                age = 23;  
            }  
        }  
    }  

       在这里InnerClass就是内部类,对于初学者来说内部类实在是使用的不多,鄙人菜鸟一个同样没有怎么使用过(貌似仅仅只在做swing 注册事件中使用过),但是随着编程能力的提高,我们会领悟到它的魅力所在,它可以使用能够更加优雅的设计我们的程序结构。在使用内部类之间我们需要明白为什么要使用内部类,内部类能够为我们带来什么样的好处。

     

     

     

     

      一、为什么要使用内部类

           为什么要使用内部类?在《Think in Java》中有这样一句话:使用内部类最吸引人的原因是:每个内部类都能独立地继承一个(接口的)实现,所以无论外部类是否已经继承了某个(接口的)实现,对于内部类都没有影响

          在我们程序设计中有时候会存在一些使用接口很难解决的问题,这个时候我们可以利用内部类提供的、可以继承多个具体的或者抽象的类的能力来解决这些程序设计问题。可以这样说,接口只是解决了部分问题,而内部类使得多重继承的解决方案变得更加完整

    public interface Father {  
      
    }  
      
    public interface Mother {  
      
    }  
      
    public class Son implements Father, Mother {  
      
    }  
      
    public class Daughter implements Father{  
      
        class Mother_ implements Mother{  
              
        }  
    }  

     

     

     

     

     

     

       其实对于这个实例我们确实是看不出来使用内部类存在何种优点,但是如果Father、Mother不是接口,而是抽象类或者具体类呢?这个时候我们就只能使用内部类才能实现多重继承了。

          其实使用内部类最大的优点就在于它能够非常好的解决多重继承的问题,但是如果我们不需要解决多重继承问题,那么我们自然可以使用其他的编码方式,但是使用内部类还能够为我们带来如下特性(摘自《Think in java》):

          1、内部类可以用多个实例,每个实例都有自己的状态信息,并且与其他外围对象的信息相互独立。

          2、在单个外围类中,可以让多个内部类以不同的方式实现同一个接口,或者继承同一个类。

          3、创建内部类对象的时刻并不依赖于外围类对象的创建。

          4、内部类并没有令人迷惑的“is-a”关系,他就是一个独立的实体。

          5、内部类提供了更好的封装,除了该外围类,其他类都不能访问。

     

     

     

     

     

     

        二、内部类基础

           在这个部分主要介绍内部类如何使用外部类的属性和方法,以及使用.this与.new。

           当我们在创建一个内部类的时候,它无形中就与外围类有了一种联系,依赖于这种联系,它可以无限制地访问外围类的元素。

    public class OuterClass {  
        private String name ;  
        private int age;  
      
        /**省略getter和setter方法**/  
          
        public class InnerClass{  
            public InnerClass(){  
                name = "chenssy";  
                age = 23;  
            }  
              
            public void display(){  
                System.out.println("name:" + getName() +"   ;age:" + getAge());  
            }  
        }  
          
        public static void main(String[] args) {  
            OuterClass outerClass = new OuterClass();  
            OuterClass.InnerClass innerClass = outerClass.new InnerClass();  
            innerClass.display();  
        }  
    }  
    --------------  
    Output:  
    name:chenssy   ;age:23  

      在这个应用程序中,我们可以看到内部了InnerClass可以对外围类OuterClass的属性进行无缝的访问,尽管它是private修饰的。这是因为当我们在创建某个外围类的内部类对象时,此时内部类对象必定会捕获一个指向那个外围类对象的引用,只要我们在访问外围类的成员时,就会用这个引用来选择外围类的成员。

           其实在这个应用程序中我们还看到了如何来引用内部类:引用内部类我们需要指明这个对象的类型:OuterClasName.InnerClassName。同时如果我们需要创建某个内部类对象,必须要利用外部类的对象通过.new来创建内部类:OuterClass.InnerClass innerClass = outerClass.new InnerClass();。

           同时如果我们需要生成对外部类对象的引用,可以使用OuterClassName.this,这样就能够产生一个正确引用外部类的引用了。当然这点实在编译期就知晓了,没有任何运行时的成本。

    public class OuterClass {  
        public void display(){  
            System.out.println("OuterClass...");  
        }  
          
        public class InnerClass{  
            public OuterClass getOuterClass(){  
                return OuterClass.this;  
            }  
        }  
          
        public static void main(String[] args) {  
            OuterClass outerClass = new OuterClass();  
            OuterClass.InnerClass innerClass = outerClass.new InnerClass();  
            innerClass.getOuterClass().display();  
        }  
    }  
    -------------  
    Output:  
    OuterClass...  

        到这里了我们需要明确一点,内部类是个编译时的概念,一旦编译成功后,它就与外围类属于两个完全不同的类(当然他们之间还是有联系的)。对于一个名为OuterClass的外围类和一个名为InnerClass的内部类,在编译成功后,会出现这样两个class文件:OuterClass.class和OuterClass$InnerClass.class。

     

     

     

     

     

     

     

      在Java中内部类主要分为成员内部类、局部内部类、静态内部类、匿名内部类、

    成员内部类

           成员内部类也是最普通的内部类,它是外部类的一个成员,所以它是可以无限制的访问外部类的所有成员属性和方法,尽管是private的,但是外部类要访问内部类的成员属性和方法则需要通过内部类实例来访问。

           在成员内部类中要注意两点,第一:成员内部类中不能存在任何static的变量和方法;第二:成员内部类是依附于外围类的,所以只有先创建了外围类才能够创建内部类

    public class OuterClass {  
        private String str;  
          
        public void outerDisplay(){  
            System.out.println("outerClass...");  
        }  
          
        public class InnerClass{  
            public void innerDisplay(){  
                //使用外围内的属性  
                str = "chenssy...";  
                System.out.println(str);  
                //使用外围内的方法  
                outerDisplay();  
            }  
        }  
          
        /*推荐使用getxxx()来获取成员内部类,尤其是该内部类的构造函数无参数时 */  
        public InnerClass getInnerClass(){  
            return new InnerClass();  
        }  
          
        public static void main(String[] args) {  
            OuterClass outer = new OuterClass();  
            OuterClass.InnerClass inner = outer.getInnerClass();  
            inner.innerDisplay();  
        }  
    }  
    --------------------  
    chenssy...  
    outerClass...  

     

      推荐使用getxxx()来获取成员内部类,尤其是该内部类的构造函数无参数时 。

     

     

     

     

     

     

     

     

    局部内部类

           有这样一种局部部类,它是嵌套在方法和作用域内的,对于这个类的使用主要是应用与解决比较复杂的问题,想创建一个类来辅助我们的解决方案,到那时又不希望这个类是公共可用的,所以就产生了局部内部类,局部内部类和成员内部类一样被编译,只是它的作用域发生了改变,它只能在该方法和属性中被使用,出了该方法和属性就会失效。

           对于局部内部类实在是想不出什么好例子,所以就引用《Think in java》中的经典例子了。

           定义在方法里:

     

    public class Parcel5 {  
        public Destionation destionation(String str){  
            class PDestionation implements Destionation{  
                private String label;  
                private PDestionation(String whereTo){  
                    label = whereTo;  
                }  
                public String readLabel(){  
                    return label;  
                }  
            }  
            return new PDestionation(str);  
        }  
          
        public static void main(String[] args) {  
            Parcel5 parcel5 = new Parcel5();  
            Destionation d = parcel5.destionation("chenssy");  
        }  
    }  

     

     

     

     

       定义在作用域内:

    public class Parcel6 {  
        private void internalTracking(boolean b){  
            if(b){  
                class TrackingSlip{  
                    private String id;  
                    TrackingSlip(String s) {  
                        id = s;  
                    }  
                    String getSlip(){  
                        return id;  
                    }  
                }  
                TrackingSlip ts = new TrackingSlip("chenssy");  
                String string = ts.getSlip();  
            }  
        }  
          
        public void track(){  
            internalTracking(true);  
        }  
          
        public static void main(String[] args) {  
            Parcel6 parcel6 = new Parcel6();  
            parcel6.track();  
        }  
    }  

     

     

     

     

     

     

     

    匿名内部类

           在做Swing编程中,我们经常使用这种方式来绑定事件。

    button2.addActionListener(    
                    new ActionListener(){    
                        public void actionPerformed(ActionEvent e) {    
                            System.out.println("你按了按钮二");    
                        }    
                    });  

     

     

      我们咋一看可能觉得非常奇怪,因为这个内部类是没有名字的,在看如下这个例子:

    public class OuterClass {  
        public InnerClass getInnerClass(final int num,String str2){  
            return new InnerClass(){  
                int number = num + 3;  
                public int getNumber(){  
                    return number;  
                }  
            };        /* 注意:分号不能省 */  
        }  
          
        public static void main(String[] args) {  
            OuterClass out = new OuterClass();  
            InnerClass inner = out.getInnerClass(2, "chenssy");  
            System.out.println(inner.getNumber());  
        }  
    }  
      
    interface InnerClass {  
        int getNumber();  
    }  
      
    ----------------  
    Output:  
    5  

     

     

     

     这里我们就需要看清几个地方:

          1、 匿名内部类是没有访问修饰符的。

          2、 new 匿名内部类,这个类首先是要存在的。如果我们将那个InnerClass接口注释掉,就会出现编译出错。

          3、 注意getInnerClass()方法的形参,第一个形参是用final修饰的,而第二个却没有。同时我们也发现第二个形参在匿名内部类中没有使用过,所以当所在方法的形参需要被匿名内部类使用,那么这个形参就必须为final。

          4、 匿名内部类是没有构造方法的。因为它连名字都没有何来构造方法。

    PS:由于篇幅有限,对匿名内部类就介绍到这里,有关更多关于匿名内部类的知识,我就会在下篇博客(java提高篇-----详解匿名内部类)做详细的介绍,包括为何形参要定义成final,怎么对匿名内部类进行初始化等等,敬请期待……

     

     

     

     

     

     

    静态内部类

           在java提高篇-----关键字static中提到Static可以修饰成员变量、方法、代码块,其他它还可以修饰内部类,使用static修饰的内部类我们称之为静态内部类,不过我们更喜欢称之为嵌套内部类。静态内部类与非静态内部类之间存在一个最大的区别,我们知道非静态内部类在编译完成之后会隐含地保存着一个引用,该引用是指向创建它的外围内,但是静态内部类却没有。没有这个引用就意味着:

          1、 它的创建是不需要依赖于外围类的。

          2、 它不能使用任何外围类的非static成员变量和方法。

    public class OuterClass {  
        private String sex;  
        public static String name = "chenssy";  
          
        /** 
         *静态内部类 
         */  
        static class InnerClass1{  
            /* 在静态内部类中可以存在静态成员 */  
            public static String _name1 = "chenssy_static";  
              
            public void display(){  
                /*  
                 * 静态内部类只能访问外围类的静态成员变量和方法 
                 * 不能访问外围类的非静态成员变量和方法 
                 */  
                System.out.println("OutClass name :" + name);  
            }  
        }  
          
        /** 
         * 非静态内部类 
         */  
        class InnerClass2{  
            /* 非静态内部类中不能存在静态成员 */  
            public String _name2 = "chenssy_inner";  
            /* 非静态内部类中可以调用外围类的任何成员,不管是静态的还是非静态的 */  
            public void display(){  
                System.out.println("OuterClass name:" + name);  
            }  
        }  
          
        /** 
         * @desc 外围类方法 
         * @author chenssy 
         * @data 2013-10-25 
         * @return void 
         */  
        public void display(){  
            /* 外围类访问静态内部类:内部类. */  
            System.out.println(InnerClass1._name1);  
            /* 静态内部类 可以直接创建实例不需要依赖于外围类 */  
            new InnerClass1().display();  
              
            /* 非静态内部的创建需要依赖于外围类 */  
            OuterClass.InnerClass2 inner2 = new OuterClass().new InnerClass2();  
            /* 方位非静态内部类的成员需要使用非静态内部类的实例 */  
            System.out.println(inner2._name2);  
            inner2.display();  
        }  
          
        public static void main(String[] args) {  
            OuterClass outer = new OuterClass();  
            outer.display();  
        }  
    }  
    ----------------  
    Output:  
    chenssy_static  
    OutClass name :chenssy  
    chenssy_inner  
    OuterClass name:chenssy  

      上面这个例子充分展现了静态内部类和非静态内部类的区别。

     

    转载于:https://www.cnblogs.com/zlslch/p/7470858.html

    展开全文
  • 如果从匿名内部类和局部内部类的存在意义来说的话,它们对于外围类的方法来说,都是不可见的,但是不可见,只是方法无法使用它们,并不能说明什么,内部类的方法和变量还是可以访问的,只要有创建实例。所以,问题就...

          

     我想了很久,还是无法得出一个准确的答案。如果从匿名内部类和局部内部类的存在意义来说的话,它们对于外围类的方法来说,都是不可见的,但是不可见,只是方法无法使用它们,并不能说明什么,内部类的方法和变量还是可以访问的,只要有创建实例。所以,问题就可以归于以下几点的解决:
    1.如果内部类的引用不是final,会怎么样?
            这问题从引用是final这点就已经解释了。因为final是不能修改该引用所指向的对象,所以如果不是final,该引用所指向的对象可以被修改。问题又来了:
    2.为什么引用一定不能被修改?
           如果修改了会怎样?我们都知道,别名现象的存在,使得指向同一个对象的引用,如果其中的引用被修改,另一个引用也会被修改,这在某些情况下是很不好的,但是不是内部类不也存在这种情况吗?但是我们又何曾想过要用final,那么,就只能是内部类本身的特点,使得它必须这样子做。内部类的特点就像前面所说的,不可见,就是这个不可见使得它必须这么做吗?
    3.我发现一个问题,如果将要传入的引用改为static,那么在内部类的方法中使用该引用是可以的,但是要传入引用,依然得使用final,所以这个问题的关键,我觉得,就在于引用的性质了。
    4.static引用和final引用到底有什么关联?
          没有什么关联。static引用因为不需与特定的对象关联在一起,所以内部类可以使用而不需通过方法将这个引用传进来。于是问题又回到原点,第二点到底要怎么解释。
          内部类在层次上,想当于类的方法,所以,它在类的层次上是与它所在的方法是相同的。那么,引用不可被修改,是否是为了保证引用的正确性呢?因为方法传进的引用,更多就只是作为内部类的引用,方法本身并不需要这个引用但又必须传参进来,但是引用可能在这个方法中被修改,所以这是否是为了保证内部类能够正确得到该引用呢?所以,这里面又涉及到方法传参的机制。方法传参,在java中就只有两种,传引用和传值。传值,会进行拷贝动作,传引用,就是相当于直接将这个对象传进来,因为对该引用所做的任何动作都会直接影响到该对象。
          这种解释我觉得,应该是目前最靠谱的。因为其他内部类并没有这种要求,只有局部内部类和匿名内部类有这种要求,其他内部类是可以直接将外围类的成员作为引用传进来,并不需要经过外面的方法这一层,所以,理应成立吧?
           这个问题网上有很多莫名其秒的解释,但是后来还是能够找到与我的答案一样的解释,他说是为防止闭包共享中发生变量取值错误的问题。我想,这与我上面的探讨是一样的,链接同下,同样都是博客园的文章:

    转载于:https://www.cnblogs.com/wenjiang/archive/2012/09/01/2666809.html

    展开全文
  • 假如你有一个 Integer 对象的列表,并且你想使用 Coolections.sort 来对它们进行排序。...这里有一些代码示例说明了该怎么做: importjava.util.*; publicclassLocalDemo1...{ //使用实...

    假如你有一个 Integer 对象的列表,并且你想使用 Coolections.sort 来对它们进行排序。另外,你还要自己指定一个比较器,因为你想按降序而不是升序排列它们。这里有一些代码示例说明了该怎么做:

    
    
    1. import java.util.*;  
    2.  
    3. public class LocalDemo1 ...{  
    4.  
    5.     // 使用实现了 Comparator 的匿名类排序。  
    6.  
    7.     static void sortanon(List list) ...{  
    8.         Collections.sort(list, new Comparator() ...{  
    9.             public int compare(  
    10.                Object o1, Object o2) ...{  
    11.                  int cc = ((Integer)o1).compareTo(o2);  
    12.                  return (cc < 0 ? 1 : cc > 0 ? -1 : 0);  
    13.             }  
    14.         });  
    15.     }  
    16.  
    17.     //  使用实现了 Comparator 的局部类排序  
    18.  
    19.     static void sortlocal(List list) ...{  
    20.         class MyComparator implements Comparator ...{  
    21.             public int compare(  
    22.                Object o1, Object o2) ...{  
    23.                  int cc = ((Integer)o1).compareTo(o2);  
    24.                  return (cc < 0 ? 1 : cc > 0 ? -1 : 0);  
    25.             }  
    26.         };  
    27.  
    28.         Collections.sort(list, new MyComparator());  
    29.     }  
    30.  
    31.     public static void main(String[] args) ...{  
    32.         List list1 = new ArrayList();  
    33.         list1.add(new Integer(1));  
    34.         list1.add(new Integer(2));  
    35.         list1.add(new Integer(3));  
    36.         sortanon(list1);  
    37.         System.out.println(list1);  
    38.  
    39.         List list2 = new ArrayList();  
    40.         list2.add(new Integer(1));  
    41.         list2.add(new Integer(2));  
    42.         list2.add(new Integer(3));  
    43.         sortlocal(list2);  
    44.         System.out.println(list2);  
    45.     }  

    这段程序的输出如下:

    [3, 2, 1]
    [3, 2, 1]

    上列中使用两种不同的方法实现了 Comparator 接口。第一种方法使用匿名类,第二种方法使用局部类,二者有何区别:

    一点区别是格式上的——匿名类的定义比较简捷,它实际上是下面这个表达式的一部分:

    Comparator c = new Comparator() {...};

    与之相反,局部类的定义看起来非常类似于常规的类定义,略为烦琐。例如,定义局部类内时可能用到 “implements”语句,而在匿名类中不需要显示的使用这条语句。

    哪一种格式“更好”取决于你自己的观点。匿名类的定义会比较难读,但在不需要使用局部类的地方使用局部类会造成一些错觉,让人觉得需要做的事比实际要做的事更多。

    让我们来看看另一个例子,更深层的比较匿名类和局部类:

    
    
    1. import java.util.*;  
    2.  
    3. public class LocalDemo2 ...{  
    4.  
    5.     // 使用两个单独的匿名类实例对两个列表进行排序  
    6.  
    7.     static void sort1(List list1, List list2) ...{  
    8.         Collections.sort(list1, new Comparator() ...{  
    9.             public int compare(  
    10.               Object o1, Object o2) ...{  
    11.                 int cc = ((Integer)o1).compareTo(o2);  
    12.                 return (cc < 0 ? 1 : cc > 0 ? -1 : 0);  
    13.             }  
    14.         });  
    15.  
    16.         Collections.sort(list2, new Comparator() ...{  
    17.             public int compare(  
    18.               Object o1, Object o2) ...{  
    19.                 int cc = ((Integer)o1).compareTo(o2);  
    20.                 return (cc < 0 ? 1 : cc > 0 ? -1 : 0);  
    21.             }  
    22.         });  
    23.     }  
    24.  
    25.     // 使用一个局部类的两个实例来对两个列表进行排序  
    26.  
    27.     static void sort2(List list1, List list2) ...{  
    28.         class MyComparator implements Comparator ...{  
    29.             public int compare(  
    30.               Object o1, Object o2) ...{  
    31.                 int cc = ((Integer)o1).compareTo(o2);  
    32.                 return (cc < 0 ? 1 : cc > 0 ? -1 : 0);  
    33.             }  
    34.         }  
    35.  
    36.         Collections.sort(list1, new MyComparator());  
    37.         Collections.sort(list2, new MyComparator());  
    38.     }  
    39.  
    40.     // 使用一个匿名类的一个实例来对两个列表进行排序   
    41.  
    42.     static void sort3(List list1, List list2) ...{  
    43.         Comparator cmp = new Comparator() ...{  
    44.             public int compare(  
    45.               Object o1, Object o2) ...{  
    46.                 int cc = ((Integer)o1).compareTo(o2);  
    47.                 return (cc < 0 ? 1 : cc > 0 ? -1 : 0);  
    48.             }  
    49.         };  
    50.  
    51.         Collections.sort(list1, cmp);  
    52.         Collections.sort(list2, cmp);  
    53.     }  
    54.  
    55.     // 使用一个局部类的一个实例来对两个列表进行排序  
    56.  
    57.     static void sort4(List list1, List list2) ...{  
    58.         class MyComparator implements Comparator ...{  
    59.             public int compare(  
    60.               Object o1, Object o2) ...{  
    61.                 int cc = ((Integer)o1).compareTo(o2);  
    62.                 return (cc < 0 ? 1 : cc > 0 ? -1 : 0);  
    63.             }  
    64.         }  
    65.  
    66.         Comparator cmp = new MyComparator();  
    67.  
    68.         Collections.sort(list1, cmp);  
    69.         Collections.sort(list2, cmp);  
    70.     }  
    71.  
    72.     static class AppComparator implements   
    73.      Comparator ...{  
    74.         public int compare(Object o1, Object o2) ...{  
    75.             int cc = ((Integer)o1).compareTo(o2);  
    76.             return (cc < 0 ? 1 : cc > 0 ? -1 : 0);  
    77.         }  
    78.     }  
    79.  
    80.     static Comparator appcomparator =   
    81.        new AppComparator();  
    82.  
    83.     // 使用应用程序中定义的比较器来对两个列表进行排序  
    84.  
    85.     static void sort5(List list1, List list2) ...{  
    86.         Collections.sort(list1, appcomparator);  
    87.         Collections.sort(list2, appcomparator);  
    88.     }  
    89.  
    90.     public static void main(String[] args) ...{  
    91.         List list1 = new ArrayList();  
    92.         list1.add(new Integer(1));  
    93.         list1.add(new Integer(2));  
    94.         list1.add(new Integer(3));  
    95.  
    96.         List list2 = new ArrayList();  
    97.         list2.add(new Integer(4));  
    98.         list2.add(new Integer(5));  
    99.         list2.add(new Integer(6));  
    100.  
    101.         //sort1(list1, list2);  
    102.         //sort2(list1, list2);  
    103.         //sort3(list1, list2);  
    104.         //sort4(list1, list2);  
    105.         sort5(list1, list2);  
    106.  
    107.         System.out.println(list1);  
    108.         System.out.println(list2);  
    109.     }  

    输出结果是:

    [3, 2, 1]
    [6, 5, 4]

    程序中所有这些排序的方法都做同样的事情——降序排序两个列表中的 Integer 对象。

    sort1 使用两个匿名类,每个都实现了 Comparator 接口。这两个类是相对独立的,但在逻辑上是重复的 (这样的代码非常糟糕)。

    sort2 方法使用了一个局部类的两个实例。这是比 sort1 更好的方法,但这并不是最有效率的。

    sort3 和 sort4 方法分均是使用的一个类的一个实例,它们在逻辑上也没有重复,所以是比较有效率的代码。这两段代码也有不同之处:sort3 比 sort4 更简捷,但 sort4 更具可读性。除了可读性,在这种情况下使用局部类没有更多的意义。

    如果你需要在一个应用程序中使用同一种排序的策略,那么上述的四种方法都不是完全正确的。这种情况下,最好使用一个全局类或者内部类 (MyComparator),或者创建一个单独的可用于应用程序中任何地方的比较器实例。sort5 就是这种情况下的例子。

    关于匿名类的另一个问题是它们必须基于一个已经存在的类或者接口创建,例如,你使用下面这句代码的时候:

    Comparator c = new Comparator() {...};

    实际上发生的事情是:从一个没有名字的类创建了实例,而这个类实现了 Comparator 接口。而对于局部类则没有这种限制。

    匿名类和局部类更深层次的区别在于:匿名类不能定义构造器,因为它是没有名字的。不过它还是可以通过 {...} 格式的初始化代码块做一些简单的初始化,例如:

    
    
    1. import java.util.*;  
    2.  
    3. public class LocalDemo3 ...{  
    4.     public static void main(String[] args) ...{  
    5.         Comparator cmp1 = new Comparator() ...{  
    6.             ...{  
    7.                 System.out.println(  
    8.                    <object initialization>);  
    9.             }  
    10.  
    11.             public int compare(  
    12.               Object o1, Object o2) ...{  
    13.                 int cc = ((Integer)o1).compareTo(o2);  
    14.                 return (cc < 0 ? 1 : cc > 0 ? -1 : 0);  
    15.             };  
    16.         };  
    17.  
    18.         class MyComparator implements Comparator ...{  
    19.             public MyComparator(int x) ...{  
    20.                 System.out.println(  
    21.                    <constructor called> +  
    22.                    < with value > + x);  
    23.             }  
    24.  
    25.             public int compare(  
    26.               Object o1, Object o2) ...{  
    27.                 int cc = ((Integer)o1).compareTo(o2);  
    28.                 return (cc < 0 ? 1 : cc > 0 ? -1 : 0);  
    29.             };  
    30.         };  
    31.         Comparator cmp2 = new MyComparator(100);  
    32.     }  

    这段程序将输出:

    object initialization
    constructor called with value 100

    局部类可以拥有通过常规方法使用的构造器,而匿名类地必须通过 {...} 来实现初始化。

    再次考虑关于对例表进行排序的例子。为局部类或内部类定义一个构造器,并通过传递参数的办法可以指定这个类的实例在操作过程中是按升序还是降序。在匿名类中要做到这一点则需要定义一个类成员级别的 final 变量。

    在对类的主流应用中更适合使用局部类,因为你更熟悉。局部类也比匿名类更具可读性。同时,局部类的功能往往会超出你所需要的,那么这个时候,就更适合使用匿名类了。
     


    本文转自边城__ 51CTO博客,原文链接:http://blog.51cto.com/jamesfancy/843241,如需转载请自行联系原作者

    展开全文
  • 内部类值无法修改原因问题描述原因分析原理分析内部类外面的局部变量不能被重复赋值否则会报错在内部类里面不能修改局部类外面定义的变量的值 问题描述 当使用内部类内部类外面的局部变量不能被重复赋值否则会...

    问题描述

    当使用内部类时
    内部类外面的局部变量不能被重复赋值否则会报错 为什么?
    在内部类里面不能修改局部类外面定义的变量的值 为什么?
    内部类使用局部变量报错图片

    原因分析

    想知道上面的原因需要知道编译器是怎么编译内部类的

    原理

    • 内部类对象创建规则

      • lambda内部类
        根据javap -p反编译可以发现会生成方法 private static void lambda$test$0(int);
        运行时会生成字节码创建对象
      • 匿名内部类
        编译器扫描到一个内部类时会在该类同包下创建一个class字节码文件
    • 命名规则

      • lambda内部类
        外部类名称lambda/ com.nailsoul.config.RedisConfiglambda内部类自增序号/随机值 如com.nailsoul.config.RedisConfigLambda$1/1535128843
      • 普通内部类
        外部类名称$普通内部类自增序号/随机值 如com.nailsoul.config.RedisConfig$1

        通过下面代码来检查上面理论

        public void test(){
        int count = 1;
        Runnable l1 = ()->{ int t = count; };
        Runnable l2 = ()->{};
        Runnable r1 = new Runnable() {@Override public void run() { }};
           System.out.println(l1.getClass().getName()+"\n"+l2.getClass().getName()+"\n"+r1.getClass().getName());
        }
        

        结果

        com.nailsoul.config.RedisConfig$$Lambda$1/1535128843
        com.nailsoul.config.RedisConfig$$Lambda$2/1586270964
        com.nailsoul.config.RedisConfig$1
        
    • 内部类final字段创建

      • 会为普通内部类创建一个类型为外部类的final成员变量并为它赋值

        • lambda内部类只有访问了外部类的成员属性或成员方法时才会创建 并且为私有的
        • 普通内部类不管有没有访问直接创建 为包访问
      • 会为这个内部类访问的所有外部局部变量创建一个同类型的私有final成员变量并为它们赋值

        • 成员变量与成员方法通过外部类对象来访问 只会创建外部类对象final字段

      可以通过反射来检查上面的理论

      static int k1 = 2;
      int v1 = 3;
      public void test(){
          int tt = 1; String xx="few";
          Runnable r = new Runnable() {@Override public void run() {}};
          Runnable lr = ()->{ String s = tt + xx + k1; }; //访问了类变量
          Runnable lr2 = ()->{ String s = tt + xx + v1; }; //访问类成员变量
          printFields(r,"//r"); printFields(lr,"//lr");printFields(lr2,"//lr2");
      }
      private void printFields(Runnable r, String tag) {
          Field[] fields = r.getClass().getDeclaredFields();
          System.out.println(tag);
          for (Field field : fields) {
              System.out.println(field);
          }
      }
      

      结果

      //r
      final com.nailsoul.config.RedisConfig$1.this$0
      //lr
      private final int com.nailsoul.config.RedisConfig$$Lambda$1/1535128843.arg$1
      private final java.lang.String com.nailsoul.config.RedisConfig$$Lambda$1/1535128843.arg$2
      //lr2
      private final com.nailsoul.config.RedisConfig com.nailsoul.config.RedisConfig$$Lambda$2/1586270964.arg$1
      private final int com.nailsoul.config.RedisConfig$$Lambda$2/1586270964.arg$2
      private final java.lang.String com.nailsoul.config.RedisConfig$$Lambda$2/1586270964.arg$3
      
    • 总结

      局部变量和内部类成员final变量不是一个变量

      • 引用对象

        包含不可变对象 如String Integer 等
        局部变量和内部类成员final字段指定的引用地址一样
        即修改了任何一方的变量里面的属性 另一方也会被修改

      • 基本对象

        不包含包装对象 如Integer等
        内部类变量和局部变量都执行同一个字面常量值  不管怎么搞都不会互相影响

    分析

    内部类外面的局部变量不能被重复赋值否则会报错

    通过上面的测试我们已经知道局部类中的变量和局部变量不是同一个变量

    • 如果为局部变量重新赋值会导致内部类和局部变量值不一致 容易造成误导

      java 可能不是应为该原因 只是我猜测 
      因为如果是这原因的话 在内部类被创建前局部变量被修改是安全的
      也有可能是为了方便 只要修改了值 就报错
      不一致推论如下

      • 如果先定义局部变量x为3在创建内部类task
      • 为task定义run方法为打印变量x
      • 这时候task内的x为3
      • 修改局部变量x为5
      • 调用task的run方法  打印出来的会是3
      • 反之 定义局部变量x为3 内部类修改成5 局部变量还是3

      验证

      public void test(){
          Integer x = 3;
          Runnable r = new Runnable() {
              @Override
              public void run() {
                  int i = x;
                  Class<? extends Runnable> aClass = getClass();
                  Field field = ReflectionUtils.findField(aClass, "val$x");
                  field.setAccessible(true);
                  ReflectionUtils.setField(field,this,5);
              }
          };
          r.run();
          Field field = ReflectionUtils.findField(r.getClass(), "val$x");
          field.setAccessible(true);
          Object inner = ReflectionUtils.getField(field, r);
          System.out.println("inner:"+inner+",outer:"+x);
      }
      

      结果 因为内部类里的变量和局部变量不是同一个变量 
      所以修改内部类的变量的值 对局部变量没影响
      如果在内部类里修改x的value那就有影响了 
      因为它们指向的引用是同一个地址

      inner:5,outer:3
      
    在内部类里面不能修改局部类外面定义的变量的值

    经过前面发分析得知内部类里面的字段被final修饰 常规修改肯定会报错 
    通过反射可以修改 但是完全没必要 因为修改了没作用对局部变量没任何影响

    展开全文
  • 匿名内部类

    2021-03-27 09:43:55
    匿名内部类是局部内部类的一种,因为这个类没有名字而得名,叫做匿名内部类 2.匿名内部类具体怎么使用? 当我们有一个接口的时候,因为接口是完全抽象的,我们不能直接new对象,所以我们需要创建一个类去实现接口,...
  • java匿名内部类

    2020-07-18 21:51:23
    局部内部类:类似于局部变量 内部类是可以用访问修饰符来修饰的 使用内部类编写的代码,可读性很差,所以能不用尽量不用。 下面来看看他们怎么写的: public class Day7_18_01 { //该类在类的内部所以被称为内部类...
  • ------- android培训、java培训、...怎么直接访问内部类中的成员? 内部类所在类名.内部类 引用变量名(自定)=new 内部类所在类名().new 内部类(); 例: Tips:内部类定义在局部时, 1,不可以
  • 十、内部类的认识

    2017-11-14 14:55:33
    口诀:局部、成员、匿名、静态 ... 内部类是一个非常有用的特性但又比较难理解使用的特性(鄙人到现在都没有怎么使用内部类,对内部类也只是略知一二)。 第一次见面 内部类我们从外面看是非常容易...
  • 非静态内部类: 成员内部类, 局部内部类、 匿名内部类。 会有对外部类的引用。这样内部类中耗时操作在用户频繁退出重启APP相关Activity时很容易导致内存泄漏。一、匿名内部类:Runnable1、泄漏版newThread...
  • java匿名内部类和lambda(匿名方法)

    千次阅读 2019-07-28 13:05:21
    内部类分4种:成员内部类,静态内部类,局部内部类和匿名内部类。我们今天讨论的就是其中最常见的匿名内部类。从名称可以看出,就是没有名字的类,因为没有名字,所以总归和其它普通类有一些些区别: 类如果没有...
  • 装箱:Int怎么转换成字符串:static如何使用:内部类如何用:成员内部类局部内部类匿名内部类 四种权限修饰符: yes可以访问,no无法访问 权限修饰符 public protected default private 同一个类 yes yes yes...
  • 匿名内部类在外部类里面时,可以在局部位置创建匿名对象调用,也可以在外界main函数里面创建匿名对象并调用; public class Test { public static void main(String[] args) { ABC abc = new ABC();//创建外部类...
  • Java基础学习笔记十 Java基础语法之final、static、匿名对象、内部类 final关键字 继承的出现提高了代码的复用性,并方便开发。但随之也有问题,有些类在描述完之后,不想被继承,或者有些类中的部分方法功能是...
  • final关键字 final的概念 继承的出现提高了代码的复用性,并方便开发。但随之也有问题,有些在描述完之后,不想被继承,或者有些中的部分方法功能是...final是个修饰符,它可以用来修饰的成员,以及局部...
  • final关键字 概述: 继承的出现提高了代码的复用性,并方便开发。但随之也有问题,有些在描述完之后,不想被继承,或者有些中的部分方法功能是...final是个修饰符,它可以用来修饰的成员,以及局部变量 ...
  • final关键字 继承的出现提高了代码的复用性,并方便开发。但随之也有问题,有些在描述完之后,不想被继承,或者有些中的部分方法功能是...final是个修饰符,它可以用来修饰的成员,以及局部变量。 final...
  • 内部类

    2020-11-16 19:05:32
    内部类 1.什么是内部类? 概念:定义在类中的类 2.怎么用的? 就像在类中定义属性一样 ...划分:内部类、局部内部类、匿名内部类 使用场景:当类或该类的对象并不是很常用的时候,就可以定义为内部类
  • 第一章 final关键字 1.1 final的概念 继承的出现提高了代码的复用性,并方便开发。但随之也有问题,有些在描述完之后,...final是个修饰符,它可以用来修饰的成员,以及局部变量。 1.2 final的特点 ...
  • 一、final关键字 继承的出现提高了代码的复用性,并方便开发。但随之也有问题,有些在描述完之后,不想被继承,或者有些中的部分方法功能是...final是个修饰符,它可以用来修饰的成员,以及局部变量。 1...
  • 前言做过Java的朋友都知道,内存管理这一块是完全透明的,new一个的实例时,只知道创建完这个的实例后,会返回这个实例的一个引用,然后拿着这个引用去访问它的成员了(属性、方法),完全不用管JVM内部怎么实现...
  • 上篇介绍了String的构造器,获取内部属性等方法,最后留下了最常用的局部操作函数没有介绍,本篇将接着上篇内容,从这些最常见的函数的操作说起,看看我们日常经常使用的这些方法的内部怎么实现的。第一个函数:...

空空如也

空空如也

1 2 3 4 5 ... 7
收藏数 131
精华内容 52
关键字:

局部内部类怎么使用