为何this.getClass().getName()与StackTraceElement.getClassName()返回结果不同?
这事儿的核心原因其实很简单:你在BaseModel构造器里创建的不是ElementModel本身,而是它的匿名内部子类,咱们一步步拆解:
1. 为什么ElementModel构造器输出BaseModel$1?
看BaseModel构造器里的这段代码:
addedElements = new ElementModel<E>(){};
这里的{}是关键——它表示你创建了一个继承自ElementModel的匿名内部类,而不是直接实例化ElementModel。Java对匿名内部类的命名规则是「外部类名$数字」,因为这个匿名类是在BaseModel里创建的第一个匿名类,所以它的全名就是BaseModel$1。
当这个匿名子类的构造器执行时,this指向的是这个匿名子类的实例,所以this.getClass().getName()自然返回的是这个匿名类的名字,而不是父类ElementModel。
2. 为什么BaseModel构造器输出CellModel?
这是Java继承的正常表现:当你创建CellModel实例时,JVM会先执行父类BaseModel的构造器,此时this指向的是正在初始化的CellModel实例(子类实例初始化过程中,父类构造器里的this始终是子类对象),所以this.getClass().getName()返回CellModel是符合多态规则的。
3. 为什么demo2里st.getClassName()返回ElementModel?
你在demo2里的逻辑是遍历栈轨迹,找到方法名为add的栈帧:
if (st.getMethodName().equalsIgnoreCase("add")) { System.out.println("ElementModel::demo2.......st.getClassName: "+st.getClassName()); }
这里的add方法是定义在ElementModel类中的,哪怕是由匿名子类实例调用,栈轨迹里记录的是方法的定义类,所以st.getClassName()返回的是ElementModel。但注意:此时this.getClass().getName()在demo2里其实还是BaseModel$1(你可以自己打印验证),你看到的符合预期只是栈帧的类名,并非当前实例的实际类型。
解决方法:获取真正的ElementModel实例
如果你希望addedElements是ElementModel的实例,只需要把BaseModel构造器里的代码改成:
addedElements = new ElementModel<E>(); // 去掉后面的{}
这样就直接实例化ElementModel本身,而非匿名子类,此时ElementModel构造器里的this.getClass().getName()就会返回ElementModel,符合你的预期。
内容的提问来源于stack exchange,提问作者Tania

