面向对象语言中,三大特性:封装,继承,多态
1.在论向上转型
对象可以作为他本身的类型使用,也可以作为他的基类型使用。这种把某个对象的引用视为对其基类的引用的做法被称为
向上转型----因为在继承树上,基类是放置在最上方的。
package com.myboy.extendstest;
class Instrument{
public void play(int i){//int 可替换为其他的引用类型
System.out.println("Instrument play()");
}
}
public class Test5 extends Instrument{
@Override
public void play(int i) {
System.out.println("Test5 play()");
}
public static void main(String[] args) {
Instrument test=new Test5();//多态体现
test.play(3);//out:Test5 play()
}
}
多态:
在Java中写了一个方法,它仅接收基类作为参数,而不是那些特殊的类。(
为什么又能确定具体的对象引用?)
2.转机(解释为什么能确定具体的对象引用)
方法调用绑定:将一个方法同一个方法主体关联起来被称为绑定。若在程序中执行前进行绑定(由编译器确定),叫前期绑定。面向过程的语言中默认的绑定语言就是前期绑定。
后期绑定::是在运行的时根据对象的类型进行绑定,也称为后期绑定或运行时绑定。编译器一直不知道对象的类型,直到方法调用机制能找到正确的方法体,并加以调用。
Java中除了static 和final()方法(private方法属于final方法)之外,所有的方法都是后期绑定。
面向接口编程是最能体现的。
缺陷1:"覆盖"私有方法
package com.myboy.extendstest;
class Test extends Test6{
public void play(){
System.out.println("Test play()");
}
}
public class Test6 {
private void play(){
System.out.println("Test6 play()");
}
public static void main(String[] args) {
Test6 t=new Test();
t.play();//out "Test play()" 为什么,真是无语了
}
}
结论:
(Java编程思想解释)只有非private 方法才可以被覆盖,但是由于private方法是自动被认为是final方法,而且对导出类是屏蔽的。因此,在这种情况下,Test类中的play()方法就是一个全新的方法;既然基类中play()不可见,不会被重载。需要密切注意覆盖private方法的现象,这是编译器虽然不会报错,但是也不会按照我们所期望的来执行。确定的说,在导出类中,对于基类中private方法,最好采用不同的名字。
缺陷2:域与静态方法
一旦了解了多态机制,可能会认为所有的事物都可以多态的发生。然而,
只有普通的方法调用是可以多态的。这个访问就将在编译期进行解析。
package com.myboy.extendstest;
class Super{
public int field=0;
public int getField(){
return field;
}
}
class Sub extends Super{
public int field=1;
public int getField(){
return field;
}
public int getSuperField(){
return super.field;
}
}
public class FieldAccess {
public static void main(String[] args) {
Super sup=new Sub();
System.out.println(""+sup.field+"|"+sup.getField());
Sub sub=new Sub();
System.out.println(""+sub.field+"|"+sub.getField()+"|"+sub.getSuperField());
}
/**
* out 0|1
* 1|1|0
*/
}
当Sub对象转型为Super引用时,任何域访问操作都将有编译器解析。因此不是多态的。Super.field和Sub.field分配了不同的存储空间。这样,Sub实际上包含了两个成为field的域。
在实际中,并不会出现,通常是将所有的域设计为private的,然后通过方法来访问(方法具有多态)
package com.myboy.extendstest;
class StaticSuper{
public static String getA(){
return "A";
}
}
class StaticSub extends StaticSuper{
public static String getA(){
return "B";
}
}
public class StaticPolymo {
public static void main(String[] args) {
StaticSuper sub=new StaticSub();
System.out.println(sub.getA());
}
//out A
}
静态方法不具有多态性。静态方法是与类,而并非与单个的对象相关联。
3.协变返回类型
Java SE5中加了协变返回类型。
package com.myboy.extendstest;
class Grain{
@Override
public String toString() {
// TODO Auto-generated method stub
return "Grain";
}
}
class Wheat extends Grain{
@Override
public String toString() {
return "Wheat";
}
}
class Mill{
public Grain process(){
return new Grain();
}
}
class WheatMill extends Mill{
public Wheat process(){//Java SE5之前process()的覆盖版本必须返回为Grain
return new Wheat();
}
}
public class Test7 {
public static void main(String[] args) {
Mill m=new Mill();
System.out.println(m.process());
m=new WheatMill();
System.out.println(m.process());
}
/**
* out Grain
* Wheat
*/
}
4.用继承进行设计(继承和组合的选择)
如何进行选择。一般而言,优先的选择方式是组合。组合更加的灵活,它可以动态选择类型(多态的完美体现),而继承在编译的时候就需要直接确切类型(后期绑定了具体的类型,无法在更改)。
5.向下转型和运行市类型识别
向上转型会丢失具体的类型信息。考虑向下转型(不安全)
package com.myboy.extendstest;
class UserFul{
public void a(){};
}
class User extends UserFul{
public void a(){};
public void b(){};
}
public class RTTI {
public static void main(String[] args) {
UserFul[] x= {new UserFul(),new User()};
x[0].a();
x[1].a();
//x[1]b() 方法找不到在UserFul
//((User)x[0]).b(); 向下转型失败 ClassCastException
((User)x[1]).b();//成功 运行时/RTTI
}
}
6.在论向上转型和向下转型
向上转型是注意的问题:基类指向子类引用对象会丢失掉与基类共有的其他方法。即子类中新的方法都会被丢失调,在编译时,系统会提示找不到子类的新方法。
向下转型:1.如果基类的引用的是导出类对象,那么在向下转型时就不会出现错误。
2.如果基类引用的是本身的话,编译时不会出错,但是运行时就会报ClassCastException,它可以用instanceof来避免此类错误。
class A{};
class B extends A{};
public class C{
main{
A a=new B();
B b=(B)a ;//正确
A c=new A();
B d=(B)c; //ClassCastException
}
}