目录
一、面向对象的概述:
二、封装:
1、封装概述
2、封装原则
3、封装好处
4、封装坏处
5、封装代码展示
三、继承:
1、概念:
2、实现格式:
3、特点:
4、好处:
5、弊端:
6、应用场景
7、继承中变量的访问特点:
8、关键字
9、方法重写
10、 Java继承中的注意事项:
11、代码演示:
(猫狗类继承动物类的案例,大家就自己试试吧?给大家演示一个完整例题。)
四、多态:
1、概念:
2、前提条件:
3、多态中成员变量的访问特点是什么?
4、多态中成员方法的访问特点是什么?
5、好处与弊端
6、类形态的转换
7、形式分类:
8、多态的优点:
9、什么是虚拟方法调用?
五、补充之六大原则:
1、单一职责原则 (SRP)(Single Responsibility Principle)
2、开放封闭原则OCP(Open-Close Principle)
3、里式替换原则LSP(the Liskov Substitution Principle LSP)
4、依赖倒置原则DIP(the Dependency Inversion Principle DIP)
5、接口分离原则ISP(the Interface Segregation Principle ISP)
6、迪米特法则(Law of Demeter,简称LoD)
面向对象简单来说就是将功能封装到对象(数据和操作结合)里,我们面向对象,让对象去完成这些功能。
什么是对象?万物皆对象,客观存在的事物皆是对象。
面向对象(Object Oriented,OO)是软件开发的方法。而面向对象的概念和应用已超脱于程序设计和软件开发,扩展到如数据库系统、交互式界面、应用结构、应用平台、分布式系统、网络管理结构、CAD技术、人工智能等领域。
面向对象是一种对现实世界理解和抽象的方法,是计算机编程技术发展到一定阶段后的产物。
JAVA是一门面向对象的语言,那么其面向对象主要有以下几个特性和原则:
三大特性:封装、继承、多态。
六大原则:SPR,CCP,LSP,DIP,ISP,LoD
今天我们主要来说一下面向对象的三大特性。
是面向对象三大特征之一(封装,继承,多态)
是面向对象编程语言对客观世界的模拟,客观世界里成员变量都是隐藏在对象内部的,外界是无法直接操作的。
将类的某些信息隐藏在类内部,不允许外部程序直接访问,而是通过该类提供的方法来实现对隐藏信息的操作和访问
成员变量private,提供对应的getXxx()/setXxx()方法
通过方法来控制成员变量的操作,提高了代码的安全性
把代码用方法进行封装,提高了代码的复用性。
如果封装的方法出现问题,或者修改,对于其所引用的对象,则需要大量的修改。
package sort;
public class Customer {
private String name;
private String id_card;
private String number;
private String home_address;
public Customer() {
super();
}
public Customer(String name, String id_card, String number, String home_address) {
this.name = name;
this.id_card = id_card;
this.number = number;
this.home_address = home_address;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getId_card() {
return id_card;
}
public void setId_card(String id_card) {
this.id_card = id_card;
}
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
public String getHome_address() {
return home_address;
}
public void setHome_address(String home_address) {
this.home_address = home_address;
}
public String say() {
String info = "姓名:" + name + "\n身份证号:" + id_card + "\n电话:" + number + "\n家庭住址:" + home_address;
return info;
}
}
成员属性,构造方法(构造器),get/set方法,命令方法。一个简单完整的封装就可以了。
封装可以使我们容易地修改类的内部实现,而无需修改使用了该类的客户代码。就可以对成员变量进行更精确的控制。
比如:
public String getSexName() {
if("0".equals(sex)){
sexName = "女";
}else if("1".equals(sex)){
sexName = "男";
}else{
sexName = "人妖";
}
return sexName;
}
继承也是面向对象三大特征之一。
可以使得子类具有父类的属性和方法,还可以在子类中重新定义,
以及追加属性和方法。
通过extends关键字实现继承
格式: class 子类 extends 父类 { }
子类可以有父类的内容,子类还可以有自己特有的内容
提高了代码的复用性(多个类相同的成员可以放到同一个类中)
提高了代码的维护性(如果方法的代码需要修改,修改一处即可)
继承让类与类之间产生了关系,类的耦合性增强了,当父类发生变化时子类实现也不得不跟着变化,削弱了子类的独立性。
使用继承,需要考虑类与类之间是否存在is..a的关系,不能盲目使用继承
is..a的关系:谁是谁的一种,例如:老师和学生是人的一种,那人就是父类,学生和老师就是子类。
例如:假设我有两个类A和B,如果满足A是B的一种,或者B是A的一种,就说明他们存在继承关系,这个时候就可以考虑使用继承来体现,否则就不能滥用继承。
在子类方法中访问一个变量,采用的是就近原则。
子类局部范围找
子类成员范围找
父类成员范围找
如果都没有就报错(不考虑父亲的父亲…)
this关键字:this:代表本类对象(成员变量)的引用。
super关键字:super:代表父类存储空间的标识(可以理解为父类对象引用)。
this和super的使用区别(三种访问方法):
成员变量:
this.成员变量 - 访问本类成员变量
super.成员变量 - 访问父类成员变量
成员方法:
this.成员方法 - 访问本类成员方法
super.成员方法 - 访问父类成员方法
构造方法:
this(…) - 访问本类构造方法
super(…) - 访问父类构造方法
概念:子类出现了和父类中一模一样的方法声明(方法名一样,参数列表也必须一样).
应用场景:当子类需要父类的功能,而功能主体子类有自己特有内容时,可以重写父类中的方法,这样,即沿袭了父类的功能,又定义了子类特有的内容。
@Override:注解:用来检测当前的方法,是否是重写的方法,起到【校验】的作用。
方法重写的注意事项:
1. 私有方法不能被重写(父类私有成员子类是不能继承的)
2. 子类方法访问权限不能更低(public > 默认 > 私有)
java中,类支持单继承,不支持多继承,支持多层继承。
错误范例:class A extends B, C { }
Java中类支持多层继承
正确范例:
class A {}
class B extends A{ }
class C extends B{ }
1、这是一个封装类
package inherit01;
/*
(1)定义一个ManKind类,包括
成员变量int sex和int salary;
方法void manOrWoman():根据sex的值显示“man”(sex==1)或者“woman”(sex==0);
方法void employeed():根据salary的值显示“no job”(salary==0)或者“ job”(salary!=0)。
*/
public class ManKind {
public int sex;
public int salary;
public ManKind() {
}
public ManKind(int sex, int salary) {
this.sex = sex;
this.salary = salary;
}
public int getSex() {
return sex;
}
public void setSex(int sex) {
this.sex = sex;
}
public int getSalary() {
return salary;
}
public void setSalary(int salary) {
this.salary = salary;
}
public void manOrWoman(int sex){
if(sex == 1){
System.out.println("man");
}else if(sex == 0){
System.out.println("woman");
}
}
public void employeed(int salary){
if(salary == 0){
System.out.println("no job");
}else {
System.out.println("job");
}
}
}
2、这是继承了上一个封装的类
package inherit01;
/*
(2)定义类Kids继承ManKind,并包括
成员变量int yearsOld;
方法printAge()打印yearsOld的值。
*/
public class Kids extends ManKind{
public int yearsOld;
public Kids() {
}
public Kids(int yearsOld) {
this.yearsOld = yearsOld;
}
public int getYearsOld() {
return yearsOld;
}
public void setYearsOld(int yearsOld) {
this.yearsOld = yearsOld;
}
public void printAge(int yearsOld){
System.out.println("yearsOld: "+yearsOld);
}
}
最后定义一个测试类
package inherit01;
/*
(3)定义类KidsTest,在类的main方法中实例化Kids的对象someKid,用该对象访问
其父类的成员变量及方法。
*/
public class KidsTest {
public static void main(String[] args) {
Kids someKid=new Kids();
someKid.setSex(1);
someKid.setSalary(6666);
someKid.setYearsOld(25);
someKid.manOrWoman(someKid.getSex());
someKid.employeed(someKid.salary);
someKid.printAge(someKid.yearsOld);
}
}
同一个对象,在不同时刻表现出来的不同形态。(多态性:多态性是对象多种表现形式的体现)
(例如:我们可以说猫是猫,猫 cat = new cat(); 我们也可以说猫是动物:动物 animal = new 猫(); 这时,猫在不同时刻表现出来了不同的形态,这就是多态。)
要有继承/实现关系
要有方法重写
要有父(类/接口)引用指向子(子/实现)类对象
(编译看左边、运行看左边)
举例:比如还是猫,我们创建对象,Animal a=new Cat(); 当我们编译(sout(a.属性)),这时我们会发现,这里的属性,寻找的是Animal类中的属性,而不是Cat中的属性。 同理,运行时(即控制台输出时的值),我们会发现也看的Animal类中的数据属性。
(编译看左边、运行看右边)
同上,编译时看创建对象的左边,即Animal父类,而不是猫Cat类。而此时的运行,却是看右边的方法里数据内容了。
(为什么成员变量和成员方法的访问不一样呢?因为成员方法有重写,而成员变量没有。)
好处:提高了代码的扩展性。
定义方法时,如果将父类型作为参数,在使用方法时,可以传递任意子类对象。
弊端:不能使用子类特有的成员,不能使用子类的特有功能,比如特有的方法。
向上转型:父类引用指向子类对象其实就是向上转型。例如:
Animal a = new Dog(); Animal是父类, Animal a中的a就是父类引用,new Dog就是子类对象。
向下转型:将父类型的引用转换成具体的子类对象。转换格式:
子类 对象名 = (子类)父类引用; 例如:Cat c= (Cat)a;
(1):具体类多态(几乎是不用的)
(2):抽象类多态
(3):接口多态
(1). 消除类型之间的耦合关系
(2). 可替换性
(3). 可扩充性
(4). 接口性
(5). 灵活性
(6). 简化性
1.子类中定义了与父类同名同参数的方法,在多态情况下,将此时父类的方法称为虚拟方法,父类根据赋给它的不同子类对象,动态调用属于子类的该方法。这样的方法调用在编译期是无法确定的。
2.多态是运行时行为,不是编译时行为。
10、代码演示
(1).我们定义一个父类:Animal
public class Animal {
public void eat() {
System.out.println("动物吃东西");
}
}
(2).我们再定义一个子类继承父类:Cat extends Animal
public class Cat extends Animal {
@Override
public void eat() {
System.out.println("猫吃鱼");
}
}
(3)最后我们再定义一个测试类,实现多态
多态:
同一个对象,在不同时刻表现出来的不同形态
举例:猫
我们可以说猫是猫:猫 cat = new 猫();
我们也可以说猫是动物:动物 animal = new 猫();
这里猫在不同的时刻表现出来了不同的形态,这就是多态
多态的前提和体现
有继承/实现关系
有方法重写
有父类引用指向子类对象
*/
public class AnimalDemo {
public static void main(String[] args) {
//有父类引用指向子类对象
Animal a = new Cat();
}
}
总述:面向对象的六大原则
六大原则指的是单一职责原则、开闭式原则、里氏替换原则、依赖倒置原则、接口隔离原则以及迪米特原则。
就一个类而言,应该只有一个引起它变化的原因。简单来说,一个类应该是一组相关性很高的函数和数据的封装。因为单一职责的划分界限并不是那么清晰,每个人的理解不一样,这就导致了不同的人划分的类承担的职责也不一样,就图片加载的例子来说,可能有的人就认为整个图片加载是一组相关性很高的功能,于是将其放入在一个类中处理。一般来说,我们首先需要具备单一职责原则的思想,如果发现一个类承担了太多的功能,这个时候就要考虑将某些功能划分到其他类中去处理,具体的划分细节要平开发者的个人经验。
软件中的对象应该对扩展是开放的,但是,对修改是关闭的。软件开发过程中,需求是不断变化的,因为变化、升级和维护等原因需要对原有的软件代码进行修改,而一旦对原有的代码进行修改,就有可能影响到原有的模块,引起bug,因此,在软件开发过程中,我们应该尽可能通过扩展的方式实现变化,而不是修改原有的代码,当然,这是一种理想的情况,在实际的软件开发中,完全通过扩展的方式实现变化是不现实的。
所有引用基类的地方都必须能够透明的使用其子类。通过建立抽象,让高层次模块依赖于抽象类,在运行时在替换成具体的实现类,保证了系统的扩展性和灵活性。
依赖倒置原则指的是高层次模块不应该依赖于低层次模块的具体实现,两者都应该依赖其抽象,具体如下:
1、高层次模块不应该依赖低层次模块的具体实现,两者都应该依赖其抽象;
2、抽象不应该依赖细节;
3、细节应该依赖抽象。
在面向对象语言中,抽象指的是接口或者抽象类,两者都不能被实例化,而细节指的是具体的实现类。其实一句话就可以概括,面向接口编程,或者说面向抽象编程,试想一下,如果类和类之间之间依赖细节,那么这两个类将会紧紧的耦合在一起,这就意味着,修改了其中一个类,很可能需要对另外一个类也需要进行修改,并且这样也大大限制了系统的可扩展性。依赖倒置原则提供了一种解耦方式,通过抽象让类和类之间不再依赖细节,而是在具体运行时再进行替换。
类间的依赖关系应该建立在最小的接口之上。接口隔离原则将非常庞大、臃肿的接口拆分成更小更具体的接口。
一个接口定义的过于臃肿,则代表它的每一个实现类都要考虑所有的实现逻辑。如果一个类实现了某个接口,也就是说这个类承载了这个接口所有的功能,维护这些功能成为了自己的职责。这就无形中增加了一个类的负担。
这里有一点需要说明,接口定义要小,但是要有限度,对接口细化可以增加灵活性,但是过度细化则会使设计复杂化。同时接口的使用率不高,提高了代码的维护成本。这种极端的体现就是每个接口只含有一个方法,这显然是不合适的。
一个对象应该对其他对象有最小的了解。迪米特原则也称作最小知道原则,即类和类直接应该建立在最小的依赖之上,一个类应该对其依赖的类有最小的了解,即只需要知道其所需要的方法即可,至于其内部具体是如何实现的则不用关心。迪米特原则的目的是减少类和类之间的耦合性,类和类之间的耦合性越大,当一个类发生修改后,会其他类造成的影响也越大。
以上,就是今天小知识课堂的全部了,咚咚咚(敲黑板),下课。