zoukankan      html  css  js  c++  java
  • Java Object-Oriented:day11 【 Inner Classes 】

    一、内部类的概念与分类

    1、什么是内部类

    如果一个事物的内部包含另一个事物,那么这就是一个类内部包含另一个类。例如:身体和心脏的关系。又如:汽车和发动机的关系。

    2、分类

    1. 成员内部类
    2. 局部内部类(包含匿名内部类)

    二、成员内部类定义使用

    1、成员内部类的定义

    成员内部类:定义在类中方法外的类

    1、定义格式:

    修饰符 class 外部类名称 {
        修饰符 class 内部类名称 {
            // ...
        }
        // ...
    }
    

    2、定义类

    package day11.demo03;
    
    public class Body { // 外部类
    
        public class Heart { // 成员内部类
    
            // 内部类的方法
            public void beat() {
                System.out.println("心脏跳动:蹦蹦蹦!");
                System.out.println("我叫:" + name); // 正确写法!
            }
    
        }
    
        // 外部类的成员变量
        private String name;
    
        // 外部类的方法
        public void methodBody() {
            System.out.println("外部类的方法");
            new Heart().beat();
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    }

    2、成员内部类的使用

    间接方式:在外部类的方法当中,使用内部类;然后main只是调用外部类的方法。

    定义测试类

    package day11.demo03;
    /*
    如果一个事物的内部包含另一个事物,那么这就是一个类内部包含另一个类。
    例如:身体和心脏的关系。又如:汽车和发动机的关系。
    
    分类:
    1. 成员内部类
    2. 局部内部类(包含匿名内部类)
    
    成员内部类的定义格式:
    修饰符 class 外部类名称 {
        修饰符 class 内部类名称 {
            // ...
        }
        // ...
    }
    
    注意:内用外,随意访问;外用内,需要内部类对象。
    
    ==========================
    如何使用成员内部类?有两种方式:
    1. 间接方式:在外部类的方法当中,使用内部类;然后main只是调用外部类的方法。
     */
    public class Demo01InnerClass {
    
        public static void main(String[] args) {
            Body body = new Body(); // 外部类的对象
            // 通过外部类的对象,调用外部类的方法,里面间接在使用内部类Heart
            body.methodBody();
            System.out.println("=====================");
        }
    
    }

    定义内部类

    package day11.demo03;
    
    public class Body { // 外部类
    
        public class Heart { // 成员内部类
    
            // 内部类的方法
            public void beat() {
                System.out.println("心脏跳动:蹦蹦蹦!");
                System.out.println("我叫:" + name); // 正确写法!
            }
    
        }
    
        // 外部类的成员变量
        private String name;
    
        // 外部类的方法
        public void methodBody() {
            System.out.println("外部类的方法");
            new Heart().beat();
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    }

    运行结果

    外部类的方法
    心脏跳动:蹦蹦蹦!
    我叫:null
    =====================
    

    直接方式,公式:类名称 对象名 = new 类名称();

    定义测试类
    package day11.demo03;
    /*
    如果一个事物的内部包含另一个事物,那么这就是一个类内部包含另一个类。
    例如:身体和心脏的关系。又如:汽车和发动机的关系。
    
    分类:
    1. 成员内部类
    2. 局部内部类(包含匿名内部类)
    
    成员内部类的定义格式:
    修饰符 class 外部类名称 {
        修饰符 class 内部类名称 {
            // ...
        }
        // ...
    }
    
    注意:内用外,随意访问;外用内,需要内部类对象。
    
    ==========================
    如何使用成员内部类?有两种方式:
    1. 间接方式:在外部类的方法当中,使用内部类;然后main只是调用外部类的方法。
    2. 直接方式,公式:
    类名称 对象名 = new 类名称();
    【外部类名称.内部类名称 对象名 = new 外部类名称().new 内部类名称();】
     */
    public class Demo01InnerClass {
    
        public static void main(String[] args) {
            Body body = new Body(); // 外部类的对象
            // 通过外部类的对象,调用外部类的方法,里面间接在使用内部类Heart
            body.methodBody();
            System.out.println("=====================");
    
            // 按照公式写:
            Body.Heart heart = new Body().new Heart();
            heart.beat();
        }
    
    }
    运行结果
    心脏跳动:蹦蹦蹦!
    我叫:null

    3、内部类的同名变量访问

    定义外部类

    package day11.demo03;
    
    // 如果出现了重名现象,那么格式是:外部类名称.this.外部类成员变量名
    public class Outer {
    
        int num = 10; // 外部类的成员变量
    
        public class Inner /*extends Object*/ {
    
            int num = 20; // 内部类的成员变量
    
            public void methodInner() {
                int num = 30; // 内部类方法的局部变量
                System.out.println(num); // 局部变量,就近原则
                System.out.println(this.num); // 内部类的成员变量
                System.out.println(Outer.this.num); // 外部类的成员变量
            }
    
        }
    
    }

    定义测试类

    package day11.demo03;
    
    public class Demo02InnerClass {
    
        public static void main(String[] args) {
            // 外部类名称.内部类名称 对象名 = new 外部类名称().new 内部类名称();
            Outer.Inner obj = new Outer().new Inner();
            obj.methodInner();
        }
    
    }

    运行结果

    30
    20
    10

    三、局部内部类定义

    如果一个类是定义在一个方法内部的,那么这就是一个局部内部类。

    “局部”:只有当前所属的方法才能使用它,出了这个方法外面就不能用了。

    1、局部内部类定义

    1、定义局部类

    package day11.demo04;
    
    /*
    如果一个类是定义在一个方法内部的,那么这就是一个局部内部类。
    “局部”:只有当前所属的方法才能使用它,出了这个方法外面就不能用了。
    
    定义格式:
    修饰符 class 外部类名称 {
        修饰符 返回值类型 外部类方法名称(参数列表) {
            class 局部内部类名称 {
                // ...
            }
        }
    }
    
     */
    class Outer {
    
        public void methodOuter() {
            class Inner { // 局部内部类
                int num = 10;
                public void methodInner() {
                    System.out.println(num); // 10
                }
            }
    
            Inner inner = new Inner();
            inner.methodInner();
        }
    
    }

    2、定义类测试类

    package day11.demo04;
    
    public class DemoMain {
        public static void main(String[] args) {
            Outer obj = new Outer();
            obj.methodOuter();
        }
    }

    3、运行结果

    10
    

    2、小结一下类的权限修饰符

    public > protected > (default) > private
    定义一个类的时候,权限修饰符规则:
    1. 外部类:public / (default)
    2. 成员内部类:public / protected / (default) / private
    3. 局部内部类:什么都不能写

        

    3、局部内部类的final问题

    局部内部类,如果希望访问所在方法的局部变量,那么这个局部变量必须是【有效final的】。

    备注:从Java 8+开始,只要局部变量事实不变,那么final关键字可以省略。

    package day11.demo04;
    
    /*
    局部内部类,如果希望访问所在方法的局部变量,那么这个局部变量必须是【有效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);
                }
            }
        }
    
    }
    

    原因:

    1. new出来的对象在堆内存当中。
    2. 局部变量是跟着方法走的,在栈内存当中。
    3. 方法运行结束之后,立刻出栈,局部变量就会立刻消失。
    4. 但是new出来的对象会在堆当中持续存在,直到垃圾回收消失

    四、匿名内部类

    1、普通方法实现

    1、定义接口

    package day11.demo05;
    
    public interface MyInterface {
        void method(); // 抽象方法
    }

    2、定义实现类

    package day11.demo05;
    
    public class MyInterfaceImpl implements MyInterface {
        @Override
        public void method() {
            System.out.println("实现类覆盖重写了方法");
        }
    }

    3、定义测试类

    package day11.demo05;
    
    public class DemoMain {
    
        public static void main(String[] args) {
            MyInterface impl = new MyInterfaceImpl();
            impl.method();
        }
    }

    4、运行结果

    实现类覆盖重写了方法

    匿名内部类 :是内部类的简化写法。它的本质是一个 带具体实现的 父类或者父接口的 匿名的 子类对象

    开发中,最常用到的内部类就是匿名内部类了。以接口举例,当你使用一个接口时,似乎得做如下几步操作

    1. 定义子类
    2. 重写接口中的方法
    3. 创建子类对象
    4. 调用重写后的方法

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

    前提

    匿名内部类必须继承一个父类或者实现一个父接口

    2、用以匿名内部类实现

    如果接口的实现类(或者是父类的子类)只需要使用唯一的一次,
    那么这种情况下就可以省略掉该类的定义,而改为使用【匿名内部类】。

    1、定义格式

    接口名称 对象名 = new 接口名称() {
        // 覆盖重写所有抽象方法
    };

    错误写法

    1、定义测试类

    package day11.demo05;
    
    /*
    如果接口的实现类(或者是父类的子类)只需要使用唯一的一次,
    那么这种情况下就可以省略掉该类的定义,而改为使用【匿名内部类】。
    
    匿名内部类的定义格式:
    接口名称 对象名 = new 接口名称() {
        // 覆盖重写所有抽象方法
    };
    
     */
    
    public class DemoMain {
    
        public static void main(String[] args) {
    
            // 使用匿名内部类,但不是匿名对象,对象名称就叫objA
            MyInterface obj = new MyInterface() {
                @Override
                public void method() {
                    System.out.println("匿名内部类实现了方法");
                }
            };
            obj.method();
        }
    }

    2、运行结果

    匿名内部类实现了方法

    两种方式比较

       

    五、匿名内部类的注意事项

    1、匿名内部类,在【创建对象】的时候,只能使用唯一一次。

    如果希望多次创建对象,而且类的内容一样的话,那么就需要使用单独定义的实现类了。

    1、测试类

    package day11.demo05;
    /*
    
    对格式“new 接口名称() {...}”进行解析:
    1. new代表创建对象的动作
    2. 接口名称就是匿名内部类需要实现哪个接口
    3. {...}这才是匿名内部类的内容
    
    另外还要注意几点问题:
    1. 匿名内部类,在【创建对象】的时候,只能使用唯一一次。
    如果希望多次创建对象,而且类的内容一样的话,那么就需要使用单独定义的实现类了。
    
     */
    public class DemoMain {
    
        public static void main(String[] args) {
    
            // 使用匿名内部类,但不是匿名对象,对象名称就叫objA
            MyInterface objA = new MyInterface() {
                @Override
                public void method() {
                    System.out.println("匿名内部类实现了方法!111-A");
                }
    
            };
            objA.method();
    
            MyInterface objB = new MyInterface() {
                @Override
                public void method() {
                    System.out.println("匿名内部类实现了方法!111-B");
                }
    
            };
            objB.method();
        }
    
    }

    2、接口

    package day11.demo05;
    
    public interface MyInterface {
        void method(); // 抽象方法
    }

    3、运行接口

    匿名内部类实现了方法!111-A
    匿名内部类实现了方法!111-B

    如果有2个方法

    1、测试类

    package day11.demo05;
    /*
    
    对格式“new 接口名称() {...}”进行解析:
    1. new代表创建对象的动作
    2. 接口名称就是匿名内部类需要实现哪个接口
    3. {...}这才是匿名内部类的内容
    
    另外还要注意几点问题:
    1. 匿名内部类,在【创建对象】的时候,只能使用唯一一次。
    如果希望多次创建对象,而且类的内容一样的话,那么就需要使用单独定义的实现类了。
    
     */
    public class DemoMain {
    
        public static void main(String[] args) {
    
            // 使用匿名内部类,但不是匿名对象,对象名称就叫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();
    
    
            MyInterface objB = new MyInterface() {
                @Override
                public void method1() {
                    System.out.println("匿名内部类实现了方法!111-B");
                }
    
                @Override
                public void method2() {
                    System.out.println("匿名内部类实现了方法!222-B");
                }
    
            };
            objB.method1();
            objB.method2();
        }
    
    }

    2、接口

    package day11.demo05;
    
    public interface MyInterface {
        void method1(); // 抽象方法
        void method2(); // 抽象方法
    }

    3、接口实现

    package day11.demo05;
    
    public class MyInterfaceImpl implements MyInterface {
        @Override
        public void method1() {
            System.out.println("实现类覆盖重写了方法!111");
        }
    
        @Override
        public void method2() {
            System.out.println("实现类覆盖重写了方法!222");
        }
    }

    4、运行结果

    匿名内部类实现了方法!111-A
    匿名内部类实现了方法!222-A
    匿名内部类实现了方法!111-B
    匿名内部类实现了方法!222-B 

    2、匿名对象,在【调用方法】的时候,只能调用唯一一次。

    1、测试类

    package day11.demo05;
    /*
    
    对格式“new 接口名称() {...}”进行解析:
    1. new代表创建对象的动作
    2. 接口名称就是匿名内部类需要实现哪个接口
    3. {...}这才是匿名内部类的内容
    
    另外还要注意几点问题:
    1. 匿名内部类,在【创建对象】的时候,只能使用唯一一次。
    如果希望多次创建对象,而且类的内容一样的话,那么就需要使用单独定义的实现类了。
    
     */
    public class DemoMain {
    
        public static void main(String[] args) {
    
            // 使用匿名内部类,但不是匿名对象,对象名称就叫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();
        }
    
    }

    2、接口

    package day11.demo05;
    
    public interface MyInterface {
        void method1(); // 抽象方法
        void method2(); // 抽象方法
    }

    3、接口实现

    package day11.demo05;
    
    public class MyInterfaceImpl implements MyInterface {
        @Override
        public void method1() {
            System.out.println("实现类覆盖重写了方法!111");
        }
    
        @Override
        public void method2() {
            System.out.println("实现类覆盖重写了方法!222");
        }
    }

    4、运行结果

    匿名内部类实现了方法!111-A
    匿名内部类实现了方法!222-A
    =================
    匿名内部类实现了方法!111-B
    

    因为匿名对象无法调用第二次方法,所以需要再创建一个匿名内部类的匿名对象 

     

     测试类

    package day11.demo05;
    /*
    
    对格式“new 接口名称() {...}”进行解析:
    1. new代表创建对象的动作
    2. 接口名称就是匿名内部类需要实现哪个接口
    3. {...}这才是匿名内部类的内容
    
    另外还要注意几点问题:
    1. 匿名内部类,在【创建对象】的时候,只能使用唯一一次。
    如果希望多次创建对象,而且类的内容一样的话,那么就需要使用单独定义的实现类了。
    
     */
    public class DemoMain {
    
        public static void main(String[] args) {
    
            // 使用匿名内部类,但不是匿名对象,对象名称就叫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();
        }
    
    }
    

    运行结果

    匿名内部类实现了方法!111-A
    匿名内部类实现了方法!222-A
    =================
    匿名内部类实现了方法!111-B
    匿名内部类实现了方法!222-B

    3. 匿名内部类是省略了【实现类/子类名称】,但是匿名对象是省略了【对象名称】

    强调:匿名内部类和匿名对象不是一回事!!!

    六、类作为成员变量类型

    1、String到底是个什么

    /*
    ........
     * @author  Lee Boynton
     * @author  Arthur van Hoff
     * @author  Martin Buchholz
     * @author  Ulf Zibis
     * @see     java.lang.Object#toString()
     * @see     java.lang.StringBuffer
     * @see     java.lang.StringBuilder
     * @see     java.nio.charset.Charset
     * @since   1.0
     * @jls     15.18.1 String Concatenation Operator +
     */
    
    public final class String
        implements java.io.Serializable, Comparable<String>, CharSequence,
                   Constable, ConstantDesc {
    

     说明 类可以作为成员变量类型

    2、自己定义一个类作为成员变量

    1、定义Hero类

    package day11.demo06;
    
    // 游戏当中的英雄角色类
    public class Hero {
    
        private String name; // 英雄的名字
        private int age; // 英雄的年龄
        private Weapon weapon; // 英雄的武器
    
        public Hero() {
        }
    
        public Hero(String name, int age, Weapon weapon) {
            this.name = name;
            this.age = age;
            this.weapon = weapon;
        }
    
        public void attack() {
            System.out.println("年龄为" + age + "的" + name + "用" + weapon.getCode() + "攻击敌方。");
        }
    
        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;
        }
    
        public Weapon getWeapon() {
            return weapon;
        }
    
        public void setWeapon(Weapon weapon) {
            this.weapon = weapon;
        }
    }

    2、定义Weapon

    package day11.demo06;
    
    public class Weapon {
    
        private String code; // 武器的代号
    
        public Weapon() {
        }
    
        public Weapon(String code) {
            this.code = code;
        }
    
        public String getCode() {
            return code;
        }
    
        public void setCode(String code) {
            this.code = code;
        }
    }

    3、定义测试类

    package day11.demo06;
    
    public class DemoMain {
    
        public static void main(String[] args) {
            // 创建一个英雄角色
            Hero hero = new Hero();
            // 为英雄起一个名字,并且设置年龄
            hero.setName("盖伦");
            hero.setAge(20);
    
            // 创建一个武器对象
            Weapon weapon = new Weapon("AK-47");
            // 为英雄配备武器
            hero.setWeapon(weapon);
    
            // 年龄为20的盖伦用多兰剑攻击敌方。
            hero.attack();
        }
    
    }
    

    4、运行结果 

    年龄为20的盖伦用AK-47攻击敌方。

    七、接口作为成员变量类型

    DemoGame

    package day11.demo07;
    
    public class DemoGame {
    
        public static void main(String[] args) {
            Hero hero = new Hero();
            hero.setName("艾希"); // 设置英雄的名称
    
            // 设置英雄技能
    //        hero.setSkill(new SkillImpl()); // 使用单独定义的实现类
    
            // 还可以改成使用匿名内部类
    //        Skill skill = new Skill() {
    //            @Override
    //            public void use() {
    //                System.out.println("Pia~pia~pia~");
    //            }
    //        };
    //        hero.setSkill(skill);
    
            // 进一步简化,同时使用匿名内部类和匿名对象
            hero.setSkill(new Skill() {
                @Override
                public void use() {
                    System.out.println("Biu~Pia~Biu~Pia~");
                }
            });
    
            hero.attack();
        }
    
    }

    Hero

    package day11.demo07;
    
    public class Hero {
    
        private String name; // 英雄的名称
        private 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;
        }
    }
    

    Skill

    package day11.demo07;
    
    public interface Skill {
    
        void use(); // 释放技能的抽象方法
    
    }

    SkillImpl

    package day11.demo07;
    
    public class SkillImpl implements Skill {
        @Override
        public void use() {
            System.out.println("Biu~biu~biu~");
        }
    }

    运行结果

    我叫艾希,开始施放技能:
    Biu~Pia~Biu~Pia~
    施放技能完成。

    八、接口作为方法的参数和或

    DemoInterface

    package day11.demo07;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /*
    java.util.List正是ArrayList所实现的接口。
     */
    public class DemoInterface {
    
        public static void main(String[] args) {
            // 左边是接口名称,右边是实现类名称,这就是多态写法
            List<String> list = new ArrayList<>();
    
            List<String> result = addNames(list);
            for (int i = 0; i < result.size(); i++) {
                System.out.println(result.get(i));
            }
        }
    
        public static List<String> addNames(List<String> list) {
            list.add("迪丽热巴");
            list.add("古力娜扎");
            list.add("玛尔扎哈");
            list.add("沙扬娜拉");
            return list;
        }
    
    }

    运行结果

    迪丽热巴
    古力娜扎
    玛尔扎哈
    沙扬娜拉
  • 相关阅读:
    UVA 11925 Generating Permutations 生成排列 (序列)
    UVA 1611 Crane 起重机 (子问题)
    UVA 11572 Unique snowflakes (滑窗)
    UVA 177 PaperFolding 折纸痕 (分形,递归)
    UVA 11491 Erasing and Winning 奖品的价值 (贪心)
    UVA1610 PartyGame 聚会游戏(细节题)
    UVA 1149 Bin Packing 装箱(贪心)
    topcpder SRM 664 div2 A,B,C BearCheats , BearPlays equalPiles , BearSorts (映射)
    UVA 1442 Cave 洞穴 (贪心+扫描)
    UVA 1609 Foul Play 不公平竞赛 (构(luan)造(gao)+递归)
  • 原文地址:https://www.cnblogs.com/luoahong/p/12844929.html
Copyright © 2011-2022 走看看