基本概念
-
1.内部类定义:将一个类的定义放在另一个类的内部,称为内部类,外面这个类称为外围类。
-
2.内部类持有外部类的引用,因此,在内部类中可以访问外围对象的所有成员,而不需要特殊条件。这里需要注意,并非是内部类对象可以访问外围类对象的成员。
.this 和.new 的使用
3.在外围类中,可以直接通过new 获取内部类引用;在内部类中,可以通过.this
获得外部类的引用:
//外围类
public class Outer {
private String name="Outer";
/**
* 在外围类中,可以直接使用new生成一个内部类的引用
*/
Inner getInner(){
return new Inner();
}
//内部类
class Inner{
void innerF(){
System.out.println("innerF()...");
}
/**
* 在内部类中,通过.this获取生成一个外围类的引用
*/
Outer outer(){
return Outer.this;
}
}
void outerF(){
System.out.println("OuterF()...");
}
}
在其他类中,如果要获取内部类对象,必须使用外部类的对象来创建该内部类对象,在拥有外部类对象之前是不可能创建内部类对象的(静态内部类除外)。创建时通过.new
关键字来创建:
public class Demo1 {
public static void main(String[] args) {
//创建外部类对象
Outer outer = new Outer();
outer.outerF();
//通过外部类对象的引用.new来创建内部类对象
Outer.Inner inner = outer.new Inner();
inner.innerF();
inner.outer().outerF();
}
}
内部类用于隐藏实现细节
将内部类以private
修饰,则在除外部类的任何类中,都无法获取该内部类实例,但可以向上转型为该内部类的基类,如下示例所示:
public interface Destination {
String readLabel();
}
public interface Contents {
int value();
}
public class Parcel {
//PContent作为Parcel的外部类,被private修饰
private class PContent implements Contents{
@Override
public int value() {
return 0;
}
}
//PContent作为Parcel的外部类,被private修饰
protected class PDestination implements Destination{
private String lable;
@Override
public String readLabel() {
return "PDestination";
}
private PDestination(String s){
lable = s;
}
}
//在其他类中将通过Parcel对象调用该方法获取PDestination实例
public Destination destination(String s) {
return new PDestination(s);
}
//在其他类中将通过Parcel对象调用该方法获取PContent实例
public Contents contents(){
return new PContent();
}
}
public class Demo1 {
public static void main(String[] args) {
Parcel p = new Parcel();
//通过Parcel对象方法可以获取到Parcel中被private修饰的内部类,且向上转型为其基类,因此隐藏了实现的细节。
Contents content = p.contents();
Destination d = p.destination("Hello");
//由于Parcel.PContent被private修饰,因此即使构造方法为public,也无法通过该方式获取其实例。
//Contents c = p.new PContent();
}
}
方法体中的局部内部类
在一个方法体中定义的类,称为局部内部类,如下面示例:
public class Demo1 {
public static void main(String[] args) {
Demo1 d = new Demo1();
Destination ds = d.destination("Hello");
ds.readLabel();
}
public Destination destination(String s) {
//PDestination位于destination(String s)方法体中,因此对外部不可见
class PDestination implements Destination{
private String label;
@Override
public String readLabel() {
// TODO Auto-generated method stub
System.out.println(label);
return label;
}
private PDestination(String s){
label = s;
}
}
//返回PDestination实例,但返回值为Destination,进行了向上转型
return new PDestination(s);
}
}
从该例中得知:
- 1.局部内部类对方法之外不可见,它不是外部类的一部分,而是方法的一部分;
- 2.并不意味着一旦方法执行完毕,方法体中的内部类就不可用了(依然可以使用该内部类实例)。
作用域内的内部类
除了在方法中定义一个内部类之外,还可以在任意的代码块中定义一个内部类,如:
/**
* 定义在代码块中的局部内部类
*/
public class Demo3 {
public static void main(String[] args) {
Demo3 demo3 = new Demo3();
demo3.test(true);
}
private void test(boolean b){
if (b){
//定义在代码块中的局部内部类,并不是该类的创建是有条件的,而是在if()之外,它是不可用的
class LocalInnerClass{
private String id;
LocalInnerClass(String id){
this.id = id;
}
String getId(){
System.out.println("id:"+id);
return id;
}
}
LocalInnerClass lic = new LocalInnerClass("no.1");
lic.getId();
}
}
}
这种形式的内部类,其设计目的不是说该类的创建是有条件的,它在编译时就已经加载了,其目的在于,只有在定义该类的作用域内可用,超过其作用域,它是不可用的,除此之外,它和其他普通类无差别。
局部内部类中不能有访问说明符,因为它不是外部类的一部分。
匿名内部类
匿名内部类,即没有名字的内部类,它将返回值的生成和表示这个返回值的类的定义结合在了一起。比如以下示例中表示了匿名内部类的语法:
public class AnonymoutInnerClass {
public static void main(String[] args) {
AnonymoutInnerClass aic = new AnonymoutInnerClass();
Animal animal = aic.getAnimal();
System.out.println(animal.getName());
}
public Animal getAnimal(){
//表示创建一个继承自Animal的匿名类的对象
return new Animal("Dog"){
public String getName(){
return super.getName()+" is Animal";
}
};
}
class Animal{
private String name;
public Animal(String name){
this.name = name;
}
public String getName(){
return name;
}
}
用一句话来概括匿名内部类的语法:创建一个继承自xxx的匿名类的实例!(经典啊)
如果要在匿名内部类中使用外部类中定义的变量时,那么这个变量必须是final类型的。如果仅仅是传递给匿名内部类基类构造器的变量,那么不要求该变量一定是final类型的,因为没有使用它。如:
public Animal getAnimal(String name){
//表示创建一个继承自Animal的匿名类的对象
return new Animal(name){
//private String str = name;如果使得该句编译通过,则需要将参数name定义为final类型的
public String getName(){
return super.getName()+" is Animal";
}
};
}
匿名内部类用途
匿名内部类经常用于定义一个接口或者抽象类的子类,比如在接口回调中,就可以通过匿名内部类得到子类,示例如下:
public class AnonymoutInnerClass2 {
public static void main(String[] args) {
AnonymoutInnerClass2 aic2 = new AnonymoutInnerClass2();
//创建了一个实现了Listener接口的匿名类
aic2.setListener(new Listener(){
@Override
public void onChange() {
System.out.println("onChange...");
}});
}
//参数为一个接口类型的对象
public void setListener(Listener l){
l.onChange();
}
//定义一个接口
interface Listener{
void onChange();
}
}
静态内部类
被static关键字修饰的内部类是静态内部类,也叫作嵌套类,因为它和外部类对象之前没有关系。普通内部类和静态内部类区别如下:
- 1.创建普通内部类时,必须首先创建其外部类对象;创建静态内部类,不需要外部类对象;
- 2.普通内部类持有创建它的外部类的引用(.this);静态内部类不持有外部类引用,因此不能使用(.this),也不能访问外部类中的非静态数据;
- 3.普通内部类中,不能有static数据和字段;静态内部类中可以是static,也可以是非static的数据;
package com.chapter10.innerclass;
public class StaticInnerClass {
public static void main(String[] args) {
//嵌套内部类
ParcelContents p = new ParcelContents();
ParcelDestination d = new ParcelDestination("lable");
//普通内部类
ParcelContents2 p2 = new StaticInnerClass().new ParcelContents2();
}
//嵌套类
private static class ParcelContents implements Contents{
@Override
public int value() {
return 0;
}
}
private class ParcelContents2 implements Contents{
@Override
public int value() {
return 0;
}
}
//嵌套类
protected static class ParcelDestination implements Destination {
private String label;
private ParcelDestination(String label){
this.label = label;
}
@Override
public String readLabel() {
return label;
}
static int x = 10;
//嵌套类
static class AnotherLevel{
public static void f(){}
static int x = 10;
}
}
}
接口内部的类
正常情况下,接口内部不能放置任何代码,但静态内部类(嵌套类)可以作为接口的一部分,并且,放置在接口中的内部类自动地是static final的,如:
public interface ClassInInterface {
void doSomething();
class Test implements ClassInInterface{
@Override
public void doSomething() {
System.out.println("doSomething");
}
public static void main(String args[]){
new Test().doSomething();
}
}
}
内部类的继承
内部类也可以被其他类继承,只不过内部类的构造器必须连接到只想其外部类对象的引用,并且,内部类持有的外围类对象的引用必须初始化。因此,继承内部类时,必须在构造器中使用特殊的语法:
enclosingClassReference.super();//外部类引用.super()
如下示例:
public class WithInner {
class Inner{
}
}
public class InheritInner extends WithInner.Inner{
//必须这样,否则编译报错
public InheritInner(WithInner wi){
wi.super();
}
public static void main(String[] args) {
WithInner wi = new WithInner();
InheritInner ii = new InheritInner(wi);
}
}
内部类标识符
每个类都会产生一个.class文件,包括内部类,内部类的类文件命名规则为外部类$内部类。如果内部类是匿名的,编译器会简单产生一个数字作为标识符。如:
StaticInnerClass$ParcelContents
StaticInnerClass$1
为什么要使用内部类?
一般来说,内部类继承于某个类或者实现某个接口。而在Java中,没有多重继承,而通过内部类,则可以实现”多重继承“,我们看下面的例子就会明白:
package com.chapter10.innerclass.mutiextends;
//定义一个接口
public interface Incrementable {
void increment();
}
//定义一个抽象类
public abstract class MyIncrement{
public abstract void increment();
}
public class Client implements Incrementable{
public static void main(String[] args) {
// TODO Auto-generated method stub
Client c = new Client();
c.increment();
}
@Override
public void increment() {
// TODO Auto-generated method stub
System.out.println("Incrementable.increment()...");
new Inner().increment();
}
class Inner extends MyIncrement{
@Override
public void increment() {
System.out.println("MyIncrement.increment()...");
}
}
}
/**
* Incrementable.increment()...
* MyIncrement.increment()...
*/
该例中,接口和抽象类中存在同名方法increment(),因此,不能同时继承和实现它们,这时通过创建一个内部类来继承或实现接口,巧妙地解决了这个问题。