public class Person { public Person() { System.out.println("this is a Person."); }}public class Teacher extends Person { private String name = "tom"; public Teacher() { System.out.println("this is a teacher."); super(); } public static void main(String[] args){ Teacher tea = new Teacher(); System.out.println(this.name); }}
答案
public class Person { public Person() { System.out.println("this is a Person."); }}public class Teacher extends Person { private String name = "tom"; public Teacher() { System.out.println("this is a teacher."); super(); // 错误:必须在首行 } public static void main(String[] args){ Teacher tea = new Teacher(); System.out.println(this.name); // 错误:静态方法不能访问 this }}
构造器调用关系
public class Exercise8 { public static void main(String[] args) { new Child("mike"); }}class People { private String name; public People() { System.out.print("1"); } public People(String name) { System.out.print("2"); this.name = name; }}class Child extends People { People father; public Child(String name) { System.out.print("3"); father = new People(name + " F"); } public Child() { System.out.print("4"); }}
答案
132
public Child(String name) { // super(); // 没写默认相当于调 super() System.out.print("3"); father = new People(name + " F");}
成员属性会被继承,但不一定能访问
public class Father { private String name = "atguigu"; int age = 0;}public class Child extends Father { public String grade; public static void main(String[] args) { Father f = new Child(); System.out.println(f.name); }}
答案
public class Father { private String name = "atguigu"; int age = 0;}public class Child extends Father { public String grade; public static void main(String[] args) { Father f = new Child(); System.out.println(f.name); // 错误:name 访问不到 }}
成员属性会被继承
public class Exercise7 { public static void main(String[] args) { Father f = new Father(); Son s = new Son(); System.out.println(f.getInfo()); System.out.println(s.getInfo()); s.setInfo("尚硅谷"); System.out.println(f.getInfo()); System.out.println(s.getInfo()); }}class Father { private String info = "atguigu"; public void setInfo(String info) { this.info = info; } public String getInfo() { return info; }}class Son extends Father {}
public class Son extends Father { private String name = "son"; public static void main(String[] args) { Son son = new Son(); System.out.println(son.getName()); }}class Father { private String name = "father"; public String getName() { return name; }}
答案
father
当父类与子类有同名的属性时:通过子类对象调用 getName() 访问的是父类的 name 还是子类的 name?
要看子类是否重写,如果没有重写,就是父类的,重写了就是子类的。
虚函数视角只能看到 Father,方法中的成员属性指向是指向当前类中的成员属性
这与 JavaScript 的原型链不同,也正是两种面向对象实现方式的差异化
父子类中属性同名,子对象有两份 1
public class Exercise12 { public static void main(String[] args) { Father f = new Father(); Son s = new Son(); System.out.println(f.getInfo()); System.out.println(s.getInfo()); s.test(); System.out.println("-----------------"); s.setInfo("大硅谷"); System.out.println(f.getInfo()); System.out.println(s.getInfo()); s.test(); }}class Father { private String info = "atguigu"; public void setInfo(String info) { this.info = info; } public String getInfo() { return info; }}class Son extends Father { private String info = "尚硅谷"; public void test() { System.out.println(this.getInfo()); System.out.println(super.getInfo()); }}
public class Exercise13 { public static void main(String[] args) { Father f = new Father(); Son s = new Son(); System.out.println(f.getInfo()); System.out.println(s.getInfo()); s.test(); System.out.println("-----------------"); s.setInfo("大硅谷"); System.out.println(f.getInfo()); System.out.println(s.getInfo()); s.test(); }}class Father { private String info = "atguigu"; public void setInfo(String info) { this.info = info; } public String getInfo() { return info; }}class Son extends Father { private String info = "尚硅谷"; public void setInfo(String info) { this.info = info; } public String getInfo() { return info; } public void test() { System.out.println(this.getInfo()); System.out.println(super.getInfo()); }}
public class Exercise22 { public static void main(String[] args) { Father f = new Father(); Father s = new Son(); Father d = new Daughter(); MyClass my = new MyClass(); /* 第一个问题,my 有没有多态引用?没有,从 my 角度来说,不需要考虑多态现象。 第二个问题,my 调用的 method 方法,去哪个类找?只 MyClass 类中找就可以,和其他类无关。 第三个问题,method 方法有三个形式,即重载的形式,那么怎么确定调用的是哪一个? 重载方法找寻原则: A:先找最匹配的:实参的“编译时”类型和形参的“声明”类型一致,个数也一致。 B:再找唯一可以兼容的 实参的“编译时”类型 < 形参的“声明”类型 形参是可变参数的话,实参的个数在它允许的范围内 */ my.method(f); // father /* 实参 f 的编译时类型是 Father,和哪个方法的形参最匹配呢?public void method(Father f) */ my.method(s); // father /* 实参 s 的编译时类型是 Father,和哪个方法的形参最匹配呢?public void method(Father f) */ my.method(d); // father /* 实参 d 的编译时类型是 Father,和哪个方法的形参最匹配呢?public void method(Father f) */ }}class MyClass { public void method(Father f) { System.out.println("father"); } public void method(Son s) { System.out.println("son"); } public void method(Daughter d) { System.out.println("daughter"); }}class Father {}class Son extends Father {}class Daughter extends Father {}
确定虚方法 2
public class Exercise23 { public static void main(String[] args) { Father f = new Father(); Son s = new Son(); Daughter d = new Daughter(); MyClass my = new MyClass(); my.method(f); // father /* 实参 f 的编译时类型仍然是 Father,找最匹配的 public void method(Father f) */ my.method(s); // son /* 实参 s 的编译时类型是 Son,找最匹配的 public void method(Son s) */ my.method(d); // father /* 实参 d 的编译时类型是 Daughter,找最匹配,没有,找兼容的,public void method(Father f) 这里如果 Daughter 不是继承 Father,而是继承 Son 呢? 就是 public void method(Son s) 了,找最近的父类 */ }}class MyClass { public void method(Father f) { System.out.println("father"); } public void method(Son s) { System.out.println("son"); }}class Father {}class Son extends Father {}class Daughter extends Father {}
确定虚方法 3
public class Exercise24 { public static void main(String[] args) { Father f = new Father(); Son s = new Son(); Daughter d = new Daughter(); MyClass my = new MySub(); // my 有多态引用了,而且 method 是虚方法 /* 如何确定它执行的是哪个方法呢? (1)编译时,去 my 的编译时类型 MyClass 中找合适的方法 (2)运行时,去 my 的运行时类型 MySub 中看是否有对刚刚找到的方法进行了“重写” */ my.method(f); // father /* (1)编译时,去 my 的编译时类型 MyClass 中找合适的方法 实参 f 的编译时类型是 Father,找最匹配的 public void method(Father f) (2)运行时,去 my 的运行时类型 MySub 中看是否有对刚刚找到的方法进行了“重写” 没有重写,仍然执行刚刚在 MyClass 中找到的合适的方法 */ my.method(s); // son /* (1)编译时,去 my 的编译时类型 MyClass 中找合适的方法 实参 s 的编译时类型是 Son,找最匹配的 public void method(Son s) (2)运行时,去 my 的运行时类型 MySub 中看是否有对刚刚找到的方法进行了“重写” 没有重写,仍然执行刚刚在 MyClass 中找到的合适的方法 */ my.method(d); // father /* (1)编译时,去 my 的编译时类型 MyClass 中找合适的方法 实参 s 的编译时类型是 Daughter,找最匹配的,没有,找兼容的,public void method(Father f) (2)运行时,去 my 的运行时类型 MySub 中看是否有对刚刚找到的方法进行了“重写” 没有重写,仍然执行刚刚在 MyClass 中找到的合适的方法 */ }}class MyClass { public void method(Father f) { System.out.println("father"); } public void method(Son s) { System.out.println("son"); }}class MySub extends MyClass { public void method(Daughter d) { // 这个不是重写,因为形参列表不同 System.out.println("daughter"); }}class Father {}class Son extends Father {}class Daughter extends Father {}
确定虚方法 4
public class Exercise25 { public static void main(String[] args) { Father f = new Father(); Son s = new Son(); Daughter d = new Daughter(); MyClass my = new MySub(); // my 有多态引用了,而且 method 是虚方法 /* 如何确定它执行的是哪个方法呢? (1)编译时,去 my 的编译时类型 MyClass 中找合适的方法 (2)运行时,去 my 的运行时类型 MySub 中看是否有对刚刚找到的方法进行了“重写” */ my.method(f); // daughter /* (1)编译时,去 my 的编译时类型 MyClass 中找合适的方法 实参 f 的编译时类型是 Father,找最匹配的 public void method(Father f) (2)运行时,去 my 的运行时类型 MySub 中看是否有对刚刚找到的方法进行了“重写” 有重写,执行重写后的代码 */ my.method(s); // son /* (1)编译时,去 my 的编译时类型 MyClass 中找合适的方法 实参 s 的编译时类型是 Son,找最匹配的 public void method(Son s) (2)运行时,去 my 的运行时类型 MySub 中看是否有对刚刚找到的方法进行了“重写” 没有重写,仍然执行刚刚在 MyClass 中找到的合适的方法 */ my.method(d); // daughter /* (1)编译时,去 my 的编译时类型 MyClass 中找合适的方法 实参 s 的编译时类型是 Daughter,找最匹配的,没有,找兼容的,public void method(Father f) (2)运行时,去 my 的运行时类型 MySub 中看是否有对刚刚找到的方法进行了“重写” 有重写,执行的是重写的方法体 daughter */ }}class MyClass { public void method(Father f) { System.out.println("father"); } public void method(Son s) { System.out.println("son"); }}class MySub extends MyClass { public void method(Father d) { // 是重写 System.out.println("daughter"); }}class Father {}class Son extends Father {}class Daughter extends Father {}
属性与多态无关 1
public class Exercise31 { public static void main(String[] args) { Base b = new Sub(); System.out.println(b.x); }}class Base { int x = 1;}class Sub extends Base { int x = 2;}
答案
1
多态性现象:编译时类型与运行时类型不一致
但是多态性是针对方法来说,方法有动态绑定一说。
属性没有多态性。属性都是按照编译时类型处理的。
属性与多态无关 2
public class Exercise26 { public static void main(String[] args) { A a = new B(); System.out.println(a.num); // 1,a 编译时类型就是 A System.out.println(((B)a).num); // 2,编译时,因为 a 被强制成 B 类,是 B 类型 System.out.println(((A)((B)a)).num); // 1,编译时,a 转成 B 又转成 A,是 A 类型 System.out.println("-------------------"); B b = new B(); System.out.println(b.num); // 2,编译时就是 B 类型 System.out.println(((A)b).num); // 1,编译时 b 被强制升级为 A 类型,按 A 类型处理 System.out.println(((B)((A)b)).num); // 2,最终是 B 类型 }}class A { int num = 1;}class B extends A { int num = 2;}
实例初始化流程 1
考核点:实例初始化方法,属性与多态无关
public class Exercise27 { public static void main(String[] args) { Father f = new Son(); System.out.println(f.x); }}class Father { int x = 10; public Father() { this.print(); x = 20; } public void print() { System.out.println("Father.x = " + x); }}class Son extends Father { int x = 30; public Son() { this.print(); x = 40; } public void print() { System.out.println("Son.x = " + x); }}
答案
Son.x = 0Son.x = 3020
/* * 1、Father f = new Son(); * 实例初始化的过程: * (1)父类的实例初始化 * <init>() { * x = 10; // 父类的 x * this.print(); // 子类的 print,因为 this 代表的是正在创建的子类对象,而子类重写了 print,所以是子类的 print * System.out.println("Son.x = " + x); // 子类的 x,此时还没有赋值,那么是默认值 x=0 x = 20; // 父类的 x * } * (2)子类的实例初始化 * <init>() { * x = 30; // 子类的x * this.print(); // 子类的 print * System.out.println("Son.x = " + x); // 子类的x,此时已经赋值 x=30 x = 40; // 子类的 x * } * * 2、执行 System.out.println(f.x); * 属性没有多态性,只看编译时类型,那么此时 f.x 表示父类的 x */
实例初始化流程 2
考核点:多态,重写,实例初始化过程
public class Exercise28 { public static void main(String[] args) { Base b1 = new Base(); Base b2 = new Sub(); }}class Base { Base() { method(100); } public void method(int i) { System.out.println("base : " + i); }}class Sub extends Base { Sub() { super.method(70); } public void method(int j) { System.out.println("sub : " + j); }}
public class Exercise29 { public static void main(String[] args) { A a1 = new A(); A a2 = new B(); B b = new B(); C c = new C(); D d = new D(); System.out.println("(1)" + a1.show(b)); // A and A System.out.println("(2)" + a2.show(d)); // A and D System.out.println("(3)" + a2.show(b)); // B and A System.out.println("(4)" + b.show(c)); // B and B System.out.println("(5)" + b.show(d)); // A and D }}class A { public String show(D obj) { return ("A and D"); } public String show(A obj) { return "A and A"; }}class B extends A { public String show(B obj) { return "B and B"; } public String show(A obj) { return "B and A"; }}class C extends B {}class D extends B {}
/* * 1、分析方法列表和继承关系 * A类: * public String show(D obj) * public String show(A obj) * B类: * public String show(D obj) 继承的 * public String show(A obj) 重写 * public String show(B obj) 自定义的 * C->B->A * D->B->A * * 2、方法重载:找最合适的形参类型 * 3、方法重写:如果子类重写,就执行重写的 * 4、分析执行结果 * a1.show(b):a1 没有多态引用,直接找 A 类的方法,b 是 B 类对象, * 只能选择 public String show(A obj) * * a2.show(d):a2 多态引用,d 是 D 类对象, * 编译时,A 有 public String show(D obj),运行时子类没有重写该方法 * * a2.show(b):a2 多态引用,b 是 D 类对象, * 编译时,A 没有完全匹配的方法,找兼容的 public String show(A obj), * 运行时子类重写了该方法:public String show(A obj),运行子类重写的方法 * * b.show(c):b 没有多态引用,直接找 B 类的方法,c 是 C 类的对象, * 选择最合适的 public String show(B obj) * * b.show(d):b 没有多态引用,直接找 B 类的方法,d 是 D 类对象, * 选最合适的public String show(D obj) */
多态、重载、重写 2
public class Exercise30 { public static void main(String[] args) { A a1 = new A(); A a2 = new B(); B b = new B(); C c = new C(); D d = new D(); System.out.println("(1)" + a1.show(b)); // A and A System.out.println("(2)" + a2.show(d)); // B and A System.out.println("(3)" + b.show(c)); // A and C System.out.println("(4)" + b.show(d)); // B and B }}class A { public String show(C obj) { return ("A and C"); } public String show(A obj) { return "A and A"; }}class B extends A { public String show(B obj) { return "B and B"; } public String show(A obj) { return "B and A"; }}class C extends B {}class D extends B {}
/* * 1、分析每个类的方法列表和继承关系 * A类: * public String show(C obj) * public String show(A obj) * B类: * public String show(C obj) 继承的 * public String show(A obj) 重写 * public String show(B obj) 自定义的 * C->B->A * D->B->A * * 2、方法重载:找最合适的形参类型 * 3、方法重写:如果子类重写,就执行重写的 * 4、如果特殊的重载,那么多态时,编译时先从父类中查找最合适的形参类型,然后如果子类如果有重写,执行子类重写的,如果没有重写,执行父类的。 * 5、分析执行结果 * a1.show(b):a1 没有多态引用,直接找 A 类的方法,b 是 B 类对象, * 只能选择 public String show(A obj) * * a2.show(d):a2 多态引用,执行子类的方法,d 是 D 类对象, * 但是因为此时编译时按 A 类编译,所以在编译期间先确定是调用 * public String show(A obj),而后执行子类重写的 public String show(A obj) * * b.show(c):b 没有多态引用,直接找 B 类的方法,c 是 C 类的对象, * 选择最合适的public String show(C obj) * * b.show(d):b 没有多态引用,直接找 B 类的方法,d 是 D 类对象, * 选最合适的public String show(B obj) */