联邦快递的网站建设,网站建设售后服务费包括哪些,枞阳县住房和城乡建设局网站,临沂seo网站管理封装
所谓封装#xff0c;意思就是隐藏内部细节#xff0c;在编程中#xff0c;指利用抽象数据类型将数据和基于数据的操作封装在一起#xff0c;使其构成一个不可分割的独立实体#xff0c;并尽可能地隐藏内部的细节#xff0c;只保留一些对外接口使之与外部发生联系。…封装
所谓封装意思就是隐藏内部细节在编程中指利用抽象数据类型将数据和基于数据的操作封装在一起使其构成一个不可分割的独立实体并尽可能地隐藏内部的细节只保留一些对外接口使之与外部发生联系。
封装的最大特点就是降低了耦合性外部无需知道内部的实现细节只需使用就行了。
比如在 Java 中方法和类等都是一种封装。
public class Cat {private String name;private int age;public String getName() {return name;}public void setName(String name) {this.name name;}public int getAge() {return age;}public void setAge(int age) {this.age age;}
}继承
事物有分类的概念比如动物、植物、猫、狗等分类有个根可以向下不断细化形成一个层次分类体系。
编程语言使用继承来描述这种概念在继承中有父类基类和子类派生类比如动物类 Animal 和狗类 DogAnimal 是父类Dog 是子类。
继承实现了 IS-A 关系比如 Animal 和 DogDog is Animal 就是一种 IS-A 关系。
Java 继承是使用已经存在的类作为基础建立新类的技术新类可以继承父类的数据或功能还可以增加子类特有的数据或功能。
这样公共的属性和行为可以放到父类中而子类只需要关注子类特有的就可以了另一方面遵循里氏替换原则不同的子类对象可以当成父类对象即父类变量可以指向子类对象称为向上转型方便统一处理。
基本语法
Java 使用 extends 关键字表示继承关系一个类最多只能有一个父类单继承默认父类是 java.lang.Object。
子类继承了父类的非私有属性和方法构造方法除外子类可以重写继承的父类方法注意重写不能降低方法的访问权限可以增加新的属性新的方法即子类可以在父类基础上进行重新实现和扩展。
重写方法不能抛出新的检查异常或者比被重写方法申明更加宽泛的异常。例如 父类的一个方法申明了一个检查异常 IOException在重写这个方法的时候不能抛出 Exception 异常因为 Exception 是 IOException 的父类可以抛出 IOException 异常或者 IOException 的子类异常。
注意为什么不能方法重写时不能降低访问权限因为继承体现的是 IS-A 关系降低可见性会减少可以对外的行为从而破坏了 IS-A 关系。
创建子类对象时会先初始化父类的部分子类构造方法默认会调用父类的无参构造方法除非通过 super 手动调用父类的有参构造方法父类必须要有无参构造方法。
super在类的内部可以通过 super 关键字明确访问父类的非私有属性、成员方法、构造方法在子类构造方法中调用父类构造方法时super 必须放在第一行。与 this 不同的是super 不能指代具体的父类对象不能作为参数值或返回值。
如果类被 final 修饰则表示最终类不能被继承如 String 类如果方法被 final 修饰则该方法不能被子类重写。
public class Animal {private String name;private int age;public String getName() {return name;}public void setName(String name) {this.name name;}public int getAge() {return age;}public void setAge(int age) {this.age age;}public void eat(){System.out.println(吃...);}
}class Cat extends Animal {// Override 注解表示重写父类方法Overridepublic void eat(){System.out.println(super.getName() 猫猫爱吃小鱼鱼);}
}谨慎继承
和分类体系相似继承是一种逐级向上抽象的体系因此继承是非常强大的广泛应用于各种 Java API、框架和类库之中提供了大量基类和基础公共代码。但继承也存在一些问题。
继承可能破坏封装
封装就是隐藏实现细节提供简化接口。使用者只需要关注怎么用而不需要关注内部是怎么实现的。实现细节可以随时修改而不影响使用者。
继承可能破坏封装是因为子类和父类之间可能存在着实现细节的依赖更具体地说子类需要知道父类的可重写方法之间的依赖关系如果父类的方法之间存在调用依赖而子类只重写了某个方法就可能导致不可预测的错误。
父类也不能随意增加公开方法因为给父类增加就是给所有子类增加而子类可能必须要重写该方法才能确保方法的正确性。
继承没有完全体现 IS-A 关系
比如绝大部分鸟都会飞可能就想给鸟类增加一个方法 fly() 表示飞但有一些鸟就不会飞比如企鹅。
优先使用组合而非继承
使用组合可以抵挡父类变化对子类的影响从而保护子类应该优先使用组合。
public class Child {private Base base;private long sum;public Child(){base new Base();}public void add(int number) {base.add(number);sumnumber;}public void addAll(int[] numbers) {base.addAll(numbers);for(int i0; inumbers.length; i){sumnumbers[i];}}public long getSum() {return sum;}
}多态
多态分为编译时多态和运行时多态编译时多态主要指方法的重载overload运行时多态指对象变量所指向的具体类型在运行期间才确定。
运行时多态有三个条件 继承/实现重写覆盖向上转型。
在 Java 中有两种形式实现运行时多态继承和接口。
通过多态Java 可以实现对不同类型的子类对象统一处理。
转型
转型分向上转型子类转父类和向下转型父类转子类需强转实质是数据类型转换当声明的数据类型与实际数据的类型不一致时需要转型而向上转型存在丢失数据或操作的问题。
Extend extend new Extend();
Base base extend; // 向上转型
extend (Extend) base; // 向下转型需强转向下转型时需要判断对象数据的实际类型是否与目标类型兼容目标类型与实际类型一致或者是两者某个中间层级的类型如果不兼容则无法转换可以使用 instanceof 关键字判断。
if( base instanceof Extend) {extend (Extend) base;
}静态绑定和动态绑定 静态类型声明变量时的数据类型通常是某个父类或接口。 动态类型创建对象时的数据类型。
在继承/实现体系中存在成员重名的问题可以认为此时子类对象有两份同名的成员变量或方法父类部分和子类部分。
对于 private 变量和方法由于只能在类内访问在父类中访问的是父类的子类中访问的是子类的。
对于 public 成员则看如何访问。在类的内部默认带参数 this访问的是类自身的但子类可以通过 super 明确指定访问父类。
在类的外部静态变量、静态方法、实例变量根据对象的静态类型判断静态绑定这符合数据类型向上转换后的特点。
对于实例方法则根据对象的动态类型判断动态绑定因为 JVM 在实现多态的时候用的是 invokevirtual 指令它会从子类部分开始往父类去寻找在所有重载版本中最匹配的方法。
静态绑定在程序编译阶段即可决定而动态绑定则要等到程序运行时。
注意动态绑定确认方法后方法中对变量的访问由于默认带 this.此时访问的是实际类中的变量。
class Base {public static int NUM;private int num;static {System.out.println(基类静态初始化代码块NUM NUM);NUM 10;}{System.out.println(基类实例初始化代码块num num);num 1;}public Base() {System.out.println(基类构造方法num num);num 2;}protected void step() {System.out.println(基类NUM NUM num num);}public void action(){step();}
}class Extend extends Base {public static int NUM;private int num;static {System.out.println(子类静态初始化代码块NUM NUM);NUM 100;}{System.out.println(子类实例初始化代码块num num);num 10;}public Extend() {System.out.println(子类构造方法num num);num 20;}Overrideprotected void step() {System.out.println(子类NUM NUM num num);}}public class Test {public static void main(String[] args) {System.out.println(创建子类 );Extend extend new Extend();System.out.println(子类 action );extend.action();Base base extend;System.out.println(基类 action );base.action();System.out.println(基类访问类变量 base.NUM);System.out.println(子类访问类变量 extend.NUM);}
}输出如下
创建子类
基类静态初始化代码块NUM 0
子类静态初始化代码块NUM 0
基类实例初始化代码块num 0
基类构造方法num 1
子类实例初始化代码块num 0
子类构造方法num 10
子类 action
子类NUM 100num 20
基类 action
子类NUM 100num 20
基类访问类变量 10
子类访问类变量 100虚方法表
动态绑定的实现机制是从对象的实际类型开始往上逐级查找要执行的方法如果继承的层次比较深要调用的方法位于比较上层的父类则每次调用都要进行多次查找效率较低。
为此大多数系统使用一种称为虚方法表的方法来优化调用的效率。
所谓虚方法表就是在类加载的时候为每个类创建一个表记录该类的对象所有动态绑定的方法包括父类的方法及其地址一个方法只有一条记录子类重写了父类方法后只会保留子类的。
这样当动态绑定方法的时候只需要查找实际类型的虚方法表就可以了。 参考《Java 编程的逻辑》 马俊昌