当前位置: 首页 > news >正文

网站建设补充协议搜狗输入法下载安装

网站建设补充协议,搜狗输入法下载安装,软件工程师发展前景,100元网站建设文章目录 继承继承的语法继承的作用与特点继承与组合的区别 覆盖 overideOverride构造方法无法覆盖 super#xff1a;和父类对象沟通的桥梁super 到底指哪个父类的实例super 严格意义上并非真的是一个父类的引用super 调用父类的构造方法子类构造方法被调用时#xff0c;Java… 文章目录 继承继承的语法继承的作用与特点继承与组合的区别 覆盖 overideOverride构造方法无法覆盖 super和父类对象沟通的桥梁super 到底指哪个父类的实例super 严格意义上并非真的是一个父类的引用super 调用父类的构造方法子类构造方法被调用时Java 会主动去调用父类的无参构造方法 父类引用和子类引用的关系如何理解一个类型的引用如何理解一个类型的对象强制类型转换 多态静态多态重载Overload动态多态覆盖Override 继承里的静态方法 继承 继承的语法 在类名后使用 extends 再加上 要继承的类名。 public class SonClass extends FatherClass{}继承的作用与特点 子类SubClass继承了父类SuperClass的方法和属性如果处在不同包则仅继承 public 方法和 public 属性若在同一个包则缺省的方法和属性也能被继承 使用子类的引用可以调用父类的共有public方法 使用子类的引用可以访问父类的共有public属性 子类的引用可以一物二用既可以当作父类的引用使用又可以当作子类的引用使用。 java 中只允许一个类有一个直接的父类即所谓”单继承“。子类可以再被其他类继承也就是说可以一代继承一代的。 虽然说 A 类可以被 B 类继承B 类又可以被 C 类继承C 类又可以被 D 类继承但一般不会有“祖父类”、“曾祖父类”、或者“一级父类”、“二级父类”的称呼。只要在这条继承链上的都可以说是上游的“父类”比如说 C 是 D 的父类B 也是 D 的父类A 也是 D 的父类。其实想想本质上 D 虽然是通过一级一级继承下来的但是本质完全可以变为直接从 A 继承然后增加相应的方法和属性完全可以这么理解所以说 A 是 D 的父类并不无道理。 子类继承了父类的所有属性和方法只要是对子类的见的但是子类不能访问父类的 private 成员包括方法和属性。 继承与组合的区别 继承 vs. 组合 其实继承的方式有点像是把一个父类的引用/实例包含进子类中使得子类引用/子类实例可以调用父类的 public 属性和方法。那么为什么不用一个普通的类然后在这个普通类里创建一个 A 类的引用/实例也可以使用到 A 类的 public 方法/属性而非得去继承 A 类同样实现使用 A 类的 public 方法/属性 这里其实有区别前者非继承实际上是一种“组合”该类中包含 A 类引用/实例has-A方式而后者继承实际上是一种“是”该类就是 A 类的一中特殊形式is-A的方式。 对问题的理解就决定了到底是使用“组合”还是“继承”的方式来编写代码两种方式并没有说一定哪种更好而是要看要解决什么实际问题这就上升到如何设计系统去解决问题对实际问题理解得好那么设计的系统就可以很简单否则就可能把系统设计的很复杂。 比如说实际问题是手机到底是一种特殊的手电筒还是手机仅仅是组合了一个特殊手电筒闪光灯的东西 如果你的理解是前者那么手机应该是继承手电筒is-A 如果你的理解是后者那么手机应该是组合了手电筒has-A。 其实用组合的方式是可以完全代替继承来实现相应的功能的但是很多时候可能会把程序逻辑设计得非常繁琐复杂。如果本身这个实际问题是 has-A你用组合方式可能还设计得挺正常的。但如果这个实际问题是 is-A你非得用组合的方式去解决那你设计的问题就可能很绕了。 比如说 A 类是一个公共类别的公司设计的你拿来用实现自己的功能。 首先你为了更好完成 B 类你自己写的类无法去修改 A 类的代码 一来 A 类是别人公司的代码你改不了 二来就算 A 类是你公司的你有权去改但是因为 A 是公共类不仅仅只有你的 B 类在用可能其他的 C、D、E、F 类都在用你为了 B 类改 A 类会直接影响 C、D、E、F 类的正常运作显然也是不行的。 你不修改 A 类就只能在自己的 B 类中做完所有的逻辑设计。那么考虑如下场景A 类中提供了 public 方法实现商品购买功能.buy()而你在 B 类中希望去限制购买数量A 类没有提供相应的购买且限制的方法那么你就得自己在 B 类中写一个方法.buyThing()但问题是你并不能阻止用户调用没有限制的 A 类暴露出来的 public 方法来购买buy()那么你的限制设计变得没有意义了完全可以被绕过。 // 用组合的方式在 B 类中创建一个 A 类的实例 public class A{// 购买加入购物车public void buy(int count){xxx;}// 付款public void pay(double price){xxxx;}xxx; }public class B{// 创建一个 A 类实例以便调用 A 类的 public 方法/属性public A createA(){return new A();}// 购买加入购物车并限制购买数量public void buyThing(int count){if (count 5){ // 做一个数量限制过滤xxxx;}xxx;} }// 别人调用 public class TestUse{public static void main(String[] args){B b new B();// 实现购买加入购物车b.buyThing(3); // 调用 B 类暴露的 public 购买加入购物车方法buyThing()// 这里你如何避免用户不去使用 A 类暴露的 public 购买加入购物车方法buy()// 用户的确可以完全绕过你在 B 类中设置的购买限制// b.createA().buy(10);// 实现付款b.createA().pay(12.3); // 调用 A 类暴露的 public 付款方法pay()} }当然如果 A 类真的完全由你设计并且最初就把这个类设计成缺省的访问控制即只有同一个包中的其他类才能访问就可以通过将组合类放入和 A 的同一个包中然后将组合类设计成 public然后用调用类非同一包中去导入这个 public 的组合类。那么的确能起到“无法绕过组合类的限制直接调用 A 类方法”的效果。但是这有非常多的限制需要调用类不在 A 所在的包中组合类又得放到和 A 同一个包中。而现实中提供一个类出来基本都是 public 的所以只有继承才能做到你无论把这些类放到哪不管是不是同一个包中都能实现你想要的限制过滤。 当然如果需求仅仅是原封不动地调用 A 类的功能那么用这种组合方式还是可以的比如说一个手机类包含一个闪光灯类这样的组合。使用时创建一个手机类的实例再用手机类的方法创建闪光灯类的实例直接沿用闪光灯的方法和属性。这个时候如果选择用继承反而不太好。并且继承只能单一继承但手机类可以同时包含闪光灯类、键盘类、听筒类、麦克风类、锂电池类、屏幕类等你要是拿手机类继承了闪光灯类那么其他类还是需要以组合的方式进入这个手机类这样设计完全没有必要并且也不合理。 覆盖 overide 子类并不是只能把父类的方法拿过来也能在父类的方法/属性基础上添加子类自己的新的与父类中不同名的方法和属性而是还能通过覆盖来替换父类中不适合的方法。对于子类来说父类的方法并不能不要但是可以改 覆盖才是继承的精髓。 通过使用和父类方法签名一样而且返回值也必须一样的方法可以让子类覆盖掉父类的方法。 方法签名一样很好理解但注意这里返回值也必须得一模一样兼容的数据类型都不行比如说子类返回值是 int 父类返回值的 long或者反过来子类返回值是 long父类返回值是 int【这点已经实际尝试过了会报错说类型不兼容。】 至于访问修饰符覆盖的话子类的该方法的开放权限应该是大于等于父类的该方法开放权限。 即如果父类的该方法是 public子类只能是 public如果父类的该方法是缺省的同一包中的其他类可访问子类可选缺省或 public如果父类的该方法是 private则子类继承时根本没有继承到这个方法因此不存在覆盖的概念。 Kevin这里其实就是说假如父类 A 和子类 B 放到同一个包中调用类放在另一个包中本来调用类是无法访问 A 类的缺省方法的但 B 继承并覆盖该缺省方法将其访问修饰符修改为 public 的话就有可能将 A 类的该方法暴露出去。 public class A{public void sayHello(String name){System.out.println(Hello name !);} }public class B extends A{public void sayHello(String uname){ // 覆盖返回值类型要和父类一模一样方法签名也要一致形参名相同与否倒无所谓方法签名相同但返回值类型不是一模一样的话会报错。访问修饰符的权限要大于或等于父类的访问权限指要更开放或同等开放程度System.out.println(Hello uname , welcome!);} }覆盖的前提是子类继承了父类的该方法如果本身父类的这个方法就不对子类可见那么子类并没有继承该方法那么创建同方法签名的方法时实际上是在子类中增添了一个和其他普通的方法没有区别的新方法而已并不是覆盖父类的方法。如果是这样的话就不存在对方法签名和返回值类型相同的限制。 这里注意所谓的不可见是整条继承链上的不可见。比如说 类继承链靠左为靠右的父类A、B、C、D。调用类 TestUse 如果 A、B、C 类在同一个包D 在另一个包A、B、C 类存在一个覆盖方法 buy()是缺省的访问修饰符号那么D类实际上因为看不到 buy()这个方法所以没有继承到这个方法因此 D 中定义 buy()是等于在添加一个新方法而不是在做方法覆盖因此没有返回值类型的限制。 如果这条链中有一个 类 和 D 是在同一个包比如说 B 类那么 B 类因为也没有能继承 A 类的该方法因为看不到 A 类的该方法所以 B 类的 buy()是个新方法哪怕 C 类看不见 B 类的这个新方法也就也没有继承即便 C 类和 A类在同一个包中C 类就是没有继承到 buy()因为从继承链来说buy()没有被继承到 B往后就更加不会有这个方法继承下去。所以导入 C 类也是无法用 C 类实例调用 buy()。而 D 类中又定义了 buy()那这个算是 D 新增添的方法么因为 C 类没有这个方法嘛。但实际上并不能。只要是在同一条类继承链中又能看到这条链上的其他同签名方法那么就得遵循返回值类型也要相同的覆盖规则。不能说 B 没继承到buy()是 B 自己新添加的而 D 也没有继承到 buy()是 D 自己新添加的。D要有同签名的方法 buy() 就只能让返回值类型和前面的 B 的buy()返回值类型一模一样并且并不能妄图将 D 的buy()访问修饰符改为 private 来躲避发现。因为覆盖的访问修饰符只能开放更大的权限而不能缩小倒是可以把 B 的buy()设置为 private这样 D 等于看不到 B 的 buy()不存在要遵循覆盖规则的问题 Kevin简而言之一条类继承链上的某个方法即便中间出现继承中断只要继承链上的两个类后代类能看到前辈类的同方法签名的方法就得把返回值类型设置成一样的否则就会报错。 Kevin其实最简单规避这些特殊容易出错的情况的方式就是将子类和父类放到不同包中让子类只继承父类的公共public方法/属性。在使用同签名方法时返回值一律都设置成相同类型。这样就能避免错乱并且从系统设计的角度同方法签名的父类子类的方法就应该是返回值类型相同的。 IDEA TIPS 当 A 方法对于子类 B 是可见的不是 private 或修饰符缺省却在同一个包或 public 时IDEA 才显示 A 和 B 该方法的覆盖跳转标志代码左侧行号会有一个蓝色圆圈标志点击会跳转到其覆盖方法或被覆盖方法。 关于连续继承链中的方法覆盖问题 IDEA TIPS 如果是继承关系是C 继承 BB 继承 A然后A中的方法 buy() 被 B 的 buy() 覆盖了并且 C 的 buy() 再次把 B 的 buy() 覆盖了那么 C 的 buy() 代码左边是“蓝色圆圈向上箭头”点击会先跳转到 B 类的buy() 而 B 的 buy() 左边是两个标志“蓝色圆圈向上箭头” 和 “蓝色圆圈向下箭头”点击下箭头会跳回 C 的 buy()点击上箭头会跳转到 A 的 buy()。而 A 的 buy() 左边是“蓝色圆圈向下箭头”点击会让你选择到底是跳转到 B 的 buy() 还是 C 的 buy() 。 疑问1 继承链靠左为父类A、B、C 都有 buy()方法B、C 都对 A 中的 buy() 进行覆盖那么 B b new B();,b调用 buy() 时会不会受到子类中的覆盖方法影响 经过实验不会。 public class A{public void buy(){System.out.println(调用的是 A 的 buy()。);} } public class B extends A{public void buy(){System.out.println(调用的是 B 的 buy()。);} } public class C extends B{public void buy(){System.out.println(调用的是 C 的 buy()。);} }// 调用类 public class TestUse{public static void main(String[] args){B b new B();b.buy(); // 调用的是 B 的 buy()。} }多态 子类对父类的方法进行覆盖就实现了同一个方法有不同的行为这就是所谓的“多态”。 Override 覆盖方法可以在方法的上方标注“Override”注意 O 要大写这是一种[[…/…/基础语法/注释|注解]]。但也可以不加不加不会影响编译运行。 加了的好处 易读 增加代码的易读性更容易区分哪些方法是普通方法哪些方法是覆盖父类的方法。语法检查 加了之后java 会帮你检查当前方法到底是否属于覆盖方法。因为有时候写代码的时候会把要覆盖的方法的方法签名搞错如果不加Override程序不会报错java 会认为你是在子类中新定义了一个方法而非覆盖父类的方法但你自己却以为覆盖了父类方法。如果加了Overridejava 会帮你确认这个是否是覆盖了父类的方法如果不是比如说你方法签名搞错了java 就会给你报错java: 方法不会覆盖或实现超类型的方法让你编译运行不成功。 public class A{public void sayHello(String name){System.out.println(Hello name !);} }public class B extends A{Overridepublic void sayHello(String uname){System.out.println(Hello uname , welcome!);} }构造方法无法覆盖 Kevin突发奇想子类能否定义构造方法将父类同方法签名构造方法覆盖。 public class A{public A(){ // 即便不在父类 A 中显式定义构造方法也一样实验结果不变。} } public class B extends A{public A(){ // 报错方法声明无效; 需要返回类型。这是因为如果和类名不同就只能是一个普通的方法就得有返回值类型。} }// 这里另外一个可能就是覆盖本身就是要求方法签名相同且返回值类型一模一样。构造方法根本就不能写返回值类型缺少了这个条件。而如果你非得在子类中定义一个如下的方法 public class B extends A{public A A(){ return new A();} } // 也没有意义。语法是不报错了但本身就不是构造方法B b new B() 时并不会自动调用仅仅是添加了一个 b.A() 返回一个 A 实例对象的普通方法罢了。super和父类对象沟通的桥梁 子类对象里可以认为有一个特殊的父类的对象这个父类对象和子类对象之间通过 super 关键字来沟通。 使用 super 可以调用父类的方法和属性当然必须要满足访问控制符的控制。 设计上父类的属性尽可能还是不要让子类能公开访问使用 语法上 super 的确可以调用父类的属性但程序设计上不应该把父类的属性设置成可以被子类直接使用而应该将父类的属性都设置为 private。由于父类属性设置成 private 就不会被子类继承子类应该自己定义自己的属性如果非得用到父类的属性比如说调用父类的方法时会用到父类的某些属性也得是通过调用父类的方法super.xxx()来访问父类的属性而非让子类直接就能 super.父类属性 来获取父类属性。 super 到底指哪个父类的实例 注意 super.方法()比如说 super.buy() 的 super 不一定是指这个子类的直接父类可以是继续往前推只要是在这条类继承链上的任何一个父类。而如果说不只有一个 buy()呢比如说发生过覆盖、重载之类那就看当前子类它能访问到哪个父类的这个 buy() 方法如果发生了覆盖就是指覆盖后的那个反正就是把所有父类看作一个整体去调用父类的方法如果发生了覆盖那么肯定只认“最新版本”也就是覆盖后的方法如果是重载就选相应的重载方法如果 A、B、C、D、E 类这样继承下去当前子类为 E只有 A 才有定义 buy() 方法并且 E 能访问到该方法那么 E 中调用的 super.buy()就是指 A 中的 buy()。 public class A{public void buy(int count){xxx;} }public class B extends A{public void sayHello(){System.out.println(Hi!);} }public class C extends B{public void VipBuy(boolean isVIP){xxxx;super.sayHello(); // 用 super 调用父类(B)的 sayHello() 方法xxx;super.buy(3); // 用 super 调用父类(A)的 buy() 方法 xxxxx;} } super 严格意义上并非真的是一个父类的引用 super 虽然看似一个父类的引用/父类的实例但严格意义上super 并不是父类的引用只是它起到了接近是父类的引用的效果但本质上它不是一个真正的父类引用/父类的实例。super 和 this 自引用自引用是真的是自己类的一个如假包换的实例是不同的不是可以简单模拟的。只不过是 super 的确能调用父类的可见的通常就是 public的方法、属性。 其实如果 super 真的是一个如假包换的父类引用那就不是继承了更像是之前提到的“组合”方式。 下面验证一下 this 和 super 本质的不同。this 是真的就是一个实例但 super 并不是一个真正的实例。 public class A{}public class B extends A{public B tryCreateB{return new B(); // 这是没有任何问题的返回一个新的 B 实例}public B tryReturnB{return this; // 这里也没有任何问题返回当前的 B 实例}// public A tryCreateA{// return super; // 不注释掉的话都不需要运行这么写 IDEA 就会提示要在super后加上.xxx强行运行会报错。// } }// 调用类 public class TestUse{public static void main(String[] args){B b new B();System.out.println(b);System.out.println(b.tryCreateB());System.out.println(b.tryReturnB());// b.tryCreateA(); // 去除注释强行运行会报错。} }当然在实际使用中可以近似地理解为创建子类对象的时候同时创建了一个隐藏的父类对象。 如果说 java 创建一般的类的对象是创建了一个能记录数据的小本子然后放到公告板上那么创建子类的对象同样也是创建了一个小本子但除了这个小本子还隐藏地创建了它之前所有的父类继承链上的所有的父类的对象的小本子然后把这些父类的小本子和当前子类的小本子装订到一块作为一个新的小本子放到公告板上。 当然这只是可以这么理解。因为这些父类的小本子实际上无法单独拿出来但你用 super.xxx 访问父类属性时这些数据是肯定有地方存储的就是在这些父类小本子上。 super 调用父类的构造方法 使用 super 调用父类的构造方法必须是子类构造方法的第一个语句。 同样地 super 调用构造方法不可以使用 super 访问父类的属性和方法不可以使用子类成员变量和方法。 super()调用构造方法时也是调用 init方法和普通类调用构造方法时会调用 init 方法一样。 // 不可以使用 super 访问父类的属性和方法 super(super.getName()); // 不合法不可以在super调用构造方法时使用super访问父类的方法/属性语法 public class A{}public class B extends A{public B(){super();} }这里可以回想起在构造方法里调用重载的构造方法this(参数列表)时必须将调用写在方法体的第一行后面可以继续有代码。 那么这里就和也同样要写在第一行的 super(参数列表) 发生冲突了。实际上如果同时出现 this() 和 super()不管谁在第一行谁在第二行都是非法的就很无解了。要如何解决呢可以另外创建一个方法比如叫 init()方法名随便起把一些公共的初始化的东西放进去。 public class A{}public class B extends A{public B(){super();init();}public B(int count){init();}public void init(){xxxx;} }为什么 java 要制造这种冲突呢因为如果允许在构造方法中同时使用 super() 和 this() 的话比如说下面当然是非法的现在假设它合法就可能会出现构造一个子类对象 super() 被调用不止一次的情况显然是有问题的。 public class A{}public class B extends A{public B(){super();this(3);}public B(int count){super();}}子类构造方法被调用时Java 会主动去调用父类的无参构造方法 子类调用构造方法时实际上一定会调用父类的构造方法不管你有没有显式地在子类的构造方法中写入 super()。 如果子类构造方法中的第一行没有写 super(参数列表)即默认缺省的情况下java 实际上就会去找父类中没有任何参数的构造方法去调用。如果父类中没有不带任何参数的构造方法就会报错。 public class A{public A(){System.out.println(Creating an A objcet...);}public A(int number){System.out.println(Creating number A objcets...);} }public class B extends A{public B(){System.out.println(Creating a B object...);}public B(int count){System.out.println(Creating count B objects...);} }// 调用类 public class TestUse{public static void main(String[] args){B b new B(); } } // 输出两行 // Creating an A objcet... // Creating a B object...// 若改为 public class TestUse{public static void main(String[] args){B b new B(3); } } // 输出两行 // Creating an A objcet... // Creating 3 B object...// 说明的确会自动在 B 的构造方法中第一行插入无参数的 super();// 若此时将 A 类的无参数构造方法注释掉或直接删掉而保留 A 类的带参数自定义的构造方法就等于 java 不会给 A 自动添加无参数构造方法A直接就没有了无参构造方法那么无论调用的是 B b new B(); 还是 B b new B(3); 都会报错。如果想不报错需要在 B 的构造方法中显式地在第一句中调用 A 中的带参数构造方法。并且要强调的是是得B中所有的构造方法都得明确写入 A 中的带参数构造方法不要以为只在你打算调用的构造方法中写就行了只要有一个没写就报错public class B extends A{public B(){super(10); System.out.println(Creating a B object...);}public B(int count){super(30);System.out.println(Creating count B objects...);} }// 即便你用的是 B b new B(); 而非 B b new B(10); 也需要在 public B(int count) 方法的第一行加入 super(参数值)而不是说你不用哪个构造方法就可以不用管编译运行反正只要漏了其中一个没显示加入super(xxxx)都会报错。如果父类中没有缺省的构造方法无参的构造方法那么子类的所有构造方法中都必须在第一行调用父类有参数的构造方法。 疑问1 “super 调用构造方法不可以使用 super 访问父类的属性和方法不可以使用子类成员变量和方法。”这句话怎么理解 public class A{public int count 3;public A(int number){} }public class B extends A{public int age 1;/* 方式1// 调用子类成员属性public B(){ super(this.age); // 报错无法在调用超类型构造器之前引用this}若改为 agepublic B(){ super(age); // 报错无法在调用超类型构造器之前引用age}// 调用父类属性public B(){super(count); // 报错无法在调用超类型构造器之前引用count}若改为public B(){super(this.count); // 报错无法在调用超类型构造器之前引用this}若改为public B(){super(super.count); // 报错无法在调用超类型构造器之前引用super}*//* 方式2public B(){super(3);this.age 2;System.out.println(this.age); // 2 不报错this.count 10; // 这里即便是 super.count 10; 下面两行的输出也是一样的System.out.println(this.count); // 10不报错System.out.println(super.count); // 10不报错}*/}因此和普通类的构造方法中类似普通类中调用构造方法/调用重载构造方法this()时不能使用成员属性/成员方法但在构造方法体内是可以使用成员属性/成员方法的。 super()调用父类构造方法时也一样只是说不能将父类的方法/属性、子类的成员方法/属性作为 super() 的参数传入这样会报错。但可以在子类的方法体内调用父类的方法/属性、子类的成员方法属性。 父类引用和子类引用的关系 这里最想传达的一个观点就是一个自定义类型的引用是可以指向这个类型的对象、或这个类型的子类或子类的子类…的对象。 这有什么用呢 比如说面对现实的问题对人类来创建一个类其实例就是一个普通人对象。人类这个类可以再分创建子类分成亚洲人类、非洲人类、欧洲人类、美洲人类等然后比如亚洲人类还可以继续再分再创建子类分成中国人、日本人、韩国人等中国人又可以再分创建子类为不同省份或不同民族的类别。而此时实际上可以直接用最开始的父类——人类的引用指向任意一个子类的对象比如说亚洲人、中国人、广东人… 这又有什么用呢 如果这些父类、子类中都有覆盖一个方法比如说这些类中都各自定义了方法签名和返回值类型一模一样的方法 buy()那么同样是人类的引用比如说Human human new 子类();调用这个覆盖方法时human.buy();实际调用的是其对应的子类中的那个 buy() 方法。这就等于说同样调用一个方法但是因为父类引用指向了不同的子类对象会有不同的行为执行不同子类中的覆盖方法。这其实就是“多态”。 IDEA 中提供了查看类的继承链的功能菜单栏 Navigate -- Type Hierarchy (ctrl h) 父类引用可以指向子类对象子类引用不可以指向父类的对象。 父类引用可以指向子类对象。 更进一步即可以用子类以及子类的子类的引用给父类的引用赋值。即继承链靠左为父类A、B、C。允许A a new C();跨越 B 因为子类继承了父类的方法和属性所以父类的对象能做到的子类的对象肯定能做到。换句话说我们可以在子类的对象上执行父类的方法。当父类的引用指向子类的实例或者父类的实例只能通过父类的引用像父类一样操作子类的对象。也就是说“名”的类型决定了能执行哪些操作。 // 父类引用可以指向子类对象 FatherClass a new SonClass(); // 正常编译运行不会报错。反过来子类引用指向父类对象则不行。因为父类并没有子类的属性和方法。父类对象无法执行子类的这些方法、访问子类的这些属性。 更进一步父类以及父类的父类的引用不能给子类的引用赋值。除非这个父类的引用指向的对象本来就是一个子类的对象但即便是这样也不能直接给子类的引用赋值而还需要通过强制类型转换才能赋值给子类的引用。 疑问1 但是父类引用指向子类的话子类对象也没有父类本身的私有属性啊。 有这个问题其实是对调用其他类的场景还比较模糊。 实际上比如说 A 类是父类B 类继承自 A 类B 类是 A 类的子类那么真的用 A、B类去创建对象的实际上并不是 A、B类自己写个 public static void main 方法来做这些事而是第三个类一个调用它们的类。那么对于这个类来说通常也不和 A、B类在同一个包中所以需要导入 A、B 类 用 A 或 B 的构造方法创建对象然后调用相应的方法。但导入类且处于不同包中本身能导入的也就只有 public 的公开出来的方法和属性。所以其实理解上基本只要去考虑父类与子类的 public 属性和 public 方法。 而的确从继承的角度来看子类就是继承了父类的所有 public 的方法和属性除此之外子类还可能增添自己独有的 public 方法和属性。 因此的确就是 父类的对象能做到的子类对象肯定能做到。子类对象上可以执行父类的方法。而父类没有子类的方法父类对象无法执行子类的方法。 因此就有父类引用可以指向子类对象子类引用不可以指向父类的对象。 // 子类引用不能指向父类对象 SonClass b new FatherClass(); // 非法会报错不兼容的类型FatherClass 无法转换为 SonClass如何理解一个类型的引用 Kevin一个类型的引用也就是说“名”的类型int a就是 a 这个引用的“名”是 inta 是 “int 类型” 的引用应该是能执行这个类型的成员特有的方法访问/修改这个类型的成员的特有属性的。即引用的类型决定引用可以调用的方法、属性。 更进一步如果一个子类的对象同时被父类引用 a 和子类引用 b 指向那么虽然它们指向的是同一个对象a就是只能调用父类的方法而b则是可以调用子类的方法因为继承问题也包含了父类的方法如果 a 也想执行子类的方法则需要先进行强制类型转换将引用类型转变成该子类才能调用子类的方法((SonClass)a).子类方法()。 所以假设 SonClass 继承自 FatherClass父类的引用不论指向的是父类对象还是子类对象它能执行的都是父类的方法这里要思考一下是如果指向的是子类对象执行的是否是子类覆盖后的方法访问、调用的也是父类的属性。 经过实验FatherClass a new SonClass(); 父类引用指向子类对象时如果子类覆盖了父类的某个方法那么用这个父类引用 a 调用这个方法时调用的实际上是子类截止至该子类的覆盖后的方法版本而不会管这个子类之后还有没有子类对这个方法有更新的覆盖方法版本。 但如果是子类不是覆盖该方法而是增加了这个方法的一个重载方法父类引用是无法调用这个重载方法的强行调用会报错。 如果继承链是靠左为父类 A、B、CB、C 都对同一个 A 中的方法.buy()进行了覆盖那么 A a new B(); 后a.buy()会不会因为多了一个子类 C 的覆盖方法.buy()而调用之而不是调用 B 中的覆盖方法.buy()呢 经过实验并不会。实际上 public class A{public void buy(){System.out.println(这是 A 的 buy。);} } public class B extends A{public void buy(){ // B 中 覆盖一次System.out.println(这是 B 的 buy。);} } public class C extends B{public void buy(){ // C 中再次覆盖System.out.println(这是 C 的 buy。);} }public class D extends B{ // D 也继承自 Bpublic void buy(){ // D 中再次覆盖System.out.println(这是 D 的 buy。);} }A a new B(); a.buy(); // 调用的是 B 的覆盖方法 buy()A a2 new C(); a2.buy(); // 调用的是 C 的覆盖方法 buy()A a3 new D(); a3.buy(); // 调用的是 D 的覆盖方法 buy()// 这就很有意思了这个父类引用调用同一个方法实际上调用的实际类型对象中定义的覆盖方法。而不管这个类有没有子类、“兄弟类”、“亲戚类”不管其他类中有没有进一步地覆盖这个方法。 // 这种设计就叫多态。而经过实验父类引用a也的确不能调用子类中添加的新方法强行调用子类有但父类没有的方法会报错。而父类引用 a 指向子类对象调用父类的方法是没有问题的。 如果父类引用比如叫a指向子类对象临时又想调用一下子类新增加的方法则可以用强制类型转换的方式将a转成成子类引用然后调用子类新增加的方法 FatherClass a new SonClass();// 强制类型转换 ((SonClass)a).Sonclass新增的方法();对于引用类型的“名”决定它的行为打个不太恰当的比方 唐王李渊和他的儿子李世民李世民的心腹部将也在朝里为官打着李渊的旗号作为朝臣他有这个朝臣应该行使的职权李渊赋予的职权。 同样地李世民的心腹部将在李世民的秦王府里打着李世民的旗号也有相应的职位行使秦王府里的相应职权。 但如果这个臣子哪怕是李世民的心腹部将站的李渊的队还打着李渊的旗号从这个朝堂上职权上看他就无法管理秦王府的内务无法行使秦王府里的职权。他得重新打上李世民的旗号前提得是能转打李世民的旗号如果本身不是李世民的心腹部将自然不允许你转打李世民的旗号才能行使在秦王府里的职权。 这叫“在其位才能谋其政”你得打着某一边的旗号才能行使那一边的职权。 一个类型的引用可以说是对应一种官职名称以及其所能行使的职权。 如何理解一个类型的对象 如果把一个类型的引用比喻成一种官职名称以及其所能行使的职权那么一个类型的对象就是真的具备行使这些职权能力的人。 打个不太恰当的比方 唐王李渊的朝臣的官位对应了他在朝堂上的官职也对应了相应可以行使的职权。 同样李世民秦王府中的职位也对应了在秦王府中可以行使的职权。 对象就是实实在在的个体到底是李渊的朝臣而非李世民的心腹还是李世民的心腹部将。 你可以让李世民的心腹部将打李渊的旗号唐朝臣子在朝堂上行使职权他也有这样的能力。 但你不能让李渊的朝臣非李世民的心腹去秦王府行使职权因为他不是李世民的心腹部将挂名本身就不合法更加不懂得如何打理秦王府他没这个经验也没有这个能力。 强制类型转换 null 可以强制类型转换成任何引用类型这是合法操作不会报错。这是因为 null 是所有引用类型的缺省值 实际测试过其实 null 不用强制转换成该引用类型也是可以直接赋值给该引用类型的。不管是自定义引用类型还是 String 这类 java 本身定义的引用类型。 如果确定一个父类的引用指向的是子类对象那么可以使用强制类型转换把这个父类引用指向的对象实际上是子类对象赋值给一个子类的引用 但如果一个父类引用指向的就是一个父类对象则无法将这个父类引用指向的对象实际上是父类对象赋值给一个子类的引用。 打个不太恰当的比方唐王李渊和他儿子李世民李世民的心腹部将当然是可以打着李渊的旗号的毕竟领着唐朝的俸禄但这些实际上是李世民的心腹部将可以随时转到李世民的旗下。但如果这个打着李渊旗号的臣子本身就不是李世民的心腹自然不能转到李世民的旗下。否则李世民小团伙关起门来策划玄武门之变的时候不就有被忠于李渊的臣子举报的风险了嘛 (ε(#) 哇哈哈哈哈 public class A{} public class B extends A{}// 调用类 public class TestUse{public static void main(String[] args){// 实际子类对象赋值给子类引用A a1 new B();B b11 a1; // 报错不兼容的类型A 无法转换为 BB b12 (B)a1; // 合法正常编译运行不会报错。// 实际父类对象赋值给子类引用A a2 new A();B b21 a2; // 报错不兼容的类型A 无法转换为 BB b22 (B)a2; // 报错A 不能被映射成 B} }多态 能够调用哪些方法取决于引用类型。但具体调用的是哪个方法取决于实例是什么类型。 父类的引用指向不同类型的对象调用某个覆盖方法时实际执行的方法取决于对象的类型而非引用的类型。 即能调用哪些方法是引用决定的具体执行哪个类的覆盖方法是引用指向的对象决定的。 这就是覆盖的精髓。覆盖是多态的一种并且是最重要的一种。 当我们用父类的引用指向子类的实例并调用某个方法时实际调用的是子类的该方法如果子类里没有覆盖这个方法就去父类里找如果父类里也没有就去父类的父类里找… 如果能让一个引用指向这个对象就说明这个对象肯定是这个引用的类型或者其子类的一个实例否则赋值会发生ClassCastException。 疑问1 如果继承链靠左为父类A、B、C。A中有buy()方法里面还调用了sayHi()方法B、C 中也分别对 buy() 进行了覆盖并且在自己的buy() 调用了父类的buy()会出现什么情况如果 A 中还有一个sayHi()方法C 中也覆盖了这个 sayHi()方法并在C 的 buy()中调用了 sayHi() 会发生什么 public class A{public void buy(){System.out.println(调用的是 A 的 buy()。);this.sayHi();}public void sayHi(){System.out.println(Hi, 这里是 A 的 HI。);} } public class B extends A{public void buy(){super.buy();System.out.println(调用的是 B 的 buy()。);} } public class C extends B{public void buy(){super.buy();System.out.println(调用的是 C 的 buy()。);this.sayHi(); // 如果注释了这一行也不会影响 A a new C();a.buy();的第二行输出结果}public void sayHi(){System.out.println(Hi, 这里是 C 的 HI。);} }// 调用类 public class TestUse{public static void main(String[] args){A a new C();a.buy();} }// 输出结果 /* 调用的是 A 的 buy()。 Hi, 这里是 C 的 HI。 // 这行很意外吧对的当实例是C时它连这个A的buy()里面调用的sayHi()也替换为了C的sayHi()。 调用的是 B 的 buy()。 调用的是 C 的 buy()。 Hi, 这里是 C 的 HI。*/// 调用类 public class TestUse {public static void main(String[] args) {A a new B();a.buy();} } // 输出结果 /* 调用的是 A 的 buy()。 Hi, 这里是 A 的 HI。 调用的是 B 的 buy()。*/结论 对一个实例执行某个方法时不管这个方法还调用了哪些方法这些凡是有被覆盖的方法都选用覆盖方法的版本从最先的父类沿着继承链到当前继承类[含]为止的最新覆盖版本进行替换。 疑问2 如果调用的是 this那么 this 是指定义那个方法包含this的类还是对象所属的类呢 public class A{public void buy(){System.out.println(调用的是 A 的 buy()。);this.sayHi();}public void sayHi(){System.out.println(Hi, 这里是 A 的 HI。);}public void returnThis(){ // 增加返回 this 对象的函数System.out.println(返回一个this对象 this);}public void thisHi(){ // 增加包含用 this来调用函数 的函数this.sayHi();} } public class B extends A{public void buy(){super.buy();System.out.println(调用的是 B 的 buy()。);} } public class C extends B{public void buy(){super.buy();System.out.println(调用的是 C 的 buy()。);this.sayHi();}public void sayHi(){System.out.println(Hi, 这里是 C 的 HI。);} }// 调用类 public class TestUse {public static void main(String[] args) {A a new A();A b new B();A c new C();System.out.println(a的调用结果);a.returnThis();a.thisHi();System.out.println(b的调用结果);b.returnThis();b.thisHi();System.out.println(c的调用结果);c.returnThis();c.thisHi();} }// 输出结果 /* a的调用结果 返回一个this对象 org.test.extendstest.polytest.A3d075dc0 // 可以看到是 A 类对象 Hi, 这里是 A 的 HI。 b的调用结果 返回一个this对象 org.test.extendstest.polytest.B214c265e // 可以看到是 B 类对象 Hi, 这里是 A 的 HI。 // B类对象中能调用到的是A类继承过来的sayHi而调用不到C的覆盖sayHI() c的调用结果 返回一个this对象 org.test.extendstest.polytest.C448139f0 // 可以看到是 C 类对象 Hi, 这里是 C 的 HI。 // C类对象能调用到的是C类的覆盖sayHI()*/结论 this 也是指当前的对象哪怕 this 出现在父类的调用方法中仍然是指当前的对象。即使是在继承自父类的代码里去调用一个方法包括含有 this的方法也是先从子类开始一层层继承关系地找。 其实回想一下在一个类中调用自己的方法是可以直接写方法名的但实际上隐含了 this.方法名()前面的 this.。所以在父类方法 methodA() 中调用父类的方法methodB()实际上是 this.methodA() 中调用 this.methodB()而 this实际上是当前对象如上例 ”疑问1“ 中的子类对象 B、C 的对象所以就能解释为什么会先从子类方法中找覆盖方法找不到就往上一级的父类里找再找不到再往上上一级找了以及为什么“在父类方法 methodA() 中调用父类的方法methodB()时连里面的 methodB()”也被替换为当前子类对象的覆盖方法因为methodB本质上也是带this.的this.methodB所以是要从当前子类对象开始找。 无论一个方法是使用哪个引用被调用的它都是在实际的对象上执行的。执行的任何一个方法都是这个对象所属的类的方法。如果没有就去父类找再没有就去父类的父类找依次寻找直到找到。 这也是 java 选择单继承的重要原因之一。以为在发生继承时多态已经很复杂了如果存还在多继承那么多继承的情况下如果使用不当多态可能会非常复杂以至于使用的代价超过其带来的好处。 如果存在多继承一个类继承自几个父类一个类有多个父类那么当前的方法到底调用哪个父类的方法这就很复杂了。 静态多态重载Overload 覆盖 vs. 重载 覆盖 用这个引用类型来调用方法并且注意覆盖需要“方法签名相同返回类型相同” 重载 调用别的方法时用这个类型引用作为参数并且注意重载仅仅需要“方法签名相同” 重载调用哪个方法和参数的引用类型相关和引用实际指向的类型无关。 注意这三个概念形参引用类型、实参引用类型、对象类型 重载看的是“实参引用类型”到底和“形参引用类型”匹配调用那个类型匹配的方法。 public class A { } public class B extends A{ } public class C extends B{ }// 重载方法的类 public class MethodTest {public void sayHi(A aObject) {System.out.println(调用的是 A 类型重载的 sayHi() aObject);}public void sayHi(B bObject) {System.out.println(调用的是 B 类型重载的 sayHi() bObject);}public void sayHi(C cObject) {System.out.println(调用的是 C 类型重载的 sayHi() cObject);}// 重载的参数类型相同位置不一定要有继承或者兼容的关系完全可以 free style比如说定义为 String 或 int。public void sayHi(String str) {System.out.println(调用的是 String 类型重载的 sayHi() str);}public void sayHi(int abc){System.out.println(调用的是 int 类型重载的 sayHi() abc);} }// 调用类 public class TestUse {public static void main(String[] args) {A a new C();MethodTest methodTest new MethodTest();methodTest.sayHi(a);methodTest.sayHi((B)a);methodTest.sayHi((C)a);methodTest.sayHi((String)null); // 这里之所以要将 null 强制转为 String是因为多个重载方法参数都是引用类型你直接用 null 为实参java 不知道具体调用哪个重载方法。如果本身重载方法就只有形参为String与int两个类型而没有上面的A、B、C自定义引用类型则可以直接 null而不用强制转换为 String。methodTest.sayHi(3);methodTest.sayHi((A)null); // 这3个调用就更加极端了进一步说明调用哪个方法与实际指向对象无关methodTest.sayHi((B)null); // 而只与实参类型有关。methodTest.sayHi((C)null); //} } // 输出 /* 调用的是 A 类型重载的 sayHi() org.test.extendstest.polyoverloadtest.C214c265e 调用的是 B 类型重载的 sayHi() org.test.extendstest.polyoverloadtest.C214c265e 调用的是 C 类型重载的 sayHi() org.test.extendstest.polyoverloadtest.C214c265e 调用的是 String 类型重载的 sayHi() null 调用的是 int 类型重载的 sayHi() 3 调用的是 A 类型重载的 sayHi() null 调用的是 B 类型重载的 sayHi() null 调用的是 C 类型重载的 sayHi() null*/// 可以看到同样是一个实际为 C 类的对象根据指向它的引用类型不同调用的重载方法也不同。疑问1 如果存在一个继承链靠左为父类A、B、C一个方法并没有同时定义三个重载方法分别以A、B、C类型为参数而是只定义了 B类型为形参的方法那么分别传入 A、B、C类型的实参会怎样 public class A { } public class B extends A{ } public class C extends B{ }// 重载方法的类 public class MethodTest { // public void sayHi(A aObject) { // System.out.println(调用的是 A 类型重载的 sayHi() aObject); // }public void sayHi(B bObject) {System.out.println(调用的是 B 类型重载的 sayHi() bObject);}// public void sayHi(C cObject) { // System.out.println(调用的是 C 类型重载的 sayHi() cObject); // }public void sayHi(String str) {System.out.println(调用的是 String 类型重载的 sayHi() str);}public void sayHi(int abc){System.out.println(调用的是 int 类型重载的 sayHi() abc);} }// 调用类 public class TestUse {public static void main(String[] args) {A a new C();MethodTest methodTest new MethodTest();methodTest.sayHi(a); // 报错methodTest.sayHi((B)a); // 调用的是 B 类型重载的 sayHi() org.test.extendstest.polyoverloadtest.C214c265emethodTest.sayHi((C)a); // 调用的是 B 类型重载的 sayHi() org.test.extendstest.polyoverloadtest.C214c265e // methodTest.sayHi((String)null); // methodTest.sayHi(3);} }// 若重载方法类为 public class MethodTest {public void sayHi(A aObject) {System.out.println(调用的是 A 类型重载的 sayHi() aObject);}public void sayHi(B bObject) {System.out.println(调用的是 B 类型重载的 sayHi() bObject);}// public void sayHi(C cObject) { // System.out.println(调用的是 C 类型重载的 sayHi() cObject); // }public void sayHi(String str) {System.out.println(调用的是 String 类型重载的 sayHi() str);}public void sayHi(int abc){System.out.println(调用的是 int 类型重载的 sayHi() abc);} } // 调用类的输出结果不变 public class TestUse {public static void main(String[] args) {A a new C();MethodTest methodTest new MethodTest();methodTest.sayHi((B)a); // 调用的是 B 类型重载的 sayHi() org.test.extendstest.polyoverloadtest.C214c265emethodTest.sayHi((C)a); // 调用的是 B 类型重载的 sayHi() org.test.extendstest.polyoverloadtest.C214c265e} }这说明“重载”仍旧只看实参类型与哪个形参相匹配就调用哪个方法但如果都不匹配的话则尝试将该实参类型强制转换看是否有能匹配的方法以供调用并且优先匹配类继承链上与该实参类型相近的父类如果不满足则再往上一级父类转换。 但父类无法向子类进行转换以匹配子类为形参的重载方法。 静态多态调用的方法和实参实际指向的对象无关只与实参引用本身的类型相关。因为调用时参数类型是确定的所以在编译期间就可以明确地知道哪个方法被调用。如果存在多种可能则会有编译错误。 确定调用哪个方法只需要引用的类型这叫做静态多态。即在编译期就知道该调用哪个方法。 如果引用类型没有完全匹配的则会根据类继承关系沿着参数当前类型往“根”父类方向转换以找到最贴近的参数类型匹配方法。 这里再次表明java 单继承是多么的必要如果是多继承压根就不知道往哪条继承链上去就近转换类型匹配重载方法整个程序设计就很容易搞得特别复杂。有些编程语言就是多继承的比如c 无论是静态方法还是成员方法重载寻找方法的顺序是一样的。这里就不赘述了。 动态多态覆盖Override 覆盖的核心寻找调用哪个方法。 两个角色 引用的类型调用的方法签名必须是这个类型中定义了的引用实际指向的对象它决定是通过这个签名调用的实际是哪个类的方法。 public class A {public void sayHi() {System.out.println(调用 A 定义的 sayHi this);}public void sayHi(int num) {System.out.println(调用 A 定义的带参数 sayHi num this);} } public class B extends A {public void sayHi() {System.out.println(调用 B 定义的 sayHi this);} } public class C extends B{public void sayHi() {System.out.println(调用 C 定义的 sayHi this);} }// 调用类 public class TestUse {public static void main(String[] args) {A a new C();a.sayHi(); // 覆盖调用 C 定义的 sayHi org.test.extendstest.polyoverloadtest.C3d075dc0// 实际对象为 C 类型但 C 中没有定义重载的 sayHi(int)向上找B中也没有那么就是继承自 A 的 sayHi(int)a.sayHi(3); // 重载 调用 A 定义的带参数 sayHi 3 org.test.extendstest.polyoverloadtest.C3d075dc0A a2 new B();a2.sayHi(); // 覆盖调用 B 定义的 sayHi org.test.extendstest.polyoverloadtest.B7b23ec81} }// 输出结果 /* 调用 C 定义的 sayHi org.test.extendstest.polyoverloadtest.C3d075dc0 调用 A 定义的带参数 sayHi 3 org.test.extendstest.polyoverloadtest.C3d075dc0 调用 B 定义的 sayHi org.test.extendstest.polyoverloadtest.B7b23ec81*/Kevin对多态的总结 静态多态重载 不同的类型的引用与引用指向的对象无关作为参数匹配到的重载方法不同。 动态多态覆盖 不同的对象与引用类型无关调用同方法签名的方法实际调用是类继承链中不同类的覆盖方法。 勿忘初心程序的执行就是找到要执行的代码并且知道执行的代码能访问哪些数据数据从哪里来。 多态核心问题就是要调用哪个类的哪个方法这个方法用到的数据this 引用是谁。 继承里的静态方法 继承里的静态方法能实现类似覆盖的效果也和覆盖一样遵循 “方法签名相同且返回值类型要一模一样” 的要求但实际上这不能称作覆盖。因为覆盖最重要的一点是需要根据实际的对象类型来选择实际执行的方法但静态方法到底选择哪个类里面的同签名静态方法不取决于实际对象的类型而是指向这个对象的引用的类型。 而且虽然静态方法可以用指向对象甚至 null的引用来调用但最正确的调用方式还是应该用类名来调用静态方法。 // 方法签名不同时返回值类型可以不同因为这是重载不是覆盖。 public class A {public static double sayHi(){System.out.println(调用的是 A 中定义的 sayHi);return 1;} } public class B extends A{} public class C extends B{public static int sayHi(int abc){ System.out.println(这里调用的是 C 中定义的 sayHi);return 1;} }// 调用类 public class TestUse {public static void main(String[] args) {A c1 new C();c1.sayHi(); // 调用的是 A 中定义的 sayHiC c2 new C();c2.sayHi(); // 调用的是 A 中定义的 sayHi 说明 C 的确能继承 A 中的静态方法c2.sayHi(3); // 这里调用的是 C 中定义的 sayHi 发生了重载} } // 输出 // 调用的是 A 中定义的 sayHi // 调用的是 A 中定义的 sayHi // 这里调用的是 C 中定义的 sayHi// ------------------------------------------------------------------------------------------------------ // 方法签名相同时返回值类型要相同否则报错也就是说和成员方法的覆盖要求是一样的。 public class A {public static double sayHi(){System.out.println(调用的是 A 中定义的 sayHi);return 1;} } public class B extends A{} public class C extends B{public static int sayHi(){ // 报错返回类型int与double不兼容。即一定得一模一样能发生自动转换的类型也不能。System.out.println(这里调用的是 C 中定义的 sayHi);return 1;} }// 调用类 public class TestUse {public static void main(String[] args) {A c new C();c.sayHi();} }// ------------------------------------------------------------------------------------------------------ // 方法签名相同且返回值类型一模一样时实际上也不是发生覆盖而是调用哪个方法和引用的类型有关与引用指向的对象类型无关。 public class A {public static double sayHi(){System.out.println(调用的是 A 中定义的 sayHi);return 1;} } public class B extends A{} public class C extends B{public static double sayHi(){System.out.println(这里调用的是 C 中定义的 sayHi);return 1;} }// 调用类 public class TestUse {public static void main(String[] args) {A c1 new C();c1.sayHi(); // 调用的是 A 中定义的 sayHiA.sayHi(); // 调用的是 A 中定义的 sayHi((A)null).sayHi(); // 调用的是 A 中定义的 sayHi((B)c1).sayHi(); // 调用的是 A 中定义的 sayHiB.sayHi(); // 调用的是 A 中定义的 sayHi((B)null).sayHi(); // 调用的是 A 中定义的 sayHiC c2 new C();c2.sayHi(); // 这里调用的是 C 中定义的 sayHiC.sayHi(); // 这里调用的是 C 中定义的 sayHi((C)null).sayHi(); // 这里调用的是 C 中定义的 sayHi} } // 输出 // 调用的是 A 中定义的 sayHi // 调用的是 A 中定义的 sayHi // 调用的是 A 中定义的 sayHi // 调用的是 A 中定义的 sayHi // 调用的是 A 中定义的 sayHi // 调用的是 A 中定义的 sayHi // 这里调用的是 C 中定义的 sayHi // 这里调用的是 C 中定义的 sayHi // 这里调用的是 C 中定义的 sayHi// 即是说调用哪个类中的静态方法压根不看实际对象的类型而是看引用类型哪怕这个引用类型指向的“对象”是 null即压根不存在对象。因为“覆盖”实际上是在 this 自引用上做文章this 调用哪个方法取决于这个引用所指向的对象的类型但静态方法本身没有 this 自引用没有访问 this 自引用所以没有覆盖的功能。 另外如果一个方法是静态的那么只能用静态的方法去覆盖反之亦然如果一个方法不是静态的你不能用静态方法来覆盖。 public class A{public static void sayHi(){System.out.println(This is from A.);} } public class B extends A{public static void sayHi(){System.out.println(This is from B.);} }/* 你不能写成 // A public static void sayHi(){ // B public void sayHi(){或// A public void sayHi(){ // B public static void sayHi(){ */// 这两种写法都不行
http://www.zqtcl.cn/news/36052/

相关文章:

  • 河北网站优化公司服务类产品
  • 游戏网站建设策划方案模板餐饮酒店网站怎么做
  • vps打开网站很慢江苏建发建设项目咨询有限公司网站
  • 广州网站备案方案淄博信息港
  • 潘家园做网站公司莱芜网站优化方案
  • 举报网站建设情况汇报网站开发 pdf
  • 二级网站怎么做wordpress 放大镜插件
  • 淄博网站制作平台形象wordpress 离线编辑
  • 新网站收录多少关键词阿里云服务器可以做几个网站
  • 网站建设需要摊销多久网页模板下载
  • 华强北网站建设十大网络营销成功案例
  • 工程建设网站怎么提交分析公司网站的开发策略
  • 湖北seo网站设计企业宣传册模板
  • 顺德专业网站制作黄骅市人事考试网
  • 枣阳网站建设怎样做网站3天赚100万
  • 自己服务器做网站如何备案wordpress自动标签添加内链插件
  • 技术支持 网站建设做食品企业网站的费用
  • 网站地图制作软件怎么自己做wordpress主题
  • 贵州网站建设营销公司资兴做网站公司
  • 备案期间 需要关闭网站网站自己推广
  • 杭州网站建设制作九江便宜做网站
  • 网站推广策划方案书中国人在国外做网站网站代理
  • 快速搭建个人网站贵阳讯玛网站建设
  • 网站备案被退回网络培训意义
  • 找网站做q币asp.net 建网站
  • 东坑镇网站建设wordpress更改目录
  • 凡客诚品官方网站首页免费的网站给一个
  • 做国外贸易哪个网站好ppt在线制作网页
  • 有需要网站建设网站推广请找我wordpress 主题漏洞
  • 企业网站优化的原则重庆是哪个省属于哪个省份