java编程思想----复用类

复用类

7.1 组合语法

  1. 对于非基本类型的对象,必须将其引用置于新的类中,但可以直接定义基本类型数据.
package cn.itcast.zhuofai728.demo1;
class WaterSource{
    private String s;
    WaterSource(){
        System.out.println("WaterSource");
        s = "Constructed";
    }
    public String toString(){return s;}
}
public class SprinklerSystem {
    private String ;
    private WaterSource source = new WaterSource();
    public String toString(){
        return "source = " + source;//注意此段代码如果直接用return source;是错误的只有前面用""+source此时默认使用""+source.toString();也就是刚刚重写toString方法
    }

    public static void main(String[] args) {
        SprinklerSystem sprinklers = new SprinklerSystem();
        System.out.println(sprinklers);
    }
}
  1. 初始化引用的四种方式
    1. 在定义对象的地方.
    2. 在类的构造器中.
    3. 就在正要使用这些对象之前,对这种方式称为惰性初始化.
    4. 使用实例初始化.

7.2 继承语法

  1. Java用super关键字表示超类的意思,当前类就是从超类继承来的.
  2. 如果没有默认的基类构造器,或者想调用一个带参数的基类构造器,就必须用关键字super显示的编写调用基类构造器的语句,并且配以适当的参数列表.
  3. 调用构造器必须是你在导出类构造器中要做的第一件事.

7.3 代理

//太空船需要一个控制板
public class SpaceShipControls {
    void up(int velocity){}
    void down(int velocity){}
    void left(int velocity){}
    void right(int velocity){}
    void forward(int velocity){}
    void back(int velocity){}
    void turboBoost(){}
}
//构造太空船的一种方式是使用继承
public class SpaceShip extends SpaceShipControls {
    private String name;
    public SpaceShip(String name){this.name = name;}
    public String toString(){return name;}
    public static void main(String[] args){
        SpaceShip protector = new SpaceShip("NSEA Protector");
        protector.forward(100);
    }
}
//可以看到下面的方法是如何传递给了底层的controls对象,而其接口由此也就与使用继承得到的接口相同了.但是我们使用代理时可以拥有更多的控制力.
//因为我们可以选择只提供在成员对象中的方法的某个子集即提供特定的几个方法而不是全部
public class SpaceShipDelgation {
    private String name;
    private SpaceShipControls controls =
            new SpaceShipControls();
    public SpaceShipDelgation(String name){
        this.name = name;
    }
    //Delegated methods:
    public void back(int velocity){
        controls.down(velocity);
    }
    public void forward(int velocity){
        controls.forward(velocity);
    }
    public void left(int velocity){
        controls.left(velocity);
    }
    public void right(int velocity){
        controls.right(velocity);
    }
    public void turboBoost(){
        controls.turboBoost();
    }
    public void up(int velocity){
        controls.up(velocity);
    }

    public static void main(String[] args) {
        SpaceShipDelgation protector = new SpaceShipDelgation("NSEA Protector");
        protector.forward(100);
    }
}

7.4结合使用组合和继承

  1. java中没有C++析构函数的概念.析构函数是一中在对象被销毁时可以被自动调用的函数.其原因可能是因为在Java中,我们的习惯只是忘掉而不是销毁对象,并且让垃圾回收器在必要时释放其内存.
  2. 可以将清理动作置于finally子句之中,以预防异常的出现.
  3. 首先,执行类的所有特定的清理动作,其顺序同生成顺序相反(通常这就要求基类元素仍旧存活)
  4. 如果需要进行清理,最好是编写自己的清理方法,不要使用finalize();
  5. 可以重载基类方法但是不要覆写.
class Homer{
    char doh(char c){
        print("doh(char)");
        return 'd';
    }
    float doh(float f){
        print("doh(float)");
        return 1.0f;
    }
}
class Milhouse{}
class Bart extends Homer{
    // void doh(Milhouse m){
    //     print("doh(Milhouse)");
    // }//这是原本对的程序
    void doh(char m){

    }//这样会产生如下错误Error:demo7_4.Bart中的doh(char)无法覆盖demo7_4.Homer中的doh(char)返回类型void与char不兼容

}
  1. “is-a”(是一个)的关系使用继承来表达的,而”has-a”(有一个)的关系则是用组合来表达的.
//感觉自己还是不会组合关系所以把代码又拷贝下来了.
class Engine{//引擎走向
    public void start(){}
    public void rev(){}
    public void stop(){}
    public void service(){}
}
class Wheel{//轮子
    public void inflate(int psi){}
}
class Window{//窗户
    public void rollup(){}
    public void rolldown(){}
}
class Door{//门
    public Window window = new Window();
    public void open(){}
    public void close(){}
}
public class Car {//它们共同组成一个车子
    //所以就创建了一个engine对象四个轮子对象还有两个门对象left和right
    //把这几个组成car的class当作基本类型来声明定义直接用
    public Engine engine  = new Engine();
    public Wheel [] wheel = new Wheel[4];
    public Door
    left = new Door(),
    right = new Door();//2-door
    public Car(){
        for (int i = 0; i < 4; i++) {
            wheel[i]  = new Wheel();
        }
    }

    public static void main(String[] args) {
        Car car = new Car();
        car.left.window.rollup();
        car.wheel[0].inflate(72);
        car.engine.service();
    }
}

7.7向上转型

  1. 由导出类转型成基类,在继承图上是向上移动的,因此一般称为向上转型.
  2. 由于向上转型是从一个较专用类型向通用类型转换,所以总是很安全的,也就是说,导出类是基类的一个超集.它可能比基类含有更多的方法,它必须至少具备基类中所含有的方法.再向上转型的过程中,类借口唯一可能发生的事情是丢失方法,而不是获取它们.这就是为什么编译器在”未曾明确表示转型”或”未曾制定特殊标记”的情况下,仍然允许向上转型的原因.
  3. 再论组合与继承:生成和使用程序代码最有可能采用的方法就是直接将数据和方法包装进一个类中,并使用该类的对象.应当慎用继承这一技术,一个最清晰判断是否要使用继承的方法就是问一问自己是否需要从新类向基类进行向上转型.如果必须向上转型,则继承是必要的;但如果不需要,则应当好好考虑自己是否需要继承.

7.8final关键字

class Value{
    int i;
    public Value(int i){this.i = i;}
}
public class FinalData {
    private static Random rand = new Random(47);
    private String id;
    public FinalData(String id){this.id = id;}
    //Can be compile-time constants;
    private final int valueOne = 9;
    private static final int VALUE_TWO = 9;
    private static final int VALUE_THREE = 99;
    //tYPICAL PUBLIC constant
    public final int i4 = rand.nextInt(20);
    static final int INT_5 = rand.nextInt(20);
    private Value v1 = new Value(11);
    private final Value v2 = new Value(22);
    private static final Value VAL_3  = new Value (33);
    //Arrays
    private final int []a = {1,2,3,4,5,6};
    public String toString(){
        return id + ": " + "i4 = " + i4 + " , INT_5 = " +INT_5;
    }

    public static void main(String[] args) {
        FinalData fd1 = new FinalData("fd1");
        //!fd1.valueOne++;//Error:can't change value
        fd1.v2.i++;//Object isn't constant!
        fd1.v1 = new Value(9);//OK -- not final
        for (int i = 0; i < fd1.a.length; i++) {
            fd1.a[i]++;//Object isn't constant!
        }
        //! fd1.v2 = new Value(0);//Error: Can't
        //! fd1.VAL_3 = new Value(1);//change reference
        //! fd1.a = new int[3];
        print(fd1);
        print("Creating new FinalData");
        FinalData fd2 =  new FinalData("fd2");
        print(fd1);
        print(fd2);
    }
}

解释

  1. 对于对象引用,final使引用恒定不变.一旦引用被初始化指向一个对象,就无法再把它改为指向另一个对象.然而,对象自身却是可以被修改的.如上图
  2. 使用final方法的原因有两个:
    1. 第一个原因是把方法锁定,以防任何继承类修改它的含义.这是处于设计的考虑,想要确保在继承中使方法行为保持不变,并且不会被覆盖.
    2. 第二个原因是效率.不过现在已经不需要final方法来进行优化了.
  3. 类中所有的private方法都隐式地指定为是final的.由于无法取用private方法,所以也就无法覆盖它.
  4. 如果某方法为private,它就不是基类接口的一部分.他仅是一些隐藏于类中的程序代码,只不过时具有相同的名称而已.
//两个类中的private final方法并不作为接口出现只是各自的方法,如果用向上转型来调用会出现错误
class WithFinals{
    //Identical to "private" alone;
    private final void f(){print("WithFinals.f()");}
    //Also automatically "final"
    private void g(){print("WithFinal.g()");}
}
class OverridingPrivate extends WithFinals{
     private final void f(){
        print("OverridingPrivate.f()");
    }
    private void g(){
        print("OverridingPrivate.g()");
    }
}

7.9 继承和初始化

  1. 运行过程:在Beetle上运行Java时,所发生的第一件事情就是试图访问Beetle.main()(一个static方法).于是加载器开始启动并找出Beetle类的编译代码(在名为Beetle.class的文件中).在对它进行加载的过程中,编译器注意到它有一个基类(这是由于关键字extends得知的),于是它继续进行加载.不管你是否打算产生一个该基类的对象,这都要发生(请尝试将对创建代码注释掉,以证明这一点). 如果该基类还有其自身的基类,那么第二个基类就会被加载,如此类推.接下来,根基类中的static初始化即会被执行,然后是下一个导出类,以此类推.这种方式很重要,因为导出类中的static初始化可能会依赖于基类成员能否被正确初始化.

多态

最重要的一点就是 编译看左边,运行看右边(域操作不是)

  1. 在有多态的类中,编译器怎样才能指导加载的是哪一个类呢?实际上编译器无法得知.有必要研究一下绑定问题.
  2. 将一个方法调用同一个方法主体关联起来称作绑定.若在程序执行前进行绑定(如果有的话,由编译器和连接程序实现),叫做前期绑定.困惑(因为它是面向过程的语言中不需要选择就默认的绑定方式.)
  3. 要知道究竟调用那个方法,办法就是后期绑定.它的含义就是在运行时根据对象的类型进行绑定.后期绑定也叫做动态绑定或者运行时绑定.
  4. java中除了static方法和final方法(private方法属于final方法)之外,其他所有方法都是后期绑定.这意味着通常情况下,我们不必判定是否应该进行后期绑定–它会自动发生.

类似于Shape s = new Circle();

  1. 只有非private方法才可以被覆盖;但是还需要密切注意覆盖private方法的现象,这时虽然编译器不会报错,但是也不会按照我们所期望的来执行.确切的说,在导出类中,对于基类中的private方法,最好采用不同的名字.
//当Sub对象转型为Super引用时,任何域访问操作都将由编译器解析,因此不是多态的.   当然这种情况很少遇到首先,你通常会将所有的域都设置成private,因此不能直接访问它们,其副作用是只能调用方法来访问.另外你可能不会对基类中的域和导出类中的域赋予相同的名字,因为这种做法容易令人混淆.
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.getSuperFild = " + sub.getSuperField());

    }
}/*Output:
sup.field = 0,sup.getField() = 1
sub.field = 1,sub.getField() = 1,sub.getSuperField() = 0
*///:~
  1. 如果某个方法是静态的,它的行为就不具有多态性.
  2. 静态方法是于类,而并非与单个的对象相关联的.

猜你喜欢

转载自blog.csdn.net/zhuofai_/article/details/82352604