首页 > 编程语言 >Java的四种内部类

Java的四种内部类

时间:2023-02-12 19:22:05浏览次数:33  
标签:部类 Java 内部 静态 System println 四种 out

Java四种内部类,静态内部类、成员内部类、局部内部类、匿名内部类的简要介绍

Ahthor: Msuenb

Date: 2023-02-11


在 Java 中,允许一个类的定义位于类一个类的内部,前者称为内部类,后者称为外部类。

根据内部类声明的位置,可以将内部类分为:

  • 成员内部类
    • 静态成员内部类
    • 非静态成员内部类
  • 局部内部类
    • 有名字的局部内部类
    • 匿名内部类

成员内部类

如果成员内部类中不使用外部类的非静态成员,那么通常将内部类声明为静态内部类,否则声明为非静态内部类。

语法格式:

【修饰符】 class 外部类名 {
    【修饰符】 【static】 class 内部类名 {
    
	}	
}

静态内部类

有static修饰的成员内部类叫做静态内部类。它的特点:

  • 和其他类一样,它只是定义在外部类中的另一个完整的类结构

    • 可以继承自己的想要继承的父类,实现自己想要实现的父接口们,和外部类的父类和父接口无关
    • 可以在静态内部类中声明属性、方法、构造器等结构,包括静态成员
    • 可以使用abstract修饰,因此它也可以被其他类继承;可以使用final修饰,表示不能被继承
    • 编译后有自己的独立的字节码文件,只不过在内部类名前面冠以外部类名和$符号
      • 静态内部类完整字节码名称是“包名.外部类名$静态内部类名"
      • 在外部类的外面使用静态内部类名时需要使用”包名.外部类名.静态内部类名“
  • 和外部类不同的是,它可以允许四种权限修饰符:public,protected,缺省,private

    • 外部类只允许public或缺省的
  • 在外部类的任意位置都可以直接使用静态内部类,也可以访问静态内部类的所有成员,包括私有的

    • 如果使用静态内部类的静态成员,就通过”静态内部类名.静态成员“的形式
    • 如果使用静态内部类的非静态成员,就先创建静态内部类的对象,然后通过“静态内部类对象.非静态成员”的形式
  • 只可以在静态内部类中使用外部类的静态成员

    • 在静态内部类中不能直接使用外部类的非静态成员哦
    • 如果在内部类中有变量与外部类的静态成员变量同名,可以使用“外部类名."进行区别
  • 如果权限修饰符允许,静态内部类也可以在外部类的外面使用。

    • 如果使用静态内部类的静态成员,就通过”外部类名.静态内部类名.静态成员“的形式
    • 如果使用静态内部类的非静态成员,就先创建静态内部类的对象,在外部类的外面不需要通过外部类的对象就可以创建静态内部类的对象(通常应该避免这样使用)
public class StaticInnerClass {
    public static void main(String[] args) {
        Outer.OuterStaticMethod();
        Outer outer = new Outer();
        outer.OuterMethod();

        Outer.Inner.InnerStaticMethod();
        Outer.Inner inner = new Outer.Inner();
        inner.InnerMethod();
    }
}
class Outer {
    private static String a = "外部类静态成员a";
    private static String b = "外部类静态成员b";
    private String c = "外部类非静态成员b";

    static class Inner {
        private static String a = "内部类静态成员a";
        private String c = "内部类非静态成员c";

        public static void InnerStaticMethod() {
            System.out.println("------内部类静态方法-------");
            System.out.println(Outer.a);   // 外部的 a
            System.out.println(a);  // 内部的 a
            System.out.println(b);  // 外部的 b

            //System.out.println(Outer.this.c);    // 不能使用外部类的非静态成员
            //System.out.println(c);  // 不能使用自己的非静态成员
        }

        public void InnerMethod() {
            System.out.println("------内部类非静态方法------");
            System.out.println(Outer.a);   // 外部的 a
            System.out.println(a);  // 内部的 a
            System.out.println(b);  // 外部的 b

            //System.out.println(Outer.this.c);    // 不能使用外部类的非静态成员
            System.out.println(c);  // 内部的 c
        }
    }

    public static void OuterStaticMethod() {
        System.out.println("------外部类静态方法------");
        System.out.println(Outer.a);    // 外部的 a
        System.out.println(b);  // 外部的 b
        //System.out.println(c); // 不能使用自己的非静态成员

        System.out.println(Inner.a);    // 内部的 a
        System.out.println(new Inner().c);  // 内部的 c
    }

    public void OuterMethod() {
        System.out.println("------外部类非静态方法------");
        System.out.println(Outer.a);   // 外部的 a
        System.out.println(b);  // 外部的 b
        System.out.println(c); // 外部的 c

        System.out.println(Inner.a);    // 内部的 a
        System.out.println(new Inner().c);  // 内部的 c
    }
}

注意:对于方法的使用也遵循上面的规则

成员内部类

没有static修饰的成员内部类叫做非静态内部类。非静态内部类的特点:

  • 和其他类一样,它只是定义在外部类中的另一个完整的类结构

    • 可以继承自己的想要继承的父类,实现自己想要实现的父接口们,和外部类的父类和父接口无关
    • 可以在非静态内部类中声明属性、方法、构造器等结构,但是不允许声明静态成员,除了静态常量,或者继承父类的静态成员。
    • 可以使用abstract修饰,因此它也可以被其他类继承;可以使用final修饰,表示不能被继承
    • 编译后有自己的独立的字节码文件,只不过在内部类名前面冠以外部类名和$符号
      • 非静态内部类完整字节码名称是“包名.外部类名$非静态内部类名"
      • 在外部类的外面使用非静态内部类名时需要使用”包名.外部类名.非静态内部类名“
  • 和外部类不同的是,它可以允许四种权限修饰符:public,protected,缺省,private

    • 外部类只允许public或缺省的
  • 在外部类中使用非静态内部类有限制

    • 在外部类的静态成员中,只能使用非静态内部类的静态常量
    • 在外部类的非静态成员中,可以创建非静态内部类对象,然后通过非静态内部类的对象使用非静态内部类的所有成员
  • 可以在非静态内部类中使用外部类的所有成员,哪怕是私有的

    • 如果没有重名问题,可以直接使用外部类的所有成员
    • 如果非静态内部类与外部类的静态成员重名,可以使用“外部类名."进行区别
    • 如果非静态内部类与外部类的非静态成员重名,可以使用“外部类名.this."进行区别
  • 如果权限修饰符允许,非静态内部类也可以在外部类的外面使用。

    • 如果使用非静态内部类的静态常量,就通过”外部类名.非静态内部类名.静态常量“的形式
    • 在外部类的外面必须通过外部类的对象才能创建非静态内部类的对象(通常应该避免这样使用)
      • 如果要在外部类的外面使用非静态内部类的对象,通常在外部类中提供一个方法来返回这个非静态内部类的对象比较合适
      • 因此在非静态内部类的方法中有两个this对象,一个是外部类的this对象,一个是内部类的this对象
public class InnerClass {
    public static void main(String[] args) {
        Outer2.OuterStaticMethod();
        Outer2 outer2 = new Outer2();
        outer2.OuterMethod();

        Outer2.Inner2 inner2 = outer2.new Inner2();
        inner2.InnerMethod();
    }
}

class Outer2 {
    private static String a = "外部类静态成员a";
    private static String b = "外部类静态成员b";
    private String c = "外部类非静态成员c";

    class Inner2 {
        //private static String a = "内部类静态成员a";   // 非静态内部类中不能声明静态成员
        private String c = "内部类非静态成员";
        public static final String s = "非静态内部类的静态常量";   // 但可以声明静态常量

        //public static void InnerStaticMethod() {} // 不可以声明静态成员

        public void InnerMethod() {
            System.out.println("-----内部类非静态方法-----");
            System.out.println(a);  // 外部 a
            System.out.println(Outer2.b);   // 外部 b

            System.out.println(Outer2.this.c);  // 外部类的 c
            System.out.println(c);  // 自己的 c
        }
    }

    public static void OuterStaticMethod() {
        System.out.println("-----外部类静态方法-----");
        System.out.println(a);
        System.out.println(Outer2.b);
        //System.out.println(c);      // 不能访问自己的非静态成员

        //Inner2 inner2 = new Inner2();   // 无法使用非静态内部类
        System.out.println(Inner2.s);
    }

    public void OuterMethod() {
        System.out.println("-----外部类非静态方法-----");
        System.out.println(a);
        System.out.println(Outer2.b);
        System.out.println(c);      // 自己的 c

        System.out.println(new Inner2().c);   // 内部的 c
        System.out.println(Inner2.s);   // 内部的 s
    }
}

局部内部类

局部内部类

语法格式:

【修饰符】 class 外部类{
    【修饰符】 返回值类型  方法名(【形参列表】){
         【final/abstract】 class 内部类{
     	 
         }
    }    
}

局部内部类的特点:

  • 和外部类一样,它只是定义在外部类的某个方法中的另一个完整的类结构
    • 可以继承自己的想要继承的父类,实现自己想要实现的父接口们,和外部类的父类和父接口无关
    • 可以在局部内部类中声明属性、方法、构造器等结构,但是不允许声明静态成员,除了静态常量,或者继承父类的静态成员
    • 可以使用abstract修饰,因此它也可以被同一个方法的在它后面的其他内部类继承
    • 可以使用final修饰,表示不能被继承
    • 编译后有自己的独立的字节码文件,只不过在内部类名前面冠以外部类名、$符号、编号。
      • 这里有编号是因为同一个外部类中,不同的方法中存在相同名称的局部内部类
      • 局部内部类完整字节码名称是“包名.外部类名$编号局部内部类名"
      • 在外部类的外面不能使用局部内部类名
  • 和成员内部类不同的是,它前面不能有权限修饰符等
  • 局部内部类如同局部变量一样,有作用域
  • 局部内部类中是否能访问外部类的非静态的成员,取决于所在的方法
  • 局部内部类中还可以使用所在方法的局部常量,即用final声明的局部变量
    • JDK1.8之后,如果某个局部变量在局部内部类中被使用了,自动加final
    • 为什么在局部内部类中使用外部类方法的局部变量要加final呢?考虑生命周期问题。
public class Main {
    public static void main(String[] args) {
        Outer3.outStaticMethod();

        Outer3 outer3 = new Outer3();
        outer3.outMethod();

        // 这里不能使用局部内部类
        // Outer3.Inner3 inner3 = outer3.new Inner3();

        // 只能可以通过多态引用局部内部类的对象
        Father father = Outer3.outTest();
        father.m();
    }
}

class Outer3 {
    private static String a = "外部类的静态成员a";
    private static String b = "外部类的静态成员b";
    private String c = "外部类的非静态成员c";

    public static void outStaticMethod() {
        class Inner3 {
            // 不能定义静态成员
            private String a = "非静态内部类1对象的非静态a";
            private static final String s = "非静态内部类1对象的静态常量s";

            public void inMethod() {
                System.out.println("--------------");
                System.out.println(a);  // 内部的 a
                System.out.println(Outer3.a);   // 外部的 a
                System.out.println(b);  // 外部的 b

                // System.out.println(Outer3.this.c);   // 不能访问外部类的非静态成员
                System.out.println(s);
            }
        }
        new Inner3().inMethod();
    }

    public void outMethod() {
        class Inner3 {
            // 不能定义静态成员
            private String a = "非静态内部类2对象的非静态a";
            private static final String s = "非静态内部类2对象的静态常量s";

            public void inMethod() {
                System.out.println("----------------");
                System.out.println(a);  // 内部的 a
                System.out.println(Outer3.a);   // 外部的 a
                System.out.println(b);  // 外部的 b

                System.out.println(Outer3.this.c);   // 外部的 c
                System.out.println(s);
            }
        }
        new Inner3().inMethod();
    }

    public static void outOtherMethod() {
        // Inner3 inner3 = new Inner3();   局部内部类有作用范围限制
    }

    public static Father outTest(){
        final int outA = 1;
        class Inner extends Father{
            @Override
            void m() {
                System.out.println("局部内部类重写父类方法");
                System.out.println("局部内部类使用外部类的局部变量outA = " + outA);
            }
        }
        return new Inner();
    }
}

abstract class Father{
    abstract void m();
}

?????

如果外部类局部变量的值是确定的常量值,那么编译器直接在局部内部类中就相当于使用了一个确定常量值;

如果外部类局部变量的值是待确定的常量值,那么编译器会在局部内部类中用一个隐式的成员变量接收该局部变量的值,当局部内部类对象创建时自动完成赋值,即局部内部类中使用的已经不再是外部类的局部变量了,而是自己的一个成员变量;

?????

public class OuterClass {
    public static Object outTest(final int value){
        final int outA = 1;
        class InnerClass{
            void m() {
                System.out.println("value = " + value);
                System.out.println("outA = " + outA);
            }

            @Override
            public String toString() {
                return "value = " + value + ",outA = " + outA;
            }
        }
//        value = 2;
//        outA = 2;
        return new InnerClass();
    }

    public static void main(String[] args) {
        System.out.println(outTest(1));
    }
}

匿名内部类

在开发过程中,需要用到一个抽象类的子类的对象或一个接口的实现类的对象,而且只创建一个对象,使用一次就行,就不需要给类命名,这时就可以使用匿名内部类。

new 父类(【实参列表】) {
    重写方法...
}

new 父接口() {
    重写方法...
}
// ()中没有参数 此时匿名内部类的直接父类是Object类

匿名内部类是没有名字的类,因此在声明类的同时就创建好了唯一的对象。

匿名内部类没有名字,因此字节码文件名是外部类名$编号.class。

匿名内部类是一种特殊的局部内部类,只不过没有名称而已。所有局部内部类的限制都适用于匿名内部类。例如:

  • 在匿名内部类中是否可以使用外部类的非静态成员变量,看所在方法是否静态
  • 在匿名内部类中如果需要访问当前方法的局部变量,该局部变量需要加final 不加好像也可以 只能访问不能修改??????
public class Main {
    private static String a = "外部类的静态成员变量a";
    private String b = "外部类的非静态成员变量b";

    public static void main(String[] args) {
        String c = "当前方法的局部变量c";     // 是否自动加上 final
        final String s = "当前方法的局部常量s";
        A obj = new A() {
            @Override
            public void method() {
                System.out.println(a);
                // System.out.println(Main.this.b);  静态方法  不能使用外部类静态成员
                System.out.println(c);  // 可以访问
                // c = "abc";  // 不能修改
                System.out.println(s);
            }
        };
        obj.method();
        System.out.println(obj.getClass().getName());   // Main$1

        new Main().outFun();
    }

    public void outFun() {
        A obj = new A() {
            @Override
            public void method() {
                System.out.println(a);
                System.out.println(Main.this.b);
            }
        };
        obj.method();
        System.out.println(obj.getClass().getName());   // Main$2	多个内部类编号顺延
    }
}

interface A {
    void method();
}

匿名内部类的使用场景:

  1. 使用匿名内部类直接调用方法

    public class Main {
        public static void main(String[] args) {
            new A() {
                @Override
                public void method() {
                    System.out.println("通过父接口的变量多态引用匿名内部类的对象");
                }
            }.method();
        }
    }
    
    interface A {
        void method();
    }
    
  2. 通过父类或父接口的变量多态引用匿名内部类的对象

    public class Main {
        public static void main(String[] args) {
            A obj = new A() {
                @Override
                public void method() {
                    System.out.println("通过父接口的变量多态引用匿名内部类的对象");
                }
            };
            obj.method();
        }
    }
    
    interface A {
        void method();
    }
    
  3. 匿名内部类的对象作为实参

    public class Main {
        public static void main(String[] args) {
            test(new A() {
                @Override
                public void method() {
                    System.out.println("匿名内部类作为实参");
                }
            });
        }
    
        public static void test(A a) {  // a 的运行时类型是 匿名类 Main$1
            a.method();
        }
    }
    
    interface A {
        void method();
    }
    

内部类对比

静态内部类 非静态内部类 局部内部类 匿名内部类
类角色 字节码文件 外部类名$静态内部类名.class 外部类名$非静态内部类名.class 外部类名$编号局部内部类名.class 外部类名$编号.class
父类或父接口 正常 正常 正常 指定一个直接父类或一个直接父接口
非静态成员 正常 正常 正常 只有默认构造器(Object的)
静态成员 正常 只能有静态常量 只能有静态常量 只能有静态常量
权限修饰符 public、protected、缺省、private 同左
其他修饰符 abstract、final 同左 同左
静态修饰符 static
成员角色 依赖于外部类 依赖 同左 同左 同左
依赖于外部类的对象 不依赖 依赖 看所在方法 看所在方法
使用 在外部类的静态成员中使用内部类 没有限制 只能使用非静态内部类的静态常量 有严格作用域 有严格作用域
在外部类的非静态成员中使用内部类 没有限制 同左 有严格作用域 有严格作用域
在内部类中使用外部类的静态成员 没有限制 同左 同左 同左
在内部类中使用外部类的非静态成员 不能 没有限制 看所在方法 看所在方法
在内部类中使用外部类的某局部变量 无(作用域问题) 同左 局部变量加final 同左
在外部类的外面使用内部类的静态成员 外部类名.静态内部类名.静态成员 外部类名.非静态内部类名.静态常量
在外部类的外面获取内部类的对象 外部类名.静态内部类名 变量 = 外部类名.静态内部类名(); 外部类名 out变量 = new 外部类(); 外部类名.非静态内部类名 in变量 = out变量.new 非静态内部类名(); 局部内部类父类或父接口 变量 = 通过外部类的某方式返回局部内部类的对象; 同左
在外部类的外面使用内部类的非静态成员 静态内部类对象.非静态成员(); 非静态内部类对象.非静态成员(); 局部内部类父类或父接口变量.重写成员 同左
重名 与外部类的静态成员重名 外部类名.静态成员 外部类名.静态成员 同左 同左
与外部类的非静态成员重名 外部类名.this.非静态成员 同左 同左
选择依据 在内部类中不使用外部类的非静态成员 在内部类中需要定义静态成员 在内部类中需要使用外部类的非静态成员。 在内部类中不定义常量以外的静态成员 该内部类仅限于当前方法使用 该内部类代码简洁,对象唯一性

标签:部类,Java,内部,静态,System,println,四种,out
From: https://www.cnblogs.com/msuenb/p/17113774.html

相关文章

  • 妙用Java 8中的 Function接口,消灭if...else
    在开发过程中经常会使用 if...else...进行判断抛出异常、分支处理等操作。这些 if...else...充斥在代码中严重影响了代码代码的美观,这时我们可以利用Java8的Function接......
  • java中的lambda表达式
    java中的lambda表达式历史背景到目前为止,在Java中传递一个代码段并不容易,不能直接传递代码段,Java是一种面向对象语言,所以必须构造一个对象,这个对象的类需要有一个方法......
  • Java基础知识点(条件控制语句之if语句)
    1.顺序结构:Java程序中默认的执行流程,按照代码的先后顺序,从上到下依次进行。2.选择结构语句<1>.if语句格式:if(关系表达式){语句体;}执行流程:1.首先计算关系表达式的值。2.如......
  • Java 抽象类
    目录1、抽象方法和抽象类2、抽象类的作用当编写一个类时,常常会为该类定义一些方法,用于描述该类的行为方式,这些方法都有具体的方法体。但在某些情况下,某个基类只是知道其子......
  • Java-异常处理
           ......
  • java——spring boot集成RabbitMQ——路由模式——应用场景
    如果多个队列都绑定了同一个交换机,通过路由key,指定消息到哪个消息队列     ......
  • Java程序设计三特性
    Java程序编写具有三大特征:封装、继承和多态;封装对类中的方法和属性进行权限访问控制,只提供特定接口供外部访问,这样一方面增加了代码的规范性另一方面又增加了代码的访......
  • java——spring boot集成RabbitMQ——发布/订阅模式——实现生产者
        首先抽离出来连接,单据一个工具类:                                     ......
  • java面试题(三)
    1、Java中会存在内存泄漏吗,请简单描述。答:理论上Java因为有垃圾回收机制(GC)不会存在内存泄露问题(这也是Java被广泛使用于服务器端编程的一个重要原因);然而在实际开发......
  • java面试题(四)
    1、什么时候用断言(assert)?答:断言在软件开发中是一种常用的调试方式,很多开发语言中都支持这种机制。一般来说,断言用于保证程序最基本、关键的正确性。断言检查通常在开发和......