转型是在继承的基础上而言的,继承是面向对象语言中,代码复用的一种机制,通过继承,子类可以复用父类的功能,如果父类不能满足当前子类的需求,则子类可以重写父类中的方法来加以扩展。
向上转型:通俗地说就是是将子类对象转为父类对象。此处父类对象可以是接口。(父类的引用指向子类对象,多态)
转型需要注意的问题:向上转型时,父类指向子类引用对象会遗失除与父类对象共有的其他方法,也就是在转型过程中,子类的新有的方法都会遗失掉,在编译时,系统会提供找不到方法的错误。实例如下:
public class Animal {
public void eat(){
System.out.println("Animal eating ...");
}
}
public class Bird extends Animal{
@Override
public void eat(){
System.out.println("Bird eating ...");
}
public void fly(){
System.out.println("Bird flying ...");
}
public static void main(String[] args) {
Animal animal = new Bird();
animal.eat();
//animal.fly();
//The method fly() is undefined for the type of Animal
}
}
向上转型:父类引用的对象转换为子类类型称为向下转型。这种情况分为两种类型:
1、如果父类引用的对象是子类对象,那么在向下转型的过程中是安全的。
Animal animal = new Bird();
Bird bird =(Bird)animal;
2、如果父类引用的对象是不是子类对象,那么在向下转型的过程中是不安全的,编译不会出错,但是运行时会出现java.lang.ClassCastException错误。它可以使用instanceof来判断父类引用是否是指向子类对象。
Animal animal = new Animal();
Bird bird = (Bird) animal;
bird.eat();//java.lang.ClassCastException(不安全的向下转型,编译无错但会运行会出错)
Animal animal = new Animal();
//animal并不是一个指向Bird实例的引用,所以不会进入if中,向下转型,所以运行时不会引发ClassCastException错误
if(animal instanceof Bird){
Bird bird = (Bird) animal;
bird.eat();
}
一个比喻:说鸟是动物,向上转型,符合逻辑,不需要强转;反过来,说动物是鸟,向下转型,不符合逻辑,需要强转。(如果动物这个称谓引用指向的是鸟,则是成功的,反之失败)
java.lang.ClassCastException异常是一种RunTimeException异常,是unchecked型的(编译器不要求处置的异常)异常,所以上面会在编译时不报错,运行时会报错。
ClassCastException是JVM在检测到两个类型间转换不兼容时引发的运行时异常。通过转换,可以指示Java编译器将给定类型的变量作为另一种变量来处理。对基础类型和用户定义类型都可以转换,如果在此运行时验证过程中检测到不兼容,JVM就会引发ClassCastException异常。例如:
Animal animal = new Animal();
Bird bird = (Bird) animal;
bird.eat();//java.lang.ClassCastException(不安全的向下转型,编译无错但会运行会出错)
当出现下列情况时,就会引发ClassCastException异常:
1、两个类不兼容。向下转型失败,当应用程序代码尝试将某一对象转换为某一子类时,如果该对象并非该子类的实例,JVM就会抛出ClassCastException异常;
2、两个类兼容,但加载时使用了不同的ClassLoader。这是这种异常发生最常见的原因。
ClassLoader
ClassLoader是允许JVM查找和加载类的一种Java类。JVM有内置的ClassLoader。不过,应用程序可以定义自定义的ClassLoader。应用程序定义新的ClassLoader通常出于以下两种原因:
自定义和扩展JVM加载类的方式。例如,增加对新的类库(网络、加密文件等)的支持。
划分JVM名称空间,避免名称冲突。例如,可以利用划分技术同时运行同一应用程序的多个版本(基于空间的划分)。此项技术在应用服务器(如WebLogic Server)内的另一个重要用途是启用应用程序热重新部署,即在不重新启动JVM的情况下启动应用程序的新版本(基于时间的划分)。
ClassLoader按层级方式进行组织。除系统BootClassLoader外,其它ClassLoader都必须有父ClassLoader。
在理解类加载的时候,需要注意以下几点:
永远无法在同一ClassLoader中重新加载类。“热重新部署”需要使用新的ClassLoader。每个类对其ClassLoader的引用都是不可变的:this.getClass().getClassLoader()。
在加载类之前,ClassLoader始终会先询问其父ClassLoader(双亲委托模型)。这意味着将永远无法重写“核心”类。
同级ClassLoader间互不了解。
由不同ClassLoader加载的同一类文件也会被视为不同的类,即便每个字节都完全相同。这是ClassCastException的一个典型原因。
可以使用Thread.setContextClassLoader(a)将ClassLoader连接到线程的上下文。