一、面向对象的三大特征
(一)继承:多个类中都要使用到一些相同的属性或者方法,我们可以将这些属性和方法定义在一个公共类中,需要这些属性和方法的类,只要继承该公共类,就可以直接使用,不用在自己的类中定义。
将公共类叫做父类、基类、超类;继承父类的类叫做子类、派生类
优点:
简化代码;
扩展性,不用改变原有代码的情况下扩展出新功能。
1.java中类只能有一个父类,单继承 (一个子类只能继承一个父类)
2.继承具有传递性
eg:A: name
B extends A
C extends B
C也具有name属性
3.继承的关键字是extends
子类扩展于父类
class 子类 extends 父类{
}
注意:
在java中Object类是根类;
所有的类都直接或者间接的继承自Object
如果一个类没有显式的继承一个父类,默认的父类为Object(Object可为最初父类的父类)
(二)多态:一个事物或者一种行为表现出不同的状态或者形态
优点:
重写的扩展性和灵活性,子类继承父类后,如果父类实现方法的功能不满足需要才重写,在重写时不需要再改父类的代码,相当于拥有了父类的功能,可以任意的扩展,或者在子类中按照自己的需求修改功能的实现过程,修改时,修改时,通过super拥有父类功能的同时,再添加额外的功能;
重写发生在运行时,运行时根据具体的对象调用本身的方法,不会混乱。
1.重写(override):在子类中重写父类中的某些方法
使用场景:子类继承了父类中的方法,但是父类中方法的实现不符合子类的需求,子类就需要重写父类的方法
重写判断标准:子类中方法的返回值和方法名必须和父类一样;
参数列表中参数的个数以及对应位置的参数类型必须一样,但参数名无所谓(可以不一样)
(方法体不一样)
注意:
自己类中定义了该方法,就调用自己的;未定义,就调用父类的该方法
重写时,该方法的访问范围不能比父类的小
@xxx:注解
@Override添加到方法上,可以检查该方法是否是重写的方法
不是重写的方法的话,编译期间会报错;如果是,不报错
自己类中定义了该方法,就调用自己的;未定义,就调用父类的该方法
eg:class Animal{
void sleep() {
System.out.println("睡觉的方法");
}
class Lion extends Animal{
@Override
void sleep() {
System.out.println("狮子的睡觉方式");
//void sleep(int a) {} (该行非重写)
}
public class MyInherit {
public static void main(String[] args) {
Lion lion = new Lion();
lion.sleep(); //此行打印 狮子的睡觉方式
}
}
2.重载(overload)
重载判断标准:方法名一样,参数列表不同,就是重载(其余部分,比如返回值类型都不管)
( 参数列表中参数的个数以及对应位置的参数类型不同)
(没有父类也可重载,不需要接收父类,在自己类里面重载自己的方法;
重载之后,调用时,主要根据实参列表来区分,参数列表的数据类型为父类类名,优先执行子类同方法名的方法体,子类没有就执行父类同方法名的方法体)
eg:
class Lion extends Animal{
void run() {
System.out.println("奔跑");
}
void run(String dest) {
System.out.println("请跑到" + dest);
}
}
public class MyInherit {
public static void main(String[] args) {
Lion lion = new Lion();
lion.run(); //调用无参 奔跑
lion.run("河边"); //调用有参 请跑到河边
}
}
重写和重载的区别:
重写要求返回值类型、方法名、参数列表必须和父类中的一致;重载方法名一样,参数列表不同;
重写必须发生在父子类之间;重载不需要,可以重载自己的方法。
1、定义不同:重载是定义相同的方法名、参数不同,重写是子类重写父类的方法
2、范围不同:重载是在一个类中,重写是子类与父类之间的
3、多态不同:重载是编译时的多态性,重写是运行时的多态性
4、参数不同:重载的参数个数、参数类型、参数的顺序可以不同,重写父类子方法参数必须相同
5、修饰不同:重载对修饰范围没有要求,重写要求重写方法的修饰范围大于被重写方法的修饰范围
3.父类引用,引用子类对象
祖先类型 对象名 = new 子孙类();
代码执行分两个阶段
1.编译期
2.运行时
在编译期 调用方法或者变量,根据声明类型来判断是否可以调用。
只有当运行起来之后(运行时),会根据对象具体的类型去调用对应的方法
方法重写之后,调用的是哪个方法,看new部分(具体的对象的类型)
父子类中有同名的成员变量,使用的是哪个成员变量,看声明的类型即可
例如 声明的类型是Animal,那就使用Animal中的该成员变量
eg:class Animal{
int age = 10;
void sleep() {
System.out.println("睡觉的方法");
}
}
class Lion extends Animal{
int age = 100;
void sleep() {
System.out.println("狮子的睡觉方式");
}
}
public class MyInherit {
public static void main(String[] args) {
Animal animal = new Lion(); //此时编译时无Lion对象,运行时有
animal.sleep(); //此行打印 狮子的睡觉方式
System.out.println(animal.age); //打印 10
//(此处声明哪个类型,就用哪个里面的成员变量)
//Animal animal = new Lion(); animal.age打印10
//Lion animal = new Lion(); animal.age打印100
}
}
//(Dog lion = (Dog)animal;没有继承关系不能强转,有风险)
如何判断animal是否可以转换为Dog类型??
instanceof: (用来判断对象类型)
形式:对象名 instanceof 类名 ,会得到一个布尔值,true就是属于,false就是不属于
判断该对象 是否属于 该类型
null instanceof 任何类 返回值都是false
对象 instanceof 真实类型(具体类型)或者该类的祖先类 返回值都是true
System.out.println(null instanceof Object); //打印false
System.out.println(animal instanceof Object); //打印出true
//animal.run(); //此处编译期间animal无run方法,报错,需强转
强转
Lion lion = (Lion)animal; //此处未new,lion和animal引用同一内存空间,是同一对象
System.out.println(lion); //com.lanou.inherit.Lion@2a139a55
System.out.println(animal); //com.lanou.inherit.Lion@2a139a55
lion.run(); //强转后等同于animal.run(); 打印 奔跑
animal = new Dog(); //该行new Dog();,以下打印animal是Dog
if(animal instanceof Dog) {
Dog lion = (Dog)animal;
System.out.println("animal是Dog");
}else if(animal instanceof Lion) {
Lion lion = (Lion)animal;
System.out.println("animal是Lion");
}else {
System.out.println("animal不知道是什么");
}
// new Lion();打印animal是Lion
举例:数组存入多种类型数据
Object[] ob = new Object[] {1,"aa",1.5,new Dog(),new Animal()};
(三)封装:将数据和操作封装起来,尽可能的隐藏操作的实现细节,让使用者只关心该对象的功能如何使用,不用关心实现过程
将数据和功能都隐藏起来,配合访问控制符,限制使用者能访问哪些数据,如何访问这些数据;不能访问哪些数据和方法
二、面向对象的五大原则
1.单一职责原则(SRP)
一个类应该有且只有一个去改变它的理由,这意味着一个类应该只有一项工作。
2.开放封闭原则(OCP)
对象或实体应该对扩展开放,对修改封闭。
更改封闭即是在我们对模块进行扩展时,勿需对源有程序代码和DLL进行修改或重新编译文件!这个原则对我们在设计类的时候很有帮助,坚持这个原则就必须尽量考虑接口封装,抽象机制和多态技术!
3.里氏替换原则(LSP)
在对象 x 为类型 T 时 q(x) 成立,那么当 S 是 T 的子类时,对象 y 为类型 S 时 q(y) 也应成立。(即对父类的调用同样适用于子类)
4.依赖倒置原则(DIP)
高层次的模块不应该依赖于低层次的模块,他们都应该依赖于抽象。具体实现应该依赖于抽象,而不是抽象依赖于实现。
5.接口隔离原则(ISP)
不应强迫客户端实现一个它用不上的接口,或是说客户端不应该被迫依赖它们不使用的方法,使用多个专门的接口比使用单个接口要好的多!