一文带你深入理解【Java基础】· 面向对象编程(中)②子类对象实例化和多态

写在前面


        Hello大家好, 我是【麟-小白】,一位软件工程专业的学生,喜好计算机知识。希望大家能够一起学习进步呀!本人是一名在读大学生,专业水平有限,如发现错误不足之处,请多多指正!谢谢大家!!!

        如果小哥哥小姐姐们对我的文章感兴趣,请不要吝啬你们的小手,多多点赞加关注呀!❤❤❤ 爱你们!!!


目录

写在前面

1. 子类对象实例化过程

1.1 流程

1.2 子类对象实例化的全过程

 2. 面向对象特征之三:多态性

2.1 多态性的概念

2.2 多态性应用举例

2.3 虚拟方法调用

2.4 方法的重载和重写

2.5 多态小结

2.6 instanceof操作符

2.7 对象类型转换(Casting)

2.8 多态性代码演示

2.9 多态的理解

结语


【往期回顾】

一文带你深入理解【Java基础】· 面向对象编程(中)①

一文带你深入理解【Java基础】· 面向对象编程(上)②

一文带你深入理解【Java基础】· 面向对象编程(上)①

一文带你深入理解【Java基础】· 数组


【习题总结】

【Java基础】· 面向对象编程(中)习题详解


1. 子类对象实例化过程


1.1 流程



1.2 子类对象实例化的全过程

1. 从结果上来看:(继承性)

  • 子类继承父类以后,就获取了父类中声明的属性或方法。
  •  创建子类的对象,在堆空间中,就会加载所有父类中声明的属性。

2. 从过程上来看:

  • 当我们通过子类的构造器创建子类对象时,我们一定会直接或间接的调用其父类的构造器,进而调用父类的父类的构造器,...
  • 直到调用了java.lang.Object类中空参的构造器为止。正因为加载过所有的父类的结构,所以才可以看到内存中有
  • 父类中的结构,子类对象才可以考虑进行调用。
  • 明确:虽然创建子类对象时,调用了父类的构造器,但是自始至终就创建过一个对象,即为new的子类对象。
/*
 * 子类对象实例化的全过程
 * 
 * 1. 从结果上来看:(继承性)
 * 		子类继承父类以后,就获取了父类中声明的属性或方法。
 *      创建子类的对象,在堆空间中,就会加载所有父类中声明的属性。
 * 
 * 2. 从过程上来看:
 * 		当我们通过子类的构造器创建子类对象时,我们一定会直接或间接的调用其父类的构造器,进而调用父类的父类的构造器,...
 *    直到调用了java.lang.Object类中空参的构造器为止。正因为加载过所有的父类的结构,所以才可以看到内存中有
 *    父类中的结构,子类对象才可以考虑进行调用。
 *    
 *  
 * 明确:虽然创建子类对象时,调用了父类的构造器,但是自始至终就创建过一个对象,即为new的子类对象。
 * 
 */

class Creature {
	public Creature() {
		System.out.println("Creature无参数的构造器");
	}
}

class Animal extends Creature {
	public Animal(String name) {
		System.out.println("Animal带一个参数的构造器,该动物的name为" + name);
	}

	public Animal(String name, int age) {
		this(name);
		System.out.println("Animal带两个参数的构造器,其age为" + age);
	}
}

public class Wolf extends Animal {
	public Wolf() {
		super("灰太狼", 3);
		System.out.println("Wolf无参数的构造器");
	}

	public static void main(String[] args) {
		new Wolf();
	}
}
//Creature无参数的构造器
//Animal带一个参数的构造器,该动物的name为灰太狼
//Animal带两个参数的构造器,其age为3
//Wolf无参数的构造器

 2. 面向对象特征之三:多态性


2.1 多态性的概念

多态性,是面向对象中最重要的概念,在 Java 中的体现:
对象的多态性:父类的引用指向子类的对象
  • 可以直接应用在抽象类和接口上
Java 引用变量有两个类型: 编译时类型 运行时类型 。编译时类型由声明该变量时使用的类型决定,运行时类型由实际赋给该变量的对象决定。简称: 编译 时,看左边;运行时,看右边。
若编译时类型和运行时类型不一致,就出现了对象的多态性(Polymorphism) 多态情况下,“看左边”: 看的是父类的引用(父类中不具备子类特有的方法)
“看右边”: 看的是子类的对象(实际运行的是子类重写父类的方法)

对象的多态 Java , 子类 的对象可以替代 父类 的对象使用
  • 一个变量只能有一种确定的数据类型
  • 一个引用类型变量可能指向(引用)多种不同类型的对象
Person p = new Student();
Object o = new Person();//Object类型的变量o,指向Person类型的对象
o = new Student(); //Object类型的变量o,指向Student类型的对象
  • 子类可看做是特殊的父类,所以父类类型的引用可以指向子类的对象:向上转型(upcasting)
  • 一个引用类型变量如果声明为父类的类型,但实际引用的是子类对象,那么该变量就不能再访问子类中添加的属性和方法
Student m = new Student();
m.school = “pku”; //合法,Student类有school成员变量
Person e = new Student(); 
e.school = “pku”; //非法,Person类没有school成员变量
属性是在编译时确定的,编译时 e Person 类型,没有 school 成员变量,因而编 译错误。

2.2 多态性应用举例

方法声明的形参类型为 父类 类型,可以使用 子类的对象 作为实参调用该方法
public class Test {
	public void method(Person e) {
		// ……
		e.getInfo();
	}

	public static void main(Stirng args[]) {
		Test t = new Test();
		Student m = new Student();
		t.method(m); // 子类的对象m传送给父类类型的参数e
	}
}


2.3 虚拟方法调用

正常的方法调用:
Person e = new Person();
e.getInfo();
Student e = new Student();
e.getInfo();
虚拟方法调用 ( 多态情况下 )
子类中定义了与父类同名同参数的方法,在多态情况下,将此时父类的方法称为虚拟方法,父 类根据赋给它的不同子类对象,动态调用属于子类的该方法。这样的方法调用在编译期是无法 确定的。
Person e = new Student();
e.getInfo(); //调用Student类的getInfo()方法
编译时类型和运行时类型:
编译时 e Person 类型,而方法的调用是在运行时确定的,所以调用的是 Student getInfo() 方法。 —— 动态绑定
虚拟方法调用举例:


2.4 方法的重载和重写

1. 二者的定义细节:
2. 从编译和运行的角度看:
重载,是指允许存在多个同名方法,而这些方法的参数不同。编译器根据方法不同的参数表,对同名方法的名称做修饰。对于编译器而言,这些同名方法就成了不同的方法。 它们的调用地址在编译期就绑定了。 Java 的重载是可以包括父类和子类的,即子类可以重载父类的同名不同参数的方法。所以:对于重载而言,在方法调用之前,编译器就已经确定了所要调用的方法,这称为 “早绑定”或“静态绑定”
而对于多态,只有等到方法调用的那一刻,解释运行器才会确定所要调用的具体方法,这称为 “晚绑定”或“动态绑定”
引用一句 Bruce Eckel 的话: “不要犯傻,如果它不是晚绑定,它就不是多态。”

2.5 多态小结

多态作用:
  • 提高了代码的通用性,常称作接口重用
前提:
  • 需要存在继承或者实现关系
  • 有方法的重写
成员方法:
  • 编译时:要查看引用变量所声明的类中是否有所调用的方法。
  • 运行时:调用实际new的对象所属的类中的重写方法。
成员变量:
  • 不具备多态性,只看引用变量所声明的类。

2.6 instanceof操作符

x instanceof A :检验 x 是否为类 A 的对象,返回值为 boolean 型。
  • 要求x所属的类与类A必须是子类和父类的关系,否则编译错误。
  • 如果x属于类A的子类Bx instanceof A值也为true
public class Person extends Object {…}
public class Student extends Person {…}
public class Graduate extends Person {…}
-------------------------------------------------------------------
public void method1(Person e) {
    if (e instanceof Person) 
    // 处理Person类及其子类对象
    if (e instanceof Student) 
    //处理Student类及其子类对象
    if (e instanceof Graduate)
    //处理Graduate类及其子类对象
}


2.7 对象类型转换(Casting)

基本数据类型的 Casting
  • 自动类型转换:小的数据类型可以自动转换成大的数据类型
    • long g=20;
    • double d=12.0f
  • 强制类型转换:可以把大的数据类型强制转换(casting)成小的数据类型
    • float f=(float)12.0; int a=(int)1200L
Java 对象的强制类型转换称为造型
  • 从子类到父类的类型转换可以自动进行
  • 从父类到子类的类型转换必须通过造型(强制类型转换)实现
  • 无继承关系的引用类型间的转换是非法的
  • 在造型前可以使用instanceof操作符测试一个对象的类型
public class ConversionTest {
    public static void main(String[] args) {
        double d = 13.4;
        long l = (long) d;
        System.out.println(l);
        int in = 5;
        // boolean b = (boolean)in;
        Object obj = "Hello";
        String objStr = (String) obj;
        System.out.println(objStr);
        Object objPri = new Integer(5);
        // 所以下面代码运行时引发ClassCastException异常
        String str = (String) objPri; 
    } 
}
public class Test {
    public void method(Person e) { // 设Person类中没有getschool() 方法
        // System.out.pritnln(e.getschool()); //非法,编译时错误
        if (e instanceof Student) {
        Student me = (Student) e; // 将e强制转换为Student类型
        System.out.pritnln(me.getschool());
    } 
}

public static void main(String[] args){
    Test t = new Test();
    Student m = new Student();
    t.method(m);
    } 
}

子类继承父类
  • 若子类重写了父类方法,就意味着子类里定义的方法彻底覆盖了父类里的同名方法,系统将不可能把父类里的方法转移到子类中。
  • 对于实例变量则不存在这样的现象,即使子类里定义了与父类完全相同的实例变量,这个实例变量依然不可能覆盖父类中定义的实例变量
class Base {
	int count = 10;

	public void display() {
		System.out.println(this.count);
	}
}

class Sub extends Base {
	int count = 20;

	public void display() {
		System.out.println(this.count);
	}
}

public class FieldMethodTest {
	public static void main(String[] args) {
		Sub s = new Sub();
		System.out.println(s.count);// 20
		s.display();// 20

		Base b = s;// 多态性
		// ==:对于引用数据类型来讲,比较的是两个引用数据类型变量的地址值是否相同
		System.out.println(b == s);// true
		System.out.println(b.count);// 10
		b.display();// 20
	}
}

2.8 多态性代码演示

/*
 * 面向对象特征之三:多态性
 * 
 * 1.理解多态性:可以理解为一个事物的多种形态。
 * 2.何为多态性:
 *   对象的多态性:父类的引用指向子类的对象(或子类的对象赋给父类的引用)
 *   
 * 3. 多态的使用:虚拟方法调用
 *   有了对象的多态性以后,我们在编译期,只能调用父类中声明的方法,但在运行期,我们实际执行的是子类重写父类的方法。
 *   总结:编译,看左边;运行,看右边。
 *   
 * 4.多态性的使用前提:  ① 类的继承关系  ② 方法的重写
 * 
 * 5.对象的多态性,只适用于方法,不适用于属性(编译和运行都看左边)
 */
public class PersonTest {

	public static void main(String[] args) {

		Person p1 = new Person();
		p1.eat();// 人:吃饭

		Man man = new Man();
		man.eat();// 男人多吃肉,长肌肉
		man.age = 25;
		man.earnMoney();// 男人负责挣钱养家

		// *************************************************
		System.out.println("*******************");
		// 对象的多态性:父类的引用指向子类的对象
		Person p2 = new Man();
		Person p3 = new Woman();
		// 多态的使用:当调用子父类同名同参数的方法时,实际执行的是子类重写父类的方法 ---虚拟方法调用
		p2.eat();// 男人多吃肉,长肌肉
		p2.walk();// 男人霸气的走路
		p3.eat();// 女人少吃,为了减肥
		p3.walk();// 女人窈窕的走路
		// p2.earnMoney();Person类中没有定义earnMoney方法,无法调用

		System.out.println(p2.id);// 1001

	}
}

public class Person {
	String name;
	int age;

	int id = 1001;

	public void eat() {
		System.out.println("人:吃饭");
	}

	public void walk() {
		System.out.println("人:走路");
	}

}

public class Man extends Person {

	boolean isSmoking;

	int id = 1002;

	public void earnMoney() {
		System.out.println("男人负责挣钱养家");
	}

	public void eat() {
		System.out.println("男人多吃肉,长肌肉");
	}

	public void walk() {
		System.out.println("男人霸气的走路");
	}

}

public class Woman extends Person {

	boolean isBeauty;

	public void goShopping() {
		System.out.println("女人喜欢购物");
	}

	public void eat() {
		System.out.println("女人少吃,为了减肥");
	}

	public void walk() {
		System.out.println("女人窈窕的走路");
	}
}
//多态性的使用举例一:
public class AnimalTest {
	public static void main(String[] args) {

		AnimalTest test = new AnimalTest();
		test.func(new Dog());

		test.func(new Cat());
	}

	public void func(Animal animal) {// Animal animal = new Dog();
		animal.eat();
		animal.shout();

		if (animal instanceof Dog) {
			Dog d = (Dog) animal;
			d.watchDoor();
		}
	}

	// public void func(Dog dog){
	// dog.eat();
	// dog.shout();
	// }
	// public void func(Cat cat){
	// cat.eat();
	// cat.shout();
	// }
}

class Animal {

	public void eat() {
		System.out.println("动物:进食");
	}

	public void shout() {
		System.out.println("动物:叫");
	}

}

class Dog extends Animal {
	public void eat() {
		System.out.println("狗吃骨头");
	}

	public void shout() {
		System.out.println("汪!汪!汪!");
	}

	public void watchDoor() {
		System.out.println("看门");
	}
}

class Cat extends Animal {
	public void eat() {
		System.out.println("猫吃鱼");
	}

	public void shout() {
		System.out.println("喵!喵!喵!");
	}
}
//面试题:多态是编译时行为还是运行时行为?
//答:运行时行为。在运行之前无法得知运行的结果
//证明如下:
class Animal {

	protected void eat() {
		System.out.println("animal eat food");
	}
}

class Cat extends Animal {

	protected void eat() {
		System.out.println("cat eat fish");
	}
}

class Dog extends Animal {

	public void eat() {
		System.out.println("Dog eat bone");

	}

}

class Sheep extends Animal {

	public void eat() {
		System.out.println("Sheep eat grass");

	}

}

public class InterviewTest {

	public static Animal getInstance(int key) {
		switch (key) {
		case 0:
			return new Cat();
		case 1:
			return new Dog();
		default:
			return new Sheep();
		}

	}

	public static void main(String[] args) {
		int key = new Random().nextInt(3);

		System.out.println(key);

		Animal animal = getInstance(key);

		animal.eat();

	}

}

2.9 多态的理解

/*
 * 面向对象特征之三:多态性
 * 
 * 1.理解多态性:可以理解为一个事物的多种形态。
 * 2.何为多态性:
 *   对象的多态性:父类的引用指向子类的对象(或子类的对象赋给父类的引用)
 *   
 * 3. 多态的使用:虚拟方法调用
 *   有了对象的多态性以后,我们在编译期,只能调用父类中声明的方法,但在运行期,我们实际执行的是子类重写父类的方法。
 *   总结:编译,看左边;运行,看右边。
 *   
 * 4.多态性的使用前提:  ① 类的继承关系  ② 方法的重写
 * 
 * 5.对象的多态性,只适用于方法,不适用于属性(编译和运行都看左边)
 * 
 * *************************************************************
 * 
 * 
 */
public class PersonTest {
	public static void main(String[] args) {

		Person p1 = new Person();
		p1.eat();

		Man man = new Man();
		man.eat();
		man.age = 25;
		man.earnMoney();

		// *************************************************
		System.out.println("*******************");
		// 对象的多态性:父类的引用指向子类的对象
		Person p2 = new Man();
		// Person p3 = new Woman();
		// 多态的使用:当调用子父类同名同参数的方法时,实际执行的是子类重写父类的方法 ---虚拟方法调用
		p2.eat();
		p2.walk();

		// p2.earnMoney();

		System.out.println(p2.id);// 1001

		System.out.println("****************************");
		// 不能调用子类所特有的方法、属性:编译时,p2是Person类型。
		p2.name = "Tom";
		// p2.earnMoney();
		// p2.isSmoking = true;
		// 有了对象的多态性以后,内存中实际上是加载了子类特有的属性和方法的,但是由于变量声明为父类类型,导致
		// 编译时,只能调用父类中声明的属性和方法。子类特有的属性和方法不能调用。

		// 如何才能调用子类特有的属性和方法?
		// 向下转型:使用强制类型转换符。
		Man m1 = (Man) p2;
		m1.earnMoney();
		m1.isSmoking = true;

		// 使用强转时,可能出现ClassCastException的异常。
		// Woman w1 = (Woman)p2;
		// w1.goShopping();

		/*
		 * instanceof关键字的使用
		 * 
		 * a instanceof A:判断对象a是否是类A的实例。如果是,返回true;如果不是,返回false。
		 * 
		 * 
		 * 使用情境:为了避免在向下转型时出现ClassCastException的异常,我们在向下转型之前,先
		 * 进行instanceof的判断,一旦返回true,就进行向下转型。如果返回false,不进行向下转型。
		 * 
		 * 如果 a instanceof A返回true,则 a instanceof B也返回true. 其中,类B是类A的父类。
		 */
		if (p2 instanceof Woman) {
			Woman w1 = (Woman) p2;
			w1.goShopping();
			System.out.println("******Woman******");
		}

		if (p2 instanceof Man) {
			Man m2 = (Man) p2;
			m2.earnMoney();
			System.out.println("******Man******");
		}

		if (p2 instanceof Person) {
			System.out.println("******Person******");
		}
		if (p2 instanceof Object) {
			System.out.println("******Object******");
		}

		// if(p2 instanceof String){
		//
		// }

		// 练习:
		// 问题一:编译时通过,运行时不通过
		// 举例一:
		// Person p3 = new Woman();
		// Man m3 = (Man)p3;
		// 举例二:
		// Person p4 = new Person();
		// Man m4 = (Man)p4;

		// 问题二:编译通过,运行时也通过
		// Object obj = new Woman();
		// Person p = (Person)obj;

		// 问题三:编译不通过
		// Man m5 = new Woman();

		// String str = new Date();

		// Object o = new Date();
		// String str1 = (String)o;

	}
}

public class Person {
	String name;
	int age;

	int id = 1001;

	public void eat() {
		System.out.println("人:吃饭");
	}

	public void walk() {
		System.out.println("人:走路");
	}

}

public class Man extends Person {

	boolean isSmoking;

	int id = 1002;

	public void earnMoney() {
		System.out.println("男人负责挣钱养家");
	}

	public void eat() {
		System.out.println("男人多吃肉,长肌肉");
	}

	public void walk() {
		System.out.println("男人霸气的走路");
	}

}

public class Woman extends Person {

	boolean isBeauty;

	public void goShopping() {
		System.out.println("女人喜欢购物");
	}

	public void eat() {
		System.out.println("女人少吃,为了减肥");
	}

	public void walk() {
		System.out.println("女人窈窕的走路");
	}
}

instanceof的理解:

/*
 * 建立InstanceTest 类,在类中定义方法method(Person e);
		在method中:
		(1)根据e的类型调用相应类的getInfo()方法。
		(2)根据e的类型执行:
		如果e为Person类的对象,输出:
		“a person”;
		如果e为Student类的对象,输出:
		“a student”
		“a person ” 
		如果e为Graduate类的对象,输出: 
		“a graduated student”
		“a student”
		“a person” 
 */
public class InstanceTest {

	public static void main(String[] args) {

		InstanceTest test = new InstanceTest();
		test.method(new Student());
	}

	public void method(Person e) {

		// 虚拟方法调用
		String info = e.getInfo();
		System.out.println(info);

		// 方式一
		// if(e instanceof Graduate) {
		// 		System.out.println("a graduated student");
		//		 System.out.println("a student");
		// 		System.out.println("a person");
		// } else if (e instanceof Student) {
		// 		System.out.println("a student");
		// 		System.out.println("a person");
		// } else {
		// 		System.out.println("a person");
		// }

		// 方式二
		if (e instanceof Graduate) {
			System.out.println("a graduated student");
		}

		if (e instanceof Student) {
			System.out.println("a student");
		}

		if (e instanceof Person) {
			System.out.println("a person");
		}

	}
}

class Person {
	protected String name = "person";
	protected int age = 50;

	public String getInfo() {
		return "Name: " + name + "\n" + "age: " + age;
	}
}

class Student extends Person {
	protected String school = "pku";

	public String getInfo() {
		return "Name: " + name + "\nage: " + age + "\nschool: " + school;
	}
}

class Graduate extends Student {
	public String major = "IT";

	public String getInfo() {
		return "Name: " + name + "\nage: " + age + "\nschool: " + school + "\nmajor:" + major;
	}
}

多态的考察:

//考查多态的笔试题目:
public class InterviewTest1 {

	public static void main(String[] args) {
		Base1 base = new Sub1();
		base.add(1, 2, 3);// sub_1

		Sub1 s = (Sub1) base;
		s.add(1, 2, 3);// sub_2
	}
}

class Base1 {
	public void add(int a, int... arr) {
		System.out.println("base1");
	}
}

class Sub1 extends Base1 {

	public void add(int a, int[] arr) {
		System.out.println("sub_1");
	}

	public void add(int a, int b, int c) {
		System.out.println("sub_2");
	}

}


结语


本人会持续更新文章的哦!希望大家一键三连,你们的鼓励就是作者不断更新的动力

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

麟-小白

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值