• java学习2 零基础


    10.方法的签名和重载

    overload

    方法的签名是一个类中的唯一标识

     1 package com.geekbang.supermarket;
     2 
     3 public class MerchandiseV2Overload {
     4 
     5     public String name;
     6     public String id;
     7     public int count;
     8     public double soldPrice;
     9     public double purchasePrice;
    10 
    11     public void init(String name, String id, int count, double soldPrice, double purchasePrice) {
    12         this.name = name;
    13         this.id = id;
    14         this.count = count;
    15         this.soldPrice = soldPrice;
    16         this.purchasePrice = purchasePrice;
    17     }
    18 
    19     public void describe() {
    20         System.out.println("商品名字叫做" + name + ",id是" + id + "。 商品售价是" + soldPrice
    21             + "。商品进价是" + purchasePrice + "。商品库存量是" + count +
    22             "。销售一个的毛利润是" + (soldPrice - purchasePrice));
    23     }
    24 
    25     public double calculateProfit() {
    26         double profit = soldPrice - purchasePrice;
    27 //        if(profit <= 0){
    28 //            return 0;
    29 //        }
    30         return profit;
    31     }
    32 
    33 
    34     // >> TODO 重载的方法可以调用别的重载方法,当然也可以调用别的不重载的方法。
    35     // >> TODO 实际上,像这种补充一些缺省的参数值,然后调用重载的方法,是重载的一个重要的使用场景。
    36     // >> TODO 在这里我们举的例子就是这样的,但是不是语法要求一定要这样。重载的方法的方法体内代码可以随便写,
    37     //    TODO 可以不调用别的重载方法
    38     public double buy() {
    39         return buy(1);
    40     }
    41 
    42 
    43     public double buy(int count) { return buy(count, false);
    44     }
    45 
    46     // TODO  最后都补充好参数,调用参数最全的一个方法
    47     public double buy(int count, boolean isVIP) {
    48         if (this.count < count) {
    49             return -1;
    50         }
    51         this.count -= count;
    52         double totalCost = count * soldPrice;
    53         if (isVIP) {
    54             return totalCost * 0.95;
    55         } else {
    56             return totalCost;
    57         }
    58     }
    59 
    60 }
    // >> TODO 理解为什么返回值不是方法签名的一部分:不能帮助确定调用哪个方法。

    重载的匹配规则

     1 package com.geekbang;
     2 
     3 import com.geekbang.supermarket.MerchandiseV2;
     4 
     5 public class MerchandiseV2AppMain {
     6     public static void main(String[] args) {
     7         MerchandiseV2 merchandise = new MerchandiseV2();
     8 
     9         merchandise.init("书桌", "DESK9527", 40, 999.9, 500);
    10 
    11         // >> TODO 使用int调用参数为double的方法
    12         int count = 3;
    13 //        merchandise.buyDouble(count);
    14 
    15         System.out.println("测试使用不完全匹配的参数调用重载方法");
    16         // >> TODO 依次使用byte, short, int, long, float, double 类型的参数调用buy方法,哪个方法会被调用呢?
    17         // >> TODO 无论是否重载参数类型可以不完全匹配的规则是"实参数可以自动类型转换成形参类型"
    18         // >> TODO 重载的特殊之处是,参数满足自动自动类型转换的方法有好几个,重载的规则是选择最"近"的去调用
    19 
    20         byte countForOverride1  = 11;
    21         merchandise.buy(countForOverride1);
    22 
    23         double countForOverride  = 11;
    24         merchandise.buy(countForOverride);
    25 
    26         short countForOverride2  = 11;
    27         merchandise.buy(countForOverride2);
    28 
    29         int countForOverride3  = 11;
    30         merchandise.buy(countForOverride3);
    31 
    32         float countForOverride4  = 11;
    33         merchandise.buy(countForOverride4);
    34 
    35         long countForOverride5  = 11;
    36         merchandise.buy(countForOverride5);
    37     }
    38 }
    测试使用不完全匹配的参数调用重载方法
    buy(int)被调用了
    buy(int,boolean)被调用了
    buy(double)被调用了
    buy(int)被调用了
    buy(int,boolean)被调用了
    buy(int)被调用了
    buy(int,boolean)被调用了
    buy(double)被调用了
    buy(double)被调用了

    11.构造方法

    自动调用 调用哪一个方法

    constructor

     1 package com.geekbang.supermarket;
     2 
     3 public class MerchandiseV2WithConstructor {
     4 
     5     public String name;
     6     public String id;
     7     public int count;
     8     public double soldPrice;
     9     public double purchasePrice;
    10 
    11     // >> TODO 构造方法(constructor)的方法名必须与类名一样,而且构造方法没有返回值。这样的方法才是构造方法。
    12     // >> TODO 构造方法可以有参数,规则和语法于普通方法一样。使用时,参数传递给 new 语句后类名的括号后面。
    13     // >> TODO 如果没有显示的添加一个构造方法,Java会给每个类都会默认自带一个无参数的构造方法。
    14     //    TODO 如果我们自己添加类构造方法,Java就不会再添加无参数的构造方法。这时候,就不能直接 new 一个对象不传递参数了(看例子)
    15     // >> TODO 所以我们一直都在使用构造方法,这也是为什么创建对象的时候类名后面要有一个括号的原因。
    16     // >> TODO 构造方法无法被点操作符调用或者在普通方法里调用,只能通过 new 语句在创建对象的时候,间接调用。
    17     // TODO 理解一下为什么构造方法不能有返回值,因为有返回值也没有意义,new 语句永远返回的是创建出来的对象的引用
    18     public MerchandiseV2WithConstructor(String name, String id, int count,
    19                                         double soldPrice, double purchasePrice) {
    20         this.name = name;
    21         this.id = id;
    22         this.count = count;
    23         this.soldPrice = soldPrice;
    24         this.purchasePrice = purchasePrice;
    25     }
    26 
    27     public void describe() {
    28         System.out.println("商品名字叫做" + name + ",id是" + id + "。 商品售价是" + soldPrice
    29             + "。商品进价是" + purchasePrice + "。商品库存量是" + count +
    30             "。销售一个的毛利润是" + (soldPrice - purchasePrice));
    31     }
    32 
    33     public double calculateProfit() {
    34         double profit = soldPrice - purchasePrice;
    35 //        if(profit <= 0){
    36 //            return 0;
    37 //        }
    38         return profit;
    39     }
    40 
    41     public double buy(int count) {
    42         if (this.count < count) {
    43             return -1;
    44         }
    45         return this.count -= count;
    46     }
    47 }
    如果我们自己添加类构造方法,Java就不会再添加无参数的构造方法。
    这时候,就不能直接 new 一个对象不传递参数了(看例子)
    也可以创建多个构造函数 这也就是构造方法的重载 如下所示
     1     // >> TODO 构造方法(constructor)的重载和普通方法一样
     2     public MerchandiseV2(String name, String id, int count, double soldPrice, double purchasePrice) {
     3         this.name = name;
     4         this.id = id;
     5         this.count = count;
     6         this.soldPrice = soldPrice;
     7         this.purchasePrice = purchasePrice;
     8 //        soldPrice = 9/0;
     9     }
    10 
    11     // >> TODO 在构造方法里才能调用重载的构造方法。语法为this(实参列表)
    12     // >> TODO 构造方法不能自己调用自己,这会是一个死循环
    13     // >> TODO 在调用重载的构造方法时,不可以使用成员变量。因为用语意上讲,这个对象还没有被初始化完成,处于中间状态。
    14     // >> TODO 在构造方法里才能调用重载的构造方法时,必须是方法的第一行。后面可以继续有代码
    15     public MerchandiseV2(String name, String id, int count, double soldPrice) {
    16          // double purPrice = soldPrice * 0.8;
    17         // this(name, id, count, soldPrice, purchasePrice);
    18         this(name, id, count, soldPrice, soldPrice * 0.8);
    19         // double purPrice = soldPrice * 0.8;
    20     }
    21 
    22     // >> TODO 因为我们添加了构造方法之后,Java就不会再添加无参数的构造方法。如果需要的话,我们可以自己添加这样的构造方法
    23     public MerchandiseV2() {
    24         this("无名", "000", 0, 1, 1.1);
    25 
    26     }

    构造方法里面可以相互调用

    this(name, id, count, soldPrice, soldPrice * 0.8);

    只有在构造方法里面才可以调用,不能自己调自己!

    可以在构造方法之前赋值

    1     public String name;
    2     public String id;
    3     // >> TODO 构造方法执行前,会执行给局部变量赋初始值的操作
    4     // >> TODO 我们说过,所有的代码都必须在方法里,那么这种给成员变赋初始值的代码在哪个方法里?怎么看不到呢?
    5     //    TODO 原来构造方法在内部变成了<init>方法。学习就是要脑洞大,敢想敢试,刨根问底。
    6     public int count = 999;// 999/0;
    7     public double soldPrice;
    8     public double purchasePrice;

    类名.<init> 构造方法 成员变量赋值 的是在这里完成的

    12.静态 static

    Magic Number 要赋一个变量名

     1 package com.geekbang.supermarket;
     2 
     3 public class MerchandiseV2WithStaticVariable {
     4 
     5     public String name;
     6     public String id;
     7     public int count;
     8     public double soldPrice;
     9     public double purchasePrice;
    10 
    11     // >> TODO 静态变量使用 static 修饰符
    12     // >> TODO 静态变量如果不赋值,Java也会给它赋以其类型的初始值
    13     // >> TODO 静态变量一般使用全大写字母加下划线分割。这是一个习惯用法
    14     // >> TODO 所有的代码都可以使用静态变量,只要根据防范控制符的规范,这个静态变量对其可见即可
    15     //    TODO 比如 public 的静态变量,所有的代码都可以使用它
    16     public static double DISCOUNT_FOR_VIP = 0.95;
    17 
    18     //    TODO 但是如果没有public修饰符,只能当前包的代码能使用它
    19     static int STATIC_VARIABLE_CURR_PACKAGE_ONLY = 100;
    20 
    21 
    22     public MerchandiseV2WithStaticVariable(String name, String id, int count, double soldPrice, double purchasePrice) {
    23         this.name = name;
    24         this.id = id;
    25         this.count = count;
    26         this.soldPrice = soldPrice;
    27         this.purchasePrice = purchasePrice;
    28     }
    29 
    30     public String getName() {
    31         return name;
    32     }
    33 
    34     public MerchandiseV2WithStaticVariable(String name, String id, int count, double soldPrice) {
    35         this(name, id, count, soldPrice, soldPrice * 0.8);
    36     }
    37 
    38     public MerchandiseV2WithStaticVariable() {
    39         this("无名", "000", 0, 1, 1.1);
    40 
    41     }
    42 
    43     public void describe() {
    44         System.out.println("商品名字叫做" + name + ",id是" + id + "。 商品售价是" + soldPrice
    45             + "。商品进价是" + purchasePrice + "。商品库存量是" + count +
    46             "。销售一个的毛利润是" + (soldPrice - purchasePrice) + "。折扣为" + DISCOUNT_FOR_VIP);
    47     }
    48 
    49     public double calculateProfit() {
    50         double profit = soldPrice - purchasePrice;
    51 //        if(profit <= 0){
    52 //            return 0;
    53 //        }
    54         return profit;
    55     }
    56 
    57 
    58     public double buy() {
    59         return buy(1);
    60     }
    61 
    62     public double buy(int count) {
    63         return buy(count, false);
    64     }
    65 
    66     public double buy(int count, boolean isVIP) {
    67         if (this.count < count) {
    68             return -1;
    69         }
    70         this.count -= count;
    71         double totalCost = count * soldPrice;
    72         if (isVIP) {
    73             // >> TODO 使用自己的使用静态变量的时候,直接写静态变量名字。
    74             return totalCost * DISCOUNT_FOR_VIP;
    75         } else {
    76             return totalCost;
    77         }
    78     }
    79 }

    静态变量就这一个,不随实例的创建而创建。

    使用静态变量

    import引入

     1 package com.geekbang;
     2 
     3 import com.geekbang.supermarket.MerchandiseV2WithStaticVariable;
     4 import  static com.geekbang.supermarket.MerchandiseV2WithStaticVariable.*;
     5 
     6 public class MerchandiseV2DescAppMain {
     7     public static void main(String[] args) {
     8         MerchandiseV2WithStaticVariable merchandise = new MerchandiseV2WithStaticVariable
     9             ("书桌", "DESK9527", 40, 999.9, 500);
    10 
    11         merchandise.describe();
    12 
    13         // >> TODO 使用import static来引入一个静态变量,就可以直接用静态变量名访问了
    14         //    TODO import static也可以使用通配符*来引入一个类里所有静态变量
    15         System.out.println(DISCOUNT_FOR_VIP);
    16     }
    17 }

    静态变量一变都会变。

     1 package com.geekbang;
     2 
     3 import com.geekbang.supermarket.LittleSuperMarket;
     4 import com.geekbang.supermarket.MerchandiseV2WithStaticVariable;
     5 
     6 public class RunLittleSupperMarketAppMain {
     7     public static void main(String[] args) {
     8         // 创建一个小超市类
     9         LittleSuperMarket littleSuperMarket = new LittleSuperMarket(
    10             "有家小超市", "浦东新区世纪大道666号",
    11             100, 200, 200);
    12 
    13         System.out.println("下面请利润最高的商品自我介绍:");
    14         MerchandiseV2WithStaticVariable bigProfit = littleSuperMarket.getBiggestProfitMerchandise();
    15         bigProfit.describe();
    16         double cost1 = bigProfit.buy(10, true);
    17         System.out.println("VIP 购买10个" + bigProfit.getName() + "的花费为" + cost1);
    18 
    19         // >> TODO 使用别的类的静态变量的时候,需要使用完整形态:类名.静态变量名字
    20         MerchandiseV2WithStaticVariable.DISCOUNT_FOR_VIP = 0.5;
    21         bigProfit.describe();
    22         double cost2 = bigProfit.buy(10, true);
    23         System.out.println("VIP 购买10个" + bigProfit.getName() + "的花费为" + cost2);
    24 
    25         // >> TODO 静态变量在整个Java程序中只有一个(对比实例变量,是每个实例有一份
    26         //    TODO 所以静态变量一旦变化,所有使用这个静态变量的地方的值都会变
    27         MerchandiseV2WithStaticVariable m0 = littleSuperMarket.getMerchandiseOf(0);
    28         m0.describe();
    29         double cost3 = m0.buy(10, true);
    30         System.out.println("VIP 购买10个" + m0.getName() + "的花费为" + cost3);
    31     }
    32 }

    静态方法

    也叫类方法:只能使用参数和静态变量,换而言之,就是没有this子引用的方法

     1     // >> TODO 静态变量使用 static 修饰符
     2     public static double DISCOUNT_FOR_VIP = 0.95;
     3 
     4     // >> TODO 静态方法使用static修饰符。
     5     // 静态方法的方法名没有约定俗称全大写
     6     public static double getVIPDiscount() {
     7         // >> TODO 静态方法可以访问静态变量,包括自己类的静态变量和在访问控制符允许的别的类的静态变量
     8         return DISCOUNT_FOR_VIP;
     9     }
    10 
    11     // >> TODO 除了没有this,静态方法的定义和成员方法一样,也有方法名,返回值和参数
    12     // >> TODO 静态方法没有this自引用,它不属于某个实例,调用的时候也无需引用,直接用类名调用,所以它也不能直接访问成员变量
    13     // >> TODO 当然在静态方法里面,也可以自己创建对象,或者通过参数,获得对象的引用,进而调用方法和访问成员变量
    14     // >> TODO 静态方法只是没有this自引用的方法而已。
    15     public static double getDiscountOnDiscount(LittleSuperMarket littleSuperMarket) {
    16         double activityDiscount = littleSuperMarket.activityDiscount;
    17         return DISCOUNT_FOR_VIP * activityDiscount;
    18     }

    this-->类      and  静态---》类

    用过方法的签名来重载静态方法

     1 package com.geekbang.supermarket;
     2 
     3 public class DiscountMgr {
     4 
     5     public static double BASE_DISCOUNT = 0.99;
     6 
     7     public static double VIP_DISCOUNT = 0.85;
     8 
     9     public static double SVIP_DISCOUNT = 0.75;
    10 
    11     // >> TODO 静态方法的重载也是一样的,方法签名不同即可:方法名+参数类型
    12     // >> TODO 判断调用哪个方法,也是根据调用时参数匹配决定的。
    13     public static double getDiscount() {
    14         return BASE_DISCOUNT;
    15     }
    16 
    17     public static double getDiscount(boolean isVIP) {
    18         // TODO >> 这节课这么无聊,我们顺带学一个三元操作符吧。
    19         // TODO    三元操作符的返回类型就是冒号两边的类型,两边的类型要和等号左边的变量类型兼容
    20         // double abc = true ? "" : 0;
    21 
    22         double svipDiscount = (isVIP ? VIP_DISCOUNT : 1);
    23         return getDiscount() * svipDiscount;
    24     }
    25 
    26     public static double getDiscount(int svipLevel) {
    27         double ret = getDiscount() * VIP_DISCOUNT;
    28         for (int i = 0; i < svipLevel; i++) {
    29             ret *= SVIP_DISCOUNT;
    30         }
    31         return ret;
    32     }
    33 
    34     // 这节课这么无聊,我们顺带看几个不是那么正经的getDiscount吧
    35 
    36     // >> TODO 返回值不算是方法签名,重载的方法可以有完全不同的返回值类型
    37     public static void getDiscount(String s) {
    38         System.out.println(s);
    39     }
    40 
    41     public static int getDiscount(int a, int b) {
    42         return a > b ? a : b;
    43     }
    44 
    45 
    46     public static boolean getDiscount(int a, int b, int c) {
    47         return a > b && b > c;
    48     }
    49 
    50     public static String getDiscount(long abc) {
    51         return "" + abc;
    52     }
    53 
    54     public static void main(String[] args) {
    55         getDiscount(1, 2);
    56     }
    57 
    58 }

    静态代码块

     1 package com.geekbang.supermarket;
     2 
     3 public class DiscountMgr {
     4 
     5     public static void main(String[] args) {
     6         System.out.println("最终main 方法中使用的SVIP_DISCOUNT是" + SVIP_DISCOUNT);
     7     }
     8 
     9     public static double BASE_DISCOUNT;
    10 
    11     public static double VIP_DISCOUNT;
    12 
    13     // >> TODO 使用某个静态变量的代码块必须在静态变量后面
    14     // >> TODO (但是仅仅赋值没有限制,很妖的语法哈,有些语法就应该在学会的第一时间忘掉它)
    15     public static double SVIP_DISCOUNT;
    16 
    17 
    18     static {
    19         BASE_DISCOUNT = 0.99;
    20         VIP_DISCOUNT = 0.85;
    21         SVIP_DISCOUNT = 0.75;
    22 
    23         // >> TODO 静态代码块里当然可以有任意的合法代码
    24         System.out.println("静态代码块1里的SVIP_DISCOUNT" + SVIP_DISCOUNT);
    25 
    26         // >> TODO 这段代码在哪个方法中呢?<clinit>,即class init。会在每个class初始化的时候被调用一次
    27 //         SVIP_DISCOUNT = 9/0;
    28     }
    29 
    30     // >> TODO 其实给静态变量赋值也是放在代码块里的,static代码块可以有多个,是从上向下顺序执行的。
    31     //    TODO 可以认为这些代码都被组织到了一个clinit方法里
    32     // public static double WHERE_AM_I = 9/0;
    33 
    34 //     public static double SVIP_DISCOUNT;
    35 
    36     static {
    37         SVIP_DISCOUNT = 0.1;
    38         System.out.println("静态代码块2里的SVIP_DISCOUNT" + SVIP_DISCOUNT);
    39     }
    40 }
    <clinit> class init  静态代码块在class init
    
    

    13.访问修饰符

    • public:全局可见
    • 缺省:当前包可见
    • private:当前类可见
      1 package com.geekbang.supermarket;
      2 
      3 // >> TODO 类,静态方法,静态变量,成员变量,构造方法,成员方法都可以使用访问修饰符
      4 public class MerchandiseV2 {
      5 
      6     // >> TODO 成员变量应该都声明为private
      7     // >> TODO 如果要读写这些成员变量,最好使用get set方法,这些方法应该是public的
      8     // >> TODO 这样做的好处是,如果有需要,可以通过代码,检查每个属性值是否合法。
      9     private String name;
     10     private String id;
     11     private int count;
     12     private double soldPrice;
     13     private double purchasePrice;
     14     private NonPublicClassCanUseAnyName nonPublicClassCanUseAnyName;
     15     public static double DISCOUNT = 0.1;
     16 
     17     // >> TODO 构造方法如果是private的,那么就只有当前的类可以调用这个构造方法
     18     public MerchandiseV2(String name, String id, int count, double soldPrice, double purchasePrice) {
     19         this.name = name;
     20         this.id = id;
     21         this.count = count;
     22         this.soldPrice = soldPrice;
     23         this.purchasePrice = purchasePrice;
     24         // soldPrice = 9/0;
     25     }
     26 
     27     // >> TODO 有些时候,会把所有的构造方法都定义成private的,然后使用静态方法调用构造方法
     28     // >> TODO 同样的,这样的好处是可以通过代码,检查每个属性值是否合法。
     29     public static MerchandiseV2 createMerchandise(String name, String id, int count,
     30                                                   double soldPrice, double purchasePrice) {
     31         if (soldPrice < 0 || purchasePrice < 0) {
     32             return null;
     33         }
     34         return new MerchandiseV2(name, id, count, soldPrice, purchasePrice);
     35     }
     36 
     37     public MerchandiseV2(String name, String id, int count, double soldPrice) {
     38         this(name, id, count, soldPrice, soldPrice * 0.8);
     39     }
     40 
     41     public MerchandiseV2() {
     42         this("无名", "000", 0, 1, 1.1);
     43     }
     44 
     45     // >> TODO public的方法类似一种约定,既然外面的代码可以使用,就意味着不能乱改。比如签名不能改之类的
     46     public void describe() {
     47         System.out.println("商品名字叫做" + name + ",id是" + id + "。 商品售价是" + soldPrice
     48             + "。商品进价是" + purchasePrice + "。商品库存量是" + count +
     49             "。销售一个的毛利润是" + (soldPrice - purchasePrice));
     50         freeStyle();
     51     }
     52 
     53     // >> TODO 对于private的方法,因为类外面掉不到,所以无论怎么改,也不会影响(直接影响)类外面的代码
     54     private void freeStyle() {
     55 
     56     }
     57 
     58     public double calculateProfit() {
     59         double profit = soldPrice - purchasePrice;
     60         return profit;
     61     }
     62 
     63     public double buy(int count) {
     64         if (this.count < count) {
     65             return -1;
     66         }
     67         return this.count -= count;
     68     }
     69 
     70     public String getName() {
     71         return name;
     72     }
     73 
     74     public void setName(String name) {
     75         this.name = name;
     76     }
     77 
     78     public String getId() {
     79         return id;
     80     }
     81 
     82     public void setId(String id) {
     83         this.id = id;
     84     }
     85 
     86     public int getCount() {
     87         return count;
     88     }
     89 
     90     public void setCount(int count) {
     91         this.count = count;
     92     }
     93 
     94     public double getSoldPrice() {
     95         return soldPrice;
     96     }
     97 
     98     public void setSoldPrice(double soldPrice) {
     99         this.soldPrice = soldPrice;
    100     }
    101 
    102     public double getPurchasePrice() {
    103         return purchasePrice;
    104     }
    105 
    106     public void setPurchasePrice(double purchasePrice) {
    107         this.purchasePrice = purchasePrice;
    108     }
    109 }

    private都可以配备一个static的方法。来控制实例是否非法


    // >> TODO 构造方法如果是private的,那么就只有当前的类可以调用这个构造方法
    private 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;
    }

    // >> TODO 有些时候,会把所有的构造方法都定义成private的,然后使用静态方法调用构造方法
    // >> TODO 同样的,这样的好处是可以通过代码,检查每个属性值是否合法。
    public static MerchandiseV2 createMerchandise(String name, String id, int count,
    double soldPrice, double purchasePrice) {
    if (soldPrice < 0 || purchasePrice < 0) {
    return null;
    }
    return new MerchandiseV2(name, id, count, soldPrice, purchasePrice);
    }

    只能在当前包里用

    1 package com.geekbang.supermarket;
    2 
    3 // >> TODO 非public的类,类名可以不和文件名相同
    4 class NonPublicClassCanUseAnyName {
    5 }
     1 package com.geekbang.learn;
     2 
     3 import java.math.BigDecimal;
     4 import java.math.BigInteger;
     5 import java.util.Scanner;
     6 
     7 public class LearnScanner {
     8     public static void main(String[] args) {
     9         // TODO Scanner是一个方便的可以帮我们从标准输入读取并转换数据的类
    10         // TODO 注释里 @since   1.5 表示它是从Java5才开始有的。
    11         Scanner scanner = new Scanner(System.in);
    12 
    13         // TODO 但是这并是说从Java5开始,这个类就没有变化过了
    14         // TODO 在源代码里搜索一下@since,会发现很多方法是在后续的 Java 版本中加进去的
    15         // TODO 但是private方法就不会有这个文档标示,因为private方法本来就不给用。
    16 
    17         System.out.println("请输入一个巨大的正数");
    18         BigInteger bigInteger = scanner.nextBigInteger();
    19         System.out.println("请输入想给这个数加多少");
    20         BigInteger toBeAdd = scanner.nextBigInteger();
    21         System.out.println("结果为:" + bigInteger.add(toBeAdd));
    22     }
    23 }

    14.String

    String 不可变immutable,用来存储的数据是private的,而且不提供人格修改内容的方法,string一旦生成就不可能被修改。

    转自:https://blog.csdn.net/qq_40434646/article/details/81914843

     Integer i = 128; 
     Integer j = 128; 
     System.out.println(i == j);//返回false
    Integer m = 127;
    Integer n = 127;
    System.out.println(m == n);//返回true

    1.如果Integer类型的两个数相等,如果范围在-128~127(默认),那么用“==”返回true,其余的范会false。

    2.两个基本类型int进行相等比较,直接用==即可。

    3.一个基本类型int和一个包装类型Integer比较,用==也可,比较时候,Integer类型做了拆箱操作。

    4.Integer类型比较大小,要么调用Integer.intValue()转为基本类型用“==”比较,要么直接用equals比较。

     1 package com.geekbang.learn;
     2 
     3 public class LearnString {
     4     public static void main(String[] args) {
     5         String content = "01234567ABCDefgh";
     6 
     7         // 数组的length是属性,没有括号   A.length
     8         // String的length()是个方法不是属性哦
     9         System.out.println(content.length());
    10 
    11         // 其实是生成了一个新的String对象
    12         System.out.println(content.toUpperCase());
    13 
    14         System.out.println(content.toLowerCase());
    15 
    16         // content指向对象的内容并没有变化
    17         System.out.println(content);
    18 
    19         System.out.println(content.charAt(1));
    20 
    21         // System.out.println(content.charAt(99));
    22 
    23         System.out.println(content.substring(5));
    24 
    25         System.out.println(content.substring(1, 5));  // 最后一个不包含
    26 
    27     }
    28 }

    字符串用法:

    package com.geekbang.learn;
    
    public class LearnString2 {
        public static void main(String[] args) {
            String content = "Orange_Apple_Banana";
    
            char[] chars = content.toCharArray();  // 字符数组
            for (int i = 0; i < chars.length; i++) {
                System.out.println(chars[i]);
            }
    
            String sp = "_";
            String[] s = content.split(sp);  // 分割
            for (int i = 0; i < s.length; i++) {
                System.out.println(s[i]);
            }
    
            int indexOf = content.indexOf('_');
            System.out.println(indexOf);
            System.out.println(content.substring(indexOf + 1, content.length()));
    
            int lastIndexOf = content.lastIndexOf("_");
            System.out.println(lastIndexOf);
            System.out.println(content.substring(0, lastIndexOf));
    
    
            // 是否包含字符串
            System.out.println(content.contains("apple"));
            System.out.println(content.contains("Apple"));
            System.out.println(content.startsWith("Orange"));
            System.out.println(content.endsWith("Banana"));
    
            String content2 = "Orange_Apple_Banana";
            String content3 = "   orange_Apple_banana   ";
    
    
            // TODO 两个String对象比较是否相等,一定要用equals方法
            // 比较两个字符串是否相等
            System.out.println(content.equals(content2));
            System.out.println(content.equals(content3));
            System.out.println(content3.trim());
            System.out.println(content.equalsIgnoreCase(content3.trim()));  // 把string前面和后面的额外空格去掉
        }
    }

     main方法也知识一个静态的,有String[]作为参数,没有返回值的方法而已,它的特殊性在于java可以吧main方法作为程序入口。

     1 package com.geekbang.learn;
     2 
     3 public class LearnMain {
     4     public static void main(String[] args) { // 以空格为分隔符号 空格字符串以引号括住
     5         System.out.println(args.length);
     6         for (int i = 0; i < args.length; i++) {
     7             System.out.println(args[i]);
     8         }
     9     }
    10 }
    1 package com.geekbang.learn;
    2 
    3 public class InvokeMain {
    4     public static void main(String[] args) {
    5         System.out.println("进入了InvokeMain的main方法");
    6         LearnMain.main(args);
    7         System.out.println("InvokeMain的main方法执行结束");
    8     }
    9 }

    System类

    • in
    • out
    • 取系统当前时间
     1 package com.geekbang.learn;
     2 
     3 public class LearnSystem {
     4     public static void main(String[] args) {
     5         // 当前的毫秒数
     6         long startMS = System.currentTimeMillis();
     7 
     8         int counter = 0;
     9         for (int i = 0; i < 1000; i++) {
    10             counter++;
    11         }
    12 
    13         long endMS = System.currentTimeMillis();
    14         System.out.println("程序执行使用了几个毫秒?" + (endMS - startMS));
    15 
    16         // 纳秒
    17         long startNS = System.nanoTime();
    18 
    19         counter = 0;
    20         for (int i = 0; i < 1000; i++) {
    21             counter++;
    22         }
    23 
    24         long endNS = System.nanoTime();
    25         System.out.println("程序执行使用了几个纳秒?" + (endNS - startNS));
    26     }
    27 }

    StringBuild

    是一个非常方便的用来拼接和处理字符串的类,和String不同的是,它是可变的。

    操作不会生成新的StringBulid和String对象

     1 package com.geekbang.learn;
     2 
     3 public class LearnStringBuilder {
     4 
     5     public static void main(String[] args) {
     6 
     7         // TODO StringBuilder首先是可变的
     8         // TODO 而且对它进行操作的方法,都会返回this自引用。这样我们就可以一直点下去,对String进行构造。
     9         StringBuilder strBuilder = new StringBuilder();
    10 
    11         long longVal = 123456789;
    12 
    13         // 可以append所有数据类型
    14         strBuilder.append(true).append("abc").append(longVal);
    15 
    16         System.out.println(strBuilder.toString());
    17         System.out.println(strBuilder.reverse().toString());  // 翻转
    18         System.out.println(strBuilder.reverse().toString());
    19         System.out.println(strBuilder.toString());
    20 
    21         System.out.println(strBuilder.delete(0, 4).toString());   // 删除
    22 
    23         System.out.println(strBuilder.insert(3,"LLLLL").toString());  // 插入
    24     }
    25 
    26 }

    StringBuffer类

    StringBuffer():返回一个空串,长度为16个字符,实现对字符的增删改查。

    除了字符所占的空间外,另外加16个字符大小的缓冲区。

     1 package com.geekbang.learn;
     2 
     3 public class LearnStringBuffer {
     4     public static void main(String[] args) {
     5         String s = new String("This is A String");
     6         StringBuffer sb = new StringBuffer(s);
     7         sb.insert(sb.length(), "BufferDemo").insert(sb.length(), 2);
     8         System.out.println(sb.toString());
     9     }
    10 }
    • String : 不可变字符序列
    • StringBuillder:可变字符序列、效率高、线程不安全
    • StringBuffer:可变字符序列、效率低、线程安全

    15.继承

    • 子类继承了父类的方法和属性
    • 使用子类的引用可以调用父类的共有方法
    • 使用子类的引用可以访问父类的共有属性
    • 就好像子类的引用可以一物体=二用,既可以当做父类的引用使用,又可以当做子类的引用使用。 
    // >> TODO 继承的语法就是在类名后面使用extends 加 要继承的类名
    // >> TODO 被继承的类叫做父类(Parent Class),比如本例中的MerchandiseV2。
    // >> TODO 继承者叫做子类(Sub Class),比如本例中的PhoneExtendsMerchandise。
    // >> TODO Java中只允许一个类有一个直接的父类(Parent Class),即所谓的单继承
    // >> TODO 没错,别的类也可以继承子类,比如可以有一个HuaweiPhone继承PhoneExtendsMerchandise
    // TODO 这时候,HuaweiPhone就是PhoneExtendsMerchandise的子类了。
    // >> TODO 子类继承了父类什么呢?所有的属性和方法。
    // >> TODO 但是子类并不能访问父类的private的成员(包括方法和属性)。

     组合 is-a 和 集成 has-a

     package com.geekbang.supermarket;

    // >> TODO 继承,其实表达的是一种"is-a"的关系,也就是说,在你用类构造的世界中,"子类是父类的一种特殊类别"

    // >> TODO 组合和继承,是拿到一个问题,设计相应的Java类的时候,不得不面对的来自灵魂的拷问
    // TODO "XX到底是YY的一种,还是只是组合了YY?","手机到底是手电筒的一种,还是组合了一个可以当手电的闪光灯?"


    // TODO 在组合的情况下,怎么限制一次只能买五个手机呢?
    // TODO 1)首先不能修改MerchandiseV2这个类,否则你会限制所有商品一次购买的数量
    // TODO 2)其次,在现实情况下,这个类可能根本不受你控制,你无权修改其代码
    // TODO 3)在每次调用buy方法的地方做限制,是不行的,
    // TODO - 你无法控制别人怎么用你的类,
    // TODO - 而且会面临到处复制代码的糟糕情况,
    // TODO - 如果说限制改成10个,所有复制的代码都要改,程序员都应该很懒,这不是一个程序员该做的事情
    // TODO 4)在只能修改手机类的情况下,我们可以提供一个buyPhone的方法,实现限制购买数量的逻辑。
    // TODO 但是这样并不能阻止别人像下面这样调用merchandise的buy方法,这个方法是会修改库存的,所以还是无法硬性的限制一次性购买手机的数量

    // TODO 我们来理清一下自己的核心诉求:针对手机,限制一次性购买的数量。必须限制死,必须不影响别的商品,必须只能改手机类的代码
    // TODO 这时候,组合就无能为力了,继承可以发挥其应有的作用。


    // >> TODO 继承不是组合,继承也不只是为了能简单的拿来父类的属性和方法。如果仅仅如此,原封不动的拿来主义,组合也能做到。
    // TODO 继承也不是通过组合的方式来实现的。和组合相比,继承更像是"融合"
    // TODO 所谓融合,即合二为一,可以互相影响。父类影响子类没问题,子类怎么影响父类呢?如何限制手机一次只能最多买五个?

    public class PhoneExtendsMerchandise extends MerchandiseV2 {}

    覆盖才是继承的精髓


    // >> TODO 通过使用和父类方法签名一样,而且返回值也必须一样的方法,可以让子类覆盖(override)掉父类的方法

    // >> TODO ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓下面才是继承的终极奥义↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

    // >> TODO 也就是说,子类并不是只能把父类的方法拿过来,而且可以通过覆盖来替换其中不适合子类的方法

    // >> TODO ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑上面才是继承的终极奥义↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑

    // >> TODO 题外话:属性是联动的,可能是有特殊意义的。
    // TODO 所以直接给属性赋值是危险的,因为没有办法检查新的值是否有意义,也没法对这个修改做联动的修改

    public double buy(int count) {

    // TODO 这个方法里代码大部分和父类一样,肯定有方法解决
    if (count > MAX_BUY_ONE_ORDER) {
    System.out.println("购买失败,手机一次最多只能买" + MAX_BUY_ONE_ORDER + "个");
    return -2;
    }
    if (this.count < count) {
    System.out.println("购买失败,库存不够");
    return -1;
    }
    this.count -= count;
    double cost = count * soldPrice;
    System.out.println("购买成功,花费为" + cost);
    return cost;
    }
    返回值必须一样,不是类型兼容,而是必须一摸一样。
    如果签名一样,但是返回值不一样,会是错误
    public String getName() {
    return name;
    }
    // >> TODO 覆盖可以覆盖掉父类的方法。同一个方法,不同的行为。
    // >> TODO 这,就是多态!
    // >> TODO 方法可以覆盖,属性访问不可以,所以这也是使用方法的一个原因。
    // TODO 即使在父类里,只是一个简单的getName,但是这样做,子类就可以覆盖掉父类的方法
    // TODO 方法不止眼前的代码,还有子类的覆盖。用方法,才能覆盖,才能多态。
    public String getName() {
    return this.brand + ":" + this.os + ":" + name;
    }

    替换父类代码  ---> super

    父类可以多继承super可以是多个类的方法和访问public属性

    子类:
    // >> TODO 使用super可以调用父类的方法和属性(当然必须满足访问控制符的控制)
    public double buy(int count) {
    if (count > MAX_BUY_ONE_ORDER) {
    System.out.println("购买失败,手机一次最多只能买" + MAX_BUY_ONE_ORDER + "个");
    return -2;
    }
    return super.buy(count);
    }
    父类:
    public double buy(int count) {
    if (this.count < count) {
    System.out.println("购买失败,库存不够");
    return -1;
    }
    this.count -= count;
    double cost = count * soldPrice;
    System.out.println("购买成功,花费为" + cost);
    return cost;
    }

    子类:

    public void describe() {
    System.out.println("此手机商品属性如下");
    super.describe();
    System.out.println("手机厂商为" + brand + ";系统为" + os + ";硬件配置如下: " +
    "屏幕:" + screenSize + "寸 " +
    "cpu主频" + cpuHZ + " GHz " +
    "内存" + memoryG + "Gb " +
    "存储空间" + storageG + "Gb");
    }

    父类:

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

    super并不会是父类的引用

      // >> TODO super是子类和父类交流的桥梁,但是并不是父类的引用
    // >> TODO 所以,super和this自引用不一样,不是简单可以模拟的(可以模拟的话不就成了组合了吗)
    // public MerchandiseV2 getParent(){
    // return super;
    // }

    public Phone getThisPhone(){
    return this;
    }

    // >> TODO 使用super可以调用父类的public属性,但是super不是一个引用。
    public void accessParentProps() {
    System.out.println("父类里的name属性:" + super.name);
    }

    public void useSuper() {
    // >> TODO super的用法就像是一个父类的引用。它是继承的一部分,像组合的那部分,但不是全部
    super.describe();
    super.buy(66);
    System.out.println("父类里的count属性:" + super.count);
    }

    可以认为,创建子类对象的时候,也就同时创建了一个隐藏的父类对象,集成链上所有的数据

    Super调用构造方法

    private String name;
    private String id;
    private int count;
    private double soldPrice;
    private double purchasePrice;
    public Phone(
    String name, String id, int count, double soldPrice, double purchasePrice,
    double screenSize, double cpuHZ, int memoryG, int storageG, String brand, String os
    ) {
    // >> TODO 使用super调用父类的构造方法,必须是子类构造方法的第一个语句
    // >> TODO 可以使用表达式
    // >> TODO super调用构造方法,不可以使用super访问父类的属性和方法,不可以使用子类成员变量和方法
    // >> TODO 可以使用静态变量和方法
    // >> TODO 都是<init>方法,我们来看一下
    super(name, id, count, soldPrice * 1.2, purchasePrice);
    init(screenSize, cpuHZ, memoryG, storageG, brand, os);
    }
    public void init(double screenSize, double cpuHZ, int memoryG, int storageG, String brand, String os) {
    this.screenSize = screenSize;
    this.cpuHZ = cpuHZ;
    this.memoryG = memoryG;
    this.storageG = storageG;
    this.brand = brand;
    this.os = os;
    }
    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;
    }

    super this 都要放第一个 矛盾  可是使用init方法来解决,init是自己写的。

    public Phone() {
    super("无名", "000", 0, 1, 1.1); 父类有参数的构造方法
    init(4.5, 4.6, 6, 128, "Uknown", "Uknown");
    }

    子类的构造方法会调用父类的构造方法,默认调用父类无参的构造方法

    public Phone() {
    init(4.5, 4.6, 6, 128, "Uknown", "Uknown");
    }
    默认调用父类方法
    public MerchandiseV2() {
    this("无名", "000", 0, 1, 1.1);
    }

    // >> TODO 可以用子类的引用给父类的引用赋值,也就是说,父类的引用可以指向子类的对象

    MerchandiseV2 m = ph;
    MerchandiseV2 m2 = new Phone(
    "手机002", "Phone002", 100, 1999, 999,
    4.5, 3.5, 4, 128, "索尼", "安卓"
    );

    // >> TODO 但是反之则不行,不能让子类的引用指向父类的对象。因为父类并没有子类的属性和方法呀

    // >> TODO 重点
    // >> TODO 因为子类继承了父类的方法和属性,所以父类的对象能做到的,子类的对象肯定能做到
    // TODO 换句话说,我们可以在子类的对象上,执行父类的方法
    // >> TODO 当父类的引用指向子类的实例(或者父类的实例),只能通过父类的引用,像父类一样操作子类的对象
    // TODO 也就是说"名"的类型,决定了能执行哪些操作


    // >> TODO ph和m都指向同一个对象,通过ph可以调用getBrand方法
    // TODO 因为ph的类型是Phone,Phone里定义了getBrand方法
    ph.getBrand();

    只能根据引用类型来决定对象的操作。

     // TODO 如果确定一个父类的引用指向的对象,实际上就是一个子类的对象(或者子类的子类的对象),可以强制类型转换
    Phone aPhone = (Phone) m2;

    // MerchandiseV2是Phone的父类,Phone是shellColorChangePhone的父类
    ShellColorChangePhone shellColorChangePhone = new ShellColorChangePhone(
    "手机002", "Phone002", 100, 1999, 999,
    4.5, 3.5, 4, 128, "索尼", "安卓"
    );

    // TODO 父类的引用,可以指向子类的对象,即可以用子类(以及子类的子类)的引用给父类的引用赋值
    MerchandiseV2 ccm = shellColorChangePhone;

    // TODO 父类的引用,可以指向子类的对象。
    // TODO 确定MerchandiseV2的引用ccm是指向的是Phone或者Phone的子类对象,那么可以强制类型转换
    Phone ccp = (Phone) ccm;

    // TODO 确定MerchandiseV2的引用ccm是指向的是ShellColorChangePhone或者ShellColorChangePhone的子类对象
    // TODO 那么可以强制类型转换
    ShellColorChangePhone scp = (ShellColorChangePhone) ccm;

    // TODO 会出错,因为m2指向的是一个Phone类型的对象,不是ShellColorChangePhone的对象
    ShellColorChangePhone notCCP = (ShellColorChangePhone) m2;



     1 package com.geekbang;
     2 
     3 import com.geekbang.supermarket.LittleSuperMarket;
     4 
     5 public class PolymorphismAppMainSimple {
     6     public static void main(String[] args) {
     7         LittleSuperMarket superMarket = new LittleSuperMarket("大卖场",
     8             "世纪大道1号", 500, 600, 100);
     9 
    10         // >> TODO 虽然是用的父类的引用指向的不同类型的对象,调用getName方法时,实际执行的方法取决于对象的类型,而非引用的类型
    11         // >> TODO 也就是说,能调用哪些方法,是引用决定的,具体执行哪个类的方法,是引用指向的对象决定的。
    12         //    TODO 这就是覆盖的精髓。覆盖是多态的一种,是最重要的一种。
    13         // >> TODO 以getName为例,父类里有这个方法,所以肯定都可以调用,但是Phone覆盖了父类的getName方法
    14         //    TODO 之前我们使用子类的引用指向子类的对象,调用子类里覆盖父类的方法,比如getName,执行的是子类的getName方法,我们觉得很自然。
    15         //    TODO 这里变换的是,我们用父类的引用指向子类的对象,调用被子类覆盖的方法,实际执行的还是子类里的getName方法
    16         //    TODO 当我们用父类的引用指向一个Phone的实例,并调用getName方法时,实际调用的就是Phone类里定义的getName方法
    17         System.out.println(superMarket.getMerchandiseOf(0).getName());
    18         System.out.println();
    19         System.out.println(superMarket.getMerchandiseOf(10).getName());
    20         // TODO 如果子类里没有覆盖这个方法,就去父类里找,父类里没有,就去父类的父类找。反之只要能让一个引用指向这个对象
    21         // TODO 就说明这个对象肯定是这个类型或者其子类的的一个实例(否则赋值会发生ClassCastException),总归有父类兜底。
    22         System.out.println();
    23         System.out.println(superMarket.getMerchandiseOf(100).getName());
    24 
    25     }
    26 }
     1 package com.geekbang;
     2 
     3 import com.geekbang.supermarket.LittleSuperMarket;
     4 
     5 public class PolymorphismAppMainComplex {
     6     public static void main(String[] args) {
     7         LittleSuperMarket superMarket = new LittleSuperMarket("大卖场",
     8             "世纪大道1号", 500, 600, 100);
     9 
    10         // >> TODO 不仅如此,我们看一个更复杂的例子,describe方法。
    11         // TODO MerchandiseV2里的describe方法,调用了calculateProfit方法
    12         // TODO Phone里的describe方法,覆盖了直接父类MerchandiseV2里的describe方法,并且使用super调用了父类的describe方法
    13         // TODO ShellColorChangePhone里的describe方法,覆盖了直接父类Phone里的describe方法,并且使用super调用了父类的describe方法
    14         // TODO 更复杂的是,ShellColorChangePhone还覆盖了间接父类MerchandiseV2里的calculateProfit方法
    15         superMarket.getMerchandiseOf(0).describe();
    16         System.out.println();
    17         superMarket.getMerchandiseOf(10).describe();
    18         System.out.println();
    19         superMarket.getMerchandiseOf(100).describe();
    20 
    21         // >> TODO 总结:无论一个方法是使用哪个引用被调用的,"它都是在实际的对象上执行的"。执行的任何一个方法,都是这个对象所属的类的方法。
    22         //    TODO 如果没有,就去父类找,再没有,就去父类的父类找,依次寻找,知道找到。
    23 
    24         //    TODO 换个角度理解。我们一直说子类里又一个(特殊的)父类的对象。这时候,这个特殊的父类的对象里的this自引用,是子类的引用。
    25         //    TODO 那么自然的,即使是在继承自父类的代码里,去调用一个方法,也是先从子类开始,一层层继承关系的找。
    26 
    27         //    TODO 这也是Java选择单继承的重要原因之一。在多继承的情况下,如果使用不当,多态可能会非常复杂,以至于使用的代价超过其带来的好处。
    28 
    29     }
    30 }

    静态多态 重载 Overload

    动态多态 覆盖 Override

    之前重载的时候,参数是用的自定义类型。现在理解了父类和子类的引用赋值关系,重载又多了一层复杂性
     1 package com.geekbang;
     2 
     3 import com.geekbang.supermarket.LittleSuperMarket;
     4 import com.geekbang.supermarket.MerchandiseV2;
     5 import com.geekbang.supermarket.Phone;
     6 import com.geekbang.supermarket.ShellColorChangePhone;
     7 
     8 public class OverloadTestAppMain {
     9     public static void main(String[] args) {
    10         LittleSuperMarket superMarket = new LittleSuperMarket("大卖场",
    11             "世纪大道1号", 500, 600, 100);
    12         MerchandiseV2 m = superMarket.getMerchandiseOf(100);
    13 
    14         MerchandiseTest merchandiseTest = new MerchandiseTest();
    15 
    16         System.out.println("-----------1-------------");
    17         // TODO 重载调用哪个方法,和参数的引用类型相关,和引用实际指向的类型无关
    18         merchandiseTest.testMerchandiseOverload(m);
    19         merchandiseTest.testMerchandiseOverload((Phone) m);
    20         merchandiseTest.testMerchandiseOverload((ShellColorChangePhone) m);
    21         // TODO 甚至是个null也可以,但是要用强制类型转换,告诉Java这个类型是什么,否则找不到一个唯一的方法去调用
    22         // TODO 重载的参数类型,相同位置,不一定要有继承或者兼容的关系,完全free style
    23         merchandiseTest.testMerchandiseOverload("");
    24 
    25         System.out.println("-----------2-------------");
    26 
    27         System.out.println();
    28         // >> TODO 引用本身是null没关系,确定调用哪个方法只需要引用的类型。这叫做静态多态。即在编译期就知道该调用哪个方法
    29         m = null;
    30         merchandiseTest.testMerchandiseOverload(m);
    31         merchandiseTest.testMerchandiseOverload((Phone) m);
    32         merchandiseTest.testMerchandiseOverload((ShellColorChangePhone) m);
    33 
    34         System.out.println("-----------3-------------");
    35 
    36         // >> TODO 如果引用类型没有完全匹配的,则会根据继承关系,沿着参数当前类型,向下撸
    37         merchandiseTest.testMerchandiseOverloadNotExactlyMatchType((ShellColorChangePhone) null);
    38 
    39 
    40         // >> TODO 重载总结:静态多态,调用的方法和参数实际指向的对象无关,只和引用本身的类型相关。
    41         // >> TODO 因为调用时参数类型是确定的,所以,在编译期间就可以明确的知道哪个方法会被调用。如果有多种可能,则会有编译错误
    42         // >> TODO 如果没有类型完全匹配的候选,则根据类型的继承关系向下撸着找。找到最贴近参数类型的那个方法
    43         // >> TODO 无论是静态方法,还是成员方法,重载寻找方法的顺序是一样的,在这里就不赘述了
    44         //    TODO (再提一句多继承,Java没有多继承,是前辈给我们的馈赠。保护了发际线。心疼隔壁CPP的程序员)
    45 
    46 
    47     }
    48 }
     1 package com.geekbang;
     2 
     3 import com.geekbang.supermarket.MerchandiseV2;
     4 import com.geekbang.supermarket.Phone;
     5 import com.geekbang.supermarket.ShellColorChangePhone;
     6 
     7 public class MerchandiseTest {
     8 
     9     // TODO 之前重载的时候,参数是用的自定义类型。现在理解了父类和子类的引用赋值关系,重载又多了一层复杂性
    10 
    11     public void testMerchandiseOverload(MerchandiseV2 me) {
    12         System.out.println("参数为MerchandiseV2的testMerchandiseOverload 被调用了");
    13     }
    14 
    15     public void testMerchandiseOverload(Phone ph) {
    16         System.out.println("参数为Phone的testMerchandiseOverload 被调用了");
    17     }
    18 
    19     public void testMerchandiseOverload(ShellColorChangePhone shellColorChangePhone) {
    20         System.out.println("参数为ShellColorChangePhone的testMerchandiseOverload 被调用了");
    21     }
    22 
    23     public void testMerchandiseOverload(String str) {
    24         System.out.println("参数为String的testMerchandiseOverload 被调用了");
    25     }
    26 
    27     public void testMerchandiseOverloadNotExactlyMatchType(MerchandiseV2 me) {
    28         System.out.println("参数为MerchandiseV2的testMerchandiseOverloadNotExactlyMatchType 被调用了");
    29     }
    30 
    31 //    public void testMerchandiseOverloadNotExactlyMatchType(Phone ph) {
    32 //        System.out.println("参数为Phone的testMerchandiseOverloadNotExactlyMatchType 被调用了");
    33 //    }
    34 
    35     public void testMerchandiseOverloadNotExactlyMatchType(String str) {
    36         System.out.println("参数为String的testMerchandiseOverloadNotExactlyMatchType 被调用了");
    37     }
    38 
    39 }

    instanceof操作

     1 package com.geekbang;
     2 
     3 import com.geekbang.supermarket.LittleSuperMarket;
     4 import com.geekbang.supermarket.MerchandiseV2;
     5 import com.geekbang.supermarket.Phone;
     6 import com.geekbang.supermarket.ShellColorChangePhone;
     7 
     8 public class InstanceOfTestAppMain {
     9     public static void main(String[] args) {
    10         int merchandiseCount = 600;
    11         LittleSuperMarket superMarket = new LittleSuperMarket("大卖场",
    12             "世纪大道1号", 500, merchandiseCount, 100);
    13 
    14         // >> TODO instanceof 操作符,可以判断一个引用指向的对象是否是某一个类型或者其子类
    15         //    TODO 是则返回true,否则返回false
    16         for(int i =0;i<merchandiseCount;i++){
    17             MerchandiseV2 m = null;// superMarket.getMerchandiseOf(i);
    18             if(m instanceof MerchandiseV2){
    19                 // TODO 先判断,再强制类型转换,比较安全
    20                 MerchandiseV2 ph = (MerchandiseV2)m;
    21                 System.out.println(ph.getName());
    22             }else {
    23                 System.out.println("not an instance");
    24             }
    25         }
    26 
    27         // >> TODO 如果引用是null,则肯定返回false
    28     }
    29 }

    Protected 继承专属的访问控制

    同一个包可见+子类可见

     1 package com.geekbang.onlinemarket;
     2 
     3 import com.geekbang.supermarket.Phone;
     4 
     5 public class OnlineSpecialPhone extends Phone {
     6 
     7     public OnlineSpecialPhone(String name, String id, int count, double soldPrice, double purchasePrice, double screenSize, double cpuHZ, int memoryG, int storageG, String brand, String os) {
     8         super(name, id, count, soldPrice, purchasePrice, screenSize, cpuHZ, memoryG, storageG, brand, os);
     9         this.screenSize = 99;
    10     }
    11 
    12     // >> TODO 子类覆盖父类的方法,不可以用可见性更低的修饰符,但是可以用更高的修饰符
    13     // >> TODO 原因是什么呢?
    14    public String getNameOfPhone(){
    15         return super.getNameOfPhone();
    16    }
    17 }

    final

    final修饰类:不可被继承

    final修饰方法: 不可被子类覆盖

    final修饰变量:不可被赋值(修改)

    final修饰变量 在 static块赋值只能赋值一次。

    final只能指向这个引用,引用的值不能修改,但是引用本身的值可以修改。

    静态方法的继承

    没有的静态方法可以话执行的话,执行父类的静态方法

    签名和返回值必须一致

     1 package com.geekbang;
     2 
     3 import com.geekbang.supermarket.LittleSuperMarket;
     4 import com.geekbang.supermarket.MerchandiseV2;
     5 import com.geekbang.supermarket.Phone;
     6 import com.geekbang.supermarket.ShellColorChangePhone;
     7 
     8 public class StaticMethodDoesNotBelieveOverride {
     9     public static void main(String[] args) {
    10         LittleSuperMarket superMarket = new LittleSuperMarket("大卖场",
    11             "世纪大道1号", 500, 600, 100);
    12 
    13 
    14         System.out.println("-----1------");
    15 
    16 
    17         // >> TODO 静态方法可以被继承
    18         MerchandiseV2.staticMethod();
    19         Phone.staticMethod();
    20         ShellColorChangePhone.staticMethod();
    21 
    22         System.out.println("-----2------");
    23 
    24         MerchandiseV2 m = superMarket.getMerchandiseOf(100);
    25 
    26         // >> TODO 用引用调用静态方法没有覆盖
    27         m.staticMethod();
    28         ((Phone) m).staticMethod();
    29         ((ShellColorChangePhone) m).staticMethod();
    30         // TODO 有些东西,学会就应该马上忘掉,比如上面使用引用调用静态方法的内容
    31 
    32         System.out.println("-----3------");
    33 
    34         ((MerchandiseV2) null).staticMethod();
    35         ((Phone) null).staticMethod();
    36         ((ShellColorChangePhone) null).staticMethod();
    37         // TODO 有些东西,学会就应该马上忘掉,比如上面使用有类型的null引用调用静态方法的内容
    38 
    39     }
    40 }

    父类可以强转成子类

    null是引用的缺省值,强转给null一个类型信息

    Object

    没有成员变量

    hashcode 表示对象的特征值的int整数

    哈希吗 散列吗

    equals判断两个对象从逻辑上是否相对

    需要覆盖

    // >> TODO hashCode 和 equals是我们最常覆盖的两个方法
    // >> TODO 覆盖的原则是,equals为true,hashCode就应该相等。这是一种约定俗成的规范
    // >> TODO 即equals为true是hashCode相等的充分非必要条件,hashCode相等是equals为true的必要不充分条件


    @Override
    public boolean equals(Object o) {
    if (this == o) return true;
    if (!(o instanceof MerchandiseV2)) return false;
    MerchandiseV2 that = (MerchandiseV2) o;
    return this.getCount() == that.getCount() &&
    Double.compare(that.getSoldPrice(), getSoldPrice()) == 0 &&
    Double.compare(that.getPurchasePrice(), getPurchasePrice()) == 0 &&
    getName().equals(that.getName()) &&
    getId().equals(that.getId());
    }

    @Override
    public int hashCode() {
    return Objects.hash(getName(), getId(), getCount(), getSoldPrice(), getPurchasePrice());
    }
    
    
    
    
  • 相关阅读:
    logback不输出日志消息,且SLF4J绑定源错误
    solr6.4.1 搜索引擎(1)启动eclipse启动
    centos7网络连接
    OutOfMemoryError异常java内存泄漏(Memory Leak)和内存溢出(Memory Overflow)
    centos7软件安装redis3.2
    centos7软件安装mysql5.7
    solr6.4.1搜索引擎(5)使用zookeeper3.4.9分布式集群
    mysql优化数据库优化、SQL优化
    solr6.4.1搜索引擎(4)tomcat重启后数据加载缓慢或丢失
    centos7软件安装jdk1.8
  • 原文地址:https://www.cnblogs.com/JCcodeblgos/p/11518298.html
Copyright © 2020-2023  润新知