面向对象的特征
1 封装
在封装的时候首先我们需要定义一个类,类也是一个引用数据类型
封装是两个步骤,分别是封和装;
封:类的定义包括属性和行为,类和类的关系(继承,接口);
在定义属性的时候,一般都会选择private作为修饰符,这样做的好处是可以只提供只读或者只写的方法;可以对数据有效性,安全性进行代码控制;可以对外部隐藏类具体属性的存放细节(比如生日在内部存放三个int,外部返一个字符串);
装:信息的隐藏和方法的定义。
访问修饰符的区别
访问权限 类 包 子类 其他包
public ∨ ∨ ∨ ∨ (对任何人都是可用的)
protect ∨ ∨ ∨ × (继承的类可以访问以及和private一样的权限)
default ∨ ∨ × × (包访问权限,即在整个包内均可被访问)
private ∨ × × × (除类型创建者和类型的内部方法之外的任何人都不能访问的元素)
2. 继承(关键字:extends)
继承的概念:通过扩展一个已有的类,并继承类的属性和行为,类创建一个新的类,这种方式称为继承。
继承给我们带来的好处是代码的复用,实现效果的分离;在使用继承的时候一定要考虑类与类之间是否有is–a 的关系,不要滥用继承。
子类和父类的区别:
1.子类通过extends,可以获得父类的属性和行为,但是能否访问,要受访问修饰符的限制;
2.父类的属性和行为子类自动继承,但子类不能继承父类的构造方法;
问题:构造方法为什么不能被继承?
1.在语法上:构造方法的语法是方法名必须与类名保持一致,如果子类继承了父类的构造方法,那么子类就会有一个叫父类名的构造方法,这点与构造方法的语法冲突;
2.在场景上:构造方法的作用是产生对象,父类的构造方法是产生父类对象,子类构造方法是产生子类对象,如果子类通过继承拥有父类的构造,说明子类可以产生父类对象,没有见过哪个儿子可以生老爸的。
3.虽然父类的构造方法没有被子类继承,但是在构建子类对象的过程中,会先在内存中产生父类对象部分,再叠加子类特有部分,从而构建成完整的子类对象,也就是说在new一个子类对象的时候,会先调用父类构造再调用子类构造。Java的继承是采用这种内存叠加的方式。
4.如果子类定义了和父类一摸一样的属性,那么在子类对象身上有两个该属性,一个在父类对象部分,一个在子类特有部分。首先在设计上不应该这么设计,因为在设计父类的时候,已经考虑了共有的,设计子类的时候只该设计特有的,每个属性只应该考虑一次,不属于父类就属于子类;如果真的这样设计了,用super.属性名 表示定义在父类部分的属性,this.属性名 代表的是定义在子类部分的同名属性
5.如果子类定义了和父类一摸一样的方法呢?
1.它指的是所有类都有共同行为,所以我们要定义在父类,但不同子类又各有各的的实现,所以需要在子类中重新书写一次。
2.重写的要求
子类和父类的方法名必须要求一致;
参数列表必须一致(类型、个数、顺序);
返回类型必须一致;
子类访问修饰符不能比父类的该方法(范围)小;
子类重写后的方法不能比父类重写前的方法抛出更多的异常,这里的更多不是指个数,而是指范围。
这里可以引入一下重写和重载的区别:
两种方法都是相同的行为,不同的实现;
重载:同一个类,拥有多个重名方法,参数列表不同;
重写:不同的类,子类把继承于父类的行为重新实现一次,不同的子类可以有各自的实现,参数列表必须一致。
3. 多态
多态概念:相同的行为,不同的实现
静态多态:在编译期就能够确定代码中的对象执行的是哪个方法;
动态多态:在编译期不确定,要运行起来以后根据实际绑定的对象是谁,才能确定调用的是哪个方法。
多态带来了设计上的丰富度具体的技术:重载,重写,动态绑定。
动态绑定: 父类引用指向子类对象
编译通过:因为子类和父类的关系就是is–a关系,所以说子类对象就是父类类型,这个没有问题;
运行通过:因为每一个子类对象在产生过程中,都是使用内存叠加的方式,先产生父类对象部分(就是一个完整的父类对象),然后再叠加上子类特有部分,所以这个子类对象拥有父类类型自己特有的属性行为。
特例(动态多态的关键):如果子类重写了父类行为,那么只要对象是子类对象,不管用什么引用去指向(父类也好,子类也好),效果都会是子类自己重写后的效果。
由于父类的引用可以指向子类对象,而且还是自动类型转换,没有任何特殊语法,那么当我们在书写代码的时候,如果有一个父类引用,我们将不确定它到底指向的是哪种数据类型。所以引用一个关键字:instanceof。
instanceof:
1.是关键字,也是运算符;
2.它是专用于判定某个对象是否是某个类型的,运算的结果只能有两个true、false;
使用语法:对象 instanced 类型名。
4. 抽象(关键字:abstract)
当设计和抽取到达一定层次的时候,父类只能确定拥有什么方法,但并不能确定这个方法的具体实现,甚至父类也没有产生对象的必要;在这种情况下引入抽象,抽象带来的是上层设计人员更多的考虑的是结构,下层程序员在设计上结构的基础上去考虑具体实现。
语法:在class关键字之前加修饰符abstract。
影响:抽象类只是不能产生对象类,但是不影响别的内容
1.该有的属性、行为、构造、初始化块···都可以有;
2.仍然可以指向子类的对象,访问共有的行为和属性;
3.与子类的关系还是is–a 关系,甚至应该说是先考虑到is–a才能设计父类,再用过是否产生父类对象,来判断是否设计为abstract。
抽象方法:父类的某些方法,只能确定方法的声明(即子类共有的方法叫什么名字,有什么参数,返回值是什么类型,访问修饰符应该是什么),但不能确定方法具体实现(应为不同子类具体实现不同,所有具体实现应该让子类重写)。
语法:在方法的修饰符部分书写abstract修饰符,方法不能有实现部分(包括不能有{ }),在方法的参数列表最后用;号结束;
抽象类于抽象方法的关系:
1.有抽象方法的类一定是抽象类;
2.抽象类不一定有抽象方法;
3.抽象类可以通过继承传递的,比如:父类有3个抽象方法,子类只能确定实现其中1个或2个,那么子类仍然具有抽象方法,所以这个子类也必须是抽象类,再由子类去进一步重写实现。