OOP(面向对象编程)的三大特征(重点)

OOP的三大特征

  • 封装/隐藏(encapsulation)
  • 继承(inherit)
    • 继承的优点
    • 关键字
    • 继承的主要特性
    • 重写
    • Object类
    • super关键字
    • 继承内存图分析
    • 继承vs组合
    • final
  • 多态
    • 必要条件
    • 底层实现原理分析

封装/隐藏(encapsulation)

在程序的设计过程中,为了提高系统安全性及便捷性,我们追求的是 “高内聚、低耦合”
高内聚:类内部的数据操作细节自己完成,不允许暴露给用户
低耦合:仅暴露少量方法给用户使用

在开发过程中,我们一般通过访问修饰符来实现封装,各种访问修饰符的访问权限,如图所示:

在这里插入图片描述

封装要点:
1:除非确定该属性会被子类继承,否则全部使用private修饰;
2:要求提供相应的get/set来访问属性(这些方法要求用public修饰,从而提供对属性的读写操作);
3:仅用于本类的辅助性方法用private,希望被其他类调用的用public;

继承(inherit)

我们知道类是对对象的抽象(抽出“象”的部分);而继承则是对某一批类的抽象,从而实现对现实世界更好的建模;

继承的优点

1:提高代码复用;(从复用性角度来考虑,组合和继承一样;后面会详细对比)
2:从OOA(面向对象分析)和OOD(面向对象设计)方面考虑,更加方便我们建模(建立整个系统的模型、结构);

关键字

extends(扩展);子类是父类的扩展

继承的主要特性

1:如果类未通过extends来继承父类,系统默认父类为java.lang.Object(根基类);
2:Java为单继承(C++为多继承);在程序较为复杂时,多继承会引起混乱,使得程序过于复杂,系统难以维护(但是Java中可通过接口来实现多继承)
3:子类继承父类,可以获得父类的所有属性和方法(记住一点:构造方法除外);

重写

重写特点:
1:方法名相同;
2:返回值相同;
3:参数列表相同;
4:不能缩小访问权限(多态的关系);

重写运用较多的地方在于子类重写父类方法;但普通方法也可重写;
重写和重载二者没有任何关系,不要搞混淆了!

Object类

Object是所有Java类的根基类;如果在类的声明中未通过extends关键字指明父类;则默认父类为Object;
Object中的toString方法经常会被我们重写;但我们需要知道在Object类中toString方法返回的是类名+包名+@+哈希码(哈希码根据对象内存位置生成,唯一不重复)

super关键字

super指代的是父类对象的直接引用,可通过super来调用父类中被子类覆盖(重写)的属性和方法;
super作用在普通方法中没有顺序,随意调用;但是在构造方法中调用必须放在第一句;并且在构造方法中,如果第一句没有加super();系统会默认调用super()作为父类的初始化函数;(在初始化一个对象时,系统会默认调用该对象的无参构造方法,无参构造方法里会默认调用父类无参构造方法,如果未指定父类,调用Object的无参构造方法;最后返回的对象呈“包裹结构”

继承内存图分析

首先我们先定义三个类:

父类—Animal

package com.project.inherit;

public class Animal {
	String eye;
	
	public Animal(){
		super();
		System.out.println("创建一个动物");
	}
	
	public void eat(){
		System.out.println("动物吃饭");
	}
	
	public void run(){
		System.out.println("动物奔跑");
	}
}

子类----Brid

package com.project.inherit;

public class Brid extends Animal {
	
	String wing;
	
	public Brid(){
		//在无参构造方法中调用被子类覆盖的父类run方法
		super.run();
		System.out.println("创建一只鸟");
	}
	
	//重写Animal run方法
	public void run(){
		System.out.println("小鸟飞");
	}
	
	//子类特有方法sleep
	public void sleep(){
		System.out.println("小鸟睡觉");
	}
}

测试类—Test

package com.project.inherit;

public class Test {
	public static void main(String[] args) {
		Brid brid = new Brid();
		brid.wing = "翅膀";	//子类特有属性
		brid.eye = "双眼";	//父类属性
		brid.sleep();		//子类特有方法
		brid.run();			//重写了父类方法
		brid.toString();    //根基类方法
	}
}

内存分析图:
OOP(面向对象编程)的三大特征(重点)_第1张图片

继承vs组合

组合是什么:
去除继承概念,我们将父类作为一个对象new出来直接放到子类中,子类通过这个“组合”对象获取到该对象的方法和属性;比如:

package com.project.inherit;

public class Brid {
	
	String wing;
	
	public Brid(){
		Animal animal = new Animal();   //去除了Brid和Animal的继承关系,将Animal作为对象
										//直接放进来;作用同继承一样。
		animal.run();
	}
}

因此,我们可以得出如果仅从代码复用的角度来考虑,组合完全可以替代继承;
但是从程序建模角度来考虑,继承比组合更加适用;继承会使程序的结构层次更加清晰,扩展性更强;
我们可以表示为:
is a的关系用继承;(比如小鸟是动物,这种关系用继承较好)
has a的关系用组合;(小鸟有一对翅膀,这种关系用组合更加合适)

final

从final这个单词的意思“最终”我们就可以看出,它对程序会有什么样的影响了;
1:修饰变量:常量,不可修改;
2:修饰方法:不可重写(可重载);
3:修饰类:不能继承其他类,也不能有子类;(比如:Math类,String类)

多态

多态主要是用来实现动态联编的,换句话说,就是程序的最终形态是由运行时决定的,而非编译时决定,即声明的引用变量最终会指向哪个实例对象;

必要条件

1:必须要有继承;
2:子类必须重写父类方法;
3:父类引用指向子类对象;

底层实现原理分析

首先,先声明三个类:
父类—Animal

package com.project.polymorphism;

public class Animal {
	String eye;
	
	public void voice(){
		System.out.println("动物叫声");
		testThis();
	}
	
	public void testThis(){
		System.out.println("this is animal");
	}
}

子类—Dog

package com.project.polymorphism;

public class Dog extends Animal{

	public void voice(){
		System.out.println("汪汪");
	}
	
	public void lookDoor(){
		System.out.println("看门");
	}
	
	public void testThis(){
		System.out.println("this is dog");
	}
}

测试类—Test

package com.project.polymorphism;

public class Test {
	
	public static void testAnimalVoice(Animal a){
		//这里的a在编译时会被统一成Animal对象,而运行时,被解释成Dog对象
		a.voice();
		
		//从包裹结构来看,可以知道,Dog对象中包含Animal对象
		//因此,a对象属于Dog类在编译上不会出错,但是在运行时必须强制转换成Dog对象
		if(a instanceof Dog){
			((Dog) a).lookDoor();
		}
	}
	
	public static void main(String[] args) {
		Animal d = new Dog();
		testAnimalVoice(d);
		
		//测试this在继承关系中所代表的对象
		d.testThis();
	}
}

以上代码在运行时在内存中的表现形式为,如图所示:
OOP(面向对象编程)的三大特征(重点)_第2张图片
说明:
1:在Test测试类中我们可以发现,一共有两个引用变量(d和a),因此,将这两个引用变量放入栈中;

2:通过继承关系我们可以知道,new出的新对象是包裹结构,如上图所示;

3:super对象中的隐式参数super指代的是直接父类的引用,而通过Test测试类中的d.testThis()方法(最后打印结果为,this is dog)我们可以得出,this指代的是最终成型的对象,也就是最外层对象Dog,因此在父类Animal的voice方法中调用testThis()方法(等同于this.testThis()),其实调用的是子类Dog的testThis()方法;

你可能感兴趣的:(JAVA知识体系)