Java静态方法问题,也许无解,求高人看看有没有思路
比如说现在两个类有继承关系, People类中有一个静态方法getClassName(),可以获取到当前的类名。Teacher类继承自People类。现在在Test方法里,调用Teacher.getClassName(),得到的类名却是People的类名,当然这也很好理解为什么,我就是想问问有没有什么办法可以使Teacher.getClassName()得到的是Teacher的类名。只能在People的getClassName()里面改代码,不可以动Teacher这个类。以下附上简单代码。
package com.test;
public class People {
public static String getClassName() {
return new Object() {
private String getName() {
String className = getClass().getName();
return className.substring(0, className.indexOf("$"));
}
}.getName();
}
}
package com.test;
public class Teacher extends People{
}
java 继承 静态方法 class 反射 --------------------编程问答-------------------- 1、从设计上来讲,父类应该尽量不要去干扰子类的实现
import com.test.Teacher;
public class Test {
public static void main(String[] args) throws Exception {
System.out.println(Teacher.getClassName());
}
}
2、如果硬要用父类去获取子类的类名,那么在子类调用时,父类至少应该要接收一个子类的参数,否则如果有多个子类去调用,父类就不知道要返回哪个了。
lz可以在静态方法中添加个参数试试 --------------------编程问答-------------------- 不行的吧。你这个就是父类在调啊。 --------------------编程问答-------------------- 还是考虑传参吧。
好像不止你一个在讨论这个问题,之前就有人发过类似的帖子。 --------------------编程问答-------------------- 怎么说呢~你这本身就是在调用People已经实现好的返回People这个类的类名的方法,如果你想让Teacher返回Teacher的类名,要么你覆盖父类实现的方法,要么你用Teacher.class.getName();应该就这两中方法了! --------------------编程问答-------------------- 看这个样子果然还是无解啊。 --------------------编程问答--------------------
public class People {
public static String getClassName(){
return People.class.getName();
}
}
为什么不是这样的? --------------------编程问答-------------------- 楼主是挑战常规啊,类方法一般是针对类来定义的,这个和子类没有关系,如果想要在客户端通过类方法调用,获取类名,可以重写Teacer的类方法获取方法名,但如果楼主不想这么做的话,那就将传递Class类型的参数吧,但是这真的没有意义!个人想法,楼主可以参考。 --------------------编程问答-------------------- 实在要取,从堆栈中取吧.比如 :
public static void getClassName() {
StackTraceElement[] stacks = new Throwable().getStackTrace();
for(StackTraceElement s : stacks){
System.out.println(s.getClassName());
}
}
判断s.getClassName()的值,去掉People这个名字,剩下那个就是Teacher了 --------------------编程问答-------------------- String className = getClass().getName();其实是String className = this.getClass().getName();除非getClassName传一个参数class --------------------编程问答-------------------- 之前有问过的,去看看这个帖子
http://bbs.csdn.net/topics/390328961 --------------------编程问答--------------------
我也想过从栈中去取,不过栈里面只能找到People的调用层次,根本没有Teacher的调用 --------------------编程问答--------------------
主要我是ruby背景,静态方法里面无法用this让我有点手足无措吧。 就是没法判断静态方法的调用方是谁了。 --------------------编程问答-------------------- 郭大神,顶起来 --------------------编程问答-------------------- 大神,找到解决方法私me我下 --------------------编程问答--------------------
这个堆栈数组就列出了层次关系,往前追诉就找到了调用者的类名。
也只能通过这种方法。 --------------------编程问答-------------------- 你们都忽略了面向对象的特性了吧。继承是面向对象的特性。静态方法,静态变量,私有变量,私有方法根本不存在继承之说。类方法只是为这个方法找个容身之所罢了。虽然你的Teacher继承了这个People类但是静态方法根本就不存在继承。楼主不信的话把这个People的getClassName()复制到Teacher类中用多态的方式去调用
People p=new Teacher();
System.out.println(p.getClassName());
看看它打印的还是不是原来的People --------------------编程问答-------------------- 不理解你的需求啊,为什么要用静态方法实现? --------------------编程问答-------------------- 单从JAVA的语法上来看,此题无解
如果从分析楼主的需求角度看,应该还是有办法可想的。楼主这样做想达到什么目的,不妨说出来讨论一下 --------------------编程问答--------------------
public static String getClassName() {
return new Object() {
private String getName() {
System.out.println(People.class);
String className = getClass().getName();
return className.substring(0, className.indexOf("$"));
}
}.getName();
}
写在people里面就默认使用了,没参数还真改不了 --------------------编程问答-------------------- 有些时候我也希望实现类似的行为,需求如下:
--------------------编程问答--------------------
class B extends A{}
class C extends B{}
class A{
private static Map<Class<? extends A>,Set<? extends A>>map=new HashMap<>();
//以下方法实现仅演示需求,可能有bug,不必深究
/**
* 初始化时将类型与实例分类存放在map中
*/
public A(){
Set<? extends A>set=A.map.get(this.getClass());
if(set==null){
set=new HashSet<>();
A.map.put(this.getClass(),set);
}
set.add(this);
}
/**
* 获取调用类型在map中储存的所有实例,如A类调用时获取所有的A的实例、B类则获取所有的B的实例
*/
public static<T extends A> Set<T>getInstances(){
StackTraceElement[] stacks = new Throwable().getStackTrace();
Class<T>lastCla=A.class;
for(StackTraceElement s : stacks){
if(s instanceof A){
lastCla=(Class<T>)s;
}
}
return A.map.get(lastCla);
}
@Override
public String toString(){
return this.getClass().getClassName();
}
public static void main(String[]args){
new A();
new B();
new C();
System.out.println(C.getInstances());//应该输出[C]
}
}
抱歉,写错了,s instanceof A改为通过s.getSuperclass()循环判断是A是否是s的超类,代码就不写了,你懂得 --------------------编程问答-------------------- Peple.this可以返回子类对象!! --------------------编程问答--------------------
这回答真是无语抡笔...
回正题,从堆栈找静态方法的实际调用类在jre7下测试似乎无效,测试代码如下:
try {
new SecurityManager(){
public void print(){
System.out.println(Arrays.asList(this.getClassContext()));
}
}.print();
} catch (IllegalArgumentException
| SecurityException e1) {
e1.printStackTrace();
}
new Throwable().printStackTrace();
以上代码打印结果都不包含实际调用类。就是说用Teacher调用方法打印出来的也只有People,8楼和10楼引用的例子是如何实现的呢? --------------------编程问答-------------------- public class Peple {
public void getObject(){
// TODO Auto-generated method stub
System.out.println(Peple.this);
}
}
class Teacher extends Peple{
public static void main(String[] args) {
new Teacher().getObject();
}
}
改成这样试试把 --------------------编程问答--------------------
你说话太伤人了,你看看我24楼写的吧 --------------------编程问答--------------------
Teacher的主方法可以放在Test类里 --------------------编程问答--------------------
我并没有人身攻击的意思,可是请你在回帖的时候至少看清LZ的问题,标题上可是清清楚楚地写着“静态方法”4个大字.... --------------------编程问答-------------------- 为啥一定要静态方法? --------------------编程问答-------------------- 我个人觉得静态方法是无法实现你说的功能的,你还是尝试用我的代码试试看吧! --------------------编程问答-------------------- 如果真的可行,希望楼主发个信息给我让我看看如何实现! --------------------编程问答-------------------- 我觉得不可能做到,证明:
Teacher.getClassName()访问的是静态代码段People.getClassName(),类名Teacher还是People是运行期特征,要动态获得只能通过this.getClass().getName() ,显然静态代码不能用this
至于new Throwable().getStackTrace()[0].getClassName() 之类本质获得的还是编译期特征,如
package script;
public class Father {
public void getClassNameDyn() {
new Throwable().printStackTrace();
System.out.println(this.getClass().getName());
}
}
public class Child extends Father{}
public class Test {
public static void main(String[] args) throws Exception {
new Child().getClassNameDyn();
}
}
的返回结果是:
java.lang.Throwable
at script.Father.getClassNameDyn(Father.java:32)
at script.Test.main(Script.java:197)
script.Child
这里Father是编译期特征
如果楼主只想实现单纯的这个需求,我看Teacher.class.getName()就行,你试着把class.getName()想象成一个不变的方法,否则只能传参数了 --------------------编程问答--------------------
其实我很纠结,为啥一定要用静态的?难道只为了好调用?24L的做法有何不可?而且无须传参! --------------------编程问答--------------------
这个....需求是不能随便推翻的
打印结果:
com.test.People
Test
堆栈里也没有Teacher --------------------编程问答-------------------- 我提供一个思路,就是比较绕:
new Throwable().printStackTrace(); 的结果可得:
java.lang.Throwable
at com.test.People.getClassName(People.java:xxx1)
at com.test.Test.main(Test.java:xxx2)
硬要找printStackTrace的运行期特征,其实是可以的,就是读文件在Test.java:xxx2行通过正则表达式解析出那个Teacher --------------------编程问答-------------------- 34楼的这个思路理论上是可行的! --------------------编程问答-------------------- static方法中是没办法的,因为static方法的执行的时候可能只加载了class,但是对象并未产生,那么父类如何能知道他的儿子呢?就像儿子都还没怀上没出声,父亲怎么知道儿子是什么样呢?所以不要再纠结于此。 --------------------编程问答--------------------
getClassName
com.study.test.People
main
com.study.test.T
测试结果,因为直接是调用的People的static方法,所以Teacher根本就不可能会有的 --------------------编程问答--------------------
这意味着源码必须在生产环境出现....而且涉及IO操作....几乎不可能实际应用 --------------------编程问答-------------------- 我是被逼的。。。 --------------------编程问答-------------------- 这个就是“静态方法继承”的问题么? 那个问题貌似有很多帖子,说清楚了的。
lz的需求是什么么?
我猜测,是有一大堆代码,其中有个 getClassName()方法一开始写成静态的了,结果后来发现实际运行中是需要根据不同的实例来给出结果,但是又一大堆代码已经完成,实在不想改了,是么?
我有个建议供参考:把方法改成不是静态的。
既然可能是运行时动态的,那我想代码里不会出现 Teacher.getClassName()这样的调用吧? 如果已经知道是Teacher类了,那就没必要调用了,所以很大可能代码里是 Teacher x; ... x.getClassName()这样的调用吧? 如果是这样的话,把getClassName改成非静态方法应该不会太影响已有的代码,用编译器跑一下就可以把需要修改的地方都找出来了;
实际上,把代码写出来,假设有3个类,People,Teacher,Test,
在Test中用Javap命令看字节码,你会看到类似
Compiled from "TestMain.java"
public class pkgname.staticinherit2.TestMain extends java.lang.Object
。。。。。。
const #16 = Method #17.#19; // pkgname/staticinherit2/Teacher.getClassName:()Ljava/lang/String;
const #17 = class #18; // pkgname/staticinherit2/Teacher
const #18 = Asciz pkgname/staticinherit2/Teacher;
const #19 = NameAndType #20:#21;// getClassName:()Ljava/lang/String;
const #20 = Asciz getClassName;
const #21 = Asciz ()Ljava/lang/String;;
。。。。。。
public static void main(java.lang.String[]);
Code:
Stack=2, Locals=2, Args_size=1
0: invokestatic #16; //Method pkgname/staticinherit2/Teacher.getClassName:()Ljava/lang/String;
3: astore_1
4: getstatic #22; //Field java/lang/System.out:Ljava/io/PrintStream;
7: aload_1
8: invokevirtual #28; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
11: return
在Teacher,class里你会看到类似
Compiled from "TestMain.java"
public class pkgname.staticinherit2.TestMain extends java.lang.Object
SourceFile: "TestMain.java"
minor version: 0
major version: 46
Constant pool:
const #1 = class #2; // pkgname/staticinherit2/TestMain
const #2 = Asciz pkgname/staticinherit2/TestMain;
const #3 = class #4; // java/lang/Object
const #4 = Asciz java/lang/Object;
const #5 = Asciz <init>;
const #6 = Asciz ()V;
const #7 = Asciz Code;
const #8 = Method #3.#9; // java/lang/Object."<init>":()V
const #9 = NameAndType #5:#6;// "<init>":()V
const #10 = Asciz LineNumberTable;
const #11 = Asciz LocalVariableTable;
const #12 = Asciz this;
const #13 = Asciz Lpkgname/staticinherit2/TestMain;;
const #14 = Asciz main;
const #15 = Asciz ([Ljava/lang/String;)V;
const #16 = Method #17.#19; // pkgname/staticinherit2/Teacher.getClassName:()Ljava/lang/String;
const #17 = class #18; // pkgname/staticinherit2/Teacher
const #18 = Asciz pkgname/staticinherit2/Teacher;
const #19 = NameAndType #20:#21;// getClassName:()Ljava/lang/String;
const #20 = Asciz getClassName;
const #21 = Asciz ()Ljava/lang/String;;
const #22 = Field #23.#25; // java/lang/System.out:Ljava/io/PrintStream;
const #23 = class #24; // java/lang/System
const #24 = Asciz java/lang/System;
const #25 = NameAndType #26:#27;// out:Ljava/io/PrintStream;
const #26 = Asciz out;
const #27 = Asciz Ljava/io/PrintStream;;
const #28 = Method #29.#31; // java/io/PrintStream.println:(Ljava/lang/String;)V
const #29 = class #30; // java/io/PrintStream
const #30 = Asciz java/io/PrintStream;
const #31 = NameAndType #32:#33;// println:(Ljava/lang/String;)V
const #32 = Asciz println;
const #33 = Asciz (Ljava/lang/String;)V;
const #34 = Asciz args;
const #35 = Asciz [Ljava/lang/String;;
const #36 = Asciz result;
const #37 = Asciz Ljava/lang/String;;
const #38 = Asciz SourceFile;
const #39 = Asciz TestMain.java;
{
public pkgname.staticinherit2.TestMain();
Code:
Stack=1, Locals=1, Args_size=1
0: aload_0
1: invokespecial #8; //Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 4: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lpkgname/staticinherit2/TestMain;
public static void main(java.lang.String[]);
Code:
Stack=2, Locals=2, Args_size=1
0: invokestatic #16; //Method pkgname/staticinherit2/Teacher.getClassName:()Ljava/lang/String;
3: astore_1
4: getstatic #22; //Field java/lang/System.out:Ljava/io/PrintStream;
7: aload_1
8: invokevirtual #28; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
11: return
LineNumberTable:
line 13: 0
line 14: 4
line 15: 11
LocalVariableTable:
Start Length Slot Name Signature
0 12 0 args [Ljava/lang/String;
4 8 1 result Ljava/lang/String;
}
D:\work\eclipse\ACP\WydLab\bin>javap -c -verbose pkgname.staticinherit2.Teacher
Compiled from "Teacher.java"
public class pkgname.staticinherit2.Teacher extends pkgname.staticinherit.People
SourceFile: "Teacher.java"
minor version: 0
major version: 46
Constant pool:
const #1 = class #2; // pkgname/staticinherit2/Teacher
const #2 = Asciz pkgname/staticinherit2/Teacher;
const #3 = class #4; // pkgname/staticinherit/People
const #4 = Asciz pkgname/staticinherit/People;
const #5 = Asciz <init>;
const #6 = Asciz ()V;
const #7 = Asciz Code;
const #8 = Method #3.#9; // pkgname/staticinherit/People."<init>":()V
const #9 = NameAndType #5:#6;// "<init>":()V
......
{
public pkgname.staticinherit2.Teacher();
Code:
Stack=1, Locals=1, Args_size=1
0: aload_0
1: invokespecial #8; //Method pkgname/staticinherit/People."<init>":()V
4: return
.....
也就是说,调用的地方“Test.class"里是有"Teacher"这个类的信息的,而Teacher这个类里(如果不覆盖父类的静态方法的话)是根本没有getClassName这个方法的,从Test.class的字节码中找到Teacher类,然后发现没有getClassName方法,然后向上找到父类的静态方法的整个过程,是JVM的字节码引擎处理的,纯java code是无法知道“getClassName"其实是从Teacher类开始找的,除非你的JVM引擎告诉你这些信息。
补充:Java , Java SE