对象是个自包含实体,用一组可识别的特性和行为来标识,面对对象编程就是针对对象编程,类是具有相同属性和功能的对象的抽象的集合。
面向对象应满足 开放-封闭原则,对扩展开放,对修改封闭。
面向对象三大原则,封装,继承,多态,
对象的继承是is-a的关系,如果是has-a就是聚合关系
继承的缺点打破封装,增加耦合
多态是不同对象执行相同动作,但要通过它们自己的实现代码执行。
抽象类是个抽象概念,提供了一个继承的出发点,当设计一个抽象类时,一定是用来继承的,在一个继承关系形
成的等级结构中,树叶结点表示具体类,树枝节点是抽象类
1.类是对对象的抽象,抽象类是对类的抽象,接口是对行为的抽象
2.如果行为跨越不同类的对象,可使用接口,对相似的类对象,用抽象类
3.从设计角度,抽象类是从子类中发现公共的东西,泛化出父类,然后子类继承父类,接口是根本不知道子类的存在,方法如何实现还不确认,预先定义
JAVA中类和类之间的关系总结为:关联 聚集 依赖 泛化 实现
1.关联,对应关系 一对一关联 一对多关联 多对多关联,有单向关联和双向关联, 用带箭头的实线表示 如客户与订单 是一对多关系,代码中表现:
public class Customer {
private Set<Order> set;
public Set<Order> getSet() {
return set;
}
public void setSet(Set<Order> set) {
this.set = set;
}
......
}
Customer类再加一些功能就可能完成自身相关的功能,这样的Bean用到很多,特别在ZB,还有 YY都喜欢这样写,一个Bean完成自身相关的很多功能。
2.聚集:整体与部分关系,有弱聚集和强聚集之分,用带箭头的菱形表示,代码中表现类似关联。
3.依赖:顾名思义 就是谁依赖谁实现,不会在类中定义为属性,代码表现为作为一个参数传进去,调用该参数的方法。用带箭头的虚线表示。
4.泛化:就是类的继承关系,用带三角形的实线表示。
5.实现:就是实现接口,用带三角形的虚线表示。
说说现实中的依赖,只要调用所依赖对象的方法即可,如画图,只要图.draw()即可,打气筒和自行车,自行车只要打气筒.打气()即可。关联就是一种对应关系,聚集就是多个组成一个整体。整体的属性包含这些部分,关联和聚集的代码很类似。
面向对象最重要的:继承(泛化)和实现
继承可继承父类的成员变量和成员方法,同一个包内的可继承默认访问级别的成员变量和成员方法。
package demo.extend;
public class Base {
public int basePublicVar = 1;
protected int baseProtectedVar = 2;
int baseDefaultVar = 3;
private int basePriviteVar = 4;
public void basePublicMethod(){
System.out.println("This is base public method");
}
public void baseProtectedMethod(){
System.out.println("This is base protected method");
}
public void baseDefaultMethod(){
System.out.println("This is base default method");
}
private void basePrivateMethod(){
System.out.println("This is base private method");
}
}
<!-- lang: java -->
package demo.extend;
public class Sub extends Base{
public static void main(String[] args) {
Sub sub = new Sub();
System.out.println(sub.basePublicVar);
System.out.println(sub.baseDefaultVar);
System.out.println(sub.baseProtectedVar);
sub.basePublicMethod();
sub.baseProtectedMethod();
sub.baseDefaultMethod();
}
}
继承是类与类之间,一个类里面有方法的重载,就是一个方法有多种实现方式,采用哪种实现方式,取决于传入的参数,表表现为方法名相同,参数不同。
类与类继承之间有方法的覆盖,就是某一个子类和父类的同方法的实现方式不同,表现为子类的方法和父类的方法名 参数 返回类型都相同。
但注意几个问题:
1.子类不能缩小父类访问权限。
2.子类不能抛出被父类还多的异常且子类的异常必须与父类异常相同或是其子类。
3.静态方法也可以覆盖,注意静态方法都是与所属的类绑定 实例方法都是与所属的对象绑定。
4.private的方法不能覆盖。
1和2其实是废话,
如果1成立,声明为Base类型的调用其Sub类的方法,如果缩写为private了,Base类型的事例就不能访问Sub的了。
如果2成立,Sub抛出了更多异常,就出现Base捕获不到那些更多的异常了。
3也是废话,成员变量成员方法其实都是和所声明的类绑定,但如声明的变量指向子类的实例,那就动态寻找执行子类的方法,声明的类没变 引用的变了。
4更是废话,覆盖的前提就是子类首先能继承父类的方法,继承不了哪来覆盖?
下边这个输出结果是什么?
package demo.extend;
public class Base1 {
public int var = 4;
public static int staticVar = 5;
public void method(){
System.out.println("This is the base's method");
}
public static void staticMethod(){
System.out.println("This is the base's static method");
}
}
<!-- lang: java -->
package demo.extend;
public class Sub1 extends Base1 {
public int subVar=1;
public int var = 10;
public static int staticVar = 100;
public void method(){
System.out.println("This is this sub's method");
}
public void methodBySuper(){
super.method();
}
public static void staticMethod(){
System.out.println("This is this sub's static method");
}
/**
* @param args
*/
public static void main(String[] args) {
Base1 baseSub = new Sub1();
System.out.println(baseSub.var);
System.out.println(baseSub.staticVar);
//baseSub.subVar; //编译能通过不?要咋访问到Sub的subVar?可强制转换
//((Sub1)baseSub).subVar即可。
baseSub.method();
baseSub.staticMethod();
// baseSub.methodBySuper(); //编译能通过不?
Sub1 subSub = new Sub1();
System.out.println(subSub.var);
System.out.println(subSub.staticVar);
subSub.method();
subSub.methodBySuper();
subSub.staticMethod();
}
}
4
5
This is this sub's method
This is the base's static method
10
100
This is this sub's method
This is the base's method
This is this sub's static method
子类父类的super this的关键字:
1.super在父类方法或者变量被子类屏蔽的时候用来调用父类的方法和变量
2在构造方法中调用父类的构造方法时用。
3.记住this是指向当前对象的指针。
4.super和this不能用在静态方法中,只能在构造方法和实例方法中使用。
关于多态,
1.实例方法与引用变量实际引用的对象绑定,这属于动态绑定,是在运行时由JAVA虚拟机动态决定的。
2.静态方法与引用变量所声明的类型绑定,这属于静态绑定,是在编译阶段做了绑定。
3.成员变量与引用变量所声明的类型绑定,这属于静态绑定,是在编译阶段做了绑定。
设计继承时,自下而上抽象,抽出子类共有属性和方法,形成父类,如果某方法实现适合大部分子类那就在父类实现这个方法,如果无法实现,但子类都有,就定义为抽象方法,一般位于继承的最上层的父类用于描述系统对外提供哪些服务(同接口)。
这里参考LM的系统实现,继承的很好。
继承的最大缺点就是打破封装,封装就是每个类都封装自己的成员变量和成员方法,继承允许子类访问父类,二者变成紧耦合关系了,父类变化子类有时就得变化,这样就消弱了子类独立性,而且父类的实现细节可以被子类继承后修改。
package demo.extend;
public class Account {
protected double balance;
public boolean isEnough(double money){
return (balance - money)>0;
}
public void widthDraw(double money) throws Exception{
if(isEnough(money)){
balance = balance - money;
}else{
throw new Exception("余额不足!");
}
}
public void save(double money){
balance = balance+money;
}
}
package demo.extend;
public class SubAccount extends Account{
@Override
public boolean isEnough(double money) {
return true;
}
public void save(double money){
balance = balance + money*10;
}
}
所以要精心设计被专门用于被继承的类。
1.尽可能封装父类实现细节,控制继承权限,父类的实现细节可定义为private,如果子类要访问实现细节,可以在父类定义个protected方法,在protected中填写实现细节。
2.把不允许被子类覆盖的方法定义为final
package demo.extend;
public class Account {
private double balance;
public final boolean isEnough(double money){
return (balance - money)>0;
}
public final void widthDraw(double money) throws Exception{
if(isEnough(money)){
balance = balance - money;
}else{
throw new Exception("余额不足!");
}
}
public final void save(double money){
balance = balance+money;
}
}
3.如果某些类不是为了继承而设计,可以把该类定义为final或者该类构造方法弄成private,通过静态方法调用,可以把上边的Account定义为public final class Account。
4.父类构造方法不允许调用可被子类覆盖的方法,因为父类总是在子类前初始化,会出现不可预料的问题。
package demo.extend;
public class Base2 {
public Base2(){
method();
}
void method(){}
}
package demo.extend;
public class Sub2 extends Base2 {
String str = null;
public Sub2(){
str = "11122";
}
public void method(){
System.out.println(str.length());
}
/**
* @param args
*/
public static void main(String[] args) {
Sub2 sub = new Sub2();
sub.method();
}
}
Sub2报错,初始化次序问题。
聚集和关联统称组合,组合的整体对应子类,部分对应父类。组合不打破封装,有时二者通用,但能用组合关系就多用组合关系。