一、关于接口interface与Object类的关系
其实这个问题讨论起来没什么意义,可能在实际开发中的用处并不是很大。
public interface T
{
void itMethod();
}
public class C implements T {
public void itMethod() {
System.out.println("override interface method!!");
}
public void clsMethod(){
System.out.println("class method!!");
}
}
public class Demo {
public static void main(String[] args) {
T it=new C ();
it.it();
it.clsMethod();//此行会报错;这是大家都知道的,因为接口没有clsMethod方法
System.out.println(it.toString());//接口中也没有看到toString方法,但此处没有报错。
Class cs= T.class;
Class sc=cs.getSuperclass(); //sc引用为空
}
}
对于这种情况,如果上述引用型变量it的类型(T)是某一个类的话,大家也就很容易理解,因为所有类的根类都是Object类,Object类中的方法也就自然的能被其直接或间接子类继承,根据多态的原理,it调用Object类中的方法是不会出错的,因为T和C类都直接或间接地继承了Object中的方法。
但重点是,如果it的类型T不是具体的类而是接口时,那么它还会有Object中的方法和属性么?有很多人都认为接口的直接或间接父类也是Object,但真的是这样的么,没错我们常说接口是一个特殊的类,如果是类的话,那么它的根类不就是Object么?事实上,我个人认为可以把接口看作是特殊的类,但它并不是Oject类的子类(如果有的话sc就不会为空),接口interface与class应该属于同一级别的概念,他们之间是不存在从属关系的,我们通常定义地接口是没有继承Object类的。但是从上面的代码中,我们也看到了我们使用T类型的引用去调用了C类(实现了T接口)中的toString(),我们都很清楚如果T中没有toString()的话,我们使用T类型的引用去调用实现类中的toString()方法在编译的时候是通不过的,但现在编译器确实没有报错,这种现象该如何解释呢?是不是编译器对这种情况进行了特殊对待?
实际上在Sun的官方文档TJLS(The Java Language Specification)就有相关说明:
其中第9章9.2节关于接口有这么一段话:
If an interface has no direct superinterfaces, then the interface implicitly declares a public abstract member method m with signature s, return type r, and throws clause t corresponding to each public instance method m with signature s, return type r, and throws clause t declared in Object, unless a method with the same signature, same return type, and a compatible throws clause is explicitly declared by the interface.
它大概意思是说当一个接口没有直接extends superinterfaces时,那么再该接口中就会隐含定义了一套与Object类中的方法签名完全相同的方法,所以,我们在程序中调用接口的那些与Object中具有相同签名的方法时,编译器不会报错!
这段描述对我很有帮助,说了这么多,只是想让大家在空闲时间来考虑JAVA的设计思路和理念,巩固和加深对它的理解。以上所有的均为个人看法,仅供参考,不对之处还望指出,大家共同研究讨论。
二、关于字符串两种创建方式的总结和说明
Java运行时会维护一个String Pool(String池),JavaDoc翻译很模糊“字符串缓冲区”。String池用来存放运行时中产生的各种字符串,并且池中的字符串的内容不重复。
1.采用字面值方式赋值:String s =“hello”;
1) 查找 String Pool中是否存在“hello”这个对象,如果不存在,则在 String Pool中创建一个“hello”对象,然后将 String Pool中的这个“hello”对象的地址返回来,赋给引用变量 s,这样 s会指向 String Pool 中的这个“hello”字符串对象
2) 如果存在,则不创建任何对象,直接将 String Pool中的这个“hello”对象地址返回来,赋给 s引用。
2.采用new的方式:String s = new String(“hello”);
1) 首先在 String Pool中查找有没有“hello”这个字符串对象,如果有,则不在 String Pool中再去创建“hello”这个对象了,直接在堆中(heap)中创建一个“hello”字符串对象,然后将堆中的这个“hello”对象的地址返回来,赋给 s 引用,导致 s 指向了堆中创建的这个“hello”字符串对象。
2) 如果没有,则首先在 String Pool中创建一个“hello“对象,然后再在堆中(heap)创建一个”hello“对象,然后将堆中的这个”hello“对象的地址返回来,赋给 s 引用,导致 s 指向了堆中所创建的这个”hello“对象。
以上两种方式比较常见,而且区别较大,所以只是比较这两种方式,至于其他方式在此不再做过多说明。
对于字面值方式还有如下例子,感兴趣的可以研究研究:
package testPackage;
class Test {
public static void main(String[] args) {
String hello = "Hello", lo = "lo";
System.out.print((hello == "Hello") + " ");
System.out.print((Other.hello == hello) + " ");
System.out.print((other.Other.hello == hello) + " ");
System.out.print((hello == ("Hel"+"lo")) + " ");
System.out.print((hello == ("Hel"+lo)) + " ");
System.out.println(hello == ("Hel"+lo).intern());
}
}
class Other {
static String hello = "Hello";
}
package other;
public class Other {
public static String hello = "Hello";
}
result:true true true true false true
有关字符串的用法和其他说明也可参照:http://lavasoft.blog.51cto.com/62575/80034/