能够将一个类的定义放在另外一个类的定义内部,这就是内部类。html
内部类是一个很是有用的特性但又比较难理解使用的特性(鄙人对内部类也只是略知一二)。java
第一次见面
内部类咱们从外面看是很是容易理解的,无非就是在一个类的内部在定义一个类。编程
1 public class OuterClass { 2 private String name ; 3 private int age; 4 5 public String getName() { 6 return name; 7 } 8 9 public void setName(String name) { 10 this.name = name; 11 } 12 13 public int getAge() { 14 return age; 15 } 16 17 public void setAge(int age) { 18 this.age = age; 19 } 20 21 class InnerClass{ 22 public InnerClass(){ 23 name = "chenssy"; 24 age = 23; 25 } 26 } 27 }
在这里InnerClass就是内部类,对于初学者来讲内部类实在是使用的很少,鄙人菜鸟一个一样没有怎么使用过,可是随着编程能力的提升,咱们会领悟到它的魅力所在,它可使用可以更加优雅的设计咱们的程序结构。在使用内部类之间咱们须要明白为何要使用内部类,内部类可以为咱们带来什么样的好处。函数
1、为何要使用内部类
为何要使用内部类?在《Think in java》中有这样一句话:使用内部类最吸引人的缘由是:每一个内部类都能独立地继承一个(接口的)实现,因此不管外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响。this
在咱们程序设计中有时候会存在一些使用接口很难解决的问题,这个时候咱们能够利用内部类提供的、能够继承多个具体的或者抽象的类的能力来解决这些程序设计问题。能够这样说,接口只是解决了部分问题,而内部类使得多重继承的解决方案变得更加完整。编码
1 public interface Father { 2 3 } 4 5 public interface Mother { 6 7 } 8 9 public class Son implements Father, Mother { 10 11 } 12 13 public class Daughter implements Father{ 14 15 class Mother_ implements Mother{ 16 17 } 18 }
其实对于这个实例咱们确实是看不出来使用内部类存在何种优势,可是若是Father、Mother不是接口,而是抽象类或者具体类呢?这个时候咱们就只能使用内部类才能实现多重继承了。url
其实使用内部类最大的优势就在于它可以很是好的解决多重继承的问题,可是若是咱们不须要解决多重继承问题,那么咱们天然可使用其余的编码方式,可是使用内部类还可以为咱们带来以下特性(摘自《Think in java》):spa
一、内部类能够用多个实例,每一个实例都有本身的状态信息,而且与其余外围对象的信息相互独立。.net
二、在单个外围类中,可让多个内部类以不一样的方式实现同一个接口,或者继承同一个类。设计
三、建立内部类对象的时刻并不依赖于外围类对象的建立。
四、内部类并无使人迷惑的“is-a”关系,他就是一个独立的实体。
五、内部类提供了更好的封装,除了该外围类,其余类都不能访问。
2、内部类基础
在这个部分主要介绍内部类如何使用外部类的属性和方法,以及使用.this与.new。
当咱们在建立一个内部类的时候,它无形中就与外围类有了一种联系,依赖于这种联系,它能够无限制地访问外围类的元素。
1 public class OuterClass { 2 private String name ; 3 private int age; 4 5 /**省略getter和setter方法**/ 6 7 public class InnerClass{ 8 public InnerClass(){ 9 name = "chenssy"; 10 age = 23; 11 } 12 13 public void display(){ 14 System.out.println("name:" + getName() +" ;age:" + getAge()); 15 } 16 } 17 18 public static void main(String[] args) { 19 OuterClass outerClass = new OuterClass(); 20 OuterClass.InnerClass innerClass = outerClass.new InnerClass(); 21 innerClass.display(); 22 } 23 } 24 -------------- 25 Output: 26 name:chenssy ;age:23
在这个应用程序中,咱们能够看到内部了InnerClass能够对外围类OuterClass的属性进行无缝的访问,尽管它是private修饰的。这是由于当咱们在建立某个外围类的内部类对象时,此时内部类对象一定会捕获一个指向那个外围类对象的引用,只要咱们在访问外围类的成员时,就会用这个引用来选择外围类的成员。
其实在这个应用程序中咱们还看到了如何来引用内部类:引用内部类咱们须要指明这个对象的类型:OuterClasName.InnerClassName。同时若是咱们须要建立某个内部类对象,必需要利用外部类的对象经过.new来建立内部类: OuterClass.InnerClass innerClass = outerClass.new InnerClass();。
同时若是咱们须要生成对外部类对象的引用,可使用OuterClassName.this,这样就可以产生一个正确引用外部类的引用了。固然这点实在编译期就知晓了,没有任何运行时的成本。
1 public class OuterClass { 2 public void display(){ 3 System.out.println("OuterClass..."); 4 } 5 6 public class InnerClass{ 7 public OuterClass getOuterClass(){ 8 return OuterClass.this; 9 } 10 } 11 12 public static void main(String[] args) { 13 OuterClass outerClass = new OuterClass(); 14 OuterClass.InnerClass innerClass = outerClass.new InnerClass(); 15 innerClass.getOuterClass().display(); 16 } 17 } 18 ------------- 19 Output: 20 OuterClass...
到这里了咱们须要明确一点,内部类是个编译时的概念,一旦编译成功后,它就与外围类属于两个彻底不一样的类(固然他们之间仍是有联系的)。对于一个名为OuterClass的外围类和一个名为InnerClass的内部类,在编译成功后,会出现这样两个class文件:OuterClass.class和OuterClass$InnerClass.class。
在Java中内部类主要分为成员内部类、局部内部类、匿名内部类、静态内部类。
3、成员内部类
成员内部类也是最普通的内部类,它是外围类的一个成员,因此他是能够无限制的访问外围类的全部 成员属性和方法,尽管是private的,可是外围类要访问内部类的成员属性和方法则须要经过内部类实例来访问。
在成员内部类中要注意两点,第一:成员内部类中不能存在任何static的变量和方法;第二:成员内部类是依附于外围类的,因此只有先建立了外围类才可以建立内部类。
1 public class OuterClass { 2 private String str; 3 4 public void outerDisplay(){ 5 System.out.println("outerClass..."); 6 } 7 8 public class InnerClass{ 9 public void innerDisplay(){ 10 //使用外围内的属性 11 str = "chenssy..."; 12 System.out.println(str); 13 //使用外围内的方法 14 outerDisplay(); 15 } 16 } 17 18 /*推荐使用getxxx()来获取成员内部类,尤为是该内部类的构造函数无参数时 */ 19 public InnerClass getInnerClass(){ 20 return new InnerClass(); 21 } 22 23 public static void main(String[] args) { 24 OuterClass outer = new OuterClass(); 25 OuterClass.InnerClass inner = outer.getInnerClass(); 26 inner.innerDisplay(); 27 } 28 } 29 -------------------- 30 chenssy... 31 outerClass...
推荐使用getxxx()来获取成员内部类,尤为是该内部类的构造函数无参数时 。
4、局部内部类
有这样一种内部类,它是嵌套在方法和做用于内的,对于这个类的使用主要是应用与解决比较复杂的问题,想建立一个类来辅助咱们的解决方案,到那时又不但愿这个类是公共可用的,因此就产生了局部内部类,局部内部类和成员内部类同样被编译,只是它的做用域发生了改变,它只能在该方法和属性中被使用,出了该方法和属性就会失效。
对于局部内部类实在是想不出什么好例子,因此就引用《Think in java》中的经典例子了。
定义在方法里:
1 public class Parcel5 { 2 public Destionation destionation(String str){ 3 class PDestionation implements Destionation{ 4 private String label; 5 private PDestionation(String whereTo){ 6 label = whereTo; 7 } 8 public String readLabel(){ 9 return label; 10 } 11 } 12 return new PDestionation(str); 13 } 14 15 public static void main(String[] args) { 16 Parcel5 parcel5 = new Parcel5(); 17 Destionation d = parcel5.destionation("chenssy"); 18 } 19 }
定义在做用域内:
1 public class Parcel6 { 2 private void internalTracking(boolean b){ 3 if(b){ 4 class TrackingSlip{ 5 private String id; 6 TrackingSlip(String s) { 7 id = s; 8 } 9 String getSlip(){ 10 return id; 11 } 12 } 13 TrackingSlip ts = new TrackingSlip("chenssy"); 14 String string = ts.getSlip(); 15 } 16 } 17 18 public void track(){ 19 internalTracking(true); 20 } 21 22 public static void main(String[] args) { 23 Parcel6 parcel6 = new Parcel6(); 24 parcel6.track(); 25 } 26 }
5、匿名内部类
在作Swing编程中,咱们常用这种方式来绑定事件
1 button2.addActionListener( 2 new ActionListener(){ 3 public void actionPerformed(ActionEvent e) { 4 System.out.println("你按了按钮二"); 5 } 6 });
咱们咋一看可能以为很是奇怪,由于这个内部类是没有名字的,在看以下这个例子:
1 public class OuterClass { 2 public InnerClass getInnerClass(final int num,String str2){ 3 return new InnerClass(){ 4 int number = num + 3; 5 public int getNumber(){ 6 return number; 7 } 8 }; /* 注意:分号不能省 */ 9 } 10 11 public static void main(String[] args) { 12 OuterClass out = new OuterClass(); 13 InnerClass inner = out.getInnerClass(2, "chenssy"); 14 System.out.println(inner.getNumber()); 15 } 16 } 17 18 interface InnerClass { 19 int getNumber(); 20 } 21 22 ---------------- 23 Output:
这里咱们就须要看清几个地方
一、 匿名内部类是没有访问修饰符的。
二、 new 匿名内部类,这个类首先是要存在的。若是咱们将那个InnerClass接口注释掉,就会出现编译出错。
三、 注意getInnerClass()方法的形参,第一个形参是用final修饰的,而第二个却没有。同时咱们也发现第二个形参在匿名内部类中没有使用过,因此当所在方法的形参须要被匿名内部类使用,那么这个形参就必须为final。
四、 匿名内部类是没有构造方法的。由于它连名字都没有何来构造方法。
PS:因为篇幅有限,对匿名内部类就介绍到这里,有关更多关于匿名内部类的知识,我就会在下篇博客作详细的介绍,包括为什么形参要定义成final,怎么对匿名内部类进行初始化等等,敬请期待……
6、静态内部类
在java提升篇-----关键字static中提到Static能够修饰成员变量、方法、代码块,其余它还能够修饰内部类,使用static修饰的内部类咱们称之为静态内部类,不过咱们更喜欢称之为嵌套内部类。静态内部类与非静态内部类之间存在一个最大的区别,咱们知道非静态内部类在编译完成以后会隐含地保存着一个引用,该引用是指向建立它的外围内,可是静态内部类却没有。没有这个引用就意味着:
一、 它的建立是不须要依赖于外围类的。
二、 它不能使用任何外围类的非static成员变量和方法。
1 public class OuterClass { 2 private String sex; 3 public static String name = "chenssy"; 4 5 /** 6 *静态内部类 7 */ 8 static class InnerClass1{ 9 /* 在静态内部类中能够存在静态成员 */ 10 public static String _name1 = "chenssy_static"; 11 12 public void display(){ 13 /* 14 * 静态内部类只能访问外围类的静态成员变量和方法 15 * 不能访问外围类的非静态成员变量和方法 16 */ 17 System.out.println("OutClass name :" + name); 18 } 19 } 20 21 /** 22 * 非静态内部类 23 */ 24 class InnerClass2{ 25 /* 非静态内部类中不能存在静态成员 */ 26 public String _name2 = "chenssy_inner"; 27 /* 非静态内部类中能够调用外围类的任何成员,不论是静态的仍是非静态的 */ 28 public void display(){ 29 System.out.println("OuterClass name:" + name); 30 } 31 } 32 33 /** 34 * @desc 外围类方法 35 * @author chenssy 36 * @data 2013-10-25 37 * @return void 38 */ 39 public void display(){ 40 /* 外围类访问静态内部类:内部类. */ 41 System.