美文网首页
Java-内部类

Java-内部类

作者: Cool_Pomelo | 来源:发表于2020-01-15 17:54 被阅读0次

    Java-内部类

    有时可以把一个类放在另一个类的内部定义,这个定义在其他类内部的类就是内部类,包含内部类的类被称为外部类

    内部类的作用:

    • 内部类提供了更好的封装,可以把内部类隐藏在外部类之内
    • 内部类成员可以直接访问外部类的私有数据,但外部类不能访问内部类的实现细节,比如内部类的成员变量

    • 匿名内部类可以用于创建只需要使用一次的类

    • 内部类比外部类可以多使用三个修饰符:private,protected,static
      外部类不可以使用这三个修饰符

    • 非静态内部类不可以拥有静态成员

    非静态内部类

    定义内部类很简单,把一个类放在另一个类内部定义就可以了,也就是可以放在任何位置(包括方法,方法里面定义的内部类叫做局部内部类)

    成员内部类是与成员变量、方法、构造函数、初始化块相似的类成员,局部内部类和匿名内部类则不是类成员

    成员内部类分两种:

    • 静态内部类:使用static修饰的成员内部类

    • 非静态内部类:没有使用static修饰的成员内部类

    例子:

    public class Cow {
    
        private double weight;
        // 外部类的两个重载的构造器
        public Cow(){}
        public Cow(double weight)
        {
            this.weight = weight;
        }
        // 定义一个非静态内部类
        private class CowLeg
        {
            // 非静态内部类的两个实例变量
            private double length;
            private String color;
            // 非静态内部类的两个重载的构造器
            public CowLeg(){}
            public CowLeg(double length , String color)
            {
                this.length = length;
                this.color = color;
            }
            // 下面省略length、color的setter和getter方法
            public void setLength(double length)
            {
                this.length = length;
            }
            public double getLength()
            {
                return this.length;
            }
            public void setColor(String color)
            {
                this.color = color;
            }
            public String getColor()
            {
                return this.color;
            }
    
    
            // 非静态内部类的实例方法
            public void info()
            {
                System.out.println("当前牛腿颜色是:"
                        + color + ", 高:" + length);
                // 直接访问外部类的private修饰的成员变量
                System.out.println("本牛腿所在奶牛重:" + weight);   //①
            }
    
    
        }
        public void test()
        {
            CowLeg cl = new CowLeg(1.12 , "黑白相间");
            cl.info();
        }
        public static void main(String[] args)
        {
            Cow cow = new Cow(378.9);
            cow.test();
        }
    }
    
    

    当在非静态内部类的方法中访问某个变量时,系统优先在该方法内查找是否存在该名字的局部变量,如果存在就使用;如果不存在,就到放在所在的内部类中看有没有该成员变量,有的话就使用;如果不存在,就到内部类所在的外部类中查找,还是不存在的话编译器报错

    如果外部类成员变量、内部类成员变量、内部类方法中局部变量同名,则可以用this、外部类类名.this作为限定来区分

    public class DiscernVariable {
    
        private String prop = "外部类的实例变量";
        private class InClass
        {
            private String prop = "内部类的实例变量";
            public void info()
            {
                String prop = "局部变量";
                // 通过 外部类类名.this.varName 访问外部类实例变量
                System.out.println("外部类的实例变量值:"
                        + DiscernVariable.this.prop);
                // 通过 this.varName 访问内部类实例的变量
                System.out.println("内部类的实例变量值:" + this.prop);
                // 直接访问局部变量
                System.out.println("局部变量的值:" + prop);
            }
        }
        public void test()
        {
            InClass in = new InClass();
            in.info();
        }
        public static void main(String[] args)
        {
            new DiscernVariable().test();
        }
    }
    
    
    

    非静态内部类的成员可以访问外部类的private成员,但反过来就不可以。非静态内部类的成员只在非静态内部类范围是可知的,并不能被外部类直接使用

    如果外部类想要访问非静态内部类的成员,则必须显式的创建非静态内部类对象来调用访问其实例成员

    public class Outer {
    
    
        private int outProp = 9;
        class Inner
        {
            private int inProp = 5;
            public void acessOuterProp()
            {
                // 非静态内部类可以直接访问外部类的private成员变量
                System.out.println("外部类的outProp值:"
                        + outProp);
            }
        }
        public void accessInnerProp()
        {
            // 外部类不能直接访问非静态内部类的实例变量,
            // 下面代码出现编译错误
            // System.out.println("内部类的inProp值:" + inProp);
            // 如需访问内部类的实例变量,必须显式创建内部类对象
            System.out.println("内部类的inProp值:"
                    + new Inner().inProp);
        }
        public static void main(String[] args)
        {
            // 执行下面代码,只创建了外部类对象,还未创建内部类对象
            Outer out = new Outer();      //①
            out.accessInnerProp();
        }
    }
    
    
    

    不可以在外部类的静态成员中直接使用非静态内部类

    
    public class StaticTest {
    
        // 定义一个非静态的内部类,是一个空类
        private class In{}
        // 外部类的静态方法
        public static void main(String[] args)
        {
            // 下面代码引发编译异常,因为静态成员(main()方法)
            // 无法访问非静态成员(In类)
    //        new In();
        }
    }
    
    

    Java不允许在非静态内部类里面定义静态成员

    public class InnerNoStatic {
    
    //    private class InnerClass
    //    {
    //        /*
    //        下面三个静态声明都将引发如下编译错误:
    //        非静态内部类不能有静态声明
    //        */
    //        static
    //        {
    //            System.out.println("==========");
    //        }
    //        private static int inProp;
    //        private static void test(){}
    //    }
    }
    
    
    
    

    静态内部类

    如果用static修饰一个内部类,那这个内部类就属于外部类本身,不属于外部类的某个对象

    静态内部类里面可以有静态成员,也可以有非静态成员,静态内部类不能访问外部类的实例成员,只能访问外部类的类成员

    即使是静态内部类的实例方法也不能访问外部类的实例成员,只能访问外部类的静态成员

    public class StaticInnerClassTest {
    
        private int prop1 = 5;
        private static int prop2 = 9;
        static class StaticInnerClass
        {
            // 静态内部类里可以包含静态成员
            private static int age;
            public void accessOuterProp()
            {
                // 下面代码出现错误:
                // 静态内部类无法访问外部类的实例变量
    //          System.out.println(prop1);
                // 下面代码正常
                System.out.println(prop2);
            }
        }
    }
    
    

    外部类依旧不可以直接访问静态内部类成员,但可以用静态内部类的类名来访问静态内部类的类成员,也可以使用静态内部类对象来访问静态内部类的实例成员

    public class AccessStaticInnerClass {
    
    
        static class StaticInnerClass
        {
            private static int prop1 = 5;
            private int prop2 = 9;
        }
        public void accessInnerProp()
        {
            // System.out.println(prop1);
            // 上面代码出现错误,应改为如下形式:
            // 通过类名访问静态内部类的类成员
            System.out.println(StaticInnerClass.prop1);
            // System.out.println(prop2);
            // 上面代码出现错误,应改为如下形式:
            // 通过实例访问静态内部类的实例成员
            System.out.println(new StaticInnerClass().prop2);
        }
    }
    
    
    
    

    Java接口里面也可以定义内部类,但是默认使用public static修饰,也就是只能是静态内部类

    使用内部类

    外部类内部 用 内部类

    这种和使用普通类没什么太大区别,可以直接通过内部类类名来定义变量,通过new调用内部类构造函数创建实例

    要注意的是:

    不可以在外部类的静态成员中使用非静态内部类

    外部类以外 使用 非静态内部类

    首先的话内部类不可以用private修饰,否则你只能在外部类内部使用private修饰的内部类

    • 默认访问权限的内部类:只能被与外部类处于同一个包中的其他类所访问

    • protected修饰的内部类:可以被与外部类处于同一个包下的其它类以及外部类的子类所访问

    • public修饰的内部类:任何地方都可以访问

    外部类以外地方定义内部类(静态与非静态)语法:

    
    OuterClass.InnerClass varName
    
    

    因为非静态内部类对象寄生在外部类对象,因此创建非静态内部类对象之前,必须先创建外部类对象,具体语法:

    
    OuterInstance.new InnerConstructor()
    
    

    例子:

    class Out
    {
        // 定义一个内部类,不使用访问控制符,
        // 即只有同一个包中其他类可访问该内部类
        class In
        {
            public In(String msg)
            {
                System.out.println(msg);
            }
        }
    }
    public class CreateInnerInstance
    {
        public static void main(String[] args)
        {
            Out.In in = new Out().new In("测试信息");
            /*
            上面代码可改为如下三行代码:
            使用OutterClass.InnerClass的形式定义内部类变量
            Out.In in;
            创建外部类实例,非静态内部类实例将寄存在该实例中
            Out out = new Out();
            通过外部类实例和new来调用内部类构造器创建非静态内部类实例
            in = out.new In("测试信息");
            */
        }
    }
    
    

    外部类以外 使用 静态内部类

    静态内部类是外部类类相关的,因此创建静态内部类对象不用创建外部类对象

    语法:

    
    new OuterClass.InnerConstructor()
    
    
    class StaticOut
    {
        // 定义一个静态内部类,不使用访问控制符,
        // 即同一个包中其他类可访问该内部类
        static class StaticIn
        {
            public StaticIn()
            {
                System.out.println("静态内部类的构造器");
            }
        }
    }
    public class CreateStaticInnerInstance
    {
        public static void main(String[] args)
        {
            StaticOut.StaticIn in = new StaticOut.StaticIn();
            /*
            上面代码可改为如下两行代码:
            使用OutterClass.InnerClass的形式定义内部类变量
            StaticOut.StaticIn in;
            通过new来调用内部类构造器创建静态内部类实例
            in = new StaticOut.StaticIn();
            */
        }
    }
    

    局部内部类

    把一个内部类放在方法里面定义,那这个内部类就是局部内部类

    局部内部类仅仅在该方法里面有效,局域内部类不能用访问控制符和static修饰符修饰

    public class LocalInnerClass {
    
        public static void main(String[] args)
        {
            // 定义局部内部类
            class InnerBase
            {
                int a;
            }
            // 定义局部内部类的子类
            class InnerSub extends InnerBase
            {
                int b;
            }
            // 创建局部内部类的对象
            InnerSub is = new InnerSub();
            is.a = 5;
            is.b = 8;
            System.out.println("InnerSub对象的a和b实例变量是:"
                    + is.a + "," + is.b);
        }
    }
    
    

    匿名内部类

    匿名内部类适合创建那种只需使用一次的类

    创建匿名内部类时会立即创建一个该类的实例,这个类定义立即消失,匿名内部类不能重复使用

    定义匿名内部类格式:

    
    new 实现接口() | 父类构造器(参数列表)
    {
        //实现部分
    }
    

    匿名内部类必须继承一个父类,或者实现一个接口,但最多只能继承一个父类,或实现一个接口

    • 匿名内部类不可以是抽象类

    • 匿名内部类不能定义构造函数,但是可以定义初始化块

    例子:

    interface Product
    {
        public double getPrice();
        public String getName();
    }
    public class AnonymousTest
    {
        public void test(Product p)
        {
            System.out.println("购买了一个" + p.getName()
                    + ",花掉了" + p.getPrice());
        }
        public static void main(String[] args)
        {
            AnonymousTest ta = new AnonymousTest();
            // 调用test()方法时,需要传入一个Product参数,
            // 此处传入其匿名实现类的实例
            ta.test(new Product()
            {
                public double getPrice()
                {
                    return 567.8;
                }
                public String getName()
                {
                    return "AGP显卡";
                }
            });
        }
    }
    
    
    

    当通过实现接口来创建匿名内部类时,匿名内部类不可以显式的创建构造函数,因此匿名内部类只有一个隐式的无参数构造函数

    如果通过继承父类来创建匿名内部类,匿名内部类将拥有和父类相似的构造函数,此处相似是说拥有一样的形参列表

    abstract class Device
    {
        private String name;
        public abstract double getPrice();
        public Device(){}
        public Device(String name)
        {
            this.name = name;
        }
        // 此处省略了name的setter和getter方法
        public void setName(String name)
        {
            this.name = name;
        }
        public String getName()
        {
            return this.name;
        }
    }
    public class AnonymousInner
    {
        public void test(Device d)
        {
            System.out.println("购买了一个" + d.getName()
                    + ",花掉了" + d.getPrice());
        }
        public static void main(String[] args)
        {
            AnonymousInner ai = new AnonymousInner();
            // 调用有参数的构造器创建Device匿名实现类的对象
            ai.test(new Device("电子示波器")
            {
                public double getPrice()
                {
                    return 67.8;
                }
            });
            // 调用无参数的构造器创建Device匿名实现类的对象
            Device d = new Device()
            {
                // 初始化块
                {
                    System.out.println("匿名内部类的初始化块...");
                }
                // 实现抽象方法
                public double getPrice()
                {
                    return 56.2;
                }
                // 重写父类的实例方法
                public String getName()
                {
                    return "键盘";
                }
            };
            ai.test(d);
        }
    }
    
    

    在JDK8之前,Java要求被局部内部类、匿名内部类访问的局部变量必须使用final修饰,从8开始这个限制取消了

    如果局部变量内匿名内部类访问,那么该局部变量相当于自动使用了final修饰

    interface A
    {
        void test();
    }
    public class ATest
    {
        public static void main(String[] args)
        {
            int age = 8;     // ①
            // 下面代码将会导致编译错误
            // 由于age局部变量被匿名内部类访问了,因此age相当于被final修饰了
    //      age = 2;
            A a = new A()
            {
                public void test()
                {
                    // 在Java 8以前下面语句将提示错误:age必须使用final修饰
                    // 从Java 8开始,匿名内部类、局部内部类允许访问非final的局部变量
                    System.out.println(age);
                }
            };
            a.test();
        }
    }
    
    
    
    

    相关文章

      网友评论

          本文标题:Java-内部类

          本文链接:https://www.haomeiwen.com/subject/vmatzctx.html