关于java的final接口内部类细节
Posted Monkey_Dog
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关于java的final接口内部类细节相关的知识,希望对你有一定的参考价值。
一、final关键字
final关键字大概可以分成三部分讨论:final数据、方法、类
首先是数据:
final数据在恒定不变的时候是很有用的,比如:
1、一个永不改变的编译时常量
2、一个在运行时被初始化的值,而你不希望它被改变
而一个static final的域只是占据一段不能改变的存储空间
细节:
1、如果是final的基本数据类型:那么这个数值是恒定不变,而final的对象引用,那么这个引用是不能改变,但是引用里面的内容是可以被修改的,如:
final int[] a = {1、2、3、4、5};
则a[i]是可以修改,例如,a[i]++(0<=i<5)
同样,可以尝试用final去修饰一个对象,在其他地方修改对象内的非final值,发现是能够被修改的,比如我们有一个final 的StringBuilder对象:
public class A { private final StringBuilder s = new StringBuilder("Hello"); private final String ss = "abc"; public static void main(String[] args) { A a = new A(); //StringBuilder能改变内容,字符串会输出HelloWorld a.s.append("World"); System.out.println(a.s); //这里却不行,因为返回的是一个新串,而final的引用不能被改变 //a.ss = "def"; //关于String跟StringBuilder下一篇博客研究一下 } }
2、空白final:被声明为final但又未给定初值的域,此时只能通过在构造方法里面给final赋值
3、final参数:说明他是只读的而不能被修改,其实这主要作用于匿名内部类,例如View.onClickListener();这个我们经常用的响应点击事件的方法,如果以匿名内部类的形式实现,这是匿名内部类的生命周期跟局部变量的生命周期不一致性导致的规则:
设方法f被调用,从而在它的调用栈中生成了变量i,此时产生了一个局部内部类对象inner_object,它访问了该局部变量i .当方法f()运行结束后,局部变量i就已死亡了,不存在了.但:局部内部类对象inner_object还可能 一直存在(只能没有人再引用该对象时,它才会死亡),它不会随着方法f()运行结束死亡.这时:出现了一个"荒唐"结果:局部内部类对象 inner_object要访问一个已不存在的局部变量i!而final的方式是给匿名内部类复制一份跟参数一样的值,那么匿名内部类未消亡的时候访问的变量也就不再是被销毁过的了
4、final禁止继承,final类里面的方法隐式是final的
参考:http://feiyeguohai.iteye.com/blog/1500108
5、方法:主要用于将方法锁定,不允许继承来修改他的含义,即使在子类final方法的名字跟父类的一样,这样也并不代表能继承,而是一个属于子类自己的方法,实质上所有private成员都会被默认当作用final修饰,使用final还有一个功能可以说是关闭“动态绑定”,什么是动态绑定和静态绑定可以参考:
http://blog.sina.com.cn/s/blog_600046120100wdza.html
类:当用final修饰类时,说明这个类已经是被认为是完美的也是为了出于安全性的考虑,并不需要被继承,final类的所有方法隐式为final的
二、接口
1、首先简单辨别一下接口(Interface)跟抽象(abstract):
abstract:仅仅提供一个抽象方法的机制,希望通过创建抽象类来提供这个接口里操纵一系列的类,如果一个类内含有抽象方法,则类必须声明为抽象类
interface:表示所有的实现看起来都像这样,是提供一个完全抽象的类,可以这样子描述:interface只是外貌,他需要被声明为如何工作的,而面向接口编程,接口的设计应该视实际情况而定,而不是实用接口提供了各种间接性是程序复杂,关于更多在以下内部类中提及
从功能特性来说,一个是为操纵子类,一个为描述实现这个接口的类
2、接口是可以被继承的,继承的子接口扩展接口的方法,接口也可以创建常量组,这也是一种很方便快捷的工具,访问的时候直接:接口名.常量名,在接口里面,这些隐式是static和final、public的,但这些却不能是空final的
3、嵌套接口:
1)、类里嵌套接口:引用java编程思想的例子:
public class A { //以默认权限的方法声明接口B interface B{ void f(); } //public、private 的内部类 public class BImp implements B{ public void f() {} } private class BImp2 implements B{ public void f() {} } //以public的方法声明接口C public interface C{ void f(); } //默认权限、private的内部类 class CImp implements C{ public void f() {} } private class CImp2 implements C{ public void f() {} } private interface D{ void f(); } private class DImp implements D{ public void f() {} } public class DImp2 implements D{ public void f() {} } public D getD(){return new DImp2();} private D dref; public void receiveD(D d){ dref = d; dref.f(); } }Main方法:
public class Main { public static void main(String[] args) { // TODO Auto-generated method stub A a = new A(); //首先不能直接访问A.D //A.D ad = a.getD(); //A.DImp2 di2 = a.getD();不能返回D,因为是private的 A.DImp2 di2 = (DImp2) a.getD();//需要向下转型才能通过编译 //a.getD().f();不能通过编译,因为D是private的,返回的D无法在其他类访问 a.receiveD(a.getD());//这个可以,并没直接访问到A的private域 } }
而嵌套在接口里面的接口:
public class NestingInterfaces { //可以访问本包内的A.B、A.C public class BImp implements A.B{ public void f(){} } class CImp implements A.C{ public void f(){} } //但是A.D是private的,不能A.D这样去实现它 //可以实现一个嵌套接口最外面那层 class EImp implements E{ public void g(){} } //也可以实现里面某一个接口 class EGImp implements E.G{ public void f(){} } //也可以在内部类方式上两个都实现 class EImpl2 implements E{ public void g() {} class EG implements E.G{ public void f(){} } } //也可以把上面那种调转,先实现E.G,再在内部类实现E }
public class B { public static int i = 0; static{ System.out.println("hello"); } public C getC(){return new C();} public class C{ {System.out.println("World");} } } public class A { public static void main(String[] args) { B b = new B(); B.C c = b.getC(); } } public class A { public static void main(String[] args) { B b = new B(); //B.C c = new B.C();不能通过编译,这样却可以:B C c = b.new C(); B.C c = b.getC(); } }
public interface A { public void testA(); } public interface B { public void testB(); }我们用普通的方法去实现它:
public class X implements A ,B{ @Override public void testA() { //实现A接口的逻辑 } @Override public void testB() { //实现B接口的逻辑 } }也可以用内部类的方法去实现:
public class Y implements A{ @Override public void testA() { //处理A的逻辑 } B makeB(){ <span style="white-space:pre"> </span>return new B(){ @Override public void testB() { //处理B的逻辑 }}; } }
public class D { public void testD(){} } public abstract class E { public void test(){} }一个是普通类,一个是抽象类,我们可以集成普通类,是用内部类去实现抽象类的方法处理多重继承,通过内部类继承,提供操作外部类的行为
public class Z extends D { @Override public void testD() { //处理D } E makeE(){ return new E() { @Override public void test() { //处理E }}; } }作为内部类,在处理多重继承方面自然有他的特性,比如:
public interface Service { void method1(); void method2(); } public interface ServiceFactory { Service getService(); }为了保持制造过程的独立性和安全性,把生产过程作为内部类封装在服务类里边
public class Implementation1 implements Service { //私有构造方法,禁止外部通过new创建实例 private Implementation1(){} //利用匿名内部类实现工厂接口,创造实例 public static ServiceFactory factory = new ServiceFactory() { @Override public Service getService() { // TODO Auto-generated method stub return new Implementation1(); } }; @Override public void method1() {System.out.println("Implementation1 method1");} @Override public void method2() {System.out.println("Implementation1 method2");} }
public class Implementation2 implements Service { private Implementation2(){} public static ServiceFactory factory = new ServiceFactory() { @Override public Service getService() { return new Implementation2(); } }; @Override public void method1() {System.out.println("Implementation2 method1");} @Override public void method2() {System.out.println("Implementation2 method2");} }
那么我们可以这样子去使用:
public class Factories { public static void serviceConsumer(ServiceFactory fact){ Service s = fact.getService(); s.method1(); s.method2(); } public static void main(String[] args) { serviceConsumer(Implementation1.factory); serviceConsumer(Implementation2.factory); } }这样子,客户端程序猿就不用在意工厂是怎么生产类的实例,他们只需要调用工厂方法去得到实例
一般来说,工厂需要用到接口(抽象工厂在这部分做的很明显),所以能够给我们带来“让子类去做决定”这个特性,让子类去生产我们需要的类——他们在高层框架实质上不知道要生产什么类。在这里使用内部类的方法实现工厂方法,并没有遵循优先使用接口编程这个原则,其实有些时候,视乎情况,我们是优先使用类的
public abstract class Event { private long eventTime; protected final long delayTime; public Event(long delayTime){ this.delayTime = delayTime; start(); } //获取当前时间,并加上一个延迟时间,生成触发器的时间 //可以通过调用start重新启动计时器,System.nanoTime()是当前的精确时间 //如果需要重复一个事件,只需简单地在action中调用一下start方法,不过前提是在工作的Controller类的list中存在这个事件 public void start(){ eventTime = System.nanoTime()+delayTime; } //当当前时间到达要执行的时间 public boolean ready(){ return System.nanoTime()>=eventTime; } public abstract void action(); } public class Controller { private List<Event> eventList = new ArrayList<Event>(); public void addEvent(Event c){eventList.add(c);} //遍历eventList,寻找就绪的ready、要运行的event对象 public void run(){ while(eventList.size()>0){ //把eventList复制一份是为了你在访问eventlList的同时修改他 //有同步的感觉 for(Event e:new ArrayList<Event>(eventList)){ if(e.ready()){ System.out.println(e); e.action(); eventList.remove(e); } } } } }到了这里控制框架基本完成了,现在我们就用内部类去为他提供具体的实现,他能在单一的类里面产生对同一个基类Event的多种导出版本
public class GreenhouseControls extends Controller { //灯光 private boolean light = false; public class LightOn extends Event{ public LightOn(long delayTime) {super(delayTime);} @Override public void action() {light = true;} public String toString(){return "Light is on";} } public class LightOff extends Event{ public LightOff(long delayTime) {super(delayTime);} @Override public void action() {light = true;} public String toString(){return "Light is off";} } //水分 private boolean water = false; public class WaterOn extends Event{ public WaterOn(long delayTime) {super(delayTime);} @Override public void action() {water = true;} public String toString(){return "Water is on";} } public class WaterOff extends Event{ public WaterOff(long delayTime) {super(delayTime);} @Override public void action() {water = false;} public String toString(){return "Water is off";} } //温度(白天、黑夜温度) private String thermostat = "Day"; public class ThermostatNight extends Event{ public ThermostatNight(long delayTime) {super(delayTime);} @Override public void action() {thermostat = "Night";} public String toString(){return "thermostat is Night";} } public class ThermostatDay extends Event{ public ThermostatDay(long delayTime) {super(delayTime);} @Override public void action() {thermostat = "Day";} public String toString(){return "thermostat is Day";} } //响铃 public class Bell extends Event{ public Bell(long delayTime) {super(delayTime);} @Override public void action() { //在这里不停的addEvent,这个方法是在Controller的run方法里面被调用 //就是说,执行一次这个方法,就往eventList里面加入一个新的Bell事件 //注意到run方法的循环条件是当eventList长度不为0的时候一直访问,可以看出这里是无限响铃的 addEvent(new Bell(delayTime)); } public String toString(){return "Bing";} } //重启系统 public class Restart extends Event{ private Event[] eventList; public Restart(long delayTime,Event[] eventList) { super(delayTime); this.eventList = eventList; for(Event e:eventList){ addEvent(e); } } //跟Bell类一样,重启系统会重复把你指定的list加入到eventList里面起,循环访问 //同时也将本事件加入到里面 @Override public void action() { for(Event e:eventList){ //start是为了让事件有一个新的延迟触发时间 e.start(); addEvent(e); } start(); addEvent(this); } public String toString(){return "Restart System";} } //系统的终止方法 public static class Teeminate extends Event{ public Teeminate(long delayTime) {super(delayTime);} public void action(){System.exit(0);} public String toString(){return "Terminating";} } }我们这main方法:
public class GreenhouseController { public static void main(String[] args) { GreenhouseControls gc = new GreenhouseControls(); gc.addEvent(gc.new Bell(900)); Event[] eventList = { gc.new ThermostatNight(0), gc.new LightOn(200), gc.new LightOff(400), gc.new WaterOn(600), gc.new WaterOff(600), gc.new ThermostatDay(1400), }; gc.addEvent(gc.new Restart(2000, eventList)); //我们在前面提到过,gc的run是不断地访问的,因为存在restart和bell,所以如果不想循环了,可以提供一个参数执行终止方法 if(args.length==1){ gc.addEvent(new GreenhouseControls.Teeminate(new Integer(args[0]))); } gc.run(); } }以上这个例子来自于《java编程思想》,它总结了内部类在控制框架上的优点:
以上是关于关于java的final接口内部类细节的主要内容,如果未能解决你的问题,请参考以下文章
JAVA07 面向对象(高级)类变量类方法代码块final抽象类接口内部类
JAVA07 面向对象(高级)类变量类方法代码块final抽象类接口内部类
JAVA07 面向对象(高级)类变量类方法代码块final抽象类接口内部类