面向对象程序设计
面向对象程序设计(Object Oriented Programming)作为一种新方法,其本质是以建立模型体现出来的抽象思维过程和面向对象的方法。模型是用来反映现实世界中事物特征的。任何一个模型都不可能反映客观事物的一切具体特征,只能对事物特征和变化规律的一种抽象,且在它所涉及的范围内更普遍、更集中、更深刻地描述客体的特征。通过建立模型而达到的抽象是人们对客体认识的深化。
面向过程: 方法一层的复用。
面向对象: 除了方法一层的复用,还包含了这个类的一些细节,直接在整个对象的复用。
面向对象的三个基本特征
面向对象的三个基本特征是:封装、继承、多态(接口,抽象)。
封装(Encapsulation)-隐藏实现
- 封装最好理解了。封装是面向对象的特征之一,是对象和类概念的主要特性。
- 封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。
- 简单的说,一个类就是一个封装了数据以及操作这些数据的代码的逻辑实体。在一个对象内部,某些代码或某些数据可以是私有的,不能被外界访问。通过这种方式,对象对内部数据提供了不同级别的保护,以防止程序中无关的部分意外的改变或错误的使用了对象的私有部分。
- 在Java中类中成员的属性有:public, protected, default, private,这四个属性的访问权限依次降低。
继承 (extends)
面向对象编程 (OOP) 语言的一个主要功能就是“继承”。继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。
- 通过继承创建的新类称为“子类”或“派生类”。
- 被继承的类称为“基类”、“父类”或“超类”。
- 继承的过程,就是从一般到特殊的过程。
继承:由一个已存在的类,抽取公共部分,然后由这个基类派生出其他子类,子类拥有父类所有的属性和方法,并且子类可以重写父类方法(如果没有重写,就会默认调用父类的方法),子类可以拥有自己的方法。
- ①A is B(A是B)
- ②关系是相对的
- ③java中只支持单继承
要实现继承,可以通过“继承”(Inheritance)和“组合”(Composition)来实现。
在某些 OOP 语言中,一个子类可以继承多个基类。但是一般情况下,一个子类只能有一个基类,要实现多重继承,可以通过多级继承来实现。
继承概念的实现方式有三类:实现继承、接口继承和可视继承。
- 实现继承是指使用基类的属性和方法而无需额外编码的能力;
- 接口继承是指仅使用属性和方法的名称、但是子类必须提供实现的能力;
- 可视继承是指子窗体(类)使用基窗体(类)的外观和实现代码的能力。
- 在考虑使用继承时,有一点需要注意,那就是两个类之间的关系应该是“属于”关系。例如,Employee 是一个人,Manager 也是一个人,因此这两个类都可以继承 Person 类。但是 Leg 类却不能继承 Person 类,因为腿并不是一个人。
- 抽象类仅定义将由子类创建的一般属性和方法,创建抽象类时,请使用关键字 Interface 而不是 Class。
- OO开发范式大致为:划分对象→抽象类→将类组织成为层次化结构(继承和合成) →用类与实例进行设计和实现几个阶段。
父类super: 引用对象的父类方法
1)super. 能调用父类的属性和父类的方法
2)super() 调用父类的构造–通过参数列表 只能写在构造器的第一句
重写 (override)
- 存在继承关系的两个类中方法
- 方法的名称一样,参数列表相同,返回类型可以不相同,但是必须是父类返回值的派生类,重写方法内代码块。
- 访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类的一个方法被声明为 public,那么在子类中重写该方法就不能声明为 protected。
- 父类的成员方法只能被它的子类重写。
- 构造方法不能被重写。
- 声明为 final 的方法不能被重写。
- 声明为 static 的方法不能被重写,但是能够被再次声明。
- 子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为 private 和 final 的方法。
- 子类和父类不在同一个包中,那么子类只能够重写父类的声明为 public 和 protected 的非 final 方法。
- 重写的方法能够抛出任何非强制异常,无论被重写的方法是否抛出异常。但是,重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更广泛的强制性异常,反之则可以。
重载 (overload)
在同一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。
每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表。
最常用的地方就是构造器的重载。
重载规则:
- 被重载的方法必须改变参数列表(参数个数或类型不一样);
- 被重载的方法可以改变返回类型;
- 被重载的方法可以改变访问修饰符;
- 被重载的方法可以声明新的或更广的检查异常;
- 方法能够在同一个类中或者在一个子类中被重载。
- 无法以返回值类型作为重载函数的区分标准。
总结
方法的重写(Override)和重载(Overload)是java多态性的不同表现,重写是父类与子类之间多态性的一种表现,重载可以理解成多态的具体表现形式。
(1)方法重载是一个类中定义了多个方法名相同,而他们的参数的数量不同或数量相同而类型和次序不同,则称为方法的重载。
(2)方法重写是在子类存在方法与父类的方法的名字相同,而且参数的个数与类型一样,返回值也一样的方法,就称为重写。
(3)方法重载是一个类的多态性表现,而方法重写是子类与父类的一种多态性表现。
多态(Polymorphism)-对象的互换的魔法
多态是同一个行为具有多个不同表现形式或形态的能力。
态就是同一个接口,使用不同的实例而执行不同操作。
- 多态的优点
- 消除类型之间的耦合关系
- 可替换性
- 可扩充性
- 接口性
- 灵活性
- 简化性 - 多态存在的三个必要条件
- 继承
- 重写
- 父类引用指向子类对象 - 多态的好处:可以使程序有良好的扩展,并可以对所有类的对象进行通用处理。
当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,再去调用子类的同名方法。
以下是一个多态实例的演示,详细说明请看注释
public class Test {
public static void main(String[] args) {
show(new Cat()); // 以 Cat 对象调用 show 方法
show(new Dog()); // 以 Dog 对象调用 show 方法
Animal a = new Cat(); // 向上转型
a.eat(); // 调用的是 Cat 的 eat
Cat c = (Cat)a; // 向下转型
c.work(); // 调用的是 Cat 的 work
}
public static void show(Animal a) {
a.eat();
// 类型判断
if (a instanceof Cat) { // 猫做的事情
Cat c = (Cat)a;
c.work();
} else if (a instanceof Dog) { // 狗做的事情
Dog c = (Dog)a;
c.work();
}
}
}
abstract class Animal {
abstract void eat();
}
class Cat extends Animal {
public void eat() {
System.out.println("吃鱼");
}
public void work() {
System.out.println("抓老鼠");
}
}
class Dog extends Animal {
public void eat() {
System.out.println("吃骨头");
}
public void work() {
System.out.println("看家");
}
}
/*执行以上程序,输出结果为:
吃鱼
抓老鼠
吃骨头
看家
吃鱼
抓老鼠*/
多态的实现方式
抽象类 (abstract)
- 用abstract 修饰的类就是抽象类
- 抽象类中可以有属性,方法,还可以有抽象方法
- 抽象类不能实例化(new)
- 使用: 用一个类去继承抽象类,并把这个抽象类中的所有抽象方法都“重写“,否则该类还是为抽象类。
- 构造方法,类方法(用 static 修饰的方法)不能声明为抽象方法。
- 可以有构造方法,只是不能实例化。
抽象方法
- 用abstract 修饰的方法就是抽象方法 。
2.抽象方法只有方法的声明,没有方法的实现(没有{ })
代码示例:
//通过父类声明,子类实例化来使用这个抽象类
public class Test{
public static void main(String[] args){
person p=new Person_C();
p.say();
System.out.println(p.i);
}
}//由于抽象类不能实例化,通过继承实现抽象类,将所有抽象方法重写
public class Person_C extends Person{
public void say(){
System.out.println("你好");
}
}//抽象类,可以有属性,方法,抽象方法public abstract Person{
public int i=1;
public abstract void say();
}
接口 (interface)
- 用interface修饰的东西就是接口
- 接口中可以有属性
接口中默认的属性类型为public static final类型(可省略) - 接口中只能有方法的声明(或者抽象方法),不能有方法的实现
- 接口不能被实例化
- 用一个类去“实现”一个接口,实现的关键字是(implements ),实现接口时必须把所有的方法声明都实现,否则该类是一个抽象类。
- 一个类可以实现多个接口,
A extends B implements C,D,E - 接口允许多继承
代码示例:
//用iterface修饰的就是接口
public interface Tools1{
float G=9.8f;
//public static final float G=9.8f;
/*接口中的属性默认为public static final类型,可以省略,接口中可以有方法的声明(或者抽象方法),不能有方法的实现 */
public void usb();//接口中不能有构造方法
}
public interface Tools2{
float PI=3.141592653f;
public void circle(int r);
}
/*实现方式一:用一个类去“实现”接口需要有implement关键字,接口可以多实现*/
public class PC implement Tools1,Tools2{
//要对接口中的所有方法实现重写,否则该类是一个抽象类
@override
public void usb(){
System.out.println("PC'susb.")
}
@override
public void circle(int a){
System.out.println(2*a*PI);
}
}
//实现方式二:一个类可以实现多个接口
public class Test1 extends Demo implements Tools1,Tools2{
public static void main(String[] args) {
Test1 t=new Test1();
t.circle(15);
t.usb();
}
@Override
public void usb() {
System.out.println("usb()");
}
@Override
public void circle(int r) {
System.out.println(2*r*PI);
}
}
/*补充:满足继承关系的时候使用抽象类,不满足抽象关系的时候使用接口,但是我们日常工作中都使用的是接口。*/
抽象类和接口的区别
- 抽象类中的方法可以有方法体,就是能实现方法的具体功能,但接口中的方法不行。
- 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final 类型的。
- 接口中不能含有静态代码块以及静态方法(用 static 修饰的方法),而抽象类是可以有静态代码块和静态方法。
- 一个类只能继承一个抽象类,而一个类却可以实现多个接口。
注:JDK 1.8 以后,接口里可以有静态方法和方法体了。
内部类
- 开闭原则:对扩展(继承,接口)开发,对修改关闭
2.内部类代码示例:
public class A {
int a=10;
/*内部类的实例化:内部类不能直接被实例化,先在外部类实例化一个内部类对象*/
public B bbb=new B();
public void say(){
System.out.println("...A...say()...");
}
class B{
int b=100;
public void say(){
System.out.println("...B...say()...");
}
}
}
public class Test {
public static void main(String[] args) {
A aaa=new A(); // 内部类的调用
aaa.bbb.say();
System.out.println(aaa.bbb.b);
}
}
父类声明,子类实例化的特点: 对象是父类的对象,能调用父类的属性和方法,也能调用子类重写父类的方法
修饰符
- 访问修饰符
1)public, private, protected,默认都可修饰成员变量,成员方法。
2)public,默认 可修饰顶层类。
3)局部变量不能用访问修饰符。
4)成员变量或成员方法的访问控制。 - 单例的实现单例:
实例化出来的对象是同一个对象方法:
写一个私有的,静态的构造方法
1)通过静态方法实现代码:
public class Run{
private static Run r=null;
public static Run gerRun(){
if(r==null){
r=new Run();
}
Return r;
}
}
public class Transfer{
public static void main(String[] args)
{ Run ru=Run.getRun();
Sysetem.out.printlin(ru);
Run ru1=Run.getRun();
Sysetem.out.printlin(ru1);
}
}
//运行结果:Run @4554617c Run @4554617c
2)通过静态块实现
public class demo02 {
private static demo02 d=null;
static {
if(d==null){
d=new demo02();
}
}
public static demo02 getdemo02(){
return d;
}
}
public class demo01 {
public static void main(String[] args)
{ demo02 d1=demo02.getdemo02();
System.out.println(d1);
demo02 d2=demo02.getdemo02();
System.out.println(d2);
}
}
//运行结果:Run @4554617c Run @4554617c
- 修饰符: static,final,abstract
(一)static-静态
- static能修饰属性,方法,静态块
- static修饰的属性:是多个对象共有的一块空间
- static修饰属性,方法–可以不通过实例化对象得到,可以通过类名直接调用
- 静态方法和静态块中只允许使用静态属性5)静态块是在类被加载时调用
(二)final:常量
- final能修饰变量,方法,类
- final修饰变量:不能被第二次赋值
- final修饰方法:不能被重写
- final修饰类:不能被继承