我看到一些类加载行为似乎与JVM规范不一致,我想知道这是否是一个错误.或者如果没有,希望有人可以解释原因.
下面的示例代码只是从其main方法打印hello.它有一个未使用的方法,它包含对方法的方法调用,该方法声明它将'C'(作为接口)作为参数.
当执行main时(类路径中没有A,B和C),接口C将抛出ClassNotFound错误.(因为它仅在永不执行的方法中引用,因此在运行时实际上不需要注释C).
这似乎违反了JVM规范
Java VM Spec的第2.17.1节第2版说:
关于何时执行解析的唯一要求是,在解析期间检测到的任何错误都必须抛到程序中的某个点,程序可能会直接或间接地需要链接到错误中涉及的类或接口.
Java VM Spec的第2.17.3节第2版说:
Java编程语言允许实现灵活性,以便何时发生链接活动(并且,由于递归,加载),只要语言的语义得到尊重,类或接口在初始化之前完全验证和准备,并且在链接期间检测到的错误被抛出到程序中的某个点,在该点上程序可能需要链接到错误中涉及的类或接口.
注意:如果我将定义中的参数类型更改为类而不是接口,则代码会正确加载和执行.
/**
* This version fails, the method call in neverCalled() is to a method whose
* parameter definition is for an Interface
*/
public class Main {
public void neverCalled(){
A a = new A();
B b = new B(); // B implements C
//method takeInter is declared to take paramters of type Interface C
//This code is causes a ClassNotFound error …Run Code Online (Sandbox Code Playgroud) 我很好奇从可变类返回一组对象时被认为是更好的做法:
public class Someclass {
public List<String> strings;
public add(String in){ strings.add(in); }
public remove(String in) { strings.remove(in); }
//THIS
public List<String> getStrings(){
return Collections.unmodifiableList(strings);
}
//OR THIS
public List<String> getStrings(){
return new ArrayList(strings);
}
}
Run Code Online (Sandbox Code Playgroud)
我总是假设将内部集合包装在Unmodifiable中是最好的方法,因为我没有理由产生创建副本的开销.但是,我发现内部对列表所做的任何更改都会暴露给客户端,这对我来说是糟糕的.
我遇到了一个我不明白的类加载器问题.我在使用Java 1.6.0和Windows XP的OSX上看到过相同的行为.
当我使用MyListener而MyObject不是在类路径中运行以下代码时,我得到了一个NoClassDefFoundError.但是,如果我删除该MyObject.add(my)行或替换它,MyObject.add(null)那么代码运行正常.
请注意,实际上从未使用过具有无法解析的依赖项的方法.
我不明白为什么MyObject.add(my)导致VM尝试加载MyListener但MyListener my = new MyListener(){};不会.
public class Main {
public void neverCalled(){
MyListener my = new MyListener(){};
MyObject.add(my);
}
public static void sayHi(){
System.out.println("Hello");
}
public static void main(String[] args) {
System.out.println("Starting...");
sayHi();
}
}
Run Code Online (Sandbox Code Playgroud)
没有什么有趣的MyObject和MyListener:
public class MyObject {
public static void add(MyListener in){}
}
public interface MyListener {}
Run Code Online (Sandbox Code Playgroud)
我根据下面的标准提供的信息做了一些额外的研究.显然,由于一些未知的原因,使用参数进行方法调用似乎会导致加载参数类,而仅仅声明变量不会.
Java VM Spec的第2.17.1节第2版说:
关于何时执行解析的唯一要求是, …
我正在使用ant 1.6.2并且我正在尝试设置一个比较源目录和目标目录的任务,识别源目录中存在的所有子目录,并删除目标目录中的like目录子目录.
所以,假设源目录中有子目录sub1,sub2和sub3,目标目录中有sub1,sub2,sub3和sub4,那么我想从目标dir中删除sub1,sub2和sub3.
我想我可以通过使用FileSelector来识别源中存在于目标中的所有目录.但是,我无法让<type>文件选择器返回目录的匹配项.
最终,我想我会做类似的事情:
<fileset id="dirSelector" dir="${install.dir}">
<type type="dir"/>
<present targetdir="${dist.dir}"/>
</fileset>
Run Code Online (Sandbox Code Playgroud)
我开始只是尝试列出源目录中的目录并将其打印出来:
<fileset id="dirSelector" dir="${install.dir}">
<type type="dir"/>
</fileset>
<property name="selected" refid="dirSelector" />
<echo>Selected: ${selected}</echo>
Run Code Online (Sandbox Code Playgroud)
但是,我从来没有打印过将类型选择器设置为目录的任何内容.如果我将类型更改为文件,我会打印出文件.
有没有更好的方法来完成我想要做的事情?我在使用类型选择器时遗漏了什么吗?
我对以下代码感到困惑:
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
public class GenericsTest<T extends List> {
public void foo() {
T var = (T) new LinkedList();
}
public static void main(String[] args) {
GenericsTest<ArrayList> gt1 = new GenericsTest<ArrayList>();
gt1.foo();
System.out.println("Done");
}
}
Run Code Online (Sandbox Code Playgroud)
无论我传递给构造函数的Type参数如何,运行时类型T似乎都是java.util.List.
那么为什么编译器T在分配var时需要转换?它不应该在编译时知道LinkedList可分配给List吗?
我理解代码是假的,我理解为什么它在运行时工作,即使它看起来不应该.令我困惑的部分是为什么编译器要求我在进行赋值时键入(T)?然而,如果没有伪造的话,它可以很好地编译.
据推测,编译器理解擦除.看起来编译器应该能够在没有强制转换的情况下编译代码.