• java基础(5)内部类


    1 成员内部类的定义和使用

    public class Outer {
        private String name;
        public class Inner {
            public void innerMethod(){
                System.out.println("heart beat:Pengpengpeng");
            }
        }
    
        public void method(){
            System.out.println("My name is:"+ name);
            Inner inner = new Inner();//要在里面创建内部类的对象
            inner.innerMethod();
        }
    }
    
    
    public class Test {
        public static void main(String[] args) {
            Outer.Inner inner = new Outer().new Inner();//new 外部类.new.内部类
            inner.innerMethod();
    
            System.out.println("===============");
            Outer outer = new Outer();
            outer.method();
        }
    }
    
    
    ///////
    heart beat:Pengpengpeng
    ===============
    My name is:null
    heart beat:Pengpengpeng
    /////访问同名变量
    public
    class Outer { private String name; private int num=10; public class Inner { private int num = 20; public void innerMethod(){ int num = 30; System.out.println("heart beat:Pengpengpeng"); System.out.println("innerMethod-num:"+num); System.out.println("innerClass-num:"+this.num); System.out.println("outterClass-num:"+Outer.this.num);//要使用Outer.this } } public void method(){ System.out.println("My name is:"+ name); Inner inner = new Inner(); inner.innerMethod(); } } public class Test { public static void main(String[] args) { Outer.Inner inner = new Outer().new Inner(); inner.innerMethod(); System.out.println("==============="); Outer outer = new Outer(); outer.method(); } }

    /////

    heart beat:Pengpengpeng
    innerMethod-num:30
    innerClass-num:20
    outterClass-num:10
    ===============
    My name is:null
    heart beat:Pengpengpeng
    innerMethod-num:30
    innerClass-num:20
    outterClass-num:10

    2 局部内部类的定义和使用

    /*
    如果一个类是定义在一个方法内部的,那么这就是一个局部内部类。
    “局部”:只有当前所属的方法才能使用它,出了这个方法外面就不能用了。
    
    定义格式:
    修饰符 class 外部类名称 {
        修饰符 返回值类型 外部类方法名称(参数列表) {
            class 局部内部类名称 {
                // ...
            }
        }
    }
    
    小节一下类的权限修饰符:
    public > protected > (default) > private
    定义一个类的时候,权限修饰符规则:
    1. 外部类:public / (default)
    2. 成员内部类:public / protected / (default) / private
    3. 局部内部类:什么都不能写
     */
    class Outer {
    
        public void methodOuter() {
            class Inner { // 局部内部类
                int num = 10;
                public void methodInner() {
                    System.out.println(num); // 10
                }
            }
    
            Inner inner = new Inner();
            inner.methodInner();
        }
    
    }

    3 局部内部类的final问题

    /*
    局部内部类,如果希望访问所在方法的局部变量,那么这个局部变量必须是【有效final的】。
    
    备注:从Java 8+开始,只要局部变量事实不变,那么final关键字可以省略。
    
    原因:
    1. new出来的对象在堆内存当中。
    2. 局部变量是跟着方法走的,在栈内存当中。
    3. 方法运行结束之后,立刻出栈,局部变量就会立刻消失。
    4. 但是new出来的对象会在堆当中持续存在,直到垃圾回收消失。
     */
    public class MyOuter {
    
        public void methodOuter() {
            int num = 10; // 所在方法的局部变量
    
            class MyInner {
                public void methodInner() {
                    System.out.println(num);
                }
            }
        }
    
    }

    4 匿名内部类

     为什么要用匿名内部类:

    以接口举例,当你使用一个接口时,似乎得做如下几步操作,
    1. 定义子类
    2. 重写接口中的方法
    3. 创建子类对象
    4. 调用重写后的方法

    我们的目的,最终只是为了调用方法,那么能不能简化一下,把以上四步合成一步呢?匿名内部类就是做这样的快
    捷方式。

    //定义一个接口
    public interface MyInterface {
        public abstract void methodAbs();
    }
    
    //定义在main方法中定义匿名内部类,它需要实现接口
    public class anonymousClass {
        public static void main(String[] args) {
    
            /*
            1.等号右边:是匿名内部类,定义并创建该接口的子类对象
            2.等号左边:是多态赋值,接口类型引用指向子类对象
            */
            MyInterface anonymous = new MyInterface() {
                @Override
                public void methodAbs() {
                    System.out.println("I am an anonymous class");
                }
            };
    
            anonymous.methodAbs();
        }
    }        

    注意事项:

    /*
    如果接口的实现类(或者是父类的子类)只需要使用唯一的一次,
    那么这种情况下就可以省略掉该类的定义,而改为使用【匿名内部类】。
    
    匿名内部类的定义格式:
    接口名称 对象名 = new 接口名称() {
        // 覆盖重写所有抽象方法
    };
    
    对格式“new 接口名称() {...}”进行解析:
    1. new代表创建对象的动作
    2. 接口名称就是匿名内部类需要实现哪个接口
    3. {...}这才是匿名内部类的内容
    
    另外还要注意几点问题:
    1. 匿名内部类,在【创建对象】的时候,只能使用唯一一次。
    如果希望多次创建对象,而且类的内容一样的话,那么就需要使用单独定义的实现类了。
    2. 匿名对象,在【调用方法】的时候,只能调用唯一一次。
    如果希望同一个对象,调用多次方法,那么必须给对象起个名字。
    3. 匿名内部类是省略了【实现类/子类名称】,但是匿名对象是省略了【对象名称】
    强调:匿名内部类和匿名对象不是一回事!!!
     */
    public class DemoMain {
    
        public static void main(String[] args) {
    //        MyInterface obj = new MyInterfaceImpl();
    //        obj.method();    
    
    //        MyInterface some = new MyInterface(); // 错误写法!
    
            // 使用匿名内部类,但不是匿名对象,对象名称就叫objA
            MyInterface objA = new MyInterface() {
                @Override
                public void method1() {
                    System.out.println("匿名内部类实现了方法!111-A");
                }
    
                @Override
                public void method2() {
                    System.out.println("匿名内部类实现了方法!222-A");
                }
            };
            objA.method1();
            objA.method2();
            System.out.println("=================");
    
            // 使用了匿名内部类,而且省略了对象名称,也是匿名对象
            new MyInterface() {
                @Override
                public void method1() {
                    System.out.println("匿名内部类实现了方法!111-B");
                }
    
                @Override
                public void method2() {
                    System.out.println("匿名内部类实现了方法!222-B");
                }
            }.method1();
            // 因为匿名对象无法调用第二次方法,所以需要再创建一个匿名内部类的匿名对象
            new MyInterface() {
                @Override
                public void method1() {
                    System.out.println("匿名内部类实现了方法!111-B");
                }
    
                @Override
                public void method2() {
                    System.out.println("匿名内部类实现了方法!222-B");
                }
            }.method2();
        }
    
    }

    综合例子:接口作为成员变量的类型,如何给该成员变量赋值(如何实现该接口):

    //接口作为成员变量的类型
    public class Hero {
    
        private String name; // 英雄的名称
        private Skill skill; // 英雄的技能,Skill是一个接口
    
        public Hero() {
        }
    
        public Hero(String name, Skill skill) {
            this.name = name;
            this.skill = skill;
        }
    
        public void attack() {
            System.out.println("我叫" + name + ",开始施放技能:");
            skill.use(); // 调用接口中的抽象方法
            System.out.println("施放技能完成。");
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Skill getSkill() {
            return skill;
        }
    
        public void setSkill(Skill skill) {
            this.skill = skill;
        }
    }
    
    //接口定义
    public interface Skill {
    
        void use(); // 释放技能的抽象方法
    
    }
    
    
    
    //方法1所需要的接口的实现类
    public class SkillImpl implements Skill {
        @Override
        public void use() {
            System.out.println("Biu~biu~biu~");
        }
    }
    
    
    
    public class DemoGame {
    
        public static void main(String[] args) {
            Hero hero = new Hero();
            hero.setName("艾希"); // 设置英雄的名称
    
            // 设置英雄技能
    //      hero.setSkill(new SkillImpl()); // 方法1:使用单独定义的实现类
    
            // 方法2:还可以改成使用匿名内部类
    //        Skill skill = new Skill() {
    //            @Override
    //            public void use() {
    //                System.out.println("Pia~pia~pia~");
    //            }
    //        };
    //        hero.setSkill(skill);
    
            // 方法3:进一步简化,同时使用匿名内部类和匿名对象
            hero.setSkill(new Skill() {
                @Override
                public void use() {
                    System.out.println("Biu~Pia~Biu~Pia~");
                }
            });
    
            hero.attack();
        }
    
    }
  • 相关阅读:
    使用 Jackson – 将字符串转换为 JsonNode 对象
    Java 8 及其后续版本的新遍历 forEach
    IntelliJ IDEA 快速插入 for 循环
    在 Discourse 中如何使用输入对话框
    如何修改 Discourse 的域名
    Confluence 数据中心版本接近生命周期了
    IntelliJ IDEA 中如何将 POM 中的版本号快速提出为属性
    RedHat 8 如何检查端口是否联通
    二、RabbitMq安装
    一、RabbitMQ 的概念
  • 原文地址:https://www.cnblogs.com/JohnTeslaaa/p/10294287.html
Copyright © 2020-2023  润新知