java中类型转换异常问题 强制类型转换

 
 

https://blog.csdn.net/czengze/article/details/52976820

1.背景

      在Java中,之所以需要强制类型转换是为了防止程序员在不知情的情况下把A类型数据错当成B类型的数据。将一种类型的值赋给另一个类型的变量是很常见的,如果这两种是兼容的,那么Java将执行自动转换,例如int和long,将int类型赋值给long类型的变量,总是可行的。但不是所有的类型都兼容。例如,没有将double类型转换为byte型的定义。但是不兼容的类型之间的转换仍然是可能的。达到这个目的,就需要使用强制类型转换。完成两个不兼容类型的强制转换。

2.自动类型转换和强制类型转换

      在Java中由于继承和向上转型,子类可以非常自然地转换成父类,但是父类转换成子类则需要强制转换。因为子类拥有比父类更多的属性、更强的功能,所以父类转换为子类需要强制。

      当我们用一个类型的构造器构造出一个对象时,这个对象的类型就已经确定的,也就说它的本质是不会再发生变化了。在Java中我们可以通过继承、向上转型的关系使用父类类型来引用它,这个时候我们是使用功能较弱的类型引用功能较强的对象,这是可行的。但是将功能较弱的类型强制转功能较强的对象时,就不一定可以行了。

[java]  view plain  copy
  1. Father father = new Son();  
  2. Son son = (Son)father;  
  3. //编译出错,ClassCastException  
  4. Father father = new Father();  
  5. Son son = (Son) father;  
      在第一个例子中,father被指向一个子类对象,子类也可以指向子类对象。而第二个例子中,father被传给一个父类对象,子类引用不能指向父类对象。即很重要的概念是:父类引用指向子类对象。将父类转换为子类之前,应该用instanceof检查。
[java]  view plain  copy
  1. static class One {  
  2.     public void foo() {  
  3.         System.out.println("One");  
  4.     }  
  5. }  
  6. static class Two extends One {  
  7.     public void foo() {  
  8.         System.out.println("Two");  
  9.     }  
  10.     public void dosth(){  
  11.         System.out.println("Two again");  
  12.     }  
  13. }  
  14. public static void main(String[] args) throws Exception{  
  15.     One t = new Two(); //向上转型,即父类引用指向子类对象,此时子类对象的类型为父类的类型  
  16.     t.foo();  
  17.     t.dosth();//编译错误  
  18.     t = (Two)t;   
  19.     t.dosth();//编译错误   
  20.     ((Two) t).dosth();//编译成功  
  21. }  

向上转型是安全的,但是会有些子类特性会丢失,向下转型可以成功。

      总结:父类引用可以指向子类对象,子类引用不能指向父类对象。把子类对象直接赋给父类引用叫做向上转型,向上转型不用强制转型,如Father f1=new Son(),把指向子类对象的父类引用赋给子类引用叫做向下转型,要强制转型,如Son s1 = (Son)f1。向上转型会丢失子类特有的方法,但是子类overriding父类的方法,子类方法有效。 

      将一种类型的数据赋给另外一种类型变量时,满足下列两种要求将执行自动类型转换:1.两种类型是兼容的;2.目的类型的范围比来源类型大。   

      数据值类型按照范围从小到大为:byte,short,char,int,long,float,double

      例如int类型比所有byte合法类型大,因此不要求显式强制类型转换。对于数字类型,整型和浮点型都是彼此兼容的,但是数字类型和字符类型和布尔类型是不兼容的,字符类型和布尔类型也不是互相兼容的。

      自动类型转换不能满足所有的转换需求,比如int型变量赋值给byte型变量,这种转换不会自动进行,因为byte型比int型范围小。为了完成两种不兼容类型转换,需要用到强制类型转换。

      目标类型指定了要转换成为的类型。例如果将int型转为byte型,int型取值范围大于byte型,它的值将堆byte型范围进行取模。而把浮点型赋值给整数型,就会出现截断,截取掉小数部分。从其他数值类型转换为char类型时,必须进行强制转换。将char类型转换为其他数值类型时,除了byte、short必须强制转换之外,int、long、float、double都不用强制转换。

[java]  view plain  copy
  1. byte b;  
  2. int i = 257;  
  3. double d = 32.13;  
  4. b = (byte) i;  
  5. System.out.println("i and b = " + i + " " + b);  
  6. i = (int)d;  
  7. System.out.println("d and i = " + d + " " + i);  
  8. b = (byte)d;  
  9. System.out.println("d and b = " + d + " " + b);  
  10. byte e=(byte)130;   
  11. System.out.println(e);  
  12.    
  13.    
  14. /* 
  15. * output 
  16. i and b = 257 1 
  17. d and i = 32.13 32 
  18. d and b = 32.13 32 
  19. -126 
  20. */  
        当257要转换为byte时,byte的范围是256,所以取模的结果是1。浮点值赋给整数型,截断了小数部分。
[java]  view plain  copy
  1. short s = 1;  
  2. s= s + 1;//编译错误  
  3. short s1 = 1;  
  4. s1 += 1;  
      上述陷阱是s = s + 1,s + 1会隐式转换为int型,把int型号赋给short,会造成类型缺失。而s1 += 1相当于s1 = (short)(s1 + 1)会把s1 + 1强制转换为short类型,就不会报错。涉及到写代码的严谨性。

      对于自动装箱和自动拆箱,将一个基本数据类型转换为对应的引用类型时,不必强制转换。将一个基本数据类型的引用类型转换为基本数据类型时,也不必要强制转换。

[java]  view plain  copy
  1. int i1 = 10;  
  2. Integer iObj1 = i1;  
  3. Integer iObj2 = new Integer(10);  
  4. int i2 = iObj2;  
      在三目运算符中,会按照算术类型中的提升原则将两端操作提升至统一类型,如果两端有一个操作数是引用类型,则整个表达式结果用Object型表示。

[java]  view plain  copy
  1. byte b = 10;   
  2. short s = 20;   
  3. int i = (b == 10) ? b : s;   
  4. int i1 = 10;   
  5. String str = "Hello";   
  6. Object obj = (i1 == 10) ? i1 : str;  // 在这里变量i进行了自动装箱操作  
      int和String互相之间的转换,首先是int转String,有两种方法
[java]  view plain  copy
  1. int i = 12345;  
  2. String s = "";  
  3. s = i + "";  
  4. s = String.valueOf(i);  
     第一种方法,s = i + "";会产生两个String对象, s = String.valueOf(i);直接使用String的静态方法。

[java]  view plain  copy
  1. String s = "12345";  
  2. int i ;  
  3. i=Integer.parseInt(s);  
  4. i=Integer.valueOf(s).intValue();  
    第一种方法直接使用静态方法,不会产生多余的对象,但会抛出异常。第二种方法相当于new Integer(Integer.parseInt(s))也会抛出异常,但是会多产生一个对象。

      valueOf返回的是原对象,toString()返回的是字符串。

3.instanceof

      instanceof是Java二元操作符,测试左边对象是否是右边类的实例,返回boolean类型的数据,作用上可以避免强制类型转换失败。

4.JVM级别原理

      对于强制类型转换,且是引用类型,如果是父类向子类转换,这种只牵扯到引用名义类型的转换,具体的内存没有什么变化。

      而基本类型转换不同,数据发生了变化。但是基本数据类型不是对象,如果涉及到自动装箱和拆箱,实际上是调用类似Integer.parseInt()和Integer.valueOf()实现。

      父类向子类强制转换,如果成功不会有任何数据丢失,如果不是子类,那么会报错。基本类型之间的转换,如果是大范围向小范围的转换,有可能发生数据丢失或者精确丢失。

 
 

猜你喜欢

转载自blog.csdn.net/javanotes/article/details/80379376