java解惑你知多少(六)

41. instanceof与转型

Java代码   收藏代码
  1. System.out.println( null   instanceof  String); //false   
  2. System.out.println(new  Object()  instanceof  String); //false   
  3. //编译能通过   
  4. System.out.println((Object) new  Date()  instanceof  String); //false   
  5. //!!程序不具有实际意义,但编译时不能通过   
  6. //!!System.out.println(new Date() instanceof String);   
  7. //!!运行时抛ClassCastException,这个程序没有任何意义,但可以编译   
  8. //!!System.out.println((Date) new Object());   
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. 父类构造器调用已重写的方法

Java代码   收藏代码
  1. public   class  P {  
  2.  private   int  x, y;  
  3.  private  String name;  
  4.   
  5.  P(int  x,  int  y) {  
  6.   this .x = x;  
  7.   this .y = y;  
  8.   // 这里实质上是调用子类被重写的方法   
  9.   name = makeName();  
  10.  }  
  11.   
  12.  protected  String makeName() {  
  13.   return   "["  + x +  ","  + y +  "]" ;  
  14.  }  
  15.   
  16.  public  String toString() {  
  17.   return  name;  
  18.  }  
  19.   
  20. }  
  21.   
  22. class  S  extends  P {  
  23.  private  String color;  
  24.   
  25.  S(int  x,  int  y, String color) {  
  26.   super (x, y);  
  27.   this .color = color;  
  28.  }  
  29.   
  30.  protected  String makeName() {  
  31.   return   super .makeName() +  ":"  + color;  
  32.  }  
  33.   
  34.  public   static   void  main(String[] args) {  
  35.   System.out.println(new  S( 12"red" )); // [1,2]:null   
  36.  }  
  37. }  
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. 静态域与静态块的初始顺序

Java代码   收藏代码
  1. public   class  T {  
  2.  public   static   int  i = prt();  
  3.  public   static   int  y =  1 ;  
  4.  public   static   int  prt() {  
  5.   return  y;  
  6.  }  
  7.   
  8.  public   static   void  main(String[] args) {  
  9.   System.out.println(T.i);// 0   
  10.  }  
  11. }  
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. 请使用引用类型调用静态方法

Java代码   收藏代码
  1. public   class  Null {  
  2.  public   static   void  greet() {  
  3.   System.out.println("Hello world!" );  
  4.  }  
  5.   
  6.  public   static   void  main(String[] args) {  
  7.   ((Null) null ).greet();  
  8.  }  
  9. }  
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. 循环中的不能声明局部变量

Java代码   收藏代码
  1. for  ( int  i =  0 ; i <  1 ; i++)   
  2.  Object o ; //!! 编译不能通过   
  3.   
  4. for  ( int  i =  0 ; i <  1 ; i++)   
  5.  Object o = new  Object();  //!! 编译不能通过   
for (int i = 0; i < 1; i++) 
 Object o ; //!! 编译不能通过

for (int i = 0; i < 1; i++) 
 Object o = new Object(); //!! 编译不能通过

 

一个本地变量声明看起来像是一条语句,但是从技术上来说不是。

 

Java语言规范不允许一个本地变量声明语句作为一条语句在for、while或do循环中重复执行。

 

一个本地变量声明作为一条语句只能直接出现在一个语句块中(一个语句块是由一对花 括号以及包含在这对花括号中的语句和声明构成的):

Java代码   收藏代码
  1. for  ( int  i =  0 ; i <  1 ; i++) {  
  2.  Object o = new  Object();  // 编译OK   
  3. }  
for (int i = 0; i < 1; i++) {
 Object o = new Object(); // 编译OK
}

 

46. 内部类反射

Java代码   收藏代码
  1. public   class  Outer {  
  2.  public   class  Inner {  
  3.   public  String toString() {  
  4.    return   "Hello world" ;  
  5.   }  
  6.  }  
  7.  public   void  getInner() {  
  8.   try  {  
  9.    // 普通方式创建内部类实例   
  10.    System.out.println(new  Outer(). new  Inner()); // Hello world   
  11.    //!! 反射创建内部类,抛异常:java.lang.InstantiationException:Outer$Inner   
  12.    System.out.println(Inner.class .newInstance());  
  13.   } catch  (Exception e) {  
  14.   }  
  15.  }  
  16.  public   static   void  main(String[] args) {  
  17.    new  Outer().getInner();  
  18.  }  
  19. }  
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:

Java代码   收藏代码
  1. Constructor c = Inner. class .getConstructor(Outer. class ); //获取带参数的内部类构造函数   
  2. System.out.println(c.newInstance(Outer.this )); //反射时还需传进外围类  

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值