面向对象1
面向过程和面向对象
一.面向过程
●程序设计的思路
● 面向对象,是软件开发中的一类编程风格、开发范式。
● 除了面向对象,还有面向过程、指令式编程和函数式编程。在所有的编程范式中, 我们接触最多的还是面向过程和面向对象两种。
● 早期先有面向过程思想,随着软件规模的扩大,问题复杂性的提高,面向过程的 弊端越来越明显,出现了面向对象思想并成为目前主流的方式。
● 面向过程的程序设计思想(procedure -Oriented Programming),简称POP
● 关注的焦点是过程:过程就是操作数据的步骤。如果某个过程的实现代码重复出 现,那么就可以把这个过程抽取为一个函数。这样就可以大大简化冗余代码,便 于维护。
● 典型的语言:C语言
● 代码结构:以函数为组织单位。
● 是一种“执行者思维”,适合解决简单问题。 分析出解决问题所需要的步骤,然后把步骤一步一步实现。 扩展能力差、后期维护难度较大。
二.面向对象
● 面向对象的程序设计思想( Object Oriented Programming),简称OOP
● 关注的焦点是类:在计算机程序设计过程中,参照现实中事物,将事物的属性特 征、行为特征抽象出来,用类来表示。
● 典型的语言:Java、C#、C++、Python、Ruby和PHP等
● 代码结构:以类为组织单位。每种事物都具备自己的属性和行为/功能。
● 是一种设计者思维,适合解决复杂问题。代码扩展性强、可维护性高。
三.问题引入
由实际问题考虑如何设计程序
思考1:如何开车?
● 面向过程思想思考问题时,我们首先思考“怎么按步骤实现?”并将步骤对 应成方法,一步一步,最终完成。 这个适合简单任务,不需要过多协作的情 况。针对如何开车,可以列出步骤:
面向过程适合简单、不需要协作的事务,重点关注如何执行。
思考2:如何造车?
● 造车太复杂,需要很多协作才能完成。此时我们思考的是“车怎么设计?”, 而不是“怎么按特定步骤造车的问题”。这就是思维方式的转变,这就是面 向对象思想。所以,面向对象思想更契合人的思维模式。
● 用面向对象思想思考“如何设计车”:
我们就会从“车由什么组成”开始思考。发现,车由如下结构组成:
我们找轮胎厂完成制造轮胎的步骤,发动机厂完成制造发动机的步骤;这样,大 家可以同时进行车的制造,最终进行组装,大大提高了效率。 但是,具体到轮胎厂的一个流水线操作,仍然是有步骤的,还是离不开面向过程 思维! 因此,面向对象可以帮助我们从宏观上把握、从整体上分析整个系统。 但是,具 体到实现部分的微观操作(就是一个个方法),仍然需要面向过程的思路去处理。
当需求单一,或者简单时,我们一步步去操作没问题,并且效率也挺高。 可随着需求的更改,功能的增多,发现需要面对每一个步骤很麻烦了,这时就开 始思索,能不能把这些步骤和功能进行封装,封装时根据不同的功能,进行不同 的封装,功能类似的封装在一起。这样结构就清晰了很多。用的时候,找到对应 的类就可以了。这就是面向对象的思想。
千万不要把面向过程和面向对象对立起来。他们是相辅相成的。面向对象离不开面 向过程!
类和对象
类(Class)和对象(Object)是面向对象的核心概念
● 什么是类? 类:具有相同特征的事物的抽象描述,是抽象的、概念上的定义。
● 什么是对象 ?对象:实际存在的该类事物的每个个体,是具体的,因而也称为实例。
可以理解为:
类 => 抽象概念的人;对象 => 实实在在的某个人
类=>汽车图纸; 对象=>实际存在的一辆汽车,拥有类中定义的功能,可以被使用
类的结构
•变量:事物属性的描述(名词)
• 方法:事物的行为(可以做的事情 动词)
• 构造方法:初始化对象
• 块:一段没有名称的代码块
• 内部类: 即在类体中声明的类
第一步:发现类
这个总结的过程,其实是抽象化的过程。
抽象出 来车的特征,可以归纳为一个车类。
类的声明格式为:
[访问权限修饰符] [修饰符] class Car{ }
访问修饰符有两种public,无(默认) 修饰符:final,abstract 关键字class用来定义一个类
Java类名的命名规范: 类名首字母大写,见名知意,驼峰表示
第二步:发现类的共有属性(成员变量)
public class Car{
//成员变量定义
String name; //名称
String color; // 颜色
float price;//价格
}
声明成员变量的格式为: [访问权限修饰符] [修饰符] type attr_name [=defaultValue] ;
成员变量是定义在类中,方法体之外的变量。 成员变量可以使用Java语言中任何一种数据类型(包括基本类型和引用类型)。 在定义成员变量时可以对其初始化,如果不对其初始化,Java使用默认的值对其初始化。 成员变量可以被类中方法、构造方法和特定类的语句块访问。 成员变量的作用范围为整个类体。
第三步:发现类的共有行为(成员方法)
创建并使用对象
对象:是类的一个实例,是以类为模板在内存中创建的实际存在的实例。
对象的创建和使用 Car bm= new Car();
Car bm:使用Car类作为类型声明一个变量bm.
new Car():使用new 创建对象,然后调用Car类的构造方法初始化对象.
= :将右边创建的对象地址赋给左边的bm变量 同一类的每个对象有不同存储空间。 对象是类的一个实例,必然具备该类事物的属性和行为(即方法)。
使用对象名.属性或对象名.方法的方式访问对象成员(包括属性和方法)
总结类和对象
类是一类事物的抽象概念,是一个模型.
对象是由这个模型所创造的,一个个具体存在的,实实在在存在的实例. 所以创建对象的过程也叫实例化对象.
现实生活中先有对象后有类,而编程时先设计类后创建对象
构造方法
构造方法名与类名相同,且没有返回值,且不需要使用void修饰
作用: 在构造方法中为创建的对象成员变量初始化赋值
特点: 每个类都有构造方法,如果没有显式地为类定义构造方法,Java将会为该类提供一个默 认无参构造方法,但是只要在一个Java类中定义了一个有参构造方法后,默认的无参构造方法 会失效。 一个类可以有多个构造方法。
方法的重载
方法的重载是指一个类中具有相同的名字,但参数不同的多个方法。
参数不同(可以有三方面的不同): 1 数量不同 2 类型不同 3 顺序不同
调用时,会根据不同的参数表选择对应的方法。
注意:方法重载跟方法的返回值类型没有任何关系
对象与引用
Java 语言中除基本类型之外的变量类型都称之为引用类型。
Java中的对象是通过引用对其操作的。
class Car{
String name;
String color;
float price;
}
Car bm= new Car(); 通常把这条语句的动作称之为创建一个对象,其实,它包含了三个动作。
1)右边的“new Car()”,是以Car类为模板,在堆空间里创建一个Car类对象。 2)左边的“Car bm”创建了一个Car类型引用变量。所谓Car类的引用,就是以后可以用来指 向Car对象的对象引用。 3)”=”操作符使对象引用指向刚创建的那个Car对象。 我们可以把这条语句拆成两部分: Car bm; bm= new Car(); 这样写,就比较清楚了,有两个实体:一个是对象引用变量,一个是对象本身。
new Car(); 这个语句在堆空间里创建了实体,尽管它们也是确确实实存在的实体,但是, 我们看不见,也摸不着。 对象没有名字,也就没法直接访问它。我们需要通过对象引用来间接访问对象。 对象好比是一只很大的气球,但是我们抓不住它。引用变量是一根绳,可以用来系汽球。 Car car1; (1)创建一根绳子,一根还没有系上任何一个汽球的绳; Car car2;(2)又做了一根绳,还没系上汽球, car1 = new Car();(3)给car1 系上气球; car2 = car1 ;(4)这里,发生了复制行为。要说明的是,对象本身并没有被复制,被复 制的只是对象引用。 结果是,car2也指向了car1所指向的对象。两根绳系的是同一只汽球。
值传递
Java中进行方法调用中传递参数时,参数传递有两种:
● 值传递:(形参数类型是基本数据类型):方法调用时,实际参数把它的值传递给对应 的形式参数,形式参数只是用实际参数的值初始化自己的存储单元内容,是两个不同 的存储单元,所以方法执行中形式参数值的改变不影响实际参数的值。
● 引用传递:(形参数类型是引用数据类型参数):也称为传地址。方法调用时,实际参 数是对象,这时实际参数与形式参数指向同一个地址,在方法执行中,对形式参数的 操作实际上就是对实际参数的操作,这个结果在方法结束后被保留了下来,所以方法 执行中形式参数的改变将会影响实际参数。
基本类型传递的是该数据值本身。引用类型传递的是对对象的引用,而不是对象本身 。 但是无论是值传递还是引用传递,其本质都是传值,所以也都统称值传递.
面向对象2
this关键字
this关键字代表当前对象
使用this关键字引用成员变量
使用this关键字引用成员方法或构造方法。
l在一个类的方法或构造方法内部,可以使用“this.成员变量名”这样的 格式来引用成员变量名,常常用来区分同名的成员变量和局部变量。
public class Demo{
int name;
}
public Demo(int name){
this.name = name;
}
static 关键字
static被称为静态,可以用来修饰类的成员变量,成员方法,代码块,内部类。 静态成员不依赖于类的实例,被类的所有实例共享,就是说 static 修饰的 方法或者变量不需要依赖于对象来进行访问,只要这个类被加载,静态成员 就会被加载创建。
特点: 1 随着类的加载而加载 2 优先于对象存在 3 修饰的成员,被所有对象所共享 4 可不创建对象,直接使用类名调用
static属性 静态属性是类的所有对象共享的,即不管创建了多少个对象,静态属性在内存中 只有一个。
1 静态变量在类被加载时由系统默认初始化。
2 静态变量值是所有对象共享。
3 静态变量可以在任意方法、代码块、构造器中直接使用。
4可以通过类名.静态变量直接访问,也可以通过对象.静态变量的方式访问(但 是更推荐使用类名.静态变量的方式)。
public class Chinese{
String name ;//姓名
static String country=”中国”;//国家
}
用static修饰的成员方法就是静态方法。
● 静态方法在本类的任意方法、代码块、构造器中都可以直接被调用。
● 静态方法在其他类中可以通过类名.静态方法的方式调用。也可以通过对 象.静态方法的方式调用(但是更推荐使用类名.静态方法的方式)。
● 在static方法内部只能访问类的static修饰的属性或方法,不能访问类的 非static的成员。
● 因为不需要实例就可以访问static方法,因此static方法内部不能有this, 也不能有super。
代码块
代码块在类中声明,类似一个没有名称的方法体(代码块),代码分实例块和静态块 实例块:每次创建对象时自动调用
{
//任何符合语法的Java代码
}
//静态块:类加载时自动调用,仅一次,与是否创建对象无关。
static {
//任何符合语法的Java代码
}
包
在编写 Java 程序时,随着程序架构越来越大,类的个数也越来越多,这时就会 发现管理程序中维护类名称也是一件很麻烦的事,尤其是一些同名问题的发生。 有时,开发人员还可能需要将处理同一方面的问题的类放在同一个目录下,以 便于管理。
● 为了解决上述问题,Java 引入了包(package)机制,提供了类的多层命名空 间,用于解决类的命名冲突、类管理等问题。
● 使用package关键字修饰包
● 每个类属于一个特定的包,所以java中一个类的完整名称等于包名+类名
● 包的作用: 按照不同功能管理类 避免类重名 控制访问权限
包(package)的命名规范: 在包名中,可以使用.号来区分包的级别;包名一般情况下是小写 第一级 指该项目的类型,如com,org,gov等, 第二级 指项目所开发或者运行的公司名称,如:oracle,sun,huawei等 第三级 指项目的名称,如:bcms,oa,erp,cms等 第四级 指项目模块的名称,如:bean,action,exception等
● 导入外部包的类,关键字“import” 当在一个类中使用其他包中的类时,需要先使用import关键字导入进来. 只有java.lang包中的类比较特殊,使用时不需要导入.
访问权限修饰符
访问权限修饰符 public protected (default) private
Java语言有四个权限访问修饰符,权限从大到小依次为:
1)public :公共权限 修饰类、属性、方法。可以在任意类中访问
2)protected:受保护的权限 修饰属性、方法。可以在同包类访问,如果 不是同包类,必须是该类的子类才可以访问。
3)default:同包权限 修饰类、属性、方法。只能在同包的类访问
4)private:私有权限 修饰属性、方法。 只能在本类中访问
面向对象特征
面向对象语言的三大特征: 封装 继承 多态
面向对象特征--封装
封装的概念 :将类的某些信息隐藏在类内部,不允许外部程序直接访问,而是 通过该类提供的方法来实现对隐藏信息的操作和访问 。
使用不同的访问权限
public class Demo{
private String name; //将类的成员变量访问权限设置为私有
public String getName (){
return name;
}
}
public void setName(String name){
this.name = name;
}
成员变量和局部变量
在类中的位置不同
成员变量:在类中定义 权限修饰不同
局部变量:在方法中定义或者方法的参数
成员变量:可以使用权限修饰符
局部变量:不可以使用权限修饰符
初始化不同
成员变量:创建对象后,由构造方法初始化
局部变量:没有默认初始化值,必须定义,赋值。
生命周期不同
成员变量:随着对象的创建而存在,随着对象的销毁而消失
局部变量:随着方法的调用而存在,随着方法的调用完毕而消失
在内存中的位置不同
成员变量:与对象一起在堆内存中
局部变量: 与方法一样在栈中
面向对象3
面向对象特征--继承
多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么,多个类中无需 再定义这些属性和行为,只需要和抽取出来的类构成继承关系。
继承的好处?
继承的出现减少了代码冗余,提高了代码的复用性。 继承的出现,更有利于功能的扩展。 继承的出现让类与类之间产生了is-a的关系,为多态的使用提供了前提。
何时使用继承?
符合is-a关系的设计,可以使用继承
继承的语法 通过 extends关键字,可以声明一个类B继承另外一个类A,定义格式如下:
[修饰符] class 类A {
...
}
[修饰符] class 类B extends 类A {
...
}
类B,称为子类/派生类 ,类A,称为父类/基类
继承性的细节:
● 子类会继承父类所有的实例变量和实例方法
● 子类不能直接访问父类中私有的(private)的成员变量和方法
● 在Java 中,继承的关键字用的是“extends”,表示子类是对父类的扩展
● Java支持多层继承(继承体系)
● 一个父类可以同时拥有多个子类
● Java只支持单继承,不支持多重继类
java.lang.Object,是类层次结构的根类,即所有其它类的父类。每个类 都使用 Object作为超类。 当一个类没有显示的继承其他类时,默认继承Object类。
方法的重写(OverRide)
当父类的方法功能实现不能满足子类需求时,可以对方法进行重写( override). 子类可以对从父类中继承来的方法进行改造,我在程序执行时,子类的方法 将覆盖父类的方法。们称为方法的重写也称为方法的覆盖。
注意:构造方法,静态方法不能重写,成员变量不存在重写
方法重写的规则 :1. 子类重写的方法必须和父类被重写的方法名称,参数列表相同。 2. 子类重写的方法的返回值类型与父类保存一致。 3. 子类重写的方法使用的访问权限不能小于父类被重写方法的访问权限。 注意:① 父类私有方法不能重写 ② 跨包的父类默认权限的方法也不能重写 4. 子类方法抛出的异常不能大于父类被重写方法的异常
@Override使用说明: @Override是java中定义的注解标签,用来进行标记(进阶部分细讲) 写在方法上面,表示此方法是从父类重写而来,用来检测是不是满足重写方法的要求。 这个注解就算不写,只要格式满足要求,也是正确的方法覆盖重写。建议保留,这 样编译器可以帮助我们检查格式,另外也可以让阅读源代码的程序员清晰的知道这 是一个重写的方法。
super关键字
在Java类中使用super来调用父类中的指定操作: super可用于访问父类中定义的属性 super可用于调用父类中定义的成员方法 super可用于在子类构造器中调用父类的构造器
注意: 尤其当子父类出现同名成员时,可以用super表明调用的是父类中的成员
super的追溯不仅限于直接父类还可以是父类的父类
super和this的用法相像,this代表本类对象的引用,super代表父类的内存空间的标识
不要把super误认为是父类对象,在创建子类对象时,不会创建父类对象. 只会将父类中的信息加载到子类对象中存储
继承中的构造方法
子类继承父类时,不会继承父类的构造方法。只能通过“super(形参列表)” 的方式调用父类指定的构造方法。
规定super(形参列表),必须声明在构造器的首行。
如果在子类构造器的首行没有显示调用super(形参列表),则子类此构造器 默认调用super(),即调用父类中空参的构造器。
这么做是为了保证先对父类成员进行初始化。
如果子类构造器中既未显式调用父类或本类的构造器,且父类中又没有空参 的构造器,则编译出错。
抽象类
如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。
抽象类除了不能实例化对象之外,类的其它功能依然存在,成员变量、成员方法和 构造方法。
用abstract修饰的类就是抽象类。如果某个类中包含有抽象方法,那么该类就必须 定义成抽象类。
特点:
抽象类不能被实例化,但可以有构造方法,因为抽象类中含有无具体实现的方法, 所以不能用抽象类创建对象。
抽象类只能用作基类,表示的是一种继承关系。继承抽象类的非抽象类必须实现 其中的所有抽象方法,而已实现方法的参数、返回值要和抽象类中的方法一样。否 则,该类也必须声明为抽象类。
抽象方法是一种特殊的方法:它只有声明,而没有具体的实现. 抽象方法必须用abstract关键字进行修饰
使用关键字abstract定义抽象类,一般语法:
[访问权限] abstract class 类名 {
成员列表
}
public abstract class Shapes {
public abstract void draw();
}
抽象类,抽象方法,在软件开发过程中都是设计层面的概念。也就是说, 设计人员会设计出抽象类,抽象方法,程序员都是来继承这些抽象类并 覆盖抽象方法,实现具体功能。
面向对象特征--多态
父类引用指向子类对象,从而产生多种形态
Animal dog = new Dog();
Animal cat = new Cat();
同一种事物,在不同时刻表现不同状态
二者存在直接或者间接的继承关系时,父类引用指向子类的对象,即 形成多态。
当编译期类型是父类,运行期类型是子类时,被称为父类引用指向子类对象
向上转型
多态性的好处:提高代码的扩展性
多态环境下对成员方法的调用
多态环境下对静态成员方法的调用
多态环境下对成员变量的调用
简单的说:编译和运行都看等号左边。 注意:变量不存在被子类覆写这一说法,只有方法存在覆写。
向下转型
父类引用仅能访问父类所声明的属性和方法,不能访问子类独有的属性 和方法。
向下转型的作用是:为了使用子类中的特有方法。
final关键字
lfinal 用于修饰类,方法,参数,和属性
类:不能被定义为抽象类或是接口,不可被继承
方法:子类里不可以重写
参数:参数值在方法中不可被修改
属性:定义时就必须直接赋值或者在构造方法中进行赋值,并且后期都不能修改
接口
从本质上讲,接口是一种特殊的抽象类,这种抽象类中包含抽象方法。
接口的定义和使用
接口的定义:使用 interface 关键字用来声明一个接口。
[访问修饰符] interface 接口名称 [extends 其他的接口名1,….其他的接口名n]
{
// 声明常量 抽象方法 静态方法 默认方法
}
接口的使用:类使用implements关键字实现接口。在类声明中,Implements 关键字放在class声明后面
[访问修饰符] class 类名 implements 接口名1,接口名2……{ }
结合继承:
[访问修饰符] class 类名 extends 父类名 implements 接口名1,接口名2……{ }
接口的特性
接口是隐式抽象的,主要用来定义功能. l 接口中可以定义静态常量,抽象方法,静态方法,默认方法.
一个接口能继承其它多个接口.
接口不能实例化对象.
接口是要被类实现,一个接口可以被多个实现
当类实现接口的时候,类要实现接口中所有的抽象方法,否则,该类必须 声明为抽象的类.
接口与实现类之间存在多态性