JAVA进阶之内部类详解

内部类

1.内部类对外围类的访问

非静态的内部类默认地持有一个外围类的对象的引用,这使得内部类可以随意的访问外围类的所有成员的访问权(包括private).这是源于当某个外围类的对象创建了一个内部类对象时,此内部类对象必定会秘密地捕获一个指向那个外围类对象的引用,然后,在你访问这个外围类的成员时,就是用那个引用来选择外围类的成员.但这一切都是编译器帮我们默默实现的,我们只需要直接使用就可以了.

​ 例如下面这个例子中,在SequenceSelector这个内部类中使用了外围类中的private属性的items数组.

package day13;

public class Sequence {
    //选择器
    interface Selector{
        boolean end();  //判断是否到底
        Object current();   //返回当前对象
        void next();    //移动到下一个对象
    }

    private Object[] items;//存放对象的数组
    private int next = 0;   //确定下一个加入对象的存放位置

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

    public void add(Object x) {
        if (next < items.length) {
            items[next++] = x;
        }
    }
    //选择器的实现
    private class SequenceSelector implements Selector {
        private int i = 0;
        @Override
        public boolean end() {
            return i == items.length;   //使用了外部类的private属性的items
        }

        @Override
        public Object current() {
            return items[i];
        }

        @Override
        public void next() {
            if(i < items.length) i++;
        }
    }
    //用于返回选择器到类外
    public Selector selector(){
        return new SequenceSelector();
    }

    public static void main(String[] args){
        Sequence s = new Sequence(10);
        for(int i = 0;i <10;i++){
            s.add(Integer.toString(i));
        }
        Selector sl = s.selector();
        while(!sl.end()) {
            System.out.println(sl.current());
            sl.next();
        }
    }
}

2…this引用外围类

通过使用外围类名加.this可以获取外围类的引用,并由此访问外围类的任意成员(包括private成员)

例:

package day13;

public class Outer {
    //外围类的private成员
    private int i = 10;
    //外围类Outer的fun();
    void fun(){System.out.println("Outer's fun()");}

    //内部类
    public class Inner{
        Outer getOuter(){return Outer.this;}    //返回外围类的引用
        void fun_0(){Outer.this.fun();}			//访问外围类中的fun()成员方法.
    }

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

    public static void main(String[] args){
        Outer o1 = new Outer(); //先获取外围类
        Inner n1 = o1.getInner();   //获取内部类
        n1.getOuter().fun();    //调用内部类getOuter()获取外围类的引用,再调用外围类的fun();
        System.out.println(n1.getOuter().i);    //访问外围类的private成员
    }
}

3…new创建外围类中的内部类对象

为了创建某个内部类的对象,直接new内部类名是不行的:

如下面这样:

package day13;

class C{
    public class Inner{}
}

public class DotNew{
    public static void main(String[] args) {
        C.Inner ci = new Inner();	//直接new
        C.Inner ci = new C.Inner();	//引用new,也不行
    }
}
/*运行结果:
java: 找不到符号
  符号:   类 Inner
  位置: 类 day13.DotNew
*/

那怎么样才能创建一个内部类呢,除了之前通过在类中定义一个返回内部类的方法以外,还可以使用外围类名.new来创建内部类,如下:

package day13;

class C{
    public class Inner{}	//内部类
}

public class TEST{
    public static void main(String[] args) {
        C c = new C();
        C.Inner ci = c.new Inner();
    }
}

这里必须注意的是,在拥有外围类对象之前是不可能通过这种方式创建内部类,如上面例子中引用的是一个已经实例化的外围类对象c,通过c.new来引用创建的内部类.当然如果你使用的是static静态内部类(嵌套类),那么直接引用外部类名就可以了,如下这样:

package day13;

class C{
    public static class Inner{}	//静态内部类
}

public class DotNew{
    public static void main(String[] args) {
        C c = new C();
        C.Inner ci = new C.Inner();	//直接引用C.Inner();
    }
}

4.内部类与向上转型

当定义了一个接口,而我们利用内部类去实现它,然后通过向上转型得到此接口的引用,这样做可以很方便的隐藏实现的全部细节,因为内部类的实现我们能够完全隐藏起来,并且不可用(private,protected)

如下这个例子:

定义了两个接口Apple和Pear,在类中通过private和protected实现接口,然后提供接口的引用,这样在获取接口的时候,其实现过程是完全不可见的.

package day13;
//Apple接口
interface pple{
    String what();
}
//Pear接口
interface Pear{
    int Price();
}


class C7{
    //通过内部类 private实现Apple接口
    private class PrivateApple implements Apple {
        @Override
        public String what() {
            return "Apple";
        }
    }
    //通过内部类 protected实现Pear接口
    protected class ProtectedPear implements Pear{
        @Override
        public int Price() {
            return 99;
        }
    }

    //将私有的子类PrivateApple向上转型后的返回公有Apple对象引用
    public Apple Apple(){
        return new PrivateApple();
    }
    //将保护的子类ProtectedPear向上转型后返回公有的Pear对象引用
    public Pear Pear(){
        return new ProtectedPear();
    }

}

public class TEST7 {
    public static void main(String[] args){
        C7 c = new C7();
        Apple apple = c.Apple();	//完全不可见实现过程,看不见内部的PrivateApple类
        Pear pear = c.Pear();
    }
}

5.匿名内部类

匿名内部类的代码段中不允许对传入变量进行修改,因此传入的参数必须是final类型,如下面代码中的Base getBase(final int x),如果在代码段中进行 x+= 2是错误的,正如所说,不允许修改。其次调用传参构造也只需按照一般new 类名(参数)即可.因为匿名内部类是没有名字的,因此如果需要做一些类似于构造函数的事情,只能通过重写基类的一个方法,如下所示的fun(),然后实例化后再调用它,达到类似于构造的效果。

package day13;

abstract class  Base {
    private int x;
    //构造函数
    Base(int x) {
        this.x = x;
    }
    //一个显示X的值的方法
    void showX() {
        System.out.println("X = " + x);
    }
    //等待重写用于模仿构造方法
    abstract void fun();
}

class Son {
    
    Base getBase(final int x) {
        return new Base(x) {//匿名内部类开始
            void showX() {
                System.out.println("X = " + x * 2);
            }
			//重写一个基类中的方法,模仿构造函数的意义
            public void fun() {
                System.out.println("fun()模仿构造被调用了");
                //在这里可以写一些类似构造的操作,但这里省略不写,以免代码过于冗杂.
            }
        };
    }
}

public class TEST12 {
    public static void main(String[] args){
        Son s = new Son();
        Base b = s.getBase(20);
        b.fun();    //模仿构造
        b.showX();
    }
}
/*运行结果:
fun()模仿构造被调用了
X = 40
*/

6.简单工厂模式与匿名内部类

这里可能内容有点多,但是很简单,耐心一步一步看就能懂.

先看一个不用匿名内部类的简单工厂模式:

这里主要是一个水果类工厂,其中有一个苹果工厂和一个梨子工厂,通过选择具体的工厂来生成对应的水果.

package day11;

interface Fruit{
    String getName();
    int getPrice();
}
//在下一个例子中将被匿名内部类替换掉的内容
class Apple implements Fruit{
    @Override
    public String getName() {
        return "Apple";
    }
    @Override
    public int getPrice() {
        return 10;
    }
}
//这里也是即将被匿名内部类替换的内容
class Pear implements Fruit{
    @Override
    public String getName() {
        return "Pear";
    }

    @Override
    public int getPrice() {
        return 15;
    }
}

interface Factory{
    Fruit getFruit();
}

class AppleFactory implements Factory{
    @Override
    public Fruit getFruit() {
        return new Apple();
    }
}

class PearFactory implements Factory{
    @Override
    public Fruit getFruit() {
        return new Pear();
    }
}

public class Test0 {
    public static void main(String[] args){
        Factory appleFactory = new AppleFactory();
        Fruit apple = appleFactory.getFruit();
        System.out.println("fruit is " + apple.getName());
    }
}

接下来使用匿名内部进行替换,其实本质就是将重写Fruit的内容移到了new的地方而已,一张图就看明白了,图中以苹果工厂为例:

在这里插入图片描述

使用了匿名内部类后的代码:

package day11;

interface Fruit{
    String getName();
    int getPrice();
}

interface Factory{
    Fruit getFruit();
}

class AppleFactory implements Factory{
    @Override
    public Fruit getFruit() {
        return new Fruit(){	//匿名内部类重写代码块
            @Override
            public String getName() {
                return "Apple";
            }
            @Override
            public int getPrice() {
                return 10;
            }
        };
    }
}

class PearFactory implements Factory{
    @Override
    public Fruit getFruit() {
        return new Fruit(){//匿名内部类重写代码块
            @Override
            public String getName() {
                return "Pear";
            }

            @Override
            public int getPrice() {
                return 15;
            }
        };
    }
}

public class Test0 {
    public static void main(String[] args){
        Factory appleFactory = new AppleFactory();
        Fruit apple = appleFactory.getFruit();
        System.out.println("fruit is " + apple.getName());
    }
}
/*运行结果:
fruit is Apple
*/

上面的替换仅仅是具体水果类的替换,下面还有这样更高级的替换:

还是先看一下图解:

在这里插入图片描述

package day11;
//抽象水果类
interface Fruit{
    String getName();
    int getPrice();
}
//工厂
interface Factory{
    Fruit getFruit();
}
//苹果类(自带匿名工厂)
class Apple implements Fruit{
    //将构造函数设为私有,可以让类外不能实例化这个类,达到内部实现过程隐藏
    private Apple(){}
    //重写Apple
    @Override
    public String getName() {
        return "Apple";
    }
    @Override
    public int getPrice() {
        return 10;
    }
    public static Factory factory = new Factory() {	//匿名工厂类
        @Override
        public Fruit getFruit() {
            return new Apple();
        }
    };
}
//梨子类(自带匿名工厂)
class Pear implements Fruit{
    //同样的道理私有化构造方法
    private Pear(){}
    //重写Pear
    @Override
    public String getName() {
        return "Pear";
    }

    @Override
    public int getPrice() {
        return 15;
    }
    public static Factory factory = new Factory() {	//匿名工厂类
        @Override
        public Fruit getFruit() {
            return new Pear();
        }
    };

}

public class Test0 {
    public static void main(String[] args){
        Fruit apple = Apple.factory.getFruit();	//创造苹果
        System.out.println(apple.getName());

        Fruit pear = Pear.factory.getFruit();	//创造梨子
        System.out.println(pear.getName());
    }
}
/*运行结果:
Apple
Pear
*/

一个模拟数据库的工厂模式实例:

package day14;

import com.sun.org.apache.xpath.internal.operations.Or;

//抽象数据库
interface Databese{
    String getName();
    String getIp();
}

//抽象工厂
interface Factory{
    Databese getDatabase(String ip);
}

//具体数据库
class MySQL implements Databese{
    String ip;
    private MySQL(String ip){this.ip = ip;};	//禁止MySQL这个类直接实例化
    @Override
    public String getName() {
        return "MySQL";
    }

    @Override
    public String getIp() {
        return ip;
    }
    public static Factory Factory(){
        return new Factory() {
            @Override
            public Databese getDatabase(String ip) {
                return new MySQL(ip);
            }
        };
    }
}

class Oracle implements Databese {
    String ip;
    //禁止直接实例化
    private Oracle(String ip) {
        this.ip = ip;
    }

    @Override
    public String getName() {
        return "Oracle";
    }

    @Override
    public String getIp() {
        return ip;
    }

    public static Factory Factory(){
        return new Factory(){
            @Override
            public Databese getDatabase(String ip) {
                return new Oracle(ip);
            }
        };
    }
}


//实现
public class Demo4 {
    public static void main(String[] args){
        Databese mySql = MySQL.Factory().getDatabase("192.168.1.1");
        System.out.println("MySQL's name  : " + mySql.getName() + "\tMySQL's ip : " + mySql.getIp());
        Databese oracle = Oracle.Factory().getDatabase("127.0.0.1");
        System.out.println("Oracle's name : " + oracle.getName() + "\tOracle's ip : " + oracle.getIp());
    }
}

7.最简单的控制框架

通过将变的与不变的进行分离,让变的部分可以被重写,这是最基本的模板框架.在事件控制框架中,将定义的事件需要触发的action()进行分离,根据不同的需要重写不同的行为,以此达到框架的目的:

package day15;

import java.util.*;

abstract class Event{
    private long eventTime;	//触发action的时间
    protected final long delayTime;	//从当前时间开始,隔delayTime就这么久后触发action
	//构造设置delayTime
    public Event(long delayTime) {
        this.delayTime = delayTime;
        start();
    }
	//确定开始时间
    public void start(){
        eventTime = System.nanoTime() + delayTime;
    }
	//确定事件是否可以运行了
    public boolean ready(){
        return System.nanoTime() >= eventTime;
    }
	//运行采取的行为,这是每个事件可能不同的地方,让它可以被重写且必须被重写
    public abstract void action();

}

class Controller {
    //事件列表
    private List<Event> eventList = new ArrayList<Event>();
	//添加新事件到列表中
    public void addEvent(Event e) {
        eventList.add(e);
    }
	//在事件列表中检索有无可以开始运行的事件,有就运行并将它从事件列表中移除
    public void run() {
        while (eventList.size() > 0) {
            for (Event e : eventList) {      //为什么要重新new一个ArrayList???
                if (e.ready()) {
                    System.out.println(e);
                    e.action();
                    eventList.remove(e);
                }
            }
        }
    }

}


public class demo6 {
    public static void main(String[] args) {
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

浔汐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值