怎么理解Java编程中的内部类,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。
内部类:在其它类内定义的类,不同于组合。虽然看上去象某种代码隐藏机制,但可以实现更多功能 -了解包含它的类并可与之交换数据,而且内部类的代码可以更优雅、清晰。
内部类定义:包含在其它类内。使用与非内部类没有太大区别。
典型用法:outer class通过方法返回inner class的引用。
区别之一:内部类名嵌套在外部类(outer class)内,在Out class的non-static方法之外用OuterClassName.InnerClassName的形式定义内部类对象。
注意,非static内部类只能在Out class的non-static方法中生成对象;在其它类中,也必须使用Out class的外部类对象实例。这就保证了下面所说的链接问题。
区别之二:内部类可以为private和protected。
Inner Class只是一种名称隐藏(name-hiding)和组织代码方式?NO。
内部类对象有一个到创建它的外部类对象的链接(link to the enclosing object that made it),因而可以直接的、没有任何限制地访问该外部类对象的成员,而且内部类可以访问outer class的所有成员(包括private)(C++的嵌套类没有这个特性);而outer class访问inner class的成员,必须创建Inner class的对象,可以访问任何成员(包括private)。
内部类对象中隐式包含了一个外部类对象的引用。内部类对象构建需要outer class对象的引用,如果没有,编译报错(非静态inner class)。
.this和.new:前者用来返回Outer class引用,编译期可知道和检查正确类型,无运行时开销;后者用来由outer class对象创建其内部类的对象,OutClassObject.new InnerClassName ()(注意,不能用outClassObj.new OutClassName.InnerClassName())。
嵌套类(nested class):static inner class,其对象创建不需要outer class对象引用,也可在static方法中创建。
内部类与upcasting
类实现了接口(interface),其它方法就可以用该interface作为参数,而不一定必须用该类(包括类对象定义)(类似继承)。可以利用upcasting->interface。
upcasting内部类->基类或者接口(尤其是后者),使内部类有了用武之地。实现接口内部类可以完全不可见、不可用(通过private或protected),所获得的只是基类或接口的引用(通过private,无法进行downcasting,protected,同一个包内,或者继承类可以进行downcasting),方便隐藏实现细节。
接口成员自动为public
private内部类可以阻止任何依赖于类型的代码,进行所有实现细节的隐藏。而且,扩展接口也没有任何意义,因为无法访问pubic接口之外的方法,这可以使JAVA产生更有效率的代码 。
inner class可以在任意作用域内定义(如方法内)。
两个理由:1. 实现一个接口
2. 需要一个不公开的类辅助解决复杂的问题
inner class形式
1. 方法内的内部类; 2. 方法中的一个作用域内的内部类; 3. 实现接口的匿名内部类; 4. 继承的匿名内部类(基类含有参的构造器); 4. 进行成员初始化的匿名内部类; 5. 使用实例初始化块进行构造的匿名内部类(匿名类没有构造器)。
局部内部类(local inner class) :在方法内或方法的一个作用域中定义的内部类。局部内部类在域外不可见并不代表其对象也不可用。条件域内定义的内部类不代表它是条件创建的。
匿名内部类(anonymous inner class) :new T(){...}; {...}为匿名内部类的定义,";"不可少(;只是该语句的结束,而不是用来表示匿名内部类的结束,所以没有什么特殊的地方),创建一个继承自T的匿名类的对象,得到的引用可自动upcast to T。是前面定义内部类的一种简写,只是该类没有名称。
前面是基类构造器为默认构造器的情况,当基类构造器有参数时:new T(args){...};此时会调用基类相应构造器。
匿名内部类初始化 :当需要用到外部定义类的对象时,传递的引用参数必须为final,否则编译报错;匿名类不能有命名的构造器(当然不能,类本身就没有名字),可以通过实例初始化(instance initialization)来完成构造器的功能。由于实例初始化不能重载(不代表只能有一个Instance initialization clause),所以匿名内部类只能有一个构造器。
匿名内部类只能在继承类和实现接口中2选一,且只能实现一个接口。
prefer classes to interfaces. (宁愿选择类,而不是接口?)
嵌套类(nested class):static inner class。有点类似C++嵌套类的概念,但Java的嵌套类可以访问outer class的所有成员(包括private,当然只能通过外部类对象访问non-static成员)。
1. 不需要通过outer class对象来创建嵌套类对象(.new不可用?);
2. 不能通过嵌套类对象访问non-static outer class对象(意思是像非嵌套类那样直接访问);
3. 嵌套类对象中不包含outer class对象引用(.this不可用)。
4. 非嵌套内部类不能有static成员、方法和嵌套类(fields、methods级别必须与class本身一致,non-static不能含有static,non-static、static内可以含有non-static)。
嵌套类可以位于接口内部,不违反接口的规则(不能定义接口实例?),只代表把嵌套类位于接口的命名空间下,位于接口内部的类自动为public static(public嵌套类),而且嵌套类本身就可以实现该接口,好处在于可以在嵌套类内编写该接口所有实现中都要用到的代码。
嵌套类的另一个用途:编写测试代码。 为每个类编写main函数增加代码长度,可以把main放在嵌套类内,要测试该类运行该嵌套类即可;而在发布的时候只要在打包前简单的删除该嵌套类的.class文件即可。
多重嵌套的类(non-static和static)可以没有限制的访问任何外层类的所有对象。
为什么用内部类?
不是总是直接和接口打交道,有时候需要用的是接口的实现。(可以实现多个接口,但不能继承多个类)
理由:每个内部类可以独立继承自一个实现,不受outer class是否已经继承另一实现的限制。从效果上来说,inner class提供了多继承(multiple-inheritance,继承自多个类)的能力,提供了另一种实现多个接口的方法(相比多继承,这个似乎没那么重要,因为多继承只能通过内部类来实现)。
额外特性:
1. 内部类可以有多个实例,每个实例可以拥有独立于outer class对象的不同信息;
2. 一个outer class可以有多个内部类,每个内部类可以以不同的方式实现同一个接口或者继承同一个类(参见习题22,两个内部类不同方式实现同一个接口,只有内部类才能完成这些);
3. 内部类实例创建时间并不受到外部类对象创建的限制;
4. 用内部类不会制造"is-a"关系的混乱,每个内部类都是个实体。
闭包(closure)和回调(callback)
闭包是一种可调用的对象,它记录了来自创建它的作用域的一些信息。
内部类是一种面向对象的闭包,不仅包含了外部类的信息,而且通过包含一个指向外部类对象的引用,可以操作所有成员,包括private。
回调,通过其它对象携带的信息,可以在稍后的某个时刻调用初始对象。
Java不支持指针类型,不能通过指针来实现回调。但内部类提供的闭包是种比较好的解决方案,更灵活,更安全(参见例callbacks)。
private class Closure implements Incrementable { public void increment() { // Specify outer-class method, otherwise // you’d get an infinite recursion: Callee2.this.increment(); } }
回调的价值在于灵活性,可以在运行时决定需要调用的方法。 GUI编程将体现得更明显。
内部类与控制框架(control frameworks)
一个应用程序框架(application framework)是指一个用来解决一个特定类型问题的类或类的集合。典型的应用方法是,继承其中一个或多个类,重写某些方法。重写方法的代码将通用解决方案特殊化,来解决特定问题。例如模板函数模式。 设计模式将不变的和变化的事情分开。
控制框架是用来响应事件的一类特殊的应用程序框架 。主要用来响应事件的系统称为事件驱动系统(event-driven system),如GUI。
内部类使得控制框架的创建和使用变得简单 。控制框架本身不包括要控制的事物的特定信息。这些信息在继承过程中,由算法的action()部分实现时提供。控制框架中变化的事情是各种事件对象的不同action,这通过创建不同event继承类来实现。(例event)
控制事件用abstract类代替接口?
内部类在控制框架中两个作用:
1. 用来表示解决问题所需的各种不同的action()。
2. 内部类可以直接访问外部类的所有成员,因而使得实现变得更灵活。
参见greenhouse(温室)的例子。
内部类的继承
内部类指向outer class object的引用必须初始化,而在它的继承类中并不存在要联接的缺省对象,必须使用特殊的语法明确指出这种关联。
继承自内部类的类构造器不能是默认构造器,要有个outer class的引用作为参数,而且必须加上enclosingClassReference.super();语句,编译才能通过。
内部类能override?继承outer class,像重写方法一样重写内部类并不起作用,此时两个内部类只是两个独立的实体。可以显式指定内部类的继承关系,然后通过复写base inner class的方法,来实现多态。 参见例BigEgg2.
//: innerclasses/BigEgg2.java // Proper inheritance of an inner class. import static net.mindview.util.Print.*; class Egg2 { protected class Yolk { public Yolk() { print("Egg2.Yolk()"); } public void f() { print("Egg2.Yolk.f()");} } private Yolk y = new Yolk(); public Egg2() { print("New Egg2()"); } public void insertYolk(Yolk yy) { y = yy; } public void g() { y.f(); } } public class BigEgg2 extends Egg2 { public class Yolk extends Egg2.Yolk { public Yolk() { print("BigEgg2.Yolk()"); } public void f() { print("BigEgg2.Yolk.f()"); } } public BigEgg2() { insertYolk(new Yolk()); } public static void main(String[] args) { Egg2 e2 = new BigEgg2(); e2.g(); } } /* Output: Egg2.Yolk() New Egg2() Egg2.Yolk() BigEgg2.Yolk() BigEgg2.Yolk.f() *///:~
局部内部类
局部内部类(local inner class)不能有访问限定符;有访问局部final变量和outer class所有类的权限;可以有命名的构造器;在方法外不能访问。
绝大部分情况下,可以用匿名类来替代局部内部类,除非:
1. 需要命名的构造器,或者需要重载构造器
2. 需要多个内部类的对象
此时就要用Local Inner class。
关于怎么理解Java编程中的内部类问题的解答就分享到这里了,希望以上内容可以对大家有一定的帮助,如果你还有很多疑惑没有解开,可以关注亿速云行业资讯频道了解更多相关知识。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。