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());
}


原文地址:https://www.cnblogs.com/JCcodeblgos/p/11518298.html