面向对象的三大基本特征
- 封装(Encapsulation)
- 继承(Inheritance)
- 多态(Polymorphism)
封装
所谓封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。
封装是面向对象的特征之一,是对象和类概念的主要特性。简单的说,一个类就是一个封装了数据以及操作这些数据的代码的逻辑实体。在一个对象内部,某些代码或某些数据可以是私有的,不能被外界访问。
访问控制
封装提供了一个重要属性:访问控制(access control)。通过封装你可以控制程序的哪一部分可以访问类的成员。通过控制访问,可以阻止对象的滥用;
一个成员如何被访问取决于修改它的声明的访问指示符。Java提供一套丰富的访问指示符; Java的访问指示符有public(公共的,全局的)、private(私有的,局部的)、和protected(受保护的)。Java也定义了一个默认访问级别default;
- 当一个类成员被public指示符修饰时,该成员可以被你的程序中的任何其他代码访问。
- 当一个类成员被指定为private时,该成员只能被它的类中的其他成员访问。
- 如果不使用访问指示符,该类成员的默认访问设置为在它自己的包内为public,但是在它的包以外不能被存取;
对于成员变量和方法的作用域,public,protected,private 以及不写之间的区别:
public : 表明该成员变量或者方法是对所有类或者对象都是可见的,所有类或者对象都可以直接访问。
private : 表明该成员变量或者方法是私有的,只有当前类对其具有访问权限,除此之外其他类或者对象都没有访问权限.子类也没有访问权限。
protected : 表明成员变量或者方法对类自身,与同在一个包中的其他类可见,其他包下的类不可访问,除非是他的子类。
default : 表明该成员变量或者方法只有自己和其位于同一个包的内可见,其他包内的类不能访问,即便是它的子类
继承
通过继承创建的新类称为“子类”或“派生类”,被继承的类称为“基类”、“父类”或“超类”。继承的过程,就是从一般到特殊的过程。
继承概念的实现方式有二类:实现继承与接口继承。实现继承是指直接使用基类的属性和方法而无需额外编码的能力;接口继承是指仅使用属性和方法的名称、但是子类必须提供实现的能力。
实现继承和接口继承的区别:
实现继承:
如果多个类的某个部分的功能相同,那么可以抽象出一个类出来,把他们的相同部分都放到父类里,让他们都继承这个类。
使用 extends 关键字实现
接口继承(实现)
如果多个类处理的目标是一样的,但是处理的方法方式不同,那么就定义一个接口,也就是一个标准,让他们的实现这个接口,各自实现自己具体的处理方法来处理那个目标。
通过 implements 关键字
- 继承的根本原因是因为要复用,而实现的根本原因是需要定义一个标准。
- Java 中支持一个类同时实现多个接口,但是不支持同时继承多个类。
继承和组合
Java 代码的复用可以使用继承,组合等表现形式;(还有一种是代理)
继承(Inheritance)是一种联结类与类的层次模型。指的是一个类(称为子类、子接口)继承另外的一个类(称为父类、父接口)的功能,并可以增加它自己的新功能的能力,继承是类与类或者接口与接口之间最常见的关系;继承是一种 is-a 关系; 在写代码的时候就要指名具体继承哪个类,所以,在编译期就确定了关系。
组合(Composition)体现的是整体与部分、拥有的关系,即 has-a 的关系; ,在写代码的时候可以采用面向接口编程。所以,类的组合关系一般在运行期确定;
public class ParentSon extends Parent{
public Box box;
@Override
public void printName() {
System.out.println("son");
}
}
以上代码:
ParentSon继承于Parent类,是is-a关系
Parentson包含有Box 是has-a关系
建议在同样可行的情况下,优先使用组合而不是继承。因为组合更安全,更简单,更灵活,更高效。
只有当子类真正是超类的子类型时,才适合用继承
多态
所谓多态就是指一个类实例的相同方法在不同情形有不同表现形式。多态机制使具有不同内部结构的对象可以共享相同的外部接口。这意味着,虽然针对不同对象的具体操作不同,但通过一个公共的类,它们(那些操作)可以通过相同的方式予以调用;
最常见的多态就是将子类传入父类参数中,运行时调用父类方法时通过传入的子类决定具体的内部结构或行为。
最具代表的是子类重写父类的方法和方法的重载;
方法的重写和重载
重载(Overloading)
简单说,就是函数或者方法有同样的名称,但是参数列表不相同的情形,这样的同名不同参数的函数或者方法之间,互相称之为重载函数或者方法重写(Overriding)
重写指的是在 Java 的子类与父类中有两个名称、参数列表都相同的方法的情况。由于他们具有相同的方法签名,所以子类中的新方法将覆盖父类中原有的方法。
有人总结的重写和重载的不同很到位:
1、重载是一个编译期概念、重写是一个运行期间概念。
2、重载遵循所谓“编译期绑定”,即在编译时根据参数变量的类型判断应该调用哪个方法。
3、重写遵循所谓“运行期绑定”,即在运行的时候,根据引用变量所指向的实际对象的类型来调用方法。
4、因为在编译期已经确定调用哪个方法,所以重载并不是多态。而重写是多态。重载只是一种语言特性,是一种语法规则,与多态无关,与面向对象也无关。(注:严格来说,重载是编译时多态,即静态多态。但是,Java 中提到的多态,在不特别说明的情况下都指
动态多态)
重载的例子:
public int getWidth(){
return 10;
}
public int getWidth(int a){
return 11;
}
public int getWidth(int a,int b){
return 12;
}
重载的条件
被重载的方法必须改变参数列表;
被重载的方法可以改变返回类型;
被重载的方法可以改变访问修饰符;
被重载的方法可以声明新的或更广的检查异常;
方法能够在同一个类中或者在一个子类中被重载。除了重载正常的方法外,构造函数也能够重载
重写的例子:
public class Parent {
public void printName(){
System.out.println("parent");
}
}
public class ParentSon extends Parent{
@Override
public void printName() {
System.out.println("son");
}
}
重写的条件
参数列表必须完全与被重写方法的相同;
返回类型必须完全与被重写方法的返回类型相同;
访问级别的限制性一定不能比被重写方法的强;
访问级别的限制性可以比被重写方法的弱;
重写方法一定不能抛出新的检查异常或比被重写的方法声明的检查异常更广泛的检查异常。
重写的方法能够抛出更少或更有限的异常(也就是说,被重写的方法声明了异常,但重写的方法可以什么也不声明)。
不能重写被标示为 final 的方法。
如果不能继承一个方法,则不能重写这个方法;
注意
声明成final的方法不能被重载。
声明一个final类,它的所有方法也都是final。