JAVA基础之内部类

创建内部类

  • 内部类
将一个类的定义放在另一个类的定义内部
如果想要创建某个内部类的对象需要指明这个对象的类型:OuterClassName.InnerClassName
内部类的对象只能在与其外围类的对象相关联的情况下才能被创建
public class Test {
    public static void main(String[] args) {
        Outer outer = new Outer("JAVA");
        Outer.Inner inner = outer.getInner();
        System.out.print(inner.value());
    }
}

class Outer {
    private String name;

    public Outer(String str) {
        name = str;
    }

    class Inner {
        public String value() {
            return name;
        }
    }

    public Inner getInner() {
        return new Inner();
    }
}
/*
输出: 
JAVA
*/

链接到外部类

内部类可以访问其外围对象的所有成员,而不需要任何特殊条件
当外围类对象创建了一个内部类对象时,此内部类对象必定会秘密地捕获一个指向那个外围类对象的引用;当访问外部类成员时,就是用这个引用来选择外围类成员
public class Test {
    public static void main(String[] args) {
        Sequence sequence = new Sequence(10);
        Random random = new Random(47);
        for (int i = 0; i < 10; i++) {
            sequence.add(Integer.toString(random.nextInt(100)));
        }

        ISelector selector = sequence.getSelector();
        while (!selector.end()) {
            System.out.print(selector.current() + " ");
            selector.next();
        }
    }
}

interface ISelector {
    boolean end();
    void next();
    Object current();
}

class Sequence {
    private int next = 0;
    private Object[] objects;

    public Sequence(int size) {
        objects = new Object[size];
    }

    public void add(Object obj) {
        if (next < objects.length)
            objects[next++] = obj;
    }

    public ISelector getSelector() {
        return new SequenceSelector();
    }

    private class SequenceSelector implements ISelector {
        private int i = 0;

        public boolean end() {
            return i == objects.length;
        }

        public void next() {
            if (i < objects.length)
                i++;
        }

        public Object current() {
            return objects[i];
        }
    }
}

/*
输出: 
58 55 93 61 61 29 68 0 22 7 
*/

使用.this和.new

生成对外部对象的引用使用外部类.this
创建内部类对象使用外部类对象.new
public class Test {
    public static void main(String[] args) {
        // 创建内部类对象可以使用 外部类对象.new
        Outer.Inner inner = new Outer().new Inner();
        inner.getOuter().print();
    }
}

class Outer {
    public void print() {
        System.out.println("Outer");
    }

    class Inner {
        // 生成对外部对象的引用可以使用  外部类.this
        public Outer getOuter() {
            return Outer.this;
        }
    }
}
/*
输出: 
Outer
*/

内部类与向上转型

将内部类向上转型为基类或一个接口的时候,所得到的只是指向基类或接口的引用,就能够很方便地隐藏实现细节
package com.test.tools;

public interface ITools {
    void print(String str);
}
package com.test.util;
import com.test.tools.ITools;

public class Tools {
    protected class InnerTools implements ITools {
        public InnerTools() {}

        @Override
        public void print(String str) {
            System.out.println(str);
        }
    }
}
package com.test;

import com.test.tools.ITools;
import com.test.util.Tools;

public class Test {
    public static void main(String[] args) {
        ITools tools = new MyTools().getTools();
        tools.print("Hello");
    }
}

class MyTools extends Tools {
    ITools getTools() {
        return new InnerTools();
    }
}

/**
输出如下: 
Hello
*/
内部类可以直接访问外围类私有方法,可以直接修改外围类私有域
public class Test {
    public static void main(String[] args) {
        new Outer().getInner().modifyName("Inner");
    }
}

class Outer {
    private String name = "Outer";
    private void setName(String str) {
        name = str;
        System.out.print("Name:" + name);
    }

    class Inner {
        void modifyName(String str) {
            // 内部类可以直接访问外围类私有方法,可以直接修改外围类私有域
            setName(str);
        }
    }

    public Inner getInner() {
        return new Inner();
    }
}

/**
输出如下: 
Name:Inner
*/

在方法和作用域内的内部类

  • 目的
1.实现某类型的接口,可以创建并返回对其的引用
2.用来解决一个复杂的问题,需要创建一个类来辅助解决,但又不希望这个类是公共可用类
public class Test {
    public static void main(String[] args) {
        new Outer().getPrinter().print("Hello");
        new Outer().innerPrint(true, "JAVA");
    }
}

interface IPrint {
    void print(String str);
}

class Outer {
    public IPrint getPrinter() {
        // 方法内部使用内部类,在方法之外是不能访问该内部类的
        class Printer implements IPrint {
            @Override
            public void print(String str) {
                System.out.println("Printer " + str);
            }
        }
        return new Printer();
    }

    public void innerPrint(boolean print,String str) {
        if(print) {
            // 作用域内创建内部类,作用域外是不能访问的
            class Printer{
                void print(String str) {
                    System.out.println("Printer " + str);
                }
            }
            new Printer().print(str);
        } else {
            System.out.println("Not print!");
        }
    }
}
/**
输出如下: 
Printer Hello
Printer JAVA
*/

匿名内部类

  • 匿名内部类
匿名内部类是没有名字的内部类,因此不具有构造方法
匿名内部类适合创建那种只需要一次使用的类
匿名内部类既可以扩展类,也可以实现接口,但不能两者兼备
当需要不止一个内部类对象时就需要使用局部内部类而不使用匿名内部类,因为匿名内部类没有命名构造器
public class Test {
    public static void main(String[] args) {
        Outer outer = new Outer();
        outer.getPrint().print("Hello");
        outer.getPrinter(10).print();
        outer.show(10).show();
    }
}

interface IPrint {
    void print(String str);
}

interface IShow {
    void show();
}

class Printer {
    private int num;

    Printer(int input) {
        num = input;
    }

    public int value() {
        return num;
    }

    public void print() {
        System.out.println("Printer " + value());
    }
}

class Outer {
    public IPrint getPrint() {
        // 实现了接口的匿名内部类
        return new IPrint() {
            @Override
            public void print(String str) {
                System.out.println("Print " + str);
            }
        };
    }

    public Printer getPrinter(int input) {
        // 扩展了带参数构造器的匿名内部类
        return new Printer(input) {
            @Override
            public int value() {
                return super.value() * 47;
            }
        };
    }

    public IShow show(final int input) {
        // 匿名类中直接使用传入的对象参数时,该参数必须是final的,否则报错
        return new IShow() {
            // 可以在匿名类中定义参数,执行字段初始化
            private int size = input;

            // 匿名类中不可能有命名构造器,但可以使用实例初始化来达到构造器效果
            {
                size = input * new Random().nextInt(10);
                if (size > 100) {
                    size = 100;
                }
            }

            @Override
            public void show() {
                System.out.println("Show " + size);
            }
        };
    }
}
/**
输出如下: 
Print Hello
Printer 470
Show 80
*/
  • 工厂模式
public class Test {
    public static void main(String[] args) {
        Games.GameConsumer(Checkers.mCheckersFactory);
        Games.GameConsumer(Chess.mChessFactory);
    }
}

interface IGame {
    boolean move();
}

interface IGameFactory {
    IGame getGame();
}

class Checkers implements IGame {
    private Checkers() {}

    public static IGameFactory mCheckersFactory = new IGameFactory() {
        @Override
        public IGame getGame() {
            return new Checkers();
        }
    };

    @Override
    public boolean move() {
        System.out.println("Checkers move");
        return true;
    }
}

class Chess implements IGame {
    private Chess() {}

    public static IGameFactory mChessFactory = new IGameFactory() {
        @Override
        public IGame getGame() {
            return new Chess();
        }
    };

    @Override
    public boolean move() {
        System.out.println("Chess move");
        return true;
    }
}

class Games {
    public static void GameConsumer(IGameFactory factory) {
        IGame game = factory.getGame();
        game.move();
    }
}
/**
输出如下: 
Checkers move
Chess move
*/

嵌套类

  • 嵌套类
声明为static的内部类
嵌套内部类对象与外围类对象之间不存在联系,普通内部类对象隐式地保存了一个外围类对象的引用
  • 特点
1.创建嵌套类的对象,不需要其外围类的对象
2.不能从嵌套类的对象中访问非静态的外围类对象
  • 与普通内部类区别
普通内部类中不能有static数据、字段、嵌套类
public class Test {
    public static void main(String[] args) {
        IPrint printer = Outer.getStaticInner();
        printer.print();

        System.out.println(Outer.StaticInner.InClass.getName());

        new Outer().show();
    }
}

interface IPrint {
    void print();
}

class Outer {
    class Inner {
        // 即使构造函数是private,外围类也可以创建其对象
        private Inner() {}

        private static final String NAME = "NAME:";

        void print(String str) {
            System.out.println(NAME + str);
        }
        // 普通内部类中不能有static 数据,字段,嵌套类
        // static int num = 10;
        // static void print() {}
        // static class InClass {}
    }

    public static StaticInner getStaticInner() {
        return new StaticInner("static");
    }

    public void show() {
        new Inner().print(StaticInner.InClass.name);
    }

    static class StaticInner implements IPrint {
        // 即使是private域,外围类也可以访问
        private static String info;

        private StaticInner(String str) {
            info = str;
        }

        private static String getInfo() {
            return info;
        }

        static class InClass {
            private static String name = "InClass";

            static String getName() {
                return name;
            }
        }

        @Override
        public void print() {
            System.out.println(getInfo() + " " + InClass.getName());
        }
    }
}

/**
编译生成的class文件:
IPrint.class
Outer.class
Outer$Inner.class
Outer$StaticInner.class
Outer$StaticInner$InClass.class
Test.class

输出如下:
static InClass
InClass
NAME:InClass
*/
  • 接口内部的类
嵌套类/内部类可以作为接口的一部分
接口中的任何类都自动地是public和static的
接口中的内部类可以实现其外围接口
public class Test {
    public static void main(String[] args) {
        IPrint.Tools.show(new PrivatePrinter(), "Hello");
        IPrint.Tools.show(new IPrint.Printer(), "Java");
    }
}

interface IPrint {
    void print(String input);

    // 内部类可以作为接口的一部分,并且可以实现其外围接口
    class Printer implements IPrint {
        @Override
        public void print(String input) {
            System.out.println("Printer " + input);
        }
    }

    // 嵌套类可以作为接口的一部分
    static class Tools {
        static void show(IPrint printer, String input) {
            printer.print(input);
        }
    }
}

class PrivatePrinter implements IPrint {
    @Override
    public void print(String input) {
        System.out.println("PrivatePrinter " + input);
    }
}
/**
输出如下:
PrivatePrinter Hello
Printer Java
*/
  • 从多层嵌套类中访问外部类的成员
内部类被嵌套多少层并不重要,它能透明地访问所有它所嵌入的外围类的所有成员
public class Test {
    public static void main(String[] args) {
        new Outer().show();
    }
}

class Outer {
    private Inner.InClass inClass = new Inner().new InClass();

    private void outer(String input) {
        inClass.print(inClass.name + input);
    }

    private class Inner {
        private void inner(String input) {
            inClass.print(inClass.name + input);
        }

        private class InClass {
            private String name = "name:";

            private void print(String input) {
                System.out.println(input);
            }

            private void inClass() {
                // 无论嵌套多少层都可以访问外围类成员,即使是private
                outer("Outer");
                inner("Inner");
            }
        }
    }

    public void show() {
        // 外围类可以访问内部类中所有成员,即使是private
        inClass.inClass();
    }
}

/**
输出如下:
name:Outer
name:Inner
*/

为什么需要内部类

一般来说,内部类继承自某个类或实现某个接口,内部类的代码操作创建它的外围类的对象
内部类提供了某种进入其外围类的窗口
内部类使得多种继承的解决方案变得完整,内部类有效地实现了“多种继承”
当基类和接口包含相同的方法时,要保证具有不同的实现,因此就不能同时继承和实现,这时就可以通过内部类来解决

内部类实现一个接口和外围类实现这个接口有什么区别?

每个内部类都能独立地继承自一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响
  • 内部类特性
1.内部类可以有多个实例,每个实例都有自己的状态信息,并且与其外围类对象的信息相互独立
2.在单个外围类中,可以让多个内部类以不同的方式实现同一个接口,或继承同一个类
3.创建内部类对象的时刻并不依赖于外围类对象的创建
4.内部类并没有令人迷惑的“is-a”关系,它就是一个独立的实体
public class Test {
    public static void main(String[] args) {
        ToolBox mToolBox = new ToolBox("MinCalculator");
        ToolBox.PlusCalculator plus = mToolBox.new PlusCalculator();
        ToolBox.MinusCalculator minus = mToolBox.new MinusCalculator();
        mToolBox.print(mToolBox.calculate(plus, 98, 99));
        mToolBox.print(mToolBox.calculate(minus, 98, 99));
    }
}

interface Calculator {
    int calculate(int input1, int input2);
}

abstract class Print {
    abstract void print(Object obj);
}

class MinSystem {
    String name;

    public MinSystem(String name) {
        this.name = name;
    }
}

class ToolBox extends MinSystem {

    public ToolBox(String name) {
        super(name);
    }

    public void print(Object obj) {
        new Printer().print(obj);
    }

    public int calculate(Calculator calculator, int input1, int input2) {
        return calculator.calculate(input1, input2);
    }

    // 内部类有效地实现了“多种继承”
    private static class Printer extends Print {
        @Override
        void print(Object obj) {
            System.out.println(obj);
        }
    }

    // 可以让多个内部类以不同的方式实现同一个接口
    class PlusCalculator implements Calculator {
        @Override
        public int calculate(int input1, int input2) {
            return input1 + input2;
        }
    }

    class MinusCalculator implements Calculator {
        @Override
        public int calculate(int input1, int input2) {
            return Math.abs(input1 - input2);
        }
    }
}
/**
 输出如下:
197
1
*/
public class Test {
    public static void main(String[] args) {
        String name = "Hello";
        Printer printer = new Printer();
        printer.print(name);
        printer.new InPrinter().print(name);
    }
}

interface IPrint {
    void print(Object obj);
}

abstract class Print {
    abstract void print(Object obj);
}

// 当基类和接口包含相同的方法时,要保证具有不同的实现
// 因此就不能同时继承和实现,这时就可以通过内部类来解决
class Printer extends Print {
    @Override
    void print(Object obj) {
        System.out.println("Print " + obj);
    }

    class InPrinter implements IPrint {
        @Override
        public void print(Object obj) {
            System.out.println("IPrint " + obj);
        }
    }
}
/**
输出如下:
Print Hello
IPrint Hello
*/
  • 闭包与回调
闭包是一个可调用的对象
内部类是面向对象的闭包,它不仅包含外围类对象的信息,还自动拥有一个指向此外围类对象的引用,在此作用域类,内部类有权操作所有的成员,包括private成员
public class Test {
    public static void main(String[] args) {
        new PackageInstaller().install("com.tencent.mobileqq");
    }
}

interface IPackageInstallObserver {
    void packageInstalled(String packageName, int returnCode);
}

class PackageInstaller {
    private PackageManagerService pms = new PackageManagerService();

    private class PackageInstallObserver implements IPackageInstallObserver {
        public void packageInstalled(String packageName, int returnCode) {
            if (returnCode == 1) {
                System.out.println("Install " + packageName + " success");
            }
        }
    }

    public void install(String pkgName) {
        pms.installPackage(new PackageInstallObserver(), pkgName);
    }
}

class PackageManagerService {
    private class InstallParams {
        final IPackageInstallObserver observer;

        InstallParams(IPackageInstallObserver observer) {
            this.observer = observer;
        }
    }

    private static class Installer {
        private static void install(InstallParams args, String pkgName) {
            // 通过InstallParams中的observer对象回调packageInstalled
            // 回调该方法返回状态给PackageInstaller
            args.observer.packageInstalled(pkgName, 1);
        }
    }

    public void installPackage(IPackageInstallObserver observer, String pkgName) {
        // 将observer对象保存到InstallParams中
        Installer.install(new InstallParams(observer), pkgName);
    }
}

/**
输出如下:
Install com.tencent.mobileqq success
*/
  • 内部类与控制框架
内部类常用在控制框架中
应用程序框架:
被设计用来解决某些特定问题的一个类或一组类
要使运用某个应用程序框架,通常是继承一个或多个类,并覆盖某些方法,以解决特定问题
控制框架:一类特殊的应用程序框架,用来解决响应事件的需求
事件驱动系统(GUI):用来响应事件的系统
import java.util.ArrayList;

public class Test {
    public static void main(String[] args) {
        SmartHouseControls controls = new SmartHouseControls();
        controls.setStatus("In");
        controls.run();

        controls.setStatus("Out");
        controls.run();
    }
}

abstract class Event {
    abstract void action();
}

class Controller {
    ArrayList<Event> events = new ArrayList<Event>();

    void addEvent(Event event) {
        events.add(event);
    }

    void run() {
        if (events.size() > 0) {
            for (Event event : new ArrayList<Event>(events)) {
                event.action();
                System.out.println(event);
                events.remove(event);
            }
        }
        System.out.println("Running ok");
    }
}

class SmartHouseControls extends Controller {
    private boolean lightOn = false;
    private boolean windowOn = false;
    private boolean doorOn = false;

    class LightOn extends Event {
        @Override
        void action() {
            lightOn = true;
        }

        public String toString() {
            return "Turn on light";
        }
    }

    class LightOff extends Event {
        @Override
        void action() {
            lightOn = false;
        }

        public String toString() {
            return "Turn off light";
        }
    }

    class WindowOn extends Event {
        @Override
        void action() {
            windowOn = true;
        }

        public String toString() {
            return "Open the window";
        }
    }

    class WindowOff extends Event {
        @Override
        void action() {
            windowOn = false;
        }

        public String toString() {
            return "Close the window";
        }
    }

    class DoorOn extends Event {
        @Override
        void action() {
            doorOn = true;
        }

        public String toString() {
            return "Open the door";
        }
    }

    class DoorOff extends Event {
        DoorOff() {
            if (doorOn)
                addEvent(this);
        }

        @Override
        void action() {
            doorOn = false;
        }

        public String toString() {
            return "Close the door";
        }
    }

    void setStatus(String status) {
        if (status.equals("In")) {
            addEvent(new DoorOn());
            addEvent(new LightOn());
            addEvent(new WindowOn());
            addEvent(new DoorOff());
        } else if (status.equals("Out")) {
            if (lightOn)
                addEvent(new LightOff());
            if (windowOn)
                addEvent(new WindowOff());
        } else {
            System.out.println("System Error!");
        }
    }
}

/**
* 输出如下:
Open the door
Turn on light
Open the window
Close the door
Running ok
Turn off light
Close the window
Running ok
*/

内部类的继承

内部类的构造器必须连接到指向其外围类对象的引用,因此在内部类的导出类构造器中必须使用如下如法:

public class Test {
    public static void main(String[] args) {
        Print.InPrint printer = new Printer(new Print());
        printer.print("Test");
    }
}

class Print {
    class InPrint {
        void print(String str){}
    }
}

class Printer extends Print.InPrint {
    // 内部类的导出类构造函数必须传入外围类对象,函数内必须显示调用外围类对象.super()
    public Printer(Print print) {
        print.super();
    }

    @Override
    void print(String str) {
        System.out.println(str);
    }
}
/*
输出: 
Test
*/

覆盖内部类

需要显式的覆盖,导出类的内部类继承基类的内部类
public class Test {
    public static void main(String[] args) {
        Print printer = new Printer();
        printer.show();
    }
}

class Print {
    InPrint p = new InPrint();

    Print() {
        System.out.println("Print");
    }

    void show() {
        p.print();
    }

    void insertPrint(InPrint ip) {
        p = ip; 
    }

    class InPrint {
        InPrint() {
            System.out.println("Print.InPrint");
        }

        void print() {
            System.out.println("Print.InPrint.print");
        }
    }
}

class Printer extends Print {
    Printer() {
        System.out.println("Printer");
        insertPrint(new InPrint());
    }

    // 必须显式的继承基类的内部类
    class InPrint extends Print.InPrint{
        InPrint() {
            System.out.println("Printer.InPrint");
        }

        void print() {
            System.out.println("Printer.InPrint.print");
        }
    }
}
/*
输出: 
Print.InPrint
Print
Printer
Print.InPrint
Printer.InPrint
Printer.InPrint.print
*/

内部类标识符

内部类也会产生一个.class文件
文件名为外围类$内部类.class
Outer$Inner.class
Outer$Inner$InClass.class
匿名内部类,编译器会产生一个数字作为标识符,如
Outer$1.class
Outer$1Inner.class
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值