内部类顾名思义:将一个类定义在另外一个类里面或者一个方法里面,这样的类称为内部类。对于不少Java初学者来讲,内部类学起来真的是一头雾水,根本理解不清楚是个什么东西,包括我本身(我太菜了!哈哈),因此接下来我要好好地来研究一下。html
咱们来看下内部类的定义格式;java
public class OuterClass { //code class InnerClass{ //code } }
这里的InnerClass就是一个内部类。不管在咱们的学习中仍是工做中,内部类用到的地方真的不是不少,通常都出如今源码中,可是咱们仍是要搞懂内部类,由于后面对咱们阅读源码很是有帮助。并且随着后面咱们编程能力的提升,天然而然会领悟到它的魅力所在,它可以让咱们设计出更加优雅的程序结构。在使用内部类以前咱们须要明白为何要使用内部类,内部类可以为咱们带来什么样的好处。编程
在《Think in java》中有这样一句话:使用内部类最吸引人的缘由是:每一个内部类都能独立地继承一个(接口的)实现,因此不管外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响。多线程
也就是说内部类拥有类的基本特征(能够继承父类,实现接口)。在咱们程序设计中有时候会存在一些使用接口很难解决的问题,这个时候咱们能够利用内部类提供的、能够继承多个具体的或者抽象的类的能力来解决这些程序设计问题。能够这样说,接口只是解决了部分问题,而内部类使得多重继承的解决方案变得更加完整。(注:内部类能够嵌套内部类,可是这极大的破换了代码的结构,这里不推荐使用)ide
那咱们来看一下使用内部类如何进行多继承,接口多继承就不举例了,由于接口自己就能够实现多继承。函数
1 class Father{ 2 public String handsome(){ 3 return "爸爸很帅气"; 4 } 5 } 6 7 class Mother{ 8 public String beautiful(){ 9 return "妈妈很漂亮"; 10 } 11 } 12 13 class Son{ 14 //内部类继承了Father类 15 class MyFather extends Father{ 16 //重写父类方法 17 public String handsome(){ 18 return "我遗传了爸爸的帅气"; 19 } 20 } 21 //内部类继承了Mother类 22 class MyMother extends Mother{ 23 //重写父类方法 24 public String beautiful(){ 25 return "我遗传了妈妈的漂亮"; 26 } 27 } 28 } 29 30 public class Test { 31 public static void main(String[] args) { 32 Son son=new Son(); 33 Son.MyFather myFather=son.new MyFather(); 34 System.out.println(myFather.handsome()); 35 Son.MyMother myMother=son.new MyMother(); 36 System.out.println(myMother.beautiful()); 37 } 38 }
运行结果:post
从上面的举例代码能够看出,两个内部类分别继承了Father、Mother类,而且重写了父类的方法,这是内部类最重要的特性:内部类能够继承一个与外部类无关的类,保证了内部类的独立性,正是基于这一点,多重继承才会成为可能。学习
能够发如今建立内部类实例的时候,使用了 .new 这个特征,与以往咱们建立实例不太相同。.new能够这样理解:根据外部类来建立内部类的对象实例。this
Java中内部类可分为四种:成员内部类、局部内部类、匿名内部类、静态内部类。下面咱们逐一介绍这四种内部类:url
成员内部类是定义在类中的类。咱们能够把成员内部类当作是外部类的一个成员,因此成员内部类能够无条件访问外部类的全部成员属性和成员方法,包括private成员和静态成员。可是外部类要访问内部类的成员属性和方法则须要经过内部类实例来访问。当成员内部类拥有和外部类同名的成员变量或者方法时,会优先访问的是成员内部类的成员,可是咱们可使用 .this(若是有继承可使用super)来访问外部类的变量和方法。
在成员内部类中要注意两点:
1 class OuterClass{ 2 private String outerName="tang_hao_outer"; 3 private int outerAge=22; 4 5 public OuterClass() { 6 } 7 8 //成员方法 9 public void outerMethod() { 10 System.out.println("我是外部类的outerMethod方法"); 11 } 12 13 //外部类静态方法 14 public static void outerStaticMethod() { 15 System.out.println("我是外部类的outerStaticMethod静态方法"); 16 } 17 //定义返回内部类实例的方法,推荐使用该方法来换取内部类实例 18 public InnerClass getInnerClassInstance(){ 19 return new InnerClass(); 20 } 21 22 //内部类 23 class InnerClass{ 24 private String innerName="tang_hao_Inner"; 25 private int innerAge=21; 26 27 public InnerClass() { 28 } 29 30 public void show(){ 31 //当名字和外部类同样时,默认调用内部类的成员属性 32 System.out.println("内部类变量:"+innerName); 33 System.out.println("内部类变量:"+innerAge); 34 //当名字和外部类同样时,可使用 。this来调用外部类属性 35 System.out.println("外部类变量:"+OuterClass.this.outerName); 36 System.out.println("外部类变量:"+OuterClass.this.outerAge); 37 //访问外部类的方法 38 outerMethod(); 39 outerStaticMethod(); 40 } 41 } 42 } 43 public class Test { 44 public static void main(String[] args) { 45 //普通方法建立实例 46 OuterClass outerClass=new OuterClass(); 47 OuterClass.InnerClass innerClass=outerClass.new InnerClass(); 48 innerClass.show(); 49 System.out.println("-------------------"); 50 //调用外部类的getInnerClassInstance来建立内部类实例 51 OuterClass.InnerClass innerClassInstance = outerClass.getInnerClassInstance(); 52 innerClassInstance.show(); 53 } 54 }
运行结果:
从上面示例中,当内部类和外部类的变量和方法同样时,咱们用了 .this来调用外部类的属性(静态除外,由于静态随类加载而加载,优于对象的建立),它能够理解为:产生一个指向外部类的引用。还有若是该内部类的构造函数无参数,强烈推荐使用相似getInnerClassInstance()这样的方法来获取成员内部类的实例对象。
局部内部类是定义在一个方法或者一个做用域里面的类,它和成员内部类的区别在于局部内部类的访问仅限于方法内或者该做用域内。注意:局部内部类就像是方法里面的一个局部变量同样,是不能有 public、protected、private 以及 static 修饰符的。
局部内部类通常都用于返回一个类或实现接口的实例。咱们用Comparable接口为例:
1 class OuterClass{ 2 //建立返回一Comparable接口实例的方法 3 public Comparable getComparable(){ 4 //建立一个实现Comparable接口的内部类:局部内部类 5 class MyComparable implements Comparable{ 6 @Override 7 public int compareTo(Object o) { 8 return 0; 9 } 10 } 11 //返回实现Comparable接口的实例 12 return new MyComparable(); 13 } 14 }
当咱们建立外部类的实例调用getComparable()方法时,就能够轻松获取实现Comparable接口的实例了。
注意:局部内部类若是想用方法传入形参,该形参必须使用final声明(JDK8形参变为隐式final声明)。上面的例子若是是getComparable(Object o),那么这个形参前面就隐式加了final关键字。
匿名内部类就是没有名字的内部类。它与局部内部类很类似,不一样的是它没有类名,若是某个局部类你只须要用一次,那么你就可使用匿名内部类。匿名内部类可使你的代码更加简洁,你能够在定义一个类的同时对其进行实例化。
1 //建立一个接口 2 interface IPerson{ 3 public void eat(); 4 public void sleep(); 5 } 6 7 public class OuterClass { 8 //这里注意,局部内部类若是须要经过方法传入参数,该形参必须使用final声明(JDK8形参变为隐式final声明) 9 //我用的JDK8,因此这里没有显式的加final,可是JVM会自动加 10 public static IPerson getInnerClassInstance(String eat,String sleep){ 11 return new IPerson() { 12 @Override 13 public void eat() { 14 System.out.println(eat); 15 } 16 17 @Override 18 public void sleep() { 19 System.out.println(sleep); 20 } 21 };//这个分好要注意 22 } 23 24 public static void main(String[] args) { 25 IPerson person = OuterClass.getInnerClassInstance("吃饭", "睡觉"); 26 person.eat(); 27 person.sleep(); 28 } 29 }
运行结果:吃饭、睡觉
咱们知道在抽象类和接口中是不能被实例化的,可是在匿名内部类中咱们却看见new了一个IPerson接口,这是怎么回事。这是由于匿名内部类是直接使用new来生成一个对象的引用,而在new对象时,系统会自动给抽象类或接口添加一个它们的实现类,固然这个引用是隐式的,咱们看不见。咱们本身拆分出来理解一下,注意这里是本身想出来的,运行时并不会有这些类存在:
1 class Farmer implements IPerson{ 2 3 @Override 4 public void eat() { 5 System.out.println("农民吃饭"); 6 } 7 8 @Override 9 public void sleep() { 10 System.out.println("农民睡觉"); 11 } 12 }
通常咱们建立抽象类或接口的实例是这样的:IPerson iPerson = new Farmer();这个能够叫作是非匿名对象非匿名类,而咱们建立的是匿名内部类,因此这个实现类不能有名字,因此只好叫父类的名字,因此就看到了前面直接new了一个接口,实际上是隐式的建立了实现类的对象。(不知道这样讲对不对,鄙人菜鸟一个,若是有什么不对的或理解错误的地方,欢迎指出,虚心接受!)
匿名内部类最经常使用的状况就是在多线程的实现上,由于要实现多线程必须继承Thread类或是继承Runnable接口。
在使用匿名内部类的过程当中,咱们须要注意以下几点:
静态内部类是指用static修饰的内部类。在前面夯实Java基础(七)——Static关键字中提到了static关键字能够修饰内部类。咱们知道普通类是不容许声明为静态的,只要内部类才能够,被static修饰的内部类它不依赖于外部类的实例。这是由于非静态内部类在编译完成以后会隐含地保存着一个引用,该引用是指向建立它的外部类。
static修饰内部类注意几点:
简单举例:
1 class OuterClass{ 2 //静态变量 3 private static int static_num=66; 4 //非静态变量 5 private int num=99; 6 7 //静态内部类 8 static class InnerStaticClass{ 9 public void print(){ 10 //静态内部类只能访问外部类的静态变量和静态方法 11 System.out.println("静态内部类方法print()=="+static_num); 12 staticShow(); 13 } 14 } 15 //非静态内部类 16 class InnerClass{ 17 public void display(){ 18 //非静态内部类中能够调用外部类的任何成员,不论是静态的仍是非静态的 19 System.out.println("外部类静态变量=="+static_num); 20 System.out.println("外部类普通变量=="+num); 21 show(); 22 System.out.println("非静态内部类方法display()=="+num); 23 24 } 25 } 26 public void show(){ 27 System.out.println("外部类非静态show()方法"); 28 } 29 public static void staticShow(){ 30 System.out.println("外部类静态staticShow()方法"); 31 } 32 } 33 public class Test { 34 public static void main(String[] args) { 35 //static对象实例 36 OuterClass.InnerStaticClass staticClass=new OuterClass.InnerStaticClass(); 37 staticClass.print(); 38 39 //非static对象实例 40 OuterClass outerClass=new OuterClass(); 41 OuterClass.InnerClass innerClass=outerClass.new InnerClass(); 42 innerClass.display(); 43 } 44 }
运行结果:
从上面的例子咱们能够看到静态内部类和非静态内部类的区别。