关于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
}

三、内部类
1、内部类指的是在一个类里面定义的另一个类,java编译内部类生成的.class文件名是:outerClass$InnerClass,如果在外部类的非静态方法之外创建内部类对象:OutClassName.InnerClassName
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();
	}
}

其实当内部类创建的时候,他会秘密捕获一个外部类的引用,当我们创建B的实例的时候,内部类是还没创建出来的,这个可以自己去试验一下(像上面的C一样搞一个构造块,new B的时候c的构造块并没有被执行)我们在创建C的时候,如果c中有b的成员变量,c是能访问的,在创建的层面上看起来并没有什么联系的两个类,除了内外关系,却能在访问层面上访问外围类的成员变量,是不是挺有趣~呵呵,能够使他访问外部类的所有成员而不需要任何特殊的条件。一般来说,我们可以通过内部类的代码操作外部类的对象,内部类更大的一个优点是,内部类能够实现一个接口,他跟外部类是否实现接口并没有特别大的关系,可以这样说,他是java多重继承问题的一个补充
可以通过以下例子去说明这个优点:
1)在一个类里面实现两个接口,除了普通的implement两个Inteface,我们还可以通过内部类的方法去实现他:
看两个接口:
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的逻辑
		}};
	}
}
当使用到Y实现的B功能时,直接y.makeB()返回在Y处定义的匿名内部类实例

2)对于上面的那种情况我们的条件是使用inteface,但是,如果我们并没有inteface,而是abstract的类,这时候只能用extends的方法去实现一个功能的时候,又需要extends多个类,试看下面的方法去处理多重继承:
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
		}};
	}
}
作为内部类,在处理多重继承方面自然有他的特性,比如:
1)内部类的独立性,每个内部类都可以有他自己特定的状态信息,但是却能够独立于外部类,他没有is-a关系,他就是一个独立的实体
2)可以使用多个内部类继承、实现同一个接口和类
3)创建内部类的时刻不依赖于外部类的创建,就像刚才所说,可以在外部类的非静态方法之外使用OutClassName.InnerClassName来创建一个内部类

2、内部类不仅可以定义在方法作用域之外,而且可以定义在方法作用域之内,但是只能在作用域之内使用它,例如在if()语句里面,就要在if语句里面使用,不能超出其范围,详细代码就不列举了
3、匿名内部类:值得注意的是匿名内部类的实例初始化,它可以达到为匿名内部类创建一个构造器的效果,假如内部类的存在构造方法,那么可以在创建匿名内部类的时候通过传参来初始化里面的值,此时,初始化的那个参数不一定是fina的,因为他并不会在匿名内部类中直接使用,为什么要用final,可以看上面final的细节
4、根据内部类的独立性和安全性,这很好的运用到工厂模式当中:一般来说工厂方法并不是用内部类去实现,关于详细的工厂方法模式将会在另一篇博客里总结
假设有两个服务类,我想利用工厂方法去生产它们,我们先为他们定义一个共同的接口Service和一个工厂接口
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);
	}
}
这样子,客户端程序猿就不用在意工厂是怎么生产类的实例,他们只需要调用工厂方法去得到实例
一般来说,工厂需要用到接口(抽象工厂在这部分做的很明显),所以能够给我们带来“让子类去做决定”这个特性,让子类去生产我们需要的类——他们在高层框架实质上不知道要生产什么类。在这里使用内部类的方法实现工厂方法,并没有遵循优先使用接口编程这个原则,其实有些时候,视乎情况,我们是优先使用类的
5、除了利用内部类去实现一个工厂方法,还会涉及到另外一个模式:命令模式——利用内部类提供一个框架响应一系列事件的需求,关于命令模式在另一篇博客里总结。

应用程序框架:就是被设计用来解决某类特定的问题的一个类或者一组类。要运用某个应用程序框架,通常是继承一个或者多个类,并覆盖某些方法。在覆盖的方法中,编写代码定制应用程序框架提供的通用解决方案。(模版方法模式)控制框架是一类特殊的的应用程序框架,他用来解决响应事件的需求。主要用来响应事件的系统被称为事件驱动系统。

这个例子涉及到命令模式,但是阅读并不会造成障碍:存在一个控制框架,他的工作就是在事件就绪的时候执行事件(本例是基于时间触发的事件)
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编程思想》,它总结了内部类在控制框架上的优点:
(1)、控制框架的完整实现是由单个的类创建的,从而使得实现的细节被封装起来,内部类用来表示解决问题必须的各种不同的action
(2)、内部类能够很容易地访问外围类的任意成员,所以可以避免这种实现变得笨拙。如果没有这种能力,代码会令人生厌,以至于你会选择别的方法

6、如果把普通的内部类声明为static,那么我们可以把他称为嵌套类,普通的内部类不能有static数据和字段,嵌套类意味着:
1)要创建嵌套类的对象,并不需要其外围类的对象
2)不能从嵌套类的对象中访问非静态的外围类对象
当然是不能使用this关键字的,也可以在接口里面写嵌套类(接口里面任何类隐式是static 和public,并不违反规则,而常量还是final的)

以上是关于关于java的final接口内部类细节的主要内容,如果未能解决你的问题,请参考以下文章

JAVA07 面向对象(高级)类变量类方法代码块final抽象类接口内部类

JAVA07 面向对象(高级)类变量类方法代码块final抽象类接口内部类

JAVA07 面向对象(高级)类变量类方法代码块final抽象类接口内部类

什么叫final修饰符?有什么作用?

java提高篇-----详解匿名内部类 ,形参为什么要用final

8 面向对象之抽象类+接口+内部类