java编程思想学习笔记(三)

本文探讨了Java中多态的概念,包括字段访问、静态多态性和构造器中的多态问题,并讨论了继承与组合的选择原则。此外,还介绍了接口的作用及其实现细节,以及内部类的使用方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Polymorphism

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.field+", sup.getField() = "+sup.getField());

       Sub sub=new Sub();

       System.out.println("sub.field = " + sub.field + ", sub.getField() = "+sub.getField()+", sub.getSuperField() = "+sub.getSuperField());

    }

}

 

Output:

sup.field = 0, sup.getField() = 1

sub.field = 1, sub.getField() = 1, sub.getSuperField() = 0

 

When a sub object is upcast to a Super reference, any field accesses are resolved by the compiler, and are thus not polymorphic. In this example, difference storage is allocated for Super.field and Sub.field. Thus, Sub actually contains two fields called field: its own and the one that it gets from Super. However, the Super version is not the default that is produced when you refer to field in Sub; in order to get the Super field you must explicityly say super.field.

Although this seems like it could be a confusing issue, in practice it virtually never comes up. For one thing, you’ll generally make all fields private and so you won’t access them directly, but only as side effects of calling methods. In addition, you probably won’t give the same name to a base-class field and a derived-class field, because it is confusing.

 

class StaticSuper{

    public static String staticGet(){

       return "Base staticGet()";

    }

    public String dynamicGet(){

       return "Base dynamicGet()";

    }

}

class StaticSub extends StaticSuper{

    public static String staticGet(){

       return "Derived staticGet()";

    }

    public String dynamicGet(){

       return "Derived dynamicGet()";

    }

}

public class StaticPolymorphism {

    public static void main(String[] args) {

       StaticSuper sup=new StaticSub();

       System.out.println(sup.staticGet());

       System.out.println(sup.dynamicGet());

    }

}

 

Output:

Base staticGet()

Derived dynamicGet()

 

static methods are associated with the class,and not the individual objects.

 

class Glyph{

    void draw(){

       System.out.println("Glyph.draw()");

    }

    Glyph(){

       System.out.println("Glyph() before draw()");

       draw();

       System.out.println("Glyph() after draw()");

    }

}

class RoundGlyph extends Glyph{

    private int radius=1;

    RoundGlyph(int r){

       radius=r;

       System.out.println("RoundGlyph.RoundGlyph(), radius = "+radius);

    }

    void draw(){

       System.out.println("RoundGlyph.draw(), radius = "+radius);

    }

}

public class PolyConstructors {

    public static void main(String[] args) {

       new RoundGlyph(5);

    }

}

Output:

Glyph() before draw()

RoundGlyph.draw(), radius = 0

Glyph() after draw()

RoundGlyph.RoundGlyph(), radius = 5

 

Glyph.draw() is designed to be overridden, which happens in RoundGlyph. But the Glyph constructor calls this method, and the call ends up in RoundGlyph.draw(), which would seem to be the intent. But if you look at the output, you can see that when Glyph’s constructor calls draw(), the value radius isn’t even the default initial value 1. It’s 0.

A good guideline for constructors is “Do as little as possible to set the object into a good state, and if you can possibly avoid it, don’t call any other methods in this class.” The only safe methods to call inside a constructor are those that are final in the base class. (This also applies to private methods, which are automatically final.) These cannot be overridden and thus cannot produce this kind of surprise. You may not always be able to follow this guideline, but it’s something to strive towards.

 

Once you learn about polymorphism, it can seem that everything ought to be inherited, because polymorphism is such a clever tool. This can burden your designs; in fact, if you choose inheritance first when you’re using an existing class to make a new class, things can become needlessly complicated.

A better approach is to choose composition first, especially when it’s not obvious which one you should use. Composition does not force a design into an inheritance hierarchy. But composition is also more flexible since it’s possible to dynamically choose a type (and thus behavior) when using composition, whereas inheritance requires that an exact type be known at compile time.

 

Interfaces

interface CanFight{

    void fight();

}

interface CanSwim{

    void swim();

}

interface CanFly{

    void fly();

}

class ActionCharacter{

    public void fight(){}

}

class Hero extends ActionCharacter implements CanFight,CanSwim,CanFly{

    public void swim(){}

    public void fly(){}

}

public class Adventure {

    public static void t(CanFight x){x.fight();}

    public static void u(CanSwim x){x.swim();}

    public static void v(CanFly x){x.fly();}

    public static void w(ActionCharacter x){x.fight();}

    public static void main(String[] args) {

       Hero h=new Hero();

       t(h);

       u(h);

       v(h);

       w(h);

    }

}

interface CanFight{

    void fight();

}

interface CanSwim{

    void swim();

}

interface CanFly{

    void fly();

}

class ActionCharacter{

    public void fight(){}

}

class Hero extends ActionCharacter implements CanFight,CanSwim,CanFly{

    public void swim(){}

    public void fly(){}

}

public class Adventure {

    public static void t(CanFight x){x.fight();}

    public static void u(CanSwim x){x.swim();}

    public static void v(CanFly x){x.fly();}

    public static void w(ActionCharacter x){x.fight();}

    public static void main(String[] args) {

       Hero h=new Hero();

       t(h);

       u(h);

       v(h);

       w(h);

    }

}

Keep in mind that one of the core reasons for interfaces is shown in the preceding example: to upcast to more than one base type (and the flexibility that this provides). However, a second reason for using interfaces is the same as using an abstract base class: to prevent the client programmer from making an object of this class and to establish that it is only an interface.

 

Interfaces may be nested within classes and within other interfaces. This reveals a number of interesting features:

class A{

    interface B{

       void f();

    }

    public class BImp implements B{

       public void f(){}

    }

    private class BImpl2 implements B{

       public void f(){}

    }

    public interface C{

       void f();

    }

    class CImp implements C{

       public void f(){}

    }

    private class CImp2 implements C{

       public void f(){}

    }

    private interface D{

       void f();

    }

    private class DImp implements D{

       public void f(){}

    }

    public class DImp2 implements D{

       public void f(){}

    }

    public D getD(){return new DImp2();}

    private D dRef;

    public void receiveD(D d){

       dRef=d;

       dRef.f();

    }

}

 

interface E{

    interface G{

       void f();

    }

    //Redundant "public";

    public interface H{

       void f();

    }

    void g();

    // Cannot be private within an interface:

    //! private interface I{}

}

 

public class NestingInterfaces {

    public class BImp implements A.B{

       public void f(){}

    }

    class CImp implements A.C{

       public void f(){}

    }

   

    //Cannot implement a private interface except

    //within that interface's defining class:

//  class DImp implements A.D{

//     public void f(){}

//  }

    class EImp implements E{

       public void g(){}

    }

    class EGImp implements E.G{

       public void f(){}

    }

    class EImp2 implements E{

       public void g(){}

       class EG implements E.G{

           public void f(){}

       }

    }

    public static void main(String[] args) {

       A a=new A();

//     Can't access A.D

//     A.D ad=a.getD();

//     Doesn't return anyting but A.D

//     A.DImp2 di2=a.getD();

//     Cannot access a member of the interface:

//     a.getD().f();

//     Only another A can do anyting with getD():

       A a2=new A();

       a2.receiveD(a.getD());

      

    }

}

Implementing a private interface is a way to force the definition of the methods in that interface without adding any type information (that is, without allowing any upcasting).

When you implement an interface, you are not required to implement any interfaces nested within. Also, private interfaces cannot be implemented outside of their defining classes.

 

Inner Classes

public class Parcel {

 

    static class Contents{

       private int i=11;

       public int value(){return i;}

    }

    class Destination{

       private String label;

       Destination(String whereTo){label=whereTo;}

       String readLabel(){return label;}

    }

    public void test(){

       Destination d=new Destination("");

    }

    public static void main(String[] args) {

       Contents c=new Contents();

       Parcel p=new Parcel();

       Parcel.Destination d=p.new Destination("");

    }

}

If you want to make an object of the inner class anywhere except from within a non-static method of the outer class, you must specify the type of that object as OuterClassName.InnerClassName, as seen in main().

It’s not possible to create an object of the inner class unless you already have an object of the outer class. This is because the object of the inner class is quietly connected to the object of the outer class that it was made from. However, if you make a nested class (a static inner class), then it doesn’t need a reference to the outer-class object.

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值