提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
目录
前言
因为内部类语法较为复杂,接触少的难以理解,网上的文章描述内部类的较少,虽然内部类平时用的地方不多,但是不能否认它的重要性,看过java核心类库源码的应该知道,内部类在里面大有作为。于是有了此文章,此文章描述了内部类的基本语法以及底层原理实现,以后要是忘记了,自己也可以看看。
提示:以下是本篇文章正文内容,下面案例可供参考
一、内部类是什么?
1,原理
简单来说就是一个类定义在一个类的内部。
首先要明确,内部类和外部类都是独立的个体,内部类在内存中跟类一样,也有存储空间。
想法一:既然内部类也有存储空间,在虚拟机中和平常的类没什么俩样,用普通类不就成了吗,何必用内部类?它存在的意义是什么。
答:1,因为内部类可以访问外围类所有的成员变量,不论访问权限类型,包括私有类型。
2,内部类可以对同一个包中的其他类隐藏。可以在外围类中设置内部类的访问权限。
想法二:既然内部类在内存中和外围类没什么俩样,都有自己的存储空间,那么,内部类为什么可以访问外围类私有类型?
答:因为在内部类中,在构造内部类的时候,外围类通过内部类构造器把自己的引用传到内部类中,内部类中会存在一个隐式的引用字段指向外围类,根据此引用可以访问外围类的一切。
请看下面的例子:
public class Duck { class Duck_in{ } }
一个非常简单的内部类例子,接下来我们去探究一下它的底层,对内部类Duck-in进行反射。
在字段文件Fields中,有一个没有定义过的字段this¥0,final类型,指向其外围类Duck。
再看构造器<init>
系统默认的构造器,在Object构成器调用之前就把外围类的引用传到了内部类中,以上就是内部类可以访问外部类私有变量的真正原因。
内部类和外部类在内存中都有其自己的空间,但是在内存中却有关联,看下面例子,把Duck与Duck-in实例化
public class Duck {
class Duck_in{
}
public static void main(String... args){
Duck duck = new Duck();
Duck_in duck_in = duck.new Duck_in();
System.out.println("Duck="+duck+" Duck_in"+duck_in);
}
}
可以看到,内部类在格式是在外围类后面 加一个美元符号在加自己的类名。
2,特性
1,因为内部类对于外部类来说类似于字段,尽管它在内存中和类一样,在外围类中可以定义它的访问权限,比如公共,保护,私有。还可以设置为静态类型。
2,创建内部类前首先要创建其外围类对象(静态内部类除外,静态内部类中没有外围类引用,只能使用静态类型),因为内部类的构造器中要拿到外围类的引用。这一点很容易理解,创建内部类的语法下面在介绍。
3,普通内部类不能拥有static型数据和字段(静态内部类除外),这个也好理解,既然你是普通型内部类,要是可以拥有静态字段的话,内部类的访问权限设置就没有什么意义了。
3,用途
内部类的用途有很多,因为它强大的隐藏机制和对外围类的访问权限。当你想某个类来实现某项功能,它就是你的强大辅助,可以帮助外围类完成相应的行为,“低调的工具人类”。因为它可以继承接口和类,就跟外围类一样。它的出现完善了java的多重继承变得完整。
打个比方,有一个鸭子类,有一个走路接口。
方案一:鸭子类直接实现此接口。
方案二:在鸭子类中定义内部类实现此接口。
既然鸭子类也可以实现接口,为什么要多此一举的用内部类实现?
当方案一可以满足需求的时候,确实应该用方案一。
什么情况不能满足?比如这个鸭子不一般,它想走,但是只有一种走路方式满足不了它,它既想它可以蹦蹦跳跳,又可以慢慢悠悠,还可以嘻嘻哈哈的走,对这个走路接口,鸭子类想有多种实现的时候,内部类就派上了用场。
”内部类最吸引人的原因是,每个内部类都能独立的继承自一个接口的实现,所以无论外围类是否已经继承了某个类(接口)的实现,对于内部类都没有影响“ ——《java编程思想》
”private内部类给类的设计的设计者提供了一种途径,通过这种方式可以完全阻止任何依赖与类型的编码,并且完全隐藏了实现了细节“ ——《java编程思想》
注:内部类的访问权限体系跟类的成员变量一样。
这俩句话实在是太精辟了,必须贴上来。
鄙人浅薄见解:一个内部类就相当于隐藏的自己在偷偷摸摸学神功,我可以有无数个隐藏的我学不同的神功,我还可以对同一种神功有不同的方向。
其实内部类的作用不仅仅于此,它更多的作用是构建类体系,完善java多重继承体系。
4,内部类创建方式:
前问我们提到了内部类中有一个指向外围类的引用,它的表达式
OuterClass.this
编写内部类对象构造器
outerObject.new InnerClass(构造器参数) //系统默认构造器没有参数
public class Duck { private int x; class Duck_in{ void f(){ x=1; Duck.this.x=2;//这是补全了引用,效果一样 } } public static void main(String... args){ Duck duck = new Duck(); Duck_in duck_in = duck.new Duck_in(); } }
二,内部类种类
1,普通内部类
普通内部类就是正正经经的在外围类中自己定义的类就像上面的Duck-in。很好理解
格式:
public class Duck {
class Duck_in{
}
}
2,局部内部类(在方法和作用域内的内部类)
这个范围就很广了,可以在方法中直接定义一个内部类,注意是任意的作用域。
比如在接口中定义内部类,接口就用之前的走路接口。
interface Walk{ void walkmethod(); class A{ } }
那么它有什么用途呢?
接口中的任何成员都是public static类型的,当你想让接口还提供别的公开资源的时候,在接口中定义内部类,它就可以向接口实现类提供-接口额外公开的资源。
还可以在方法中定义内部类:
public Walk getwalk(){ class Duck_Walk implements Walk{ @Override public void walkmethod() { print("走路"); } } Duck_Walk duck_walk = new Duck_Walk(); return duck_walk; }
此方法是像外围类返回一个实现走路接口的内部类。注意并不意味着方法运行完毕就不可用了,它返回的是一个Walk引用,仍然可以调用Walk接口中的方法,实现多态。
还可以这样
if(1==1){ class If{ } }
是不是觉得很离谱???这样都行!
没错,注意是任意作用域!任意作用域!任意作用域!不过在if中定义的并不是说明它有创造条件,其实它和别的类一起编译过了,不过出了if{}外,它就不可用了,别的和正常类一样,此用途非常罕见。
这个用途也很好理解,当满足条件的时候,就对它进行实例化操作。
细节
注:局部内部类不仅可以访问外部类变量,还可以访问变量,但是它必须是final型的变量
这是因为final型变量在编译的时侯就已经别确认了值,这样是为了保证安全性,这也说明,他们一旦赋值就绝对不会改变。
可以通过向方法传递值来初始化构造器,还是那个例子。
public Walk getwalk(final String walkname){ class Duck_Walk implements Walk{ private String walkname1; Duck_Walk(String name){this.walkname1 = name;} @Override public void walkmethod() { print("走路"); } } Duck_Walk duck_walk = new Duck_Walk(walkname); return duck_walk; }
3,匿名内部类
这个种类的内部类不配拥有姓名,深藏功与名,因为没有姓名,所以没有构造器。他就是局部内部类它弟弟!它的存在就是为了简化代码!当然还有别的作用。可以把它简单的理解为局部内部类的简易版。但是它和局部内部类还是有差异,先看几个例子,具体细节往下看:
//注意这个方法是原来的局部内部类,贴过来对比更直观 public Walk getwalk(){ class Duck_Walk implements Walk{ @Override public void walkmethod() { print("走路"); } } Duck_Walk duck_walk = new Duck_Walk(); return duck_walk; } //分界线 public Walk getwalk1(){ return new Walk() { @Override public void walkmethod() { print("走路"); } }; }
可以很直观的看到,代码少了点(咳咳),我认为当在方法中如果只返回内部类引用,而不用在方法中用这个引用做些别的事情,那么用匿名内部类就更符合我们的编程想法和意图。
问题:匿名内部类的父类如果需要用有参构造器怎么办?可以这样
public Walk getwalk1(){ return new Walk() { { //在代码块进行初始化 } @Override public void walkmethod() { print("走路"); } };
以上就是匿名内部类了
问题:既然匿名内部类和局部内部类很相似,那么什么时候用匿名内部类?什么时候用局部内部类?
当需要对内部类进行构造器重载的时候,只能用局部内部类,因为匿名内部类只能在代码块进行成员初始化,不能完成构造器重载。
其他具体情况具体分析,看你想要这个内部类完成什么行为。
4,静态内部类
静态内部类就是在类名前面加上static
细节:
用法类似于static方法,里面只能访问静态变量。
里面没有外围类的引用。
而且和其他内部类不同的是里面可以拥有静态方法和静态成员。
因为没有外围类引用,要创建静态内部类对象并不需要外围类的对象。
public class Duck {
static class Duck_Static{
static int x=0;
static void p(){
print("p()被调用");
}
}
public static void main(String... args){
Duck.Duck_Static.p();
Duck_Static.x=2;
}
}
什么时候用静态内部类?
当不需要用到外围类引用的时候就应该使用静态内部类,这样更节省空间
总结
以上就是内部类的相关语法。
内部类用法较复杂,内部类也可以被继承,里面也能嵌套内部类,套娃大法。内部类对于构建类体系有重要的作用,用多了就理解了。
-优雅结束-