内部类介绍
JAVA中可以将一个类定义到另外一个类的内部,这个就是内部类。
内部类是一种非常有用的机制。它允许你把一些逻辑相关的类组织到一起。虽然内部类看起来像一种代码隐藏机制:将代码置于其它类的内部。但是,内部类远远不止如此,它了解外围的类,并且能与之通信。使用内部内可以帮助你写出更加优雅的代码(尽管往往并不是这样)。
我们需要了解的是,内部类和组合完全是两种不同的概念;组合是为了增强代码的复用性,而内部类则是一种隐藏机制。
为什么需要内部类?
一个很重要的原因是:java是不允许有多继承的,但是每个内部类可以独立的继承自一个接口,无论外围类是否已经继承该接口,对于内部类都没有影响。java通过内部类有效的实现了"多重继承"。增强了java语言的多态特性。
非静态内部类
下面是非静态内部内的定义和使用:
/**
*
* @Desc 非静态内部类的使用
* */
public class Inner001 {
public static void main(String[] args) {
Inner001 inner = new Inner001();
//使用 .new 创建内部内对象
Inner001.Contents contents = inner.new Contents(); //方式1
Inner001.Contents contents1 = inner.newContext(); //方式2
System.out.println(contents.value());
System.out.println(contents1.value());
System.out.println(inner);
System.out.println(contents.outer());
System.out.println(contents1.outer());
contents1.printMax();
}
public int max = 1024;
/**
* @desc 在外部类定义一个方法创建其内部类
* */
public Contents newContext(){
return new Contents();
}
/**
* @desc 定义非静态内部类
* */
class Contents {
private int i =11;
public int value(){return i;}
//返回外部类的引用
public Inner001 outer(){
return Inner001.this;
}
//内部类可以访问外部类的所有属性
public void printMax(){
System.out.println(max);
}
}
}
1、可以看到内部类是定义在外部类的里面。
2、要创建内部类对象有两种方式,一种是外部类对象通过.new来创建;还有一种是在外部类定义一个创建内部类的方法。
3、非静态内部类创建的时候包含一个额外的引用指向外围对象的引用。在非静态内部类内部可以通过.this来获取到。
4、内部类可以访问外围类对象的所有成员,而不需要任何特殊的条件,如上面例子对max属性的打印。
静态内部类
如果不需要内部类对象和其外围类对象之间有联系,那么可以将内部类声明围static。非静态内部类保存了外部类的引用,而静态内部类则不会保存外部类的引用。
静态内部类意味着:
1、不需要访问其外围类的对象;
2、不能访问外围类的非静态对象,但是可以访问外围类的静态对象;
/**
*
* @Desc 静态内部类的使用
* */
public class Inner002 {
public static void main(String[] args) {
//不需要创建外部内对象,直接new
Contents context = new Contents();
System.out.println(context.value());
}
public int max = 1024;
/**
* @desc 定义静态内部类
* */
static class Contents {
private int i =11;
public int value(){return i;}
}
}
静态内部类和非静态内部类最大的区别是,它不需要依赖外部类对象。
匿名内部类
请看下面的一个例子:
interface Counter{
int next();
}
public class Inner003 {
private int count = 100;
//匿名内部类使用
Counter getCounter(final String name){
/**
* 匿名内部类
* */
return new Counter() {
@Override
public int next() {
System.out.println(name);
return count++;
}
};
}
public static void main(String[] args) {
Inner003 inner = new Inner003();
Counter c1= inner.getCounter("匿名内部类");
for(int i = 0; i<5; i++){
System.out.println(c1.next());
}
}
}
getCounter(final String name)中将方法返回值的生成和表示这个返回值的类的定义结合在了一起!另外这个类是匿名的,它是没有名字的(实现了Counter接口)。这就是匿名类。
1、匿名类只能使用基类提供的构造器(上面例子为默认构造器),而不能够自己定义构造器,因为其本身是没有名字的。
2、在匿名内部类内部,要使用外部类的局部变量必须使用final修饰。
那我们思考一下"匿名内部类为什么使用局部变量一定要使用final修饰"。
首先,匿名内部类采用的是复制局部变量的方式(将局部变量复制到匿名内部类中):
基本类型:复制值
引用类型:复制地址
之所以采用复制的方式,是因为如果直接引用,当外部方法运行结束的时候,退栈,局部变量也就死亡回收变成null,但有可能这个内部类的对象还在,在匿名内部类里就无法访问该变量,就可能发生异常。
复制方式如果不使用final修饰,那么外部方法和内部匿名类都可能修改局部变量,从而导致数据不一致。
局部内部类
JAVA中允许在方法体或者结构体中创建内部类,我们称其为局部类。局部内部类不能有访问说明符(private、public),因为它不是外围类的一部分,但是它可以访问外围类的所有成员。
interface Counter1{
int next();
}
public class Inner004 {
private int count = 100;
//匿名内部类使用
Counter1 getCounter(final String name){
class LocalCounter implements Counter1{
@Override
public int next() {
System.out.println(name);
return count++;
}
}
return new LocalCounter();
}
public static void main(String[] args) {
Inner004 inner = new Inner004();
Counter1 c1= inner.getCounter("局部内部类");
for(int i = 0; i<5; i++){
System.out.println(c1.next());
}
}
}
可以看到局部内部类和匿名内部类非常的相似;那么他们的区别是什么了?
1、局部内部类可以灵活定义自己的构造器;
2、当内部类的对象不止一个的时候,可以使用局部内部类替代匿名内部类。
除了上述2种情况之外,应该优先使用匿名内部类。
优先考虑静态内部类
在了解了内部类的基础语法之后,需要思考的是什么情况下应该使用内部类?以及应该使用哪种内部类?
我们已经了解到内部类有四种:静态内部类、非静态内部类、匿名类和局部类。
1、内部类存在的目的应该只是为它的外部类提供服务。如果嵌套类将来可能会用于其它的环境中,它就应该是顶层类。
2、如果内部类成员不要求访问外围类的实例,则应该使用静态内部类(static修饰);因为每个非静态内部类实例都将包含一个额外的引用指向外围对象的引用,这将消耗内存和时间。并且可能会导致外围实例在符合垃圾回收时却任然得以保留。
静态内部类一种常见的使用方法是作为公有类的辅助类。考虑一个枚举,编写一个计算器类(Calculator),那么可以将诸如PLUS、MINUS这样的操作,封装到静态内部类Calculator.Operation 中。再比如Map的静态内部类Entry;
3、如果内部类成员需要要求访问外围的实例,那么就需要使用非静态内部类;当非静态内部类的实例被创建的时候,它和外围类的关系也随之被建立起来。
4、匿名内部类的常见用法是动态地创建函数对象。例如利用Comparator对集合实现排序。
5、局部类是四种内部类中使用的最少的类,在任何 "可以声明局部变量"的地方,都可以声明局部类。他们设计的目的是单个方法内可见,并且他们需要足够简短,不然会影响程序的可读性。