J004-[Java菜鸟系列] 内部类老湿:在上一节我们说到,我们需要让计算机知道,事件发生的时候该调用什么方法所以,我们需要建立一个类,签署一个协议,之后我们把这个对象传递给相应的函数….菜鸟:好了,你不要讲了!啰里啰嗦的,还有一天就9102年了好不好,他妈的这么麻烦,你到底有没有本事啊,工资还想不想要了?老湿:(⊙o⊙)… 息怒,息怒,同学你好性急啊,今天我们就要讲到这个事情,这份工资我是有信心拿的到的,看我的,瞧好吧。两个尴尬:繁琐和危险的确,很多时候,我们只需要用一次方法就够了,又要声明类,又要实例化,写半天只用一次,实在是没什么必要。还有个问题,我们要向方法中传数据,如果方法就在类中,那么完全可以设数据为private;但是现在需要新建一个类,所以为了让它读取数据,必须要搞getter,既麻烦又不安全。案例计时器:我们需要让计算机每隔一定的时间报时和响铃一次。第一个情况是简化版,不使用Timer,第二个情况是正常版本,使用Timer回调。示例-J004-1:TalkingClockSimple//如果我们不需要使用回调,那么beep就可以是private的。public class TalkingClockSimple { public static void main(String[] args) { new TalkingClockSimple(true).start(); } private boolean beep; public TalkingClockSimple( boolean beep) { this.beep = beep; } public void start() { System.out.println(“At the tone,the time is " + new Date()); if (TalkingClockSimple.this.beep) Toolkit.getDefaultToolkit().beep(); }}示例-J004-2:TalkingClockExterior//如果我们使用回调,那么动作就在外部的类中,为了让其能获取beep,我们必须搞一个getter。而且整个过程都非常繁琐。public class TalkingClockExterior { public static void main(String[] args) { new TalkingClockExterior(1000, true).start(); } private int interval; private boolean beep;//为了让方法得到数据,我们必须要开放一个getter public boolean isBeep() { return beep; } public TalkingClockExterior(int interval, boolean beep) { this.interval = interval; this.beep = beep; }public void start() { TimePrinterExterior timeprinter = new TimePrinterExterior(this); new Timer(interval,timeprinter).start(); JOptionPane.showMessageDialog(null, “Quit program?”); System.exit(0);}}//这个类我们明明只需要用一次,但是它现在在在包内都是可见的,完全没这个必要。我是没办法的事情,因为class的可见性就只有两种,要不就是默认的包内,要不就是public。 class TimePrinterExterior implements ActionListener { public TimePrinterExterior(TalkingClockExterior talkingclock) { this.talkingclock = talkingclock; } @Override public void actionPerformed(ActionEvent e) { System.out.println(“At the tone,the time is " + new Date()); if (talkingclock.isBeep()) Toolkit.getDefaultToolkit().beep(); } TalkingClockExterior talkingclock;}最大的问题是安全-内部类俗话说得好,留得青山在,不怕没柴烧,只要小命在,什么都好说。安全这个问题永远是最重要的。前面我们提到了之前方案的两个重要缺点,一个是"麻烦”,另一个是"不安全”,本着"要事第一"的原则,我们首先解决它的安全性问题。那我们究竟该如何解决呢?在移动通信领域,高通垄断了高端基带,所以其他公司主要生产手机就必须交钱。高通基带谁也可以买,是公共的,厂商很难在这个层面搞创术,就算搞了,也很容易被仿制。不仅这样,还交大量的专利费,这又被称为"高通税",更可恶的是,专利费不是按照芯片来收,而是按照整机的售价抽取一定比例。苹果的老大库克终于坐不住了,他娘的,老资苹果公司还要看你高通脸色,“老资不跟你玩了!"。苹果一气之下放弃了高通的方案,拉上了同样失意的小兄弟英特尔,一起搞了一个新的基带,自研自产自销。由于是自研技术,当然不用再交专利费,再添加什么其他功能也非常的安全,软硬深度结合,不怕被抄袭了。看完这个例子,你知道我要说什么了吧?既然公共的这么不安全,那我们干脆自己搞一个吧,这就是"内部类"了。内部类怎么搞?其实很简单,我们把class的定义移动到TalkingClock内部即可。重要的是我们的内部类是支持private关键字的,也就是说,他在包内是完全不可见的,这非常安全;另外,由于它处于TalkingClock的内部,所以也不需要给beep设置getter,内部类可以直接访问外围类的数据。public class TalkingClock { private int interval; private boolean beep; public TalkingClock(int interval, boolean beep) { this.interval = interval; this.beep = beep; }public void start() { new Timer(interval,new TimePrinter()).start(); //new Timer(interval,this.new TimePrinter()).start(); this是可以省略的} private class TimePrinter implements ActionListener { @Override public void actionPerformed(ActionEvent e) { System.out.println(“At the tone,the time is " + new Date()); //内部类可以直接访问private beep if (beep) Toolkit.getDefaultToolkit().beep(); //if (TalkingClock.this.beep) Toolkit.getDefaultToolkit().beep(); TalkingClock.this是可以省略的 } }}不过在这里要特别提示的是,内部类不会随着外围类的实例化而实例化,如果需要实例化它,那么必须调用其构造器。没有不透风的墙要说明的是,这种内部类并不是绝对的安全。内部类并不是一个虚拟机机制,而只是编译器的把戏罢了,所以如果黑客非常清楚class的结构,是可以调用虚拟机指令获取内部类数据的。不过不用过分担心,在大多数时候,安全性已经足够了。具体"编译器"玩的什么把戏,这里就不介绍了,稍微有点复杂,用到再详细说。得寸进尺-匿名内部类好了,我们已经解决了安全的问题,我们需要更简洁一些。比喻:很多时候,我们需要给东西起一个"名字”,主要原因是因为我们需要多次用到它。如果一个东西,我们只用它一次,你就懒得给他起一个特殊的名字。比如,上茅房的时候,我们都会用手纸屁股,你不会给每一张手纸起一个名字的,因为没有必要,他很快就会进入马桶了,这辈子你都见不到他了。同理,但很多情况下,我们只需要用到类的实例一次,那么我们就直接舍弃名字,这样会方便好多。这被称为"匿名内部类”。class TalkingClockAnonymous {private int interval;private boolean beep; public static void main(String[] args) { new TalkingClockAnonymous(1000,true).start(); JOptionPane.showMessageDialog(null, “Quit program?”); System.exit(0); } public TalkingClockAnonymous(int interval, boolean beep) { this.interval = interval; this.beep = beep; } public void start() { //匿名内部类 ActionListener actionListener= new ActionListener(){ @Override public void actionPerformed(ActionEvent e) { System.out.println(“At the tone,the time is " + new Date()); if (beep) Toolkit.getDefaultToolkit().beep(); } }; new Timer(interval,actionListener).start();}}人心向简-lambda表达式对于一块手纸来说,我们还是搞得太繁琐了,竟然只用一次,那么连他的对象我们也干脆省略了名字吧。另外,我们也不需要指定方法的参数,因为很明显,只要我们传入的地方要求ActionListener协议,那么方法的参数就一定是ActionEvent了。public class TalkingClocklambda { public static void main(String[] args) { new TalkingClocklambda().start(1000, true); JOptionPane.showMessageDialog(null, “Quit program?”); System.exit(0); } public void start(final int interval, final boolean beep) { new Timer(interval, e -> { System.out.println(“At the tone,the time is " + new Date()); if (beep) Toolkit.getDefaultToolkit().beep(); }).start(); }}End心如止水是Java/AHK持续学习者,欢迎您来和我探讨Java/AHK问题 ^_^版权声明:该文章版权系“心如止水”所有,欢迎分享、转发,但如需转载,请联系QQ:2531574300,得到许可并标明出处和原链接后方可转载。未经授权,禁止转载。版权所有 ©心如止水 保留一切权利。