内部类的那些事

本文深入探讨Java内部类的四种类型:成员内部类、局部内部类、静态内部类和匿名内部类的特点与应用场景,并通过示例代码展示如何使用内部类访问外部类的私有数据。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

内部类是定义在另一个类中的类,为什么使用内部类呢?其主要原因有三
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这不是我在黑马那盗用的代码,是我一点一点自己打的,只是在这段期间想去黑马培训,所以使用的包也跟黑马有关来以此激励一下自己!!!




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值