目录

        1.前言

        匿名内部类在我们java程序员的日常工作中经常要用到,但是很多时候也只是照本宣科地用,虽然也在用,但往往忽略了以下几点:为什么能这么用?匿名内部类的语法是怎样的?有哪些限制?因此,最近,我在完成了手头的开发任务后,查阅了一下java官方文档,将匿名内部类的使用进行了一下总结,案例也摘自官方文档。感兴趣的可以查阅官方文档(https://docs.oracle.com/javase/tutorial/java/javaoo/anonymousclasses.html)。

        2.匿名内部类

        匿名内部类可以使你的代码更加简洁,你可以在定义一个类的同时对其进行实例化。它与局部类很相似,不同的是它没有类名,如果某个局部类你只需要用一次,那么你就可以使用匿名内部类(anonymous classes enable you to make your code more concise. they enable you to declare and instantiate a class at the same time. they are like local classes except that they do not have a name. use them if you need to use a local class only once.)

        本节包括以下几个方面:

        • 定义匿名内部类
        • 匿名内部类的语法
        • 访问作用域的局部变量、定义和访问匿名内部类成员
        • 匿名内部类实例

        2.1 定义匿名内部类

        首先看下官方文档中给的例子:

        public class helloworldanonymousclasses {
        
            /**
             * 包含两个方法的helloworld接口
             */
            interface helloworld {
                public void greet();
                public void greetsomeone(string someone);
            }
        
            public void sayhello() {
        
                // 1、局部类englishgreeting实现了helloworld接口
                class englishgreeting implements helloworld {
                    string name = "world";
                    public void greet() {
                        greetsomeone("world");
                    }
                    public void greetsomeone(string someone) {
                        name = someone;
                        system.out.println("hello " + name);
                    }
                }
        
                helloworld englishgreeting = new englishgreeting();
        
                // 2、匿名类实现helloworld接口
                helloworld frenchgreeting = new helloworld() {
                    string name = "tout le monde";
                    public void greet() {
                        greetsomeone("tout le monde");
                    }
                    public void greetsomeone(string someone) {
                        name = someone;
                        system.out.println("salut " + name);
                    }
                };
        
                // 3、匿名类实现helloworld接口
                helloworld spanishgreeting = new helloworld() {
                    string name = "mundo";
                    public void greet() {
                        greetsomeone("mundo");
                    }
                    public void greetsomeone(string someone) {
                        name = someone;
                        system.out.println("hola, " + name);
                    }
                };
        
                englishgreeting.greet();
                frenchgreeting.greetsomeone("fred");
                spanishgreeting.greet();
            }
        
            public static void main(string... args) {
                helloworldanonymousclasses myapp = new helloworldanonymousclasses();
                myapp.sayhello();
            }
        }

        运行结果为:

        1 hello world
        2 salut fred
        3 hola, mundo

        该例中用局部类来初始化变量englishgreeting,用匿类来初始化变量frenchgreeting和spanishgreeting,两种实现之间有明显的区别:

        1)局部类englishgreetin继承helloworld接口,有自己的类名,定义完成之后需要再用new关键字实例化才可以使用;

        2)frenchgreeting、spanishgreeting在定义的时候就实例化了,定义完了就可以直接使用;

        3)匿名类是一个表达式,因此在定义的最后用分号”;”结束。

        2.2 匿名内部类的语法

        如上文所述,匿名类是一个表达式,匿名类的语法就类似于调用一个类的构建函数(new  helloworld()),除些之外,还包含了一个代码块,在代码块中完成类的定义,见以下两个实例:

        案例一,实现接口的匿名类:

        helloworld frenchgreeting = new helloworld() {
           string name = "tout le monde";
           public void greet() {
                 greetsomeone("tout le monde");
           }
           public void greetsomeone(string someone) {
                name = someone;
                system.out.println("salut " + name);
           }
         };
        

        案例二,匿名子类(继承父类):

        public class animaltest {
        
            private final string animal = "动物";
        
            public void accesstest() {
                system.out.println("匿名内部类访问其外部类方法");
            }
        
            class animal {
                private string name;
        
                public animal(string name) {
                    this.name = name;
                }
        
                public void printanimalname() {
                    system.out.println(bird.name);
                }
            }
        
            // 鸟类,匿名子类,继承自animal类,可以覆写父类方法
            animal bird = new animal("布谷鸟") {
        
                @override
                public void printanimalname() {
                    accesstest();           // 访问外部类成员
                    system.out.println(animal);  // 访问外部类final修饰的变量
                    super.printanimalname();
                }
            };
        
            public void print() {
                bird.printanimalname();
            }
        
            public static void main(string[] args) {
        
                animaltest animaltest = new animaltest();
                animaltest.print();
            }
        }

        运行结果:

        运行结果:
        匿名内部类访问其外部类方法
        动物
        布谷鸟

        从以上两个实例中可知,匿名类表达式包含以下内部分:

        • 操作符:new;
        • 一个要实现的接口或要继承的类,案例一中的匿名类实现了hellowworld接口,案例二中的匿名内部类继承了animal父类;
        • 一对括号,如果是匿名子类,与实例化普通类的语法类似,如果有构造参数,要带上构造参数;如果是实现一个接口,只需要一对空括号即可;
        • 一段被”{}”括起来类声明主体;
        • 末尾的”;”号(因为匿名类的声明是一个表达式,是语句的一部分,因此要以分号结尾)。

        3.访问作用域内的局部变量、定义和访问匿名内部类成员

        匿名内部类与局部类对作用域内的变量拥有相同的的访问权限。

        (1)、匿名内部类可以访问外部内的所有成员;

        (2)、匿名内部类不能访问外部类未加final修饰的变量(注意:jdk1.8即使没有用final修饰也可以访问);

        (3)、属性屏蔽,与内嵌类相同,匿名内部类定义的类型(如变量)会屏蔽其作用域范围内的其他同名类型(变量):

        案例一,内嵌类的属性屏蔽:

        public class shadowtest {
        
            public int x = 0;
        
            class firstlevel {
        
                public int x = 1;
        
                void methodinfirstlevel(int x) {
                    system.out.println("x = " + x);
                    system.out.println("this.x = " + this.x);
                    system.out.println("shadowtest.this.x = " + shadowtest.this.x);
                }
            }
        
            public static void main(string... args) {
                shadowtest st = new shadowtest();
                shadowtest.firstlevel fl = st.new firstlevel();
                fl.methodinfirstlevel(23);
            }
        }

        输出结果为:

        x = 23
        this.x = 1
        shadowtest.this.x = 0

        这个实例中有三个变量x:1、shadowtest类的成员变量;2、内部类firstlevel的成员变量;3、内部类方法methodinfirstlevel的参数。

        methodinfirstlevel的参数x屏蔽了内部类firstlevel的成员变量,因此,在该方法内部使用x时实际上是使用的是参数x,可以使用this关键字来指定引用是成员变量x:

        system.out.println("this.x = " + this.x); 

        利用类名来引用其成员变量拥有最高的优先级,不会被其他同名变量屏蔽,如:

        system.out.println("shadowtest.this.x = " + shadowtest.this.x); 

        案例二,匿名内部类的属性屏蔽:

         public class shadowtest {
            public int x = 0;
        
            interface firstlevel {
             void methodinfirstlevel(int x);
            }
        
            firstlevel firstlevel =  new firstlevel() {
        
                public int x = 1;
        
                @override
                public void methodinfirstlevel(int x) {
                    system.out.println("x = " + x);
                    system.out.println("this.x = " + this.x);
                    system.out.println("shadowtest.this.x = " + shadowtest.this.x);
                }
            };
        
            public static void main(string... args) {
                shadowtest st = new shadowtest();
                shadowtest.firstlevel fl = st.firstlevel;
                fl.methodinfirstlevel(23);
            }
        }

        输出结果为:

        x = 23
        this.x = 1
        shadowtest.this.x = 0

        (4)、匿名内部类中不能定义静态属性、方法;  

        public class shadowtest {
            public int x = 0;
        
            interface firstlevel {
             void methodinfirstlevel(int x);
            }
        
            firstlevel firstlevel =  new firstlevel() {
        
                public int x = 1;
        
                public static string str = "hello world";   // 编译报错
        
                public static void aa() {        // 编译报错
                }
        
                public static final string finalstr = "hello world";  // 正常
        
                public void extramethod() {  // 正常
                    // do something
                }
            };
        }
        
        

        (5)、匿名内部类可以有常量属性(final修饰的属性);

        (6)、匿名内部内中可以定义属性,如上面代码中的代码:private int x = 1;

        (7)、匿名内部内中可以可以有额外的方法(父接口、类中没有的方法);

        (8)、匿名内部内中可以定义内部类;

        (9)、匿名内部内中可以对其他类进行实例化。

        4.匿名内部类实例

        官方提供的两个实例供大家参考:

        实例一:

        import javafx.event.actionevent;
        import javafx.event.eventhandler;
        import javafx.scene.scene;
        import javafx.scene.control.button;
        import javafx.scene.layout.stackpane;
        import javafx.stage.stage;
        
        public class helloworld extends application {
            public static void main(string[] args) {
                launch(args);
            }
        
            @override
            public void start(stage primarystage) {
                primarystage.settitle("hello world!");
                button btn = new button();
                btn.settext("say 'hello world'");
                btn.setonaction(new eventhandler<actionevent>() {
        
                    @override
                    public void handle(actionevent event) {
                        system.out.println("hello world!");
                    }
                });
        
                stackpane root = new stackpane();
                root.getchildren().add(btn);
                primarystage.setscene(new scene(root, 300, 250));
                primarystage.show();
            }
        }
        
        

        实例二:

        import javafx.application.application;
        import javafx.event.actionevent;
        import javafx.event.eventhandler;
        import javafx.geometry.insets;
        import javafx.scene.group;
        import javafx.scene.scene;
        import javafx.scene.control.*;
        import javafx.scene.layout.gridpane;
        import javafx.scene.layout.hbox;
        import javafx.stage.stage;
        
        public class customtextfieldsample extends application {
        
            final static label label = new label();
        
            @override
            public void start(stage stage) {
                group root = new group();
                scene scene = new scene(root, 300, 150);
                stage.setscene(scene);
                stage.settitle("text field sample");
        
                gridpane grid = new gridpane();
                grid.setpadding(new insets(10, 10, 10, 10));
                grid.setvgap(5);
                grid.sethgap(5);
        
                scene.setroot(grid);
                final label dollar = new label("$");
                gridpane.setconstraints(dollar, 0, 0);
                grid.getchildren().add(dollar);
        
                final textfield sum = new textfield() {
                    @override
                    public void replacetext(int start, int end, string text) {
                        if (!text.matches("[a-z, a-z]")) {
                            super.replacetext(start, end, text);
                        }
                        label.settext("enter a numeric value");
                    }
        
                    @override
                    public void replaceselection(string text) {
                        if (!text.matches("[a-z, a-z]")) {
                            super.replaceselection(text);
                        }
                    }
                };
        
                sum.setprompttext("enter the total");
                sum.setprefcolumncount(10);
                gridpane.setconstraints(sum, 1, 0);
                grid.getchildren().add(sum);
        
                button submit = new button("submit");
                gridpane.setconstraints(submit, 2, 0);
                grid.getchildren().add(submit);
        
                submit.setonaction(new eventhandler<actionevent>() {
                    @override
                    public void handle(actionevent e) {
                        label.settext(null);
                    }
                });
        
                gridpane.setconstraints(label, 0, 1);
                gridpane.setcolumnspan(label, 3);
                grid.getchildren().add(label);
        
                scene.setroot(grid);
                stage.show();
            }
        
            public static void main(string[] args) {
                launch(args);
            }
        }

        写在最后:

        这篇文章是我在阅读官方文档的同时加以自己的理解整理出来的,可能受英文原版的影响,有些地方表达得不准确或是不清楚还希望读者能够指正。另外,体会到了那些翻译英文技术书的人确实不容易,英文的文章看上去意思都很清楚,但是想要再用中文表述出来却不那么容易。

        到此这篇关于java匿名内部类(anonymous classes)的具体使用的文章就介绍到这了,更多相关java 匿名内部类内容请搜索www.887551.com以前的文章或继续浏览下面的相关文章希望大家以后多多支持www.887551.com!