从零开始学JAVA(05):面向对象编程--03

时间:2022-08-01 01:12:30

一、方法的签名和重载

  • 方法签名:方法名+依次参数类型。注意:返回值不属于方法签名。方法签名是一个方法在一个类中的唯一标识;
  • 同一个类中方法可以重名,但是签名不可以重复。一个类中如果定义了名字相同,但是签名不同的方法,就叫做方法的重载;
  • 代码示例:
public class MerchandiseV2Overload {

    public String name;
    public String id;
    public int count;
    public double soldPrice;
    public double purchasePrice;

    public void init(String name, String id, int count, double soldPrice, double purchasePrice) {
        this.name = name;
        this.id = id;
        this.count = count;
        this.soldPrice = soldPrice;
        this.purchasePrice = purchasePrice;
    }

    public void describe() {
        System.out.println("商品名字叫做" + name + ",id是" + id + "。 商品售价是" + soldPrice
            + "。商品进价是" + purchasePrice + "。商品库存量是" + count +
            "。销售一个的毛利润是" + (soldPrice - purchasePrice));
    }

    public double calculateProfit() {
        double profit = soldPrice - purchasePrice;
//        if(profit <= 0){
//            return 0;
//        }
        return profit;
    }


    // >> TODO 重载的方法可以调用别的重载方法,当然也可以调用别的不重载的方法。
    // >> TODO 实际上,像这种补充一些缺省的参数值,然后调用重载的方法,是重载的一个重要的使用场景。
    // >> TODO 在这里我们举的例子就是这样的,但是不是语法要求一定要这样。重载的方法的方法体内代码可以随便写,
    //    TODO 可以不调用别的重载方法
    public double buy() {
        return buy(1);
    }


    public double buy(int count) { return buy(count, false);
    }

    // TODO  最后都补充好参数,调用参数最全的一个方法
    public double buy(int count, boolean isVIP) {
        if (this.count < count) {
            return -1;
        }
        this.count -= count;
        double totalCost = count * soldPrice;
        if (isVIP) {
            return totalCost * 0.95;
        } else {
            return totalCost;
        }
    }

}

二、重载的参数匹配规则

  • 无论是否重载参数类型可以不完全匹配的规则是"实参数可以自动类型转换成形参类型";
  • 重载的特殊之处是,参数满足自动自动类型转换的方法有好几个,重载的规则是选择最"近"的去调用;

三、构造方法:构造实例的方法

1、构造方法

  • 构造方法(constructor)的方法名必须与类名一样,而且构造方法没有返回值。这样的方法才是构造方法;
  • 构造方法可以有参数,规则和语法与普通方法一样。使用时,参数传递给new语句后的括号里面;
  • 如果没有显示的添加一个构造方法,java会给每个类都会默认带一个无参数的构造方法;
  • 如果我们自己添加类构造方法,java就不会再添加无参数的构造方法。这时候就不能直接new一个对象不传递参数了;
  • 所以我们一直都在使用构造方法,这也是为什么创建对象的时候类名后面必须有一个括号的原因;
  • 构造方法无法被点操作符调用或者在普通方法里调用,只能通过new语句在创建对象的时候,间接调用;
public class MerchandiseV2WithConstructor {

    public String name;
    public String id;
    public int count;
    public double soldPrice;
    public double purchasePrice;

    public MerchandiseV2WithConstructor(String name, String id, int count, double soldPrice, double purchasePrice) {
        this.name = name;
        this.id = id;
        this.count = count;
        this.soldPrice = soldPrice;
        this.purchasePrice = purchasePrice;
    }

    // ......
}

2、构造方法的重载和互相调用

和方法的重载是一致的,定义多个同名不同参的构造方法

构造方法在互相调用时,使用this()

public class MerchandiseV2 {

    public String name;
    public String id;
    // 构造方法执行前,会执行给局部变量赋初始值的操作
    // 所有的代码都必须在方法里,那么这种给成员变赋初始值的代码在哪个方法里?怎么看不到呢?
    // 构造方法在内部变成了<init>方法。
    public int count = 999;
    public double soldPrice;
    public double purchasePrice;

    // 构造方法(constructor)的重载和普通方法一样
    public MerchandiseV2(String name, String id, int count, double soldPrice, double purchasePrice) {
        this.name = name;
        this.id = id;
        this.count = count;
        this.soldPrice = soldPrice;
        this.purchasePrice = purchasePrice;
//        soldPrice = 9/0;
    }

    // 在构造方法里才能调用重载的构造方法。语法为this(实参列表)
    // 构造方法不能自己调用自己,这会是一个死循环
    // 在调用重载的构造方法时,不可以使用成员变量。因为用语意上讲,这个对象还没有被初始化完成,处于中间状态。
    // 在构造方法里才能调用重载的构造方法时,必须是方法的第一行。后面可以继续有代码
    public MerchandiseV2(String name, String id, int count, double soldPrice) {
        // double purPrice = soldPrice * 0.8;
        // this(name, id, count, soldPrice, purchasePrice);
        this(name, id, count, soldPrice, soldPrice * 0.8);
        // double purPrice = soldPrice * 0.8;
    }

    //因为我们添加了构造方法之后,Java就不会再添加无参数的构造方法。如果需要的话,我们可以自己添加这样的构造方法
    public MerchandiseV2() {
        this("无名", "000", 0, 1, 1.1);

    }

    public void describe() {
        System.out.println("商品名字叫做" + name + ",id是" + id + "。 商品售价是" + soldPrice
            + "。商品进价是" + purchasePrice + "。商品库存量是" + count +
            "。销售一个的毛利润是" + (soldPrice - purchasePrice));
    }

    public double calculateProfit() {
        double profit = soldPrice - purchasePrice;
        return profit;
    }

    public double buy(int count) {
        if (this.count < count) {
            return -1;
        }
        return this.count -= count;
    }
}

四、静态变量

  • 静态变量使用static修饰符
  • 静态变量如果不赋值,Java也会给它赋予其类型的初始值
  • 静态变量一般使用全大写加下划线分割
  • 使用import static来引入一个静态变量,就可以直接用变量名访问了
  • import static 也可以使用通配符*来引入一个类里所有静态变量
// 被public修饰的静态变量,所有的代码都可以使用它
public static double DOUBLE_GRADE = 0.99;

// 没有public修饰的静态变量,只有当前包的代码可以使用它
static int INT_GRAGE = 10;
package com.geekbang;

import com.geekbang.supermarket.MerchandiseV2WithStaticVariable;
import  static com.geekbang.supermarket.MerchandiseV2WithStaticVariable.*;

public class MerchandiseV2DescAppMain {
    public static void main(String[] args) {
        MerchandiseV2WithStaticVariable merchandise = new MerchandiseV2WithStaticVariable
            ("书桌", "DESK9527", 40, 999.9, 500);

        merchandise.describe();

        // 使用import static来引入一个静态变量,就可以直接用静态变量名访问了
        // import static也可以使用通配符*来引入一个类里所有静态变量
        System.out.println(DISCOUNT_FOR_VIP);
    }
}

五、静态方法

  • 静态方法(也叫类方法)的特点:只能使用参数和静态变量。换言之,就是没有this自引用的方法;
  • 静态方法和静态变量一样,使用static修饰符;
  • 静态方法可以访问静态变量,包括自己类的静态变量和在访问控制符允许的别的类的静态变量;
  • 除了没有this,静态方法的定义和成员方法一样,也有方法名、返回值和参数;
  • 静态方法没有this自引用,它不属于某个实例,调用的时候也无需引用,直接用类名调用,所以它也不能直接访问成员变量;
  • 在静态方法里边,也可以自己创建对象,或者通过参数获得对象的引用,进而调用方法和访问成员变量;
  • 静态方法只是没有this自引用的方法而已;
package com.geekbang.supermarket;

public class MerchandiseV2 {

    public String name;
    public String id;
    public int count;
    public double soldPrice;
    public double purchasePrice;

    // >> TODO 静态变量使用 static 修饰符
    public static double DISCOUNT_FOR_VIP = 0.95;

    // >> TODO 静态方法使用static修饰符。
    public static double getVIPDiscount() {
        // >> TODO 静态方法可以访问静态变量,包括自己类的静态变量和在访问控制符允许的别的类的静态变量
        return DISCOUNT_FOR_VIP;
    }
    public static double getDiscountOnDiscount(LittleSuperMarket littleSuperMarket) {
        double activityDiscount = littleSuperMarket.activityDiscount;
        return DISCOUNT_FOR_VIP * activityDiscount;
    }
}

静态方法的重载和成员方法(实例方法)一样。

六、static代码块和static变量初始化

  • 使用某个静态变量的代码块必须在静态变量后边;
  • 静态代码块中可以有任意合法的代码;
  • 静态代码块可以有多个,从上到下顺序执行;
package com.geekbang.supermarket;

public class DiscountMgr {

    public static void main(String[] args) {
        System.out.println("最终main 方法中使用的SVIP_DISCOUNT是" + SVIP_DISCOUNT);
    }

    public static double BASE_DISCOUNT;

    public static double VIP_DISCOUNT;

    // >> TODO 使用某个静态变量的代码块必须在静态变量后面
    // >> TODO (但是仅仅赋值没有限制,很妖的语法哈,有些语法就应该在学会的第一时间忘掉它)
    public static double SVIP_DISCOUNT;


    static {
        BASE_DISCOUNT = 0.99;
        VIP_DISCOUNT = 0.85;
        SVIP_DISCOUNT = 0.75;

        // >> TODO 静态代码块里当然可以有任意的合法代码
        System.out.println("静态代码块1里的SVIP_DISCOUNT" + SVIP_DISCOUNT);
    }

    // >> TODO 其实给静态变量赋值也是放在代码块里的,static代码块可以有多个,是从上向下顺序执行的。
    //    TODO 可以认为这些代码都被组织到了一个clinit方法里
    static {
        SVIP_DISCOUNT = 0.1;
        System.out.println("静态代码块2里的SVIP_DISCOUNT" + SVIP_DISCOUNT);
    }


}

七、方法和属性的可见性修饰符 

  • 可见性修饰符用在类、成员方法、构造方法、静态方法和属性上,其可见性的范围是一样的;
  • public:全局可见
  • 缺省:当前包可见
  • private:当前类可见
  • 访问修饰符:不只是为了限制不让人使用,更为了有规矩才成方圆。成员变量应该是private的,不需要让外部使用的方法应该都是private的;