14、Java基础教程之面向对象·第六讲

  • 1️⃣ 概念
  • 2️⃣ 优缺点
  • 3️⃣ 使用
    • 3.1 成员内部类
  • 3.2 局部内部类
  • 3.3 匿名内部类
  • 3.4 静态内部类
  • 3.5 小结:外部类访问四种内部类的特点
  • 3.6 小结:其他类访问四种内部类的特点
  • 4️⃣ 内部类与外部类的关系
  • 5️⃣ 应用场景
  • 6️⃣ 内部类在并发编程中的应用
  • * 总结
  • * 本文源码下载地址

*

1️⃣ 概念

Java内部类是一种类的结构扩充, 一个类的内部除了属性与方法外,还可以存在其他类的结构,并且内部类也可以定义在方法或代码块中。

内部类是一种嵌套在其他类中的类,它可以直接访问包含它的外部类的成员(包括私有成员),而无需通过实例化外部类对象来访问。内部类为程序提供了更好的封装和组织代码的能力,也是一种实现特定设计模式和编写更复杂的程序结构的方式。

2️⃣ 优缺点

Java内部类具有一些独特的优势和限制。优点如下:

  • 内部类可以访问外部类的私有成员,增加了灵活性和封装性;
  • 内部类可以实现多重继承,减轻了Java单继承的限制;
  • 内部类提供了更好的代码组织和封装能力,使得程序更加清晰;
  • 内部类常用于实现设计模式,提高了程序的可读性和可维护性。

缺点如下:

  • 内部类增加了代码的复杂度、耦合度,理解和维护难度较高;
  • 内部类不能声明静态成员(除了静态嵌套类);
  • 匿名内部类无法重用,只适用于实现某个具体功能的情况。

3️⃣ 使用

Java内部类分为四种类型:成员内部类、局部内部类、匿名内部类和静态嵌套内部类。

3.1 成员内部类

成员内部类是定义在一个类的内部的普通类,它与外部类有着紧密的联系。成员内部类可以直接访问外部类的成员,包括私有成员,并且可以使用this关键字访问自身。

以下是一个示例代码,其中定义了一个外部类 OuterClass 和一个成员内部类 InnerClass

//	案例 1 : 定义成员内部类
public class OuterClass {
   
     
	//定义了一个外部类的整型变量 outerVariable,并初始化为 10
    private int outerVariable = 10;

    public String outerMethod() {
   
     
        return "这是外部类的方法";
    }

    public void outerUseInnerMethod() {
   
     
        // 创建内部类对象
        InnerClass inner = new InnerClass();

        // 使用内部类的属性
        int variable= inner.innerVariable;
        System.out.println("这是在外部类访问内部类的属性:" + variable);

        // 调用内部类的方法
        String str = inner.innerMethod();
        System.out.println("这是在外部类访问内部类的方法:" + str);
    }

    class InnerClass {
   
     
    	//定义了一个内部类的整型变量 innerVariable ,并初始化为 20
        private int innerVariable = 20;

        public String innerMethod() {
   
     
            return "这是成员内部类的方法";
        }

        public void innerUseOuterMethod() {
   
     
            // 在内部类中使用外部类的属性
            int variable= outerVariable;
            System.out.println("这是在成员内部类访问外部类的属性:" + variable);

            // 在内部类中调用外部类的方法
            String str = outerMethod();
            System.out.println("这是在成员内部类访问外部类的方法:" + str);
        }
    }

}

上面代码中,外部类的outerUseInnerMethod()方法展示了如何在外部类使用成员内部类的属性和方法,主要是通过先创建内部类的对象 new InnerClass();,然后就可开始访问内部类的属性与方法。

而内部类中的InnerUseOuterMethod()方法则演示了如何在成员内部类中使用外部类的属性和方法,可以发现在成员内部类可以直接访问到外部类的属性int variable= outerVariable; 与方法 String str = outerMethod();

实际上内部类的基本定义形式并不难理解,就是将类的定义拿到类的内部,也就是说类中除了属性和方法外,也可以定义属于自己内部的结构体。这样做的最大缺点在于:破坏了类的结构性。但是这种牺牲对开发者而言也是有帮助的,它最大的帮助就是可以轻松地访问外部类中的私有属性。

以下是创建外部类及内部类对象并调用方法的使用案例:

//	案例 2 : 在其他类创建外部类及内部类对象并调用方法
 public static void main(String[] args) {
   
     
        //创建外部类对象,调用其方法
        OuterClass outer = new OuterClass();
        outer.outerUseInnerMethod();
        System.out.println();

        //创建成员内部类对象,调用其方法(方式一)
        OuterClass.InnerClass inner1 = outer.new InnerClass();
        inner1.innerUseOuterMethod();
        System.out.println();

        //创建成员内部类对象,调用其方法(方式二)
        OuterClass.InnerClass inner2 = new OuterClass(). new InnerClass();
        inner2.innerUseOuterMethod();
        System.out.println();
    }

运行结果:

这是在外部类访问内部类的属性:20
这是在外部类访问内部类的方法:这是成员内部类的方法

这是在成员内部类访问外部类的属性:10
这是在成员内部类访问外部类的方法:这是外部类的方法

这是在成员内部类访问外部类的属性:10
这是在成员内部类访问外部类的方法:这是外部类的方法

从案例中可以发现,成员内部类对象的实例化语法如下:

外部类.内部类 对象 = new 外部类().new 内部类();

由于内部类需要使用外部类中的属性,而所有的属性只有在对象实例化之后才会分配空间,所以在实例化内部类对象时首先要实例化外部类对象。

3.2 局部内部类

局部内部类是定义在方法或作用域中的类,其作用域被限制在所在的块内。局部内部类提供了一种对代码进行封装和隐藏的机制,使得代码更加清晰。

以下是一个示例代码,其中定义了一个外部类 OuterClass 和一个局部内部类 LocalInnerClass

//	案例 3 :定义局部内部类
public class OuterClass {
   
     
    //定义了一个外部类的整型变量 outerVariable,并初始化为 10
    private int outerVariable = 10;

    public String outerMethod() {
   
     
        return "这是外部类的方法";
    }

    public void outerMethod2() {
   
     

        class LocalInnerClass {
   
     
            //定义了一个局部内部类的整型变量 innerVariable,并初始化为 20
            private int innerVariable = 20;

            public String innerMethod() {
   
     
                return "这是局部内部类的方法";
            }

            public void innerUseOuterMethod() {
   
     
                // 在内部类中使用外部类的属性
                int variable = outerVariable;
                System.out.println("这是在局部内部类访问外部类的属性:" + variable);

                // 在内部类中调用外部类的方法
                String str = outerMethod();
                System.out.println("这是在局部内部类访问外部类的方法:" + str);
            }
        }

        LocalInnerClass localInner = new LocalInnerClass();

        // 使用内部类的属性
        int variable = localInner.innerVariable;
        System.out.println("这是在外部类访问内部类的属性:" + variable);

        // 调用内部类的方法
        String str = localInner.innerMethod();
        System.out.println("这是在外部类访问内部类的方法:" + str);

        System.out.println();
        localInner.innerUseOuterMethod();
    }

}

以下是创建外部类对象并调用方法的使用案例:

//	案例 4 :只能通过外部类间接访问局部内部类
public class UseDemo {
   
     
	//创建外部类对象,调用其方法
    public static void main(String[] args) {
   
     
        OuterClass outer = new OuterClass();
        outer.outerMethod2();
    }
}

运行结果:

这是在外部类访问内部类的属性:20
这是在外部类访问内部类的方法:这是局部内部类的方法

这是在局部内部类访问外部类的属性:10
这是在局部内部类访问外部类的方法:这是外部类的方法

3.3 匿名内部类

匿名内部类是没有明确声明类名的内部类。它通常用于实现接口、抽象类或作为方法参数,可以省去编写单独的类的麻烦。

以下是一个示例代码,其中定义了一个Java类OuterClass,它包含了一个内部接口MyInterface

//	案例 5 :定义匿名内部类
public class OuterClass {
   
     
    //定义了一个外部类的整型变量 outerVariable,并初始化为 10
    private static int outerVariable = 10;

    public static String outerMethod() {
   
     
        return "这是外部类的方法";
    }

    public void outerUseInnerMethod(){
   
     
        MyInterface myInterface = new MyInterface() {
   
     
            @Override
            public String abstractMethod() {
   
     
                return "这是匿名内部类方法";
            }
        };
        // 在外部类访问内部类的属性
        int variable = myInterface.innerVariable;
        System.out.println("这是在外部类访问内部类的属性:" + variable);

        // 在外部类中调用内部类的方法
        String str = myInterface.abstractMethod();
        System.out.println("这是在外部类访问内部类的方法:" + str);
    }

    interface MyInterface {
   
     
        //定义了一个内部类的整型变量 innerVariable,并初始化为 20. 默认使用 public static final 修饰
        int innerVariable = 20;

        String abstractMethod();

        default void innerUseOuterMethod(){
   
     
            // 在内部类中使用外部类的属性
            int variable = outerVariable;
            System.out.println("这是在内部类访问外部类的属性:" + variable);

            // 在内部类中调用外部类的方法
            String str = outerMethod();
            System.out.println("这是在内部类访问外部类的方法:" + str);
        }
    }
    
}

这段代码中,展示了如何在外部类中访问内部类的属性和方法,以及在内部类中访问外部类的属性和方法。

outerUseInnerMethod这个方法创建了一个匿名内部类实现了MyInterface接口,并重写了其中的抽象方法abstractMethod。在这个方法中,通过内部类实例访问了内部类的属性innerVariable并输出,然后调用了内部类的方法abstractMethod并输出。

innerUseOuterMethod是接口里的一个默认方法,可以在实现该接口的类中选择性地覆盖它。在该方法中,通过外部类的实例访问了外部类的属性outerVariable并输出,然后调用了外部类的方法outerMethod并输出。

以下是创建外部类及匿名内部类对象并调用其方法的使用案例:

//	案例 6 :使用匿名内部类
public class UseDemo {
   
     

    public static void main(String[] args) {
   
     
        OuterClass outer = new OuterClass();
        outer.outerUseInnerMethod();

        System.out.println();
        new OuterClass.MyInterface() {
   
     
            @Override
            public String abstractMethod() {
   
     
                return "";
            }
        }.innerUseOuterMethod();

        System.out.println();
        String str = new OuterClass.MyInterface() {
   
     
            @Override
            public String abstractMethod() {
   
     
                return "这是在其他类访问匿名内部类的方法:匿名内部类方法";
            }
        }.abstractMethod();
        System.out.println(str);
    }
}

在这段代码,首先创建了一个OuterClass的实例outer,然后调用该实例的outerUseInnerMethod()方法。

然后,创建了一个匿名内部类,实现了接口MyInterface的抽象方法abstractMethod()并返回一个空字符串。然后,在匿名内部类的实例中,调用了它的方法innerUseOuterMethod()

最后,创建了一个匿名内部类,实现了接口MyInterface的抽象方法返回了一个字符串,并将其赋值给变量str。然后打印出这个字符串。

这些代码展示了匿名内部类如何能够在外部类以及其他类的方法中被创建和使用,从而提供了一种便捷的方式来实现接口并重写其中的抽象方法。

运行结果:

这是在外部类访问内部类的属性:20
这是在外部类访问内部类的方法:这是匿名内部类方法

这是在内部类访问外部类的属性:10
这是在内部类访问外部类的方法:这是外部类的方法

这是在其他类访问匿名内部类的方法:匿名内部类方法

3.4 静态内部类

静态嵌套类(静态内部类)实际上是一个静态类,它与外部类没有直接的关联。静态嵌套类不会自动持有外部类的引用,只有在需要访问外部类的静态成员时才需要使用外部类名进行引用。

使用static 定义的属性或方法是不受类实例化对象控制的,所以如果使用 static 定义内部类,它一定不可能受到外部类的实例化对象控制。而如果一个内部类使用了 static 定义,那么这个内部类就变为一个“外部类”,并且只能直接访问外部类中定义的 static操作。

以下是一个示例代码,其中定义了一个Java类OuterClass,它包含了一个静态内部类 StaticInnerClass

//	案例 7 :定义静态内部类
public class OuterClass {
   
     
    //定义了一个外部类的静态变量 outerStaticVariable,并初始化为 10
    private static int outerStaticVariable = 10;
    //定义了一个外部类的非静态变量 outerVariable,并初始化为 6
    private int outerVariable = 6;

    public static String outerStaticMethod() {
   
     
        return "这是外部类的静态方法";
    }

    public String outerMethod(){
   
     
        return "这是外部类的非静态方法";
    }

    public void outerUseInnerMethod(){
   
     
        StaticInnerClass staticInnerClass = new StaticInnerClass();

        // 在外部类访问内部类的属性
        int variable = staticInnerClass.innerVariable;
        System.out.println("这是在外部类访问内部类的属性:" + variable);

        // 在外部类中调用内部类的方法
        String str = staticInnerClass.innerMethod();
        System.out.println("这是在外部类访问内部类的方法:" + str);
    }

    public static class StaticInnerClass {
   
     
        //定义了一个内部类的整型变量 innerVariable,并初始化为 20. 默认使用 public static final 修饰
        private int innerVariable = 20;

        public String innerMethod() {
   
     
            return "这是静态内部类方法";
        }

        public void innerUseOuterMethod(){
   
     
            // 在内部类中使用外部类的属性
            int variable = outerStaticVariable;
            System.out.println("这是在静态内部类访问外部类的静态属性:" + variable);

            variable = new OuterClass().outerVariable;
            System.out.println("这是在静态内部类访问外部类的非静态属性:" + variable);

            // 在内部类中调用外部类的方法
            String str = outerStaticMethod();
            System.out.println("这是在静态内部类访问外部类的静态方法:" + str);

            str = new OuterClass().outerMethod();
            System.out.println("这是在静态内部类访问外部类的非静态方法:" + str);
        }
    }

}

在这段代码中,定义了一个名为OuterClass的类,类中定义了一个方法outerUseInnerMethod(),在该方法中创建了静态内部类对象并访问内部类属性innerVariable,调用内部类的方法 innerMethod()

定义了一个静态内部类StaticInnerClass,类中定义了一个方法 innerUseOuterMethod(),可以发现,该方法中可以直接访问外部类的静态属性outerStaticVariable 以及调用外部类的静态方法 outerStaticMethod(),而在访问非静态属性outerVariable和非静态方法 outerMethod()时是需要先创建外部类对象的new OuterClass()

以下是创建外部类及静态内部类对象并调用其方法的使用案例:

//	案例 8 :访问静态内部类
public static void main(String[] args) {
   
     
        OuterClass outer = new OuterClass();
        outer.outerUseInnerMethod();

        System.out.println();
        OuterClass.StaticInnerClass inner = new OuterClass.StaticInnerClass();
        inner.innerUseOuterMethod();
    }

运行结果:

这是在外部类访问内部类的属性:20
这是在外部类访问内部类的方法:这是静态内部类方法

这是在静态内部类访问外部类的静态属性:10
这是在静态内部类访问外部类的非静态属性:6
这是在静态内部类访问外部类的静态方法:这是外部类的静态方法
这是在静态内部类访问外部类的非静态方法:这是外部类的非静态方法

3.5 小结:外部类访问四种内部类的特点

外部类访问成员内部类、局部内部类、匿名内部类和静态内部类的特点及方式如下:

  • 外部类访问成员内部类:可以直接通过内部类的实例访问其成员。 示例:
public class OuterClass {
     
       
	class InnerClass{
     
       }
    InnerClass inner = new InnerClass();
}

  • 外部类访问局部内部类:定义局部内部类时,仅在特定的代码块内部可见。在外部类方法中,可以创建并访问局部内部类的实例。 示例:
public class OuterClass {
     
       
	public void outerMethod() {
     
       
    	class LocalInnerClass {
     
       }
    	LocalInnerClass localInner = new LocalInnerClass();
	}
}

  • 外部类访问匿名内部类:通过实例化一个接口或抽象类的方式使用匿名内部类。 匿名内部类是没有显式名称的内部类,在实例化时定义并实现它们的方法。示例:
public class OuterClass {
     
       
    interface MyInterface {
     
       
        void abstractMethod();
    }

    public void method(){
     
       
        new MyInterface() {
     
       
            @Override
            public void abstractMethod() {
     
       
                // 实现匿名内部类的方法 
            }
        }.abstractMethod();
    }
}

  • 外部类访问静态内部类:可以直接通过内部类的实例访问其成员。 示例:
public class OuterClass {
     
       
    StaticInnerClass staticInnerClass = new StaticInnerClass();
    public static class StaticInnerClass {
     
       }
}

3.6 小结:其他类访问四种内部类的特点

在其他类中使用外部类的成员内部类、局部内部类、匿名内部类和静态内部类,有如下的特点及方式:

  • 在其他类中访问外部类的成员内部类:需要通过外部类的实例来访问其成员内部类。示例:
OuterClass outer = new OuterClass(); 
OuterClass.InnerClass inner = outer.new InnerClass();

  • 在其他类中访问外部类的局部内部类:由于局部内部类的可见性限制,无法在其他类中直接访问局部内部类。
  • 在其他类中访问外部类的匿名内部类:匿名内部类通常用作实现某个接口或抽象类的临时实例。 示例:
 new OuterClass.MyInterface() {
     
       
 	@Override
    public String abstractMethod() {
     
       
        // 实现匿名内部类的方法
    }
  }.abstractMethod();

  • 在其他类中访问外部类的静态内部类:外部类的静态内部类可以通过外部类的名称直接访问。 示例:
OuterClass.StaticInnerClass staticInner = new OuterClass.StaticInnerClass();

4️⃣ 内部类与外部类的关系

在Java中,内部类是定义在另一个类的内部的类。它们与外部类存在一种特殊的关系,可以访问外部类的成员变量和方法。几种常见的内部类与外部类的关系如下:

  • 成员内部类(非静态内部类)

  • 非静态内部类是外部类的成员,它可以直接访问外部类的成员变量和方法,无论是静态还是非静态。

  • 在非静态内部类的实例化过程中,需要先创建外部类的对象,然后使用该对象来创建内部类的对象。

  • 静态内部类

  • 静态内部类不持有外部类的引用,因此它不能直接访问外部类的非静态成员变量和方法,但可以访问外部类的静态成员变量和方法。

  • 实例化静态内部类时,无需先创建外部类的对象,可以直接使用外部类名加上静态内部类名进行实例化。

  • 局部内部类(方法内部类)

  • 局部内部类是定义在方法内部的类,它只能在所在方法内部被访问。

  • 局部内部类可以访问外部类的变量、方法和所在方法的参数,但仅限于被声明为final的方法参数以及被事实上声明为final的局部变量。

  • 匿名内部类

  • 匿名内部类没有显式的类名,它通常用于在创建对象时直接定义并实现一个接口或继承一个类。

  • 匿名内部类可以访问外部类的成员变量、方法和所在方法的参数,但同样受到访问权限的限制。

无论是哪种类型的内部类,它们都可以像普通类一样具有自己的成员变量和方法,并且可以被实例化和使用。内部类允许在特定情况下更灵活地组织代码,并具有更好的封装性和可读性。

5️⃣ 应用场景

Java内部类提供了一种更灵活的方式来设计程序结构,可以实现封装、隐藏和模块化等功能。下面是一些常见的使用方法:

  • 访问外部类成员
    内部类可以直接访问外部类的成员(包括私有成员),这为程序的编写和维护提供了方便。内部类可以很好地组织和封装代码。
  • 实现多重继承
    Java的内部类允许在同一个类中同时继承多个类或实现多个接口,实现了Java的单继承限制。通过内部类的特性,我们可以在一个类中实现多种行为。
  • 设计模式的实现
    内部类常用于实现设计模式,如观察者模式、迭代器模式、策略模式等。内部类为这些模式的实现提供了方便的结构和封装能力,使程序更加简洁和可读。
  • 事件监听
    在GUI编程中,内部类可以用于实现事件监听器。通过内部类,我们将事件处理逻辑与界面布局分离,提高了代码的可维护性和可扩展性。
  • 辅助类:内部类可以作为外部类的辅助类存在。当外部类需要一个特定的帮助类来完成某些操作时,内部类提供了一种集成这个功能的简洁方式。

需要注意的是,在选择内部类作为设计的一部分时,应该考虑代码结构的合理性和可维护性。过多或过复杂的内部类可能会使代码难以阅读和理解。因此,使用内部类时需要谨慎并选择适当的场景。

6️⃣ 内部类在并发编程中的应用

内部类在并发编程中的应用场景有很多,它可以用来实现线程间的协作、封装共享资源以及简化并发代码的编写。下面是一些常见的内部类在并发编程中的应用:

  • 线程池:内部类可以用于定义线程池中的线程类。通过使用内部类,可以方便地访问线程池的私有成员变量和方法,并与其他线程池中的线程进行通信和协调。
  • 锁机制:内部类可以作为锁对象,实现对临界区的同步访问。例如,在一个类的内部定义一个私有的ReentrantLock或者Semaphore内部类,用于实现对共享资源的互斥访问。
  • 事件处理:内部类可以作为事件监听器,用于处理异步事件。当一个事件触发时,可以通过内部类来定义事件监听器,处理相应的事件逻辑,并且获取外部类的私有成员变量。
  • 迭代器:内部类可以用于实现安全的迭代器。在多线程环境下,使用内部类定义的迭代器可以确保遍历集合时的线程安全性,避免并发修改。
  • 闭包和回调:内部类可以用于实现闭包和回调机制,使得任务能够在不同的上下文中执行。通过内部类,可以捕获外部类的状态信息,并在合适的时机调用回调函数。

总之,内部类在并发编程中的应用主要是利用其封装性和对外部类私有成员的访问特点,来简化多线程编程的复杂性,提高程序的可读性和可维护性,并保证线程安全。

* 总结

Java内部类是一种非常强大的语言特性,它使得程序写作更加模块化、封装化和灵活化。了解和掌握内部类的使用方法和特性,可以对于设计和编写复杂的Java程序非常有帮助。然而,内部类也有其自身的限制和缺点,需要在实际应用中权衡利弊,并选择适合的编程方式。

虽然Java内部类概念较为简单,但其应用场景广泛,通过灵活运用内部类,我们能够写出更优雅、可维护性高的代码,提高程序的开发效率和质量。希望本文能够帮助读者更好地理解和使用Java内部类。

* 本文源码下载地址

Java的内部类讲解案例代码(成员内部类、局部内部类、匿名内部类、静态内部类、外部类访问四种内部类、其他类访问四种内部类…)
*


[* ]nbsp_nbsp 2