public class A {
B b = new B();
}public class B {
A a = new A();
}类之间循环引用问题,单独使用javac A 不能通过编译,会报“找不到类符号B”的错误。但使用javac *.java却可以编译通过,有谁知道原理
本人学了两年java想不通惭愧ing
B b = new B();
}public class B {
A a = new A();
}类之间循环引用问题,单独使用javac A 不能通过编译,会报“找不到类符号B”的错误。但使用javac *.java却可以编译通过,有谁知道原理
本人学了两年java想不通惭愧ing
你一样可以用javac A.java B.java
b也需要引用a
但是你得在a引用b的时候告诉b在哪里吧
同时也得告诉系统b引用a的时候a在哪里啊如果没有提前打包
b的定义在a后面
系统都还没有跑到哪里去编译
当然就不存在b
自然就找不到b咯
B b = new B(); 需要用到B类型,已经B()构造函数,这个时候,就需要编译器去检查B是否存在,而且B()是否存在
如果没有,就去找.java,编译一下。感觉循环引用,确实违反直觉,其实没什么想不通的:java.lang.Object {public String toString() {....}}
Object这个元始天尊都需要依赖别人。编译A的时候,只要知道B存在,而且所有对B的操作都是合法的(方法存在、参数类型正确等),那么A就能编译通过。因为编译到A中的只是B和B()这个名字(可以想象成字符串)而不是B本身。
你可以先没有B,直接写Class.forName("B"),然后只要在运行以前编写class B就可以了。
上面这样是会内存溢出,不过将B的声明改为
public class B {
A a ;
}
就不会有任何问题了
感觉这位说得挺对的,不过照这样说的话,那么在javac A的时候其实就应该是已经对的了,因为编译器会去智能的去找B的声明。而不应该在javac *.java的时候才去找吧。
public class A {}即使上面代码,楼主只javac A仍然会编译不通过,必须javac A.java才可以
javac A.java编译不通过的情况:D:\test>del *.classD:\test>type A.java
public class A {
B b = new B();
}
D:\test>type B.java
public class B {
A a = new A();
}
D:\test>javac -version
javac 1.6.0_27D:\test>javac -verbose A.java
[解析开始时间 A.java]
[解析已完成时间 15ms]
[源文件的搜索路径: .]
[类文件的搜索路径: D:\Java\jdk1.6.0\jre\lib\resources.jar,D:\Java\jdk1.6.0\jre\
lib\rt.jar,D:\Java\jdk1.6.0\jre\lib\sunrsasign.jar,D:\Java\jdk1.6.0\jre\lib\jsse
.jar,D:\Java\jdk1.6.0\jre\lib\jce.jar,D:\Java\jdk1.6.0\jre\lib\charsets.jar,D:\J
ava\jdk1.6.0\jre\lib\modules\jdk.boot.jar,D:\Java\jdk1.6.0\jre\classes,D:\Java\j
dk1.6.0\jre\lib\ext\dnsns.jar,D:\Java\jdk1.6.0\jre\lib\ext\localedata.jar,D:\Jav
a\jdk1.6.0\jre\lib\ext\sunjce_provider.jar,.]
[正在装入 java\lang\Object.class(java\lang:Object.class)]
[正在装入 .\B.java]
[解析开始时间 .\B.java]
[解析已完成时间 1ms]
[正在检查 A]
[已写入 A.class]
[正在检查 B]
[已写入 .\B.class]
[总时间 406ms]D:\test>
可以使用-sourcepath参数
D:\test>cd \D:\>del test\*.classD:\>type test\A.java
public class A {
B b = new B();
}
D:\>type test\B.java
public class B {
A a = new A();
}
D:\>javac -verbose test\A.java
[解析开始时间 test\A.java]
[解析已完成时间 15ms]
[源文件的搜索路径: .]
[类文件的搜索路径: D:\Java\jdk1.6.0\jre\lib\resources.jar,D:\Java\jdk1.6.0\jre\
lib\rt.jar,D:\Java\jdk1.6.0\jre\lib\sunrsasign.jar,D:\Java\jdk1.6.0\jre\lib\jsse
.jar,D:\Java\jdk1.6.0\jre\lib\jce.jar,D:\Java\jdk1.6.0\jre\lib\charsets.jar,D:\J
ava\jdk1.6.0\jre\lib\modules\jdk.boot.jar,D:\Java\jdk1.6.0\jre\classes,D:\Java\j
dk1.6.0\jre\lib\ext\dnsns.jar,D:\Java\jdk1.6.0\jre\lib\ext\localedata.jar,D:\Jav
a\jdk1.6.0\jre\lib\ext\sunjce_provider.jar,.]
[正在装入 java\lang\Object.class(java\lang:Object.class)]
test\A.java:2: 找不到符号
符号: 类 B
位置: 类 A
B b = new B();
^
[正在检查 A]
test\A.java:2: 找不到符号
符号: 类 B
位置: 类 A
B b = new B();
^
[总时间 243ms]
2 错误D:\>javac -verbose -sourcepath test test\A.java
[解析开始时间 test\A.java]
[解析已完成时间 15ms]
[源文件的搜索路径: test]
[类文件的搜索路径: D:\Java\jdk1.6.0\jre\lib\resources.jar,D:\Java\jdk1.6.0\jre\
lib\rt.jar,D:\Java\jdk1.6.0\jre\lib\sunrsasign.jar,D:\Java\jdk1.6.0\jre\lib\jsse
.jar,D:\Java\jdk1.6.0\jre\lib\jce.jar,D:\Java\jdk1.6.0\jre\lib\charsets.jar,D:\J
ava\jdk1.6.0\jre\lib\modules\jdk.boot.jar,D:\Java\jdk1.6.0\jre\classes,D:\Java\j
dk1.6.0\jre\lib\ext\dnsns.jar,D:\Java\jdk1.6.0\jre\lib\ext\localedata.jar,D:\Jav
a\jdk1.6.0\jre\lib\ext\sunjce_provider.jar,.]
[正在装入 java\lang\Object.class(java\lang:Object.class)]
[正在装入 test\B.java]
[解析开始时间 test\B.java]
[解析已完成时间 0ms]
[正在检查 A]
[已写入 test\A.class]
[正在检查 B]
[已写入 test\B.class]
[总时间 262ms]D:\>
1 为什么循环引用可以编译的道理,见上面9F,10F
2 会报“找不到类符号B”的错误的原因,见26F,27F
下面是通过jasml看到的java字节码
public class exception.A extends java.lang.Object{
exception.B b
public void <init> (){
aload_0
invokespecial void java.lang.Object.<init>()
return
[MaxStack : 1]
[MaxLocal : 1]
}[SourceFile : A.java]
}
里面是只需要B的名字 不过既然这样,那在单独编译A的时候,java编译器为啥不会直接去当前目录智能的搜索class B的定义呢
而非要通过javac A.java -sourcepath exception B.java这样的命令去指定依赖关系
之所以是当前目录是因为两个类是声明在同一个包下的,如果不是同一个包则通过com.sun.xx声明去找相应的包,java编译器不会这么傻吧
为啥不能智能,因为路径可能性太大假设D:\src是src目录有test包D:\src>javac test\A.java
D:\src\test>javac A.java
D:\src\test>javac ..\test\A.java
E:\>javac D:\src\test\A.java
....另外,可能还会有多个src路径,假设D:\test\A.java,C:\B.java
javac -sourcepath D:\test;C:\ D:\test\A.java
不指定-sourcepath,没法搞
你只是打错了 少打了个.java
我刚才亲自试了一下 javac A.java是可以的 但是他会把B.class也编译出来
看了下
好多人只是研究 从理论上探讨 运用自己并不熟悉的知识试图去解决问题 这简直太符合应试教育下的学生了
对待问题就像考试一样 会不会的不管 不分析问题 而是把自己会的全部写出来其实自己去试一下就知道怎么回事了
哎 悲哀啊
-sourcepath sourcepath
Specify the source code path to search for class or interface definitions. As with the user class path, source path entries are separated by semicolons (;) and can be directories, JAR archives, or ZIP archives. If packages are used, the local path name within the directory or archive must reflect the package name.
Note that classes found through the classpath are subject to automatic recompilation if their sources are found.
刚照你说的做了下javac -sourcepath D:\test\A.java 是不行的哈,用javac -sorucepath D:\test就可以。
官方文档是的说明sourcepath 后面所接的参数can be directories, JAR archives, or ZIP archives.
我也没有刻意去学,只是涉及到这个问题时去google和在这边讨论发现的,今天参考了java虚拟机规范,接触了字节码。哈哈,一步一步积累吧CSDN的“连续3次回复限制”真蛋疼
感谢各位的回复,尤其感谢shine333
如果有没明白的请看shine333的回复~
我认为哈,会有这种想法的是不是不太理解汇编语言,编译原理等计算机基础课程的人呢?在汇编级别的程序里面哪有什么类啊,全都是指令和数据。public class A {
B b = new B();
}public class B {
A a = new A();
}
这两个类其实很简单
在A类里面有一个类型为B的数据,
在B类里面有一个类型为A的数据。
对计算机来说A,B和一个string没啥区别,都是某种类型的数据而已。
at B.<init>(B.java:2)
at A.<init>(A.java:2)
at B.<init>(B.java:2)
at A.<init>(A.java:2)
at B.<init>(B.java:2)
at A.<init>(A.java:2)
at B.<init>(B.java:2)
at A.<init>(A.java:2)
at B.<init>(B.java:2)无线重复以后
自动退出来了!
这个应该是 报错吧
这个当然是死循环喽,不能运行的,除非去掉一个new ,比如说把A a = new A();改为A a;就可以了
话说没把握的事得验证一下,没有人了解所有东西。
推荐的帖子质量不断下降啊
LS有个人说javac A.java直接通过 我估计你没删干净.class文件
不知道是不是JVM版本的问题,现在编译完全没有问题啊?