Java面向对象:内部类详解
在Java中,允许一个类的定义位于另一个类的内部,前者称为内部类,后者 称为外部类。
当一个事物的内部,需要一个完整的结构进行描述,而这个内部的完整的结构又只为外部事物提供服务,那么整个内部的完整结构最好使用内部类。
Inner class一般用在定义它的类或语句块之内,在外部引用它时必须给出完整的名称。
注意:Inner class的名字不能与包含它的外部类类名相同
一、为什么要使用内部类
使用内部类最吸引人的原因是:每个内部类都能独立地继承一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响。
在我们程序设计中有时候会存在一些使用接口很难解决的问题,这个时候我们可以利用内部类提供的、可以继承多个具体的或者抽象的类的能力来解决这些程
序设计问题。可以这样说,接口只是解决了部分问题,而内部类使得多重继承的解决方案变得更加完整。
代码实例:
public interface Father {
}
public interface Mother {
}
public class Son implements Father, Mother {
}
// 如果Father、Mother是抽象类或者具体类的话就解决了多重继承问题
public class Daughter implements Father{
class Mother_ implements Mother{
}
}
其实使用内部类最大的优点就在于它能够非常好的解决多重继承的问题,但是如果我们不需要解决多重继承问题,那么我们自然可以使用其他的编码方式,但是使用内部类还能够为我们带来如下特性:
1、内部类可以用多个实例,每个实例都有自己的状态信息,并且与其他外围对象的信息相互独立。
2、在单个外围类中,可以让多个内部类以不同的方式实现同一个接口,或者继承同一个类。
3、创建内部类对象的时刻并不依赖于外围类对象的创建。
4、内部类并没有令人迷惑的“is-a”关系,他就是一个独立的实体。
5、内部类提供了更好的封装,除了该外围类,其他类都不能访问。
二、内部类基础
内部类是如何使用外部类的属性和方法,以及使用 .this 与 .new。
当我们在创建一个内部类的时候,它无形中就与外围类有了一种联系,依赖于这种联系,它可以无限制地访问外围类的元素。
public class OuterClass {
private String name ;
private int age;
// 省略getter和setter方法
public class InnerClass{
public InnerClass(){
name = "chenssy";
age = 23;
}
public void display(){
System.out.println("name:" + name +" ;age:" + age);
}
}
public static void main(String[] args) {
OuterClass outerClass = new OuterClass();
OuterClass.InnerClass innerClass = outerClass.new InnerClass();
innerClass.display();
}
}
在这个程序中,我们可以看到内部了InnerClass可以对外围类OuterClass的属性进行无缝的访问,尽管它是private修饰的。这是因为当我们在创建某个外围类的内部类对象时,此时内部类对象必定会捕获一个指向那个外围类对象的引用,只要我们在访问外围类的成员时,就会用这个引用来选择外围类的成员。
其实在这个应用程序中我们还看到了如何来引用内部类:引用内部类我们需要指明这个对象的类型:OuterClasName.InnerClassName。同时如果我们需要创建某个内部类对象,必须要利用外部类的对象通过.new来创建内部类: OuterClass.InnerClass innerClass = outerClass.new InnerClass();。
同时如果我们需要生成对外部类对象的引用,可以使用OuterClass.this,这样就能够产生一个正确引用外部类的引用了。当然这点实在编译期就知晓了,没有任何运行时的成本。
public class OuterClass {
public void display(){
System.out.println("OuterClass...");
}
public class InnerClass{
public OuterClass getOuterClass(){
return OuterClass.this;
}
}
public static void main(String[] args) {
OuterClass outerClass = new OuterClass();
OuterClass.InnerClass innerClass = outerClass.new InnerClass();
innerClass.getOuterClass().display();
}
}
内部类是个编译时的概念,一旦编译成功后,它就与外围类属于两个完全不同的类(当然他们之间还是有联系的)。对于一个名为OuterClass的外围类和一个
名为InnerClass的内部类,在编译成功后,会出现这样两个class文件:OuterClass.class和OuterClass$InnerClass.class。
在Java中内部类主要分为成员内部类、局部内部类、匿名内部类、静态内部类。
三、成员内部类
成员内部类也是最普通的内部类,它是外围类的一个成员,所以他是可以无限制的访问外围类的所有 成员属性和方法,尽管是private的,
但是外围类要访问内部类的成员属性和方法则需要通过内部类实例来访问。
在成员内部类中要注意两点: 第一:成员内部类中不能存在任何static的变量和方法;
第二:成员内部类是依附于外围类的,所以只有先创建了外围类才能够创建内部类。
public class OuterClass {
private String str;
public void outerDisplay(){
System.out.println("outerClass...");
}
public class InnerClass{
public void innerDisplay(){
//使用外围内的属性
str = "Inner fadd...";
System.out.println(str);
//使用外围内的方法
outerDisplay();
}
}
/*推荐使用getxxx()来获取成员内部类,尤其是该内部类的构造函数无参数时 */
public InnerClass getInnerClass(){
return new InnerClass();
}
public static void main(String[] args) {
OuterClass outer = new OuterClass();
OuterClass.InnerClass inner = outer.getInnerClass();
inner.innerDisplay();
}
}
四、局部内部类
有这样一种内部类,它是嵌套在方法内的,对于这个类的使用主要是应用与解决比较复杂的问题,想创建一个类来辅助我们的解决方案,到那时又不希望这个类是公共可用的,所以就产生了局部内部类。
局部内部类和成员内部类一样被编译,只是它的作用域发生了改变,它只能在该方法和作用域中被使用,出了该方法和作用域就会失效。但是它的对象可以通过外部方法的返回值返回使用,返回值类型只能是局部内部类 的父类或父接口类型
定义在方法里(返回值类型只能是局部内部类 的父类或父接口类型):
public class OuterClass {
public Person person(String name){
int a= 200; //默认final
class InnerClass implements Person{
private String iname;
private InnerClass(String iiname){
iname = iiname;
}
public String readName(){
return iname;
}
}
return new InnerClass(name);
}
public static void main(String[] args) {
OuterClass out = new OuterClass();
Person person = out.person("Tom");
}
}
定义在作用域内:
public class OuterClass1 {
private void play(boolean b){
if(b){
class InnerClass1{
private String id;
InnerClass1(String s) {
id = s;
}
String getSlip(){
return id;
}
}
InnerClass1 inner = new InnerClass1("1001");
String id = inner.getSlip();
}
}
public void track(){
play(true);
}
public static void main(String[] args) {
OuterClass1 out = new OuterClass1();
out.track();
}
}
五、匿名内部类
public class NMInnerClass {
public InnerClass getInnerClass(int num,int a){
a++;
return new InnerClass() {
// int a = a+5; 这里会报错,当所在方法的形参需要被匿名内部类使用,那么这个形参就必须为final,且不能在匿名内部类调用之前调用。
int number = num + 5; //
@Override
public int getNumber() {
// TODO Auto-generated method stub
return number;
}
};
}
public static void main(String[] args) {
NMInnerClass outClass = new NMInnerClass();
InnerClass inner = outClass.getInnerClass(3);
System.out.println(inner.getNumber()); // 8
}
}
public interface InnerClass {
int getNumber();
}
注意:
1、 匿名内部类是没有访问修饰符的。
2、 new 匿名内部类,这个类首先是要存在的。如果我们将那个InnerClass接口注释掉,就会出现编译出错。
3、 注意getInnerClass()方法的形参,第一个形参在匿名内部类调用之前没被调用,可以正常使用 。同时我们也发现第二个形参在匿名内部类之前使用过,
就不可以在匿名内类中使用。所以当所在方法的形参需要被匿名内部类使用,那么这个形参就必须为final,且不能在匿名内部类调用之前调用。
4、 匿名内部类是没有构造方法的。因为它连名字都没有何来构造方法。
六、静态内部类
在java中,关键字static 中提到 Static可以修饰成员变量、方法、代码块,还可以修饰内部类,使用static修饰的内部类我们称之为静态内部类,不过我们更喜欢称之为嵌套内部类。
静态内部类与非静态内部类之间存在一个最大的区别,我们知道非静态内部类在编译完成之后会隐含地保存着一个引用,该引用是指向创建它的外围内,但是静态内部类却没有。没有这个引用就意味着:
1、 它的创建是不需要依赖于外围类的。
2、 它不能使用任何外围类的非static成员变量和方法。
注意:
1.非静态内部类中不允许定义静态成员
2.外部类的静态成员不可以直接使用非静态内部类(因为非静态内部类的创建需要依赖于外围类 )
3.静态内部类,不能访问外部类的实例成员,只能访问外部类的类成员
public class OuterClass {
private String sex;
public static String name = "OutCalss static name";
/**
*静态内部类
*/
static class InnerClass1{
/* 在静态内部类中可以存在静态成员 */
public static String _name1 = "InnerCalss1";
public void display(){
/*
* 静态内部类只能访问外围类的静态成员变量和方法
* 不能访问外围类的非静态成员变量和方法
*/
System.out.println("OutClass name :" + name);
}
}
/**
* 非静态内部类
*/
class InnerClass2{
/* 非静态内部类中不能存在静态成员 */
public String _name2 = "chenssy_inner";
/* 非静态内部类中可以调用外围类的任何成员,不管是静态的还是非静态的 */
public void display(){
System.out.println("OuterClass name:" + name);
}
}
/**
* @desc 外围类方法
* @author chenssy
* @data 2013-10-25
* @return void
*/
public void display(){
/* 外围类访问静态内部类:内部类. */
System.out.println(InnerClass1._name1);
/* 静态内部类 可以直接创建实例不需要依赖于外围类 */
new InnerClass1().display();
/* 非静态内部类的创建需要依赖于外围类 */
OuterClass.InnerClass2 inner2 = new OuterClass().new InnerClass2();
/* 访问非静态内部类的成员需要使用非静态内部类的实例 */
System.out.println(inner2._name2);
inner2.display();
}
public static void main(String[] args) {
OuterClass outer = new OuterClass();
outer.display();
}
}