41. instanceof与转型
- System.out.println( null instanceof String); //false
- System.out.println( new Object() instanceof String); //false
- //编译能通过
- System.out.println((Object) new Date() instanceof String); //false
- //!!程序不具有实际意义,但编译时不能通过
- //!!System.out.println(new Date() instanceof String);
- //!!运行时抛ClassCastException,这个程序没有任何意义,但可以编译
- //!!System.out.println((Date) new Object());
null可以表示任何引用类型,但是instanceof操作符被定义为在其左操作数为null时返回false。
如果instanceof告诉你一个对象引用是某个特定类型的实例,那么你就可以将其转型为该类型,并调用该类型的方法
,而不用担心会抛出ClassCastException或NullPointerException异常。
instanceof操作符有这样的要求:左操作数要是一个对象的或引用,右操作数是一个引用类型,并且这两个操作数的
类型是要父子关系(左是右的子类,或右是左的子类都行),否则编译时就会出错。
42. 父类构造器调用已重写的方法
- public class P {
- private int x, y;
- private String name;
- P( int x, int y) {
- this .x = x;
- this .y = y;
- // 这里实质上是调用子类被重写的方法
- name = makeName();
- }
- protected String makeName() {
- return "[" + x + "," + y + "]" ;
- }
- public String toString() {
- return name;
- }
- }
- class S extends P {
- private String color;
- S( int x, int y, String color) {
- super (x, y);
- this .color = color;
- }
- protected String makeName() {
- return super .makeName() + ":" + color;
- }
- public static void main(String[] args) {
- System.out.println( new S( 1 , 2 , "red" )); // [1,2]:null
- }
- }
在一个构造器调用一个已经被其子类重写了的方法时,可能会出问题:如果子类重写的方法要访问的子类的域还未初
始化,因为这种方式被调用的方法总是在实例初始化之前执行。要想避免这个问题,就千万不要在父类构造器中调用
已重写的方法。
43. 静态域与静态块的初始顺序
- public class T {
- public static int i = prt();
- public static int y = 1 ;
- public static int prt() {
- return y;
- }
- public static void main(String[] args) {
- System.out.println(T.i); // 0
- }
- }
上面的结果不是1,而是0,为什么?
类初始化是按照静态域或静态块在源码中出现的顺序去执行这些静态初始器的(即谁先定义,就先初始化谁),上现程序中由于i先于y声明,所以先初始化i,但由于i初始化时需要由y来决定,此时y又未初始化,实为初始前的值0,所以i的最后结果为0。
44. 请使用引用类型调用静态方法
- public class Null {
- public static void greet() {
- System.out.println( "Hello world!" );
- }
- public static void main(String[] args) {
- ((Null) null ).greet();
- }
- }
上面程序运行时不会打印NullPointerException异常,而是输出"Hello world!",关键原因是:调用静态方法时将忽略前面的调用对象或表达示,只与对象或表达式计算结果的类型有关。
在调用静态方法时,一定要使用类去调用,或是静态导入后直接使用。
45. 循环中的不能声明局部变量
- for ( int i = 0 ; i < 1 ; i++)
- Object o ; //!! 编译不能通过
- for ( int i = 0 ; i < 1 ; i++)
- Object o = new Object(); //!! 编译不能通过
一个本地变量声明看起来像是一条语句,但是从技术上来说不是。
Java语言规范不允许一个本地变量声明语句作为一条语句在for、while或do循环中重复执行。
一个本地变量声明作为一条语句只能直接出现在一个语句块中(一个语句块是由一对花 括号以及包含在这对花括号中的语句和声明构成的):
- for ( int i = 0 ; i < 1 ; i++) {
- Object o = new Object(); // 编译OK
- }
46. 内部类反射
- public class Outer {
- public class Inner {
- public String toString() {
- return "Hello world" ;
- }
- }
- public void getInner() {
- try {
- // 普通方式创建内部类实例
- System.out.println( new Outer(). new Inner()); // Hello world
- //!! 反射创建内部类,抛异常:java.lang.InstantiationException:Outer$Inner
- System.out.println(Inner. class .newInstance());
- } catch (Exception e) {
- }
- }
- public static void main(String[] args) {
- new Outer().getInner();
- }
- }
上面因为构造内部类时外部类实例不存在而抛异常。
一个非静态的嵌套类的构造器,在编译的时候会将一个隐藏的参数作为它的第一个参数,这个参数表示它的直接外围实例。如果使用反射创建内部类,则要传递个隐藏参数的唯一方法就是使用java.lang.reflect.Constructor:
- Constructor c = Inner. class .getConstructor(Outer. class ); //获取带参数的内部类构造函数
- System.out.println(c.newInstance(Outer. this )); //反射时还需传进外围类