1. java内部类
1.1 内部类简介
内部类就是定义在另一个类中的类,使用内部类的好处:
(1)内部类可以对同一个包中的其他类隐藏
(2)内部类的方法可以访问外部类所有属性,包括私有属性
1.2 内部类举例
package innerClass;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.time.Instant;
import javax.swing.JOptionPane;
import javax.swing.Timer;
public class InnerClassTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
TalkingClock talkingClock = new TalkingClock(1000,true);
talkingClock.start();
JOptionPane.showMessageDialog(null, "是否退出");
System.exit(0);
}
}
// 外部类
class TalkingClock{
private int interval;
private boolean beep; // 是否开启蜂鸣
public TalkingClock(int interval,boolean beep) {
this.interval = interval;
this.beep = beep;
}
// 启动函数
public void start() {
TimePrinter timePrinter = new TimePrinter(1); // 内部类的对象在创建的时候,编译器会自动将外部类的this引用
// 传入内部类的构造器,例如:TimePrinter timePrinter = new TimePrinter(this);
Timer timer = new Timer(interval,timePrinter);
timer.start(); // 启动定时器
}
// 内部类: 编译器会自动修改所有内部类的构造器,添加一个对应外部类引用参数,这些都是编译器来完成
// TimePrinter添加外部类引用类参数后,构造函数为:
// public TimePrinter(TalkingClock clock) {
// outer = clock;
// }
public class TimePrinter implements ActionListener{
public TimePrinter(int i) { // 内部类构造方法
}
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("当前时间:" + Instant.ofEpochMilli(e.getWhen()));
if(beep) { // beep是外部类的私有属性
Toolkit.getDefaultToolkit().beep();
}
}
}
}
2. 访问外部类属性
为什么内部类可以访问外部类的属性,底层是如何实现的,下面将对以上问题进行详解:
我们通过反射来对内部类和外部类的class文件进行分析,其中反射类定义如下:
package reflection;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Scanner;
public class ReflectionDemo {
public static void main(String[] args) throws ClassNotFoundException {
// 反射
String className; // 类名
if(args.length > 0 ) {
className = args[0];
}else {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入类名:");
className = scanner.next();
}
Class cl = Class.forName(className); // 根据类名找到类对象
Class sucl = cl.getSuperclass(); // 得到此类的父类的Class对象
Class[] interfaces = cl.getInterfaces(); // 获得实现接口
String modifiers = Modifier.toString(cl.getModifiers()); // 得到类修饰符号 public final
System.out.print(modifiers + " " + className);
if(sucl!=null && sucl != Object.class) {
System.out.print(" extends " + sucl.getName());
}
if(interfaces.length>0) {
System.out.print(" implements ");
for(int i=0;i<interfaces.length;i++) {
System.out.print(interfaces[i].getName()+" ");
}
}
System.out.println("\n{");
//输出构造方法
printConstructors(cl);
// 输出方法
printMethods(cl);
// 输出字段
printFields(cl);
System.out.println("}");
}
private static void printFields(Class cl) {
Field[] fields = cl.getDeclaredFields();
for(Field f:fields) {
String fname = f.getName(); // 字段的名称
String modifiers = Modifier.toString(f.getModifiers()); // 字段修饰符号
Class type = f.getType(); // 字段类型
System.out.println(modifiers+" "+type.getName()+" "+fname+";");
}
}
private static void printMethods(Class cl) {
Method[] methods = cl.getDeclaredMethods();
for(Method method : methods) {
String name = method.getName(); // 获得方法名
String modifiers = Modifier.toString(method.getModifiers()); // 方法修饰符
Class[] parameterTypes = method.getParameterTypes(); // 获得方法参数类型
Class runClassType = method.getReturnType(); // 获得方法返回参数类型
System.out.print(modifiers + " " + runClassType.getName() + " " + name + " (");
for(int i=0;i<parameterTypes.length;i++) {
if(i>0) {
System.out.print(",");
}
System.out.print(parameterTypes[i].getName());
}
System.out.println(");");
}
}
public static void printConstructors(Class cl) {
Constructor[] constructors = cl.getDeclaredConstructors(); // 获得所有构造方法
for(Constructor constructor : constructors) {
String name = constructor.getName();
String modifiers = Modifier.toString(constructor.getModifiers()); // 方法修饰符
Class[] parameterTypes = constructor.getParameterTypes(); // 获得参数类型
System.out.print(modifiers + " " + name + " (");
for(int i=0;i<parameterTypes.length;i++) {
if(i>0) {
System.out.print(",");
}
System.out.print(parameterTypes[i].getName());
}
System.out.println(");");
}
}
}
接下将对第一节定义内部类进行剖析,其中它们class文件名如下:
接下来将对这个class文件采用ReflectionDemo类进行分析,可以等到这两个类具体信息如下:
// 内部类实际实现:
public innerClass.TalkingClock$TimePrinter implements java.awt.event.ActionListener
{
// 通过构造函数将外部类对象传入,实现在内部类中完成对外部类属性访问(第一步)
public innerClass.TalkingClock$TimePrinter (innerClass.TalkingClock,int);
public void actionPerformed (java.awt.event.ActionEvent);
final innerClass.TalkingClock this$0; // innerClass.TalkingClock 外部类对象属性
}
// 外部类实际实现:
innerClass.TalkingClock
{
public innerClass.TalkingClock (int,boolean);
public void start ();
static boolean access$0 (innerClass.TalkingClock); //这个方法用于实现内部类访问外部类的私有属性的方法(第二步)
private int interval;
private boolean beep;
}
通过对内外部类的方法签名和属性分析可知,内部类通过添加外部类对象属性,并在构造方法的参数列表中添加外部类对象参数,实现内部类与外部类关联,使得内部类可以访问外部类的公有属性,但是对于私有属性访问,可以通过外部类的static boolean access$0 (innerClass.TalkingClock)方法对私有属性进行访问。如果要达到上诉效果,还需要结合内部类方法Start方法和内部类的actionPerformed方法和构造方法进行说明:
// 启动函数
public void start() {
TimePrinter timePrinter = new TimePrinter(1); // 编译器会自动将this(外部类对象)传入,于是这句号就变成了:
// TimePrinter timePrinter = new TimePrinter(this,1); 这就相当于内部类拥有了外部对象
Timer timer = new Timer(interval,timePrinter);
timer.start(); // 启动定时器
}
// 内部类构造方法
public TimePrinter(int i) {
}
// 编译器会自动变成:
public innerClass.TalkingClock$TimePrinter (innerClass.TalkingClock clock,int i)
{
this$0 = clock;
}
// actionPerformed方法
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("当前时间:" + Instant.ofEpochMilli(e.getWhen()));
if(beep) { // beep会被编译器替换成:TalkingClock.access$0(this$0)
Toolkit.getDefaultToolkit().beep();
}
}
3. 局部内部类
将类定义在方法内部称为局部内部类,值得注意的是局部内部类不能用public和private修饰。局部内部类不仅可以访问外部类的属性还可以访问定义方法中的局部变量。
package innerClass;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.time.Instant;
import javax.swing.JOptionPane;
import javax.swing.Timer;
public class InnerClassTest2 {
public static void main(String[] args) {
// 局部内部类
TalkingClock_2 talkingClock = new TalkingClock_2();
talkingClock.start(1000,true);
JOptionPane.showMessageDialog(null, "是否退出");
System.exit(0);
}
}
class TalkingClock_2{
// 启动函数
public void start(int interval,boolean beep) {
class TimePrinter_2 implements ActionListener{
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("当前时间:" + Instant.ofEpochMilli(e.getWhen()));
if(beep) {
Toolkit.getDefaultToolkit().beep();
}
}
}
TimePrinter_2 timePrinter = new TimePrinter_2(); // 内部类的对象在创建的时候,编译器会自动将外部类的this引用
// 传入内部类的构造器,例如:TimePrinter timePrinter = new TimePrinter(this);
Timer timer = new Timer(interval,timePrinter);
timer.start(); // 启动定时器
}
}
局部内部类访问访问局部变量分析:
通过反射对这个两个class分析可以得到,局部内部类会把引用局部变量定义成内部私有final属性,并通过内部构造方法传入:
// 内部类:
innerClass.TalkingClock_2$1TimePrinter_2 implements java.awt.event.ActionListener
{
innerClass.TalkingClock_2$1TimePrinter_2 (innerClass.TalkingClock_2,boolean);
public void actionPerformed (java.awt.event.ActionEvent);
final innerClass.TalkingClock_2 this$0;
private final boolean val$beep; // 针对局部变量定义私有属性
}
// 外部类:
innerClass.TalkingClock_2
{
public void start (int,boolean);
}
4. 匿名内部类
只是创建这个类的一个对象,不需要类名字,我们把这种类叫做匿名内部类,并且多用于接口对象的创建,匿名内部类通常用于实现事件监听和回调,不过匿名内部类可以用Lambda表达式代替。
语法:
new InterfaceType()
{
// 属性和方法
};
注意:在类中静态方法种可以通过匿名内部类的方式获取这个类名称:
package reflection;
public class StaticLoag {
// getEnclosingClass()获得外部类的Class对象
public static void main(String[] args) {
System.out.println(new Object() {}.getClass().getEnclosingClass().getName()); //输出:reflection.StaticLoag
}
}
5. 静态内部类
静态内部类也是定义在另一个类里面的类,只不过在类的前面多了一个关键字static。静态内部类是不需要依赖于外部类的,这点和类的静态成员属性有点类似,并且它不能使用外部类的非static成员变量或者方法,这点很好理解,因为在没有外部类的对象的情况下,可以创建静态内部类的对象,如果允许访问外部类的非static成员就会产生矛盾,因为外部类的非static成员必须依附于具体的对象。
语法
public class Test {
public static void main(String[] args) {
Outter.Inner inner = new Outter.Inner();
}
}
class Outter {
public Outter() {
}
static class Inner {
public Inner() {
}
}
}