内部类是定义在另一个类中的类,为什么使用内部类呢?其主要原因有三
1.内部类方法可以访问该类定义所在作用域中的数据,包括私有数据
2.内部类可以对同一个包中的其他类隐藏起来
3.当想要调用一个回调函数且不想编写大量代码时,可以使用匿名内部类
内部类有4 中类型:1.成员内部类2.局部内部类 3.静态内部类 4.匿名内部类
one by one
成员内部类
看一下下列代码:
public class TalkingClock {
private int interval;
private boolean beep;
public TalkingClock(int interval, boolean beep) {
super();
this.interval = interval;
this.beep = beep;
}
public void start() {
ActionListener listener = new TimePrinter();
Timer t = new Timer(interval,listener);
t.start();
}
public class TimePrinter implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("At the tone, the time is" + new Date());
if(TalkingClock.this.beep) Toolkit.getDefaultToolkit().beep();
}
我们会看到有一个奇怪的地方
if(TalkingClock.this.beep) Toolkit.getDefaultToolkit().beep();
////TalkingClock.this.beep是什么意思呢?
其实你也可以改写成beep,其作用是访问外部类的私有域,那摩为什么内部类可以访问外部类的域呢?
在解决这可问题之前我们需要敲一段关于反射分析类的代码,如果不清楚反射的话,可以参考java核心技术卷1.代码如下:
public class Test {
public static void main(String[] args) {
String classname = "com.heima.inner.TalkingClock$TimePrinter";
//String classname = "com.heima.inner.TalkingClock";
printWantClass(classname);
}
public static void printWantClass(String classname) { ///打印类的详细信息
try {
Class c1 = Class.forName(classname); ///获取该类的Class对象
Class superC1 = c1.getSuperclass(); ///获取该类的父类的class对象
String modifiers = Modifier.toString(c1.getModifiers()); ///获取该类的访问权限
if(modifiers.length()>0) {
System.out.print(modifiers + " ");
System.out.print(c1.getName()); ////打印访问权限及类名
}
if(superC1.getName() != null &&superC1.getName() != "Object.class") {
System.out.println(" extends" + superC1.getName() +"{");
}
printFields(c1); /////调用该函数一次打印变量域
System.out.println();
printConstructors(c1); //////调用该函数依次打印构造函数
System.out.println();
printMethods(c1); //////调用该函数依次打印方法
System.out.println();
System.out.println("}");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static void printFields(Class c1) {
Field[] fields = c1.getDeclaredFields();
for(Field f : fields) {
Class c = f.getType();
String name = f.getName();
String modifier = Modifier.toString(f.getModifiers());
if(modifier !=null && modifier.length() !=0) {
System.out.print(modifier);
}
System.out.println(" " + c.getName() + name + ";");
}
}
public static void printConstructors(Class c1) {
Constructor[] constructor = c1.getDeclaredConstructors();
for(Constructor c : constructor) {
String name = c.getName();
String modifier = Modifier.toString(c.getModifiers());
System.out.print(modifier + " " + name + "(");
Class[] a = c.getParameterTypes();
for (int i = 0; i < a.length; i++) {
if(i < (a.length-1)){
System.out.print(a[i].getName() + ",");
}
else {
System.out.print(a[i].getName());
}
}
System.out.println(");");
}
}
public static void printMethods(Class c1) {
Method[] method = c1.getDeclaredMethods();
for (Method method2 : method) {
Class returnType = method2.getReturnType();
String name = method2.getName();
String modifier = Modifier.toString(method2.getModifiers());
System.out.print(modifier + " " + returnType.getName() + " " + name + "(");
Class[] c = method2.getParameterTypes();
for (int i = 0; i < c.length; i++) {
if(i < (c.length-1)){
System.out.print(c[i].getName() + ",");
}
else {
System.out.print(c[i].getName());
}
}
System.out.println(");");
}
}
}
好了,工具都有了,让我们来分析一下内部类的类结构吧!!!
打印结果如下:
public com.heima.inner.TalkingClock$TimePrinter extendsjava.lang.Object{
final com.heima.inner.TalkingClockthis$0;
public com.heima.inner.TalkingClock$TimePrinter(com.heima.inner.TalkingClock);
public void actionPerformed(java.awt.event.ActionEvent);
}
大家会看到我们没有在内部类中定义
final com.heima.inner.TalkingClock this$0;
其实这是编译器自动为我们添加上的,虚拟机对此毫不知情,好了,有了外部类的引用,使用其域的东西也是自然而然的事情了,
public com.heima.inner.TalkingClock$TimePrinter(com.heima.inner.TalkingClock);
当我们在new 一个内部类的时候,编译器会自动在内部类的构造函数中添加上this引用。
局部内部类
仔细看一下上面的代码,我们会发现TimePrinter这个类的名字只在start方法中使用了一下,再遇到这种情况下,可以在一个方法中定义局部内部类。
局部内部类不能使用public private等访问说明符来进行声明,她和普通的局部变量的作用域相同。
局部内部类还可以访问局部变量,但变量必须声明为final。
好了,我们重新整理一下代码在出征!
public class TalkingClock {
/*private int interval;
private boolean beep;
public TalkingClock(int interval, boolean beep) {
super();
this.interval = interval;
this.beep = beep;
}*/
public void start(int interval,final boolean beep) {
class TimePrinter implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("At the tone, the time is" + new Date());
if(beep) Toolkit.getDefaultToolkit().beep();
}
}
ActionListener listener = new TimePrinter();
Timer t = new Timer(interval,listener);
t.start();
}
/*public class TimePrinter implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("At the tone, the time is" + new Date());
if(TalkingClock.this.beep) Toolkit.getDefaultToolkit().beep();
}
}*/
}
关于为什么只能访问局部变量中被final修饰的呢?让我们来分析一下上面这段代码的运行流程哦
1.调用TalkingClock对象的start()方法
2.调用TimePrinter()构造函数以便初始化
3.将listener引用传递给Timer(),定时器开始计时,因为其为两个线程,所以alkingClock对象的start()方法执行完毕,局部变量的被释放
4.然后 actionPerformed() 执行if(deep)
所以为了让actionPerformed() 方法顺利执行,TimePrinter类在beep域释放之前将其拷贝进自己的类中,设置为final就使得局部变量与在局部内部类内建立的拷贝保持了一致。
我们在用反射来分析一下TimePrinter类:
final com.heima.inner.TalkingClockthis$0;
private final booleanval$beep;
com.heima.inner.TalkingClock$1TimePrinter(com.heima.inner.TalkingClock,boolean);
public void actionPerformed(java.awt.event.ActionEvent);
这是TimePrinter类的反射分析工具打印出来的结果,看到上面我们知道 final com.heima.inner.TalkingClock this$0是指向外部类的引用
private final booleanval$beep;即为局部内部类对局部变量的拷贝,构造函数有两个显示参数com.heima.inner.TalkingClock,boolean 即初始化这两个域。
匿名内部类
将局部内部类的使用在深入一点,当创建这个类的一个对象时,就不必命名了,这种类被称为匿名内部类
通常的语法格式为:
new SuperClass(construction parameters) {
inner class method and date
}
由于构造器要和类的名字必须相同,而匿名内部类没有名字,所以匿名内部类不能有构造函数,取而代之,将构造器参数传递给父类的构造器。
静态内部类
有时候使用内部类的目的是把当前类隐藏在外部类中,而并不需要外部类的引用,为此把内部类声明为static,以便需要产生的引用。
我们可以用反射分析类的工具看一下:
public static com.heima.inner.TalkingClock$TimePrinter extendsjava.lang.Object{
public com.heima.inner.TalkingClock$TimePrinter();
public void actionPerformed(java.awt.event.ActionEvent);
}
看见了吗,当我们把该内部类声明为静态的时候就没有那个外部类引用的域了。
package com.heima.staticinner;
public class StaticInnerTest {
public static void main(String[] args) {
int[] a = new int[] {1,2,3,4,5,6,7,8,9,10};
ArrayAlg.Pair pair = ArrayAlg.minMax(a);
System.out.println("max" + pair.getMax());
System.out.println("min" + pair.getMin());
}
}
package com.heima.staticinner;
public class ArrayAlg {
static class Pair {
private int max;
private int min;
public Pair(int max, int min) {
super();
this.max = max;
this.min = min;
}
public int getMax() {
return max;
}
public void setMax(int max) {
this.max = max;
}
public int getMin() {
return min;
}
public void setMin(int min) {
this.min = min;
}
}
public static Pair minMax(int[] a) {
int min = a[0];
int max = a[0];
for (int i : a) {
if(min > i) min = i;
if(max < i) max = i;
}
return new Pair(max,min);
}
}
这里我们看到Pair为一个静态内部类,因为Pair是一个十分大众的名字,其他程序员定义的类也有可能使用了这个名字,为了增强专一性和名字的冲突,我们将其定义为内部类,此后通过ArrayAlg.Pair来访问简明知意!
好了这四种内部类我们也说的差不多了,对了有一定需要声明一下大家看我写的每一个类都是建立在com.heima.XXX这不是我在黑马那盗用的代码,是我一点一点自己打的,只是在这段期间想去黑马培训,所以使用的包也跟黑马有关来以此激励一下自己!!!