企业真题

父类哪些成员可以被继承,属性可以被继承吗?

来源:北京明??信

父类的属性、方法可以被继承。构造器可以被子类调用。

什么是 Override,与 Overload 的区别

来源:顺?、软??力、明?数据、阳?科技、中?软

方法重载与重写

Overload 的方法是否可以改变返回值的类型?

来源:新?陆

当然可以。

重写就不能任意改变返回值类型了:需要 <=

构造器是否可被 override?

来源:鸿?网络、深圳德??技、航??普

不能!构造器可以重载,可以被子类调用

为什么要有重载,我随便命名一个别的函数名不行吗?谈谈你是怎么理解的。

来源:腾?

见名知意。

superthis 的区别

来源:蚂??服、北京楚?龙

  • super:代表父类
  • this:代表当前类

谈谈你对多态的理解

来源:三?重工、江?智能、银?数据、君?科技、国?电网、上?银行、贝?

多态

多态 new 出来的对象跟不多态 new 出来的对象区别在哪?

来源:万?智能

虚方法调用。屏蔽了子类特有的属性和方法。

说说你认为多态在代码中的体现

来源:楚?龙

无处不在!

1. ==equals 的区别

来源:拓?思

  • ==:比较两个变量的值
    • 基础类型:数据
    • 引用类型:指针、引用对象的堆内存地址
  • equalsObject 的方法,用来判断两个对象是否相同
    • 基础类型:不继承 Object,无法调用
    • 引用类型:一般重写该方法以对成员属性判等,没重写和 == 一样

重写 equals 方法要注意什么?

来源:安??网络科技

  • 明确判定两个对象实体 equals() 的标准。是否需要所有的属性参与。
  • 对象的属性,又是自定义的类型,此属性也需要重写 equals()

equals

Java 中所有类的父类是什么?他都有什么方法?

来源:阿??巴

Object

拓展练习

是否可通过编译

如下代码是否可以编译通过,如果能,结果是什么,如果不能,为什么?

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 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");
	}
}
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 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 {}

私有的属性是否可以被继承到子类中?

  • 如果从可以访问性的角度来说:不能,因为在子类中不能直接访问父类的私有的属性,但是可以通过有访问权限的 get/set 方法操作
  • 如果从类的概念来说,类是一类具有相同特性(属性、方法等)的事物的抽象描述,那么子类是从父类派生出来的,那么子类是有父类的这个特征的,即有这个属性的

每一个对象的非静态属性是独立的(无论权限),其中一个对象修改和另一个对象是无关的

虚函数中成员属性的指向

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

当父类与子类有同名的属性时:通过子类对象调用 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());
	}
}

当子类有与父类的属性同名时,那么通过子类对象调用 get/set 方法操作的是父类继承还是子类自己的属性呢?

要看子类是否重写:

  • 如果没有重写,操作的都是父类的,不管是直接 getInfo() 还是 this.getInfo(),还是 super.getInfo()
  • 如果重写了,如果通过子类对象调用,操作的是子类的,例如:getInfo()this.getInfo(),如果通过 super. 调用的,操作的是父类的。

父子类中属性同名,子对象有两份 2

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

确定虚方法 1

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

多态性现象:编译时类型与运行时类型不一致

  • 但是多态性是针对方法来说,方法有动态绑定一说。
  • 属性没有多态性。属性都是按照编译时类型处理的。

属性与多态无关 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);
	}
}
/*
 * 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);
	}
}
/*
 * 1、Base b1 = new Base();
 * 父类的实例初始化,和子类无关
 * 
 * <init>() {
 * 		method(100);
 * 			System.out.println("base : " + i);  base:100
 * }
 * 
 * 2、Base b2 = new Sub();
 * (1) 父类的实例初始化
 * 
 * <init>() {
 * 		method(100); // 执行了子类重写的 method()
 * 			System.out.println("sub : " + j);  sub:100
 * }
 * 
 * (2)子类的实例初始化
 * <init>() {
 * 		super.method(70);
 * 			System.out.println("base : " + i);	base:70
 * }
 */

多态、重载、重写 1

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)
 */