第三章:java的三大特征

系列文章目录


文章目录

  • 系列文章目录
  • 前言
  • 一、封装
  • 二、继承
  • 三、多态
  • 总结


前言

面向对象编程有三大特征: 封装、 继承和多态。


一、封装

封装(encapsulation)就是把抽象出的数据【属性】和对数据的操作【方法】封装在一起,数据被保护在内部,程序的其他部分只有通过被授权的操作【方法】,才能对数据进行操作。

封装的好处
隐藏实现细节:方法(链接数据库)<–调用(传入参数)
可以对数据进行验证,保证安全合理
实现封装的三个步骤
属性私有化private
提供一个公共的(public)set方法,用于对属性判断并赋值
提供一个公共的(public)get方法,用于获取属性的值

二、继承

继承可以解决代码复用,让我们的编程更加靠近人类思维.当多个类存在相同的属性(变量)和方法时,可以从这些类中抽象出父类,在父类中定义这些相同的属性和方法, 所有的子类不需要重新定义这些属性和方法, 只需要通过 extends 来声明继承父类即可。
第三章:java的三大特征_第1张图片

class 子类 extends 父类
{}
继承的好处
代码的复用性提高了
代码的扩展性和维护性提高了
继承的使用细则
子类会自动拥有父类定义的属性和方法
子类又叫派生类,父类又叫超类、基类
子类必须调用父类的构造器, 完成父类的初始化
不能滥用继承, 子类和父类之间必须满足 is-a 的逻辑关系
java 所有类都是 Object 类的子类, Object 是所有类的基类
父类构造器的调用不限于直接父类! 将一直往上追溯直到 Object 类(顶级父类)
子类最多只能继承一个父类(指直接继承), 即 java 中是单继承机制
super 在使用时, 必须放在构造器第一行(super 只能在构造器中使用)
super() 和 this() 都只能放在构造器第一行, 因此这两个方法不能共存在一个构造器
如果希望指定去调用父类的某个构造器, 则显式的调用一下 : super(参数列表)
子类继承了所有的属性和方法, 非私有的属性和方法可以在子类直接访问, 但是私有属性和方法不能在子类直接访问, 要通过父类提供公共的方法去访问
当创建子类对象时, 不管使用子类的哪个构造器, 默认情况下总会去调用父类的无参构造器, 如果父类没有提供无参构造器, 则必须在子类的构造器中用 super 去指定使用父类的哪个构造器完成对父类的初始化工作, 否则, 编译不会通过
继承的本质
先加载父类信息和父类属性
首先看本类是否有该属性或方法,如果本类没有就看父类有没有,直到Object
找到了但不能访问或找不到就报错,如此反复循环寻找
package com.hspedu.extend_;
/**
* 讲解继承的本质
*/
public class ExtendsTheory {
	public static void main(String[] args) {
	Son son = new Son();//内存的布局
	//?-> 这时请大家注意, 要按照查找关系来返回信息
	//(1) 首先看子类是否有该属性
	//(2) 如果子类有这个属性, 并且可以访问, 则返回信息
	//(3) 如果子类没有这个属性, 就看父类有没有这个属性(如果父类有该属性, 并且可以访问, 就返回信息..)
	//(4) 如果父类没有就按照(3)的规则, 继续找上级父类, 直到 Object...
	System.out.println(son.name);//返回就是大头儿子
	//System.out.println(son.age);//返回的就是 39
	//System.out.println(son.getAge());//返回的就是 39
	System.out.println(son.hobby);//返回的就是旅游
	}
} 
class GrandPa { //爷类
	String name = "大头爷爷";
	String hobby = "旅游";
} 
class Father extends GrandPa {//父类
	String name = "大头爸爸";
	private int age = 39;
	public int getAge() {
		return age;
	}
} 
class Son extends Father { //子类
	String name = "大头儿子";
}

第三章:java的三大特征_第2张图片

三、多态

方法或对象具有多种形态,多态是建立在封装和继承基础之上的,多态的前提的是:两个对象(类)存在继承关系。

多态的应用
方法的多态:方法的重载/重写
对象的多态:编译类型与运行类型
属性没有多态:属性的值看编译类型
编译类型与运行类型
编译类型为对象引用的类型,运行类型为对象本身的类型
编译类型看定义时 = 号的左边,运行类型看 = 号的右边
一个对象的编译类型和运行类型可以不一致
编译类型是不能改变的(javac)
运行类型是可以改变的(java)
向上转型
本质父类的引用指向子类对象
父类类型 引用名 = new 子类类型();
可以调用父类中的所有成员(需要遵守访问权限)
不能调用子类中特有成员
最终运行效果看子类的具体实现
向下转型
本质子类的引用指向子类对象
子类类型 引用名 = (子类类型)父类引用
只能强转父类的引用,不能强转父类对象
要求父类引用必须指向当前目标类型的对象
可以调用子类类型中所有成员(需要遵守访问权限)
instanceof 比较操作符
bb instanceof BB
用于判断对象的运行类型是否为 XX 类型或 XX 类型的子类型
动态绑定机制
当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定
当调用对象属性时,没有动态绑定机制,哪里声明,哪里使用
package com.hspedu.poly_.dynamic_;

public class DynamicBinding {

	public static void main(String[] args) {
		//a 的编译类型 A, 运行类型 B
		A a = new B();//向上转型
		System.out.println(a.sum());//?40 -> 30
		System.out.println(a.sum1());//?30-> 20
	}
} 
		
class A {//父类
	public int i = 10;
	//动态绑定机制:
	public int sum() {//父类 sum()
		return getI() + 10;//20 + 10
	} 
	public int sum1() {//父类 sum1()
		return i + 10;//10 + 10
	} 
	public int getI() {//父类 getI
		return i;
	}
} 

class B extends A {//子类
	public int i = 20;
	// public int sum() {
	// return i + 20;
	// }
	public int getI() {
		//子类 getI()
		return i;
	}
	// public int sum1() {
	// return i + 10;
	// }
}
多态的应用
多态数组:用数组指向向上转型的对象,数组存储的是父类型编译类型的对象名
多态参数:方法定义的形参类型为父类型,实参类型允许为子类类型
package com.hspedu.poly_.polyarr_;

public class PloyArray {
	public static void main(String[] args) {
	//应用实例:现有一个继承结构如下: 要求创建 1 个 Person 对象、
	// 2 个 Student 对象和 2 个 Teacher 对象, 统一放在数组中, 并调用每个对象 say 方法
	
	Person[] persons = new Person[5];
	persons[0] = new Person("jack", 20);
	persons[1] = new Student("mary", 18, 100);
	persons[2] = new Student("smith", 19, 30.1);
	persons[3] = new Teacher("scott", 30, 20000);
	persons[4] = new Teacher("king", 50, 25000);
	
	//循环遍历多态数组, 调用 say
		for (int i = 0; i < persons.length; i++) {
			//老师提示: person[i] 编译类型是 Person ,运行类型是是根据实际情况有 JVM 来判断
			System.out.println(persons[i].say());//动态绑定机制
			//这里大家聪明. 使用 类型判断 + 向下转型.
			if(persons[i] instanceof Student) {//判断 person[i] 的运行类型是不是 Student
				Student student = (Student)persons[i];//向下转型
				student.study();
				//小伙伴也可以使用一条语句 ((Student)persons[i]).study();
			} else if(persons[i] instanceof Teacher) {
				Teacher teacher = (Teacher)persons[i];
				teacher.teach();
			} else if(persons[i] instanceof Person){
				//System.out.println("你的类型有误, 请自己检查...");
			} else {
				System.out.println("你的类型有误, 请自己检查...");
			}
		}
		
	}
	
}

package com.hspedu.poly_.polyparameter_;

public class PloyParameter {

	public static void main(String[] args) {
		Worker tom = new Worker("tom", 2500);
		Manager milan = new Manager("milan", 5000, 200000);
		PloyParameter ployParameter = new PloyParameter();
		ployParameter.showEmpAnnual(tom);
		ployParameter.showEmpAnnual(milan);
		
		ployParameter.testWork(tom);
		ployParameter.testWork(milan);
	} 
	//showEmpAnnual(Employee e)
	//实现获取任何员工对象的年工资,并在 main 方法中调用该方法 [e.getAnnual()]
	public void showEmpAnnual(Employee e) {
		System.out.println(e.getAnnual());//动态绑定机制.
	}
	//添加一个方法, testWork,如果是普通员工, 则调用 work 方法, 如果是经理, 则调用 manage 方法
	public void testWork(Employee e) {
		if(e instanceof Worker) {
			((Worker) e).work();//有向下转型操作
		} else if(e instanceof Manager) {
			((Manager) e).manage();//有向下转型操作
		} else {
			System.out.println("不做处理...");
		}
	}
}


总结

封装、 继承和多态每一个特征都很重要。

你可能感兴趣的:(#,java面向对象编程中级知识,java)