面向对象OOP
引言
计算机语言发展的主线
随着计算机深入人们的生活,所要解决的问题越来越复杂,同时计算机运行速度越来越快,问题的解决更大程度上指的是问题本身而不是解决问题的效率,为了适应这种需求,计算机语言越来越向方便人类思考问题的方向发展。
最初的计算机语言只有基本变量(类似我们学习的基本数据类型),用来保持数据。
但是数据多了怎么办?成千上万的数据怎么处理?于是有了数组的概念,通过数组将这同类型的数组进行组织。
后来,数据不仅多了而且复杂了怎么办?数组只能存放同类型的数据,于是产生了结构体(c/c++中的struct),可以将不同类型的数据包括在一个数据结构。
后来,数据不仅多了,复杂了,而且对数据的操作(指的就是方法)也多了复杂了,怎么办?结构体只包含了数据,没有包含对数据操作的方法。即,数据和方法是分离的,虽然他们联系紧密但是各自为政。于是,类和对象产生了。说白了,对象也是一种数据结构而已。包含了:不同的数据类型(属性field)和相关的操作处理(方法method)。
面向过程:数据和方法是分离的。
面向对象:数据和方法集成到一起,便于扩展。
事务处理的主线
我们常说高级语言分为:面向过程和面向对象。面向对象和面向过程首先存在思想上的区别和联系。
对于小而简单的问题,为了快速,为了方便,我自己会动手从头到尾处理;对于庞大且复杂的问题,我一个人,已经不够用了,时间不够,精力不够,学识也不够,所以我会找其他人来帮忙。而且个个都是专业的。
例如:让你搬一块砖从小店到迎泽,很简单,弯腰捡起砖头,扛在肩上,大步朝前走就行,到达目的地放下即可。但是如果现在要让你从小店修一条路到迎泽,这个…
再例如:原来开一间10平米的小杂货铺,一个人足够负责铺子里的每一样事物。现在要开一家五层的百货商场,如果只有你自己,想想都头疼…
作为整个大局的规划者(程序的设计者),我需要从宏观上把握、从整体上合理分析,此时我们需要使用面向对象的思路来分析整个系统。但是,具体到微观操作,还是需要面向过程的思路去处理。
总结
- 都是解决问题的思维方式,都是代码组织的方式。
- 解决简单问题可以使用面向过程
- 解决复杂问题:宏观上使用面向对象把握,微观处理上仍然是面向过程。
面向对象思考方式:遇到复杂问题,先从问题中找名词,然后确立这些名词哪些可以作为类,再根据问题需求确定的类的属性和方法,确定类之间的关系。
对象和类的概念
认识事物的过程
首先,我们需要考虑一个问题“为什么叫做面向对象,而不是面向类?”。计算机语言越到高级越接近人的思维方式,这是语言发展的一个基本规律。我们人认识世界,其实就是面向对象的。大家眼里都是一个个对象,要不怎么老说搞对象搞对象啊。
当我们认识一个新事物的时候,都会有一个抽象的过程,比如“龙”这个神兽,现实中没有吧,但是我们现在是能够分辨出来的,那是因为我们在电视上、书上都见过很多龙的身影了。在我们脑子里已经将之前看到过的这些龙的特征抽象出来了。
对象:是具体的实物
类:是对对象的抽象
类可以看做是一个模版,或者图纸,系统根据类的定义来造出对象。
类和对象的关系
对象和类的关系是特殊到一般,具体到抽象。
类:我们叫做 class 。
对象:我们叫做 Object , instance (实例)。以后我们说某个类的对象,某个类的实例。是一样的意思。
类的定义方式
定义
语法:
class 类名{
}
class是定义类的关键字
类名需要满足命名规则和规范,类的命名应该使用大驼峰命名原则。
示例:
class Phone{
}
上面的类定义好后,没有任何的其他信息,就跟我们拿到一张张图纸,但是纸上没有任何信息,这是一个空类,没有任何实际意义。所以,我们需要定义类的具体信息。对于一个类来说,一般有三种常见的成员:属性 field
、方法 method
、构造器 constructor
。这三种成员都可以定义零个或多个。
类的属性
属性用于定义该类或该类对象包含的数据或者说静态特征。
属性作用范围是整个类体。
属性定义格式:在类中,所有方法和代码块之外。
[修饰符] 属性类型 属性名 [= 默认值] ;
类的方法
方法则用于定义该类或该类的实例的行为特征和功能实现。方法是类和对象行为特征的抽象。方法很类似于面向过程中的函数。面向过程中,函数是最基本单位,整个程序由一个个函数调用组成;面向对象中,整个程序的基本单位是类,方法是从属于对象。
方法定义格式和之前讲的方法相同,其实就是一个东西
[修饰符] 方法返回值类型 方法名(形参列表) {
方法体;
}
类的方法的调用:如果调用的是自己方法,直接调用 ; 如果调用的是别人(别的类)的方法,需要加前缀(引用,类名)。
对象的创建和使用
必须使用 new 关键字创建对象。
Person person= new Person ();
使用对象(引用) . 成员变量来引用对象的成员变量。
person.age;
使用对象(引用) . 方法名(参数列表)来调用对象的方法。
person. setAge(23);
同一类的每个对象有不同的成员变量存储空间。
同一类的每个对象共享该类的方法。
构造器(constructor)
构造器用于构造该类的实例。
Java通过new关键字来调用构造器,从而返回该类的实例。
构造器是一种特殊的方法:
格式如下
[修饰符] 类名(形参列表){
//n条语句
}
注意:
- 通过new关键字调用。
- 构造器虽然有返回值,但是不能定义返回类型(返回值的类型肯定是本类),不能在构造器里使用带数据的return语句。
- 如果我们没有定义构造器,则编译代码时(java–>class)会自动定义一个无参的构造函数。如果已定义则编译器不会添加!
- 构造器的方法名必须和类名一致!
构造函数不是没有返回值吗?为什么不能用void?
规定。事实上,构造函数是有返回值的就是构造的对象。如此写法只是针对构造函数的特殊规定。
内存分析
栈:
- 每个线程私有,不能实现线程间的共享!
- 局部变量放置于栈中。
- 栈是由系统自动分配,速度快!栈是一个连续的内存空间!
堆:
- 放置new出来的对象!
- 堆是一个不连续的内存空间,分配灵活,速度慢!
方法区:
- 被所有线程共享!
- 用来存放程序中永远是不变或唯一的内容。(类代码信息、静态变量、字符串常量
this关键字
基本含义:当前对象
普通方法中,this总是指向调用该方法的对象。
构造方法中,this总是指向正要初始化的对象。
this最常的用法:
- 让类中的一个方法,访问该类的另一个方法(this.方法名())或属性(this.属性名)。访问属性更常用,用来区分同名的形参和属性。
- 使用this关键字调用重载构造方法。避免写相同的初始化代码,只能在构造方法中用,并且必须位于构造方法的第一句。
static 关键字
static属性: 属性被所有对象共享; 这个属性先于对象存在的
static 方法:这个方法先于对象存在
static 表示静态的, 所有对象共用的(所有对象都看到一份,而且只要其中任何一个对象对其进行修改,其他所有对象都会看到这个变化),所有的静态内容都是跟随类存在,在对象还没有的时候就已经存在。
在类中,用static声明的成员变量为静态成员变量
- 它为该类的公用变量,属于类,被该类的所有实例共享,在类被载入时被显式初始化
- 对于该类的所有对象来说,static成员变量只有一份。被该类的所有对象共享
- 可以使用” 对象.类属性 ”来调用。不过,一般都是用“ 类名.类属性 ()”。
用static声明的方法为静态方法
- 不需要对象,就可以调用( 类名.方法名() )
- 在调用该方法时,不会将对象的引用传递给它,所以在static方法中不可访问非static的成员。也不可以出现this关键字
可以通过对象引用或类名(不需要实例化)访问静态成员。
静态初始化块
经常用来初始化类,加载类信息时执行!
如果希望加载后,对整个类进行某些初始化操作,可以使用static初始化块。
- 是在类初始化时执行,不是在创建对象时执行。
- 静态初始化块中不能访问非static成员。
执行顺序:上溯到Object类,先执行Object的静态初始化块,再向下执行子类的静态初始化块,直到我们的类的静态初始化块为止。(到继承再看)
package
为什么需要package?
为了解决类之间的重名问题。
为了便于管理类:合适的类位于合适的包!
package怎么用?
通常是类的第一句非注释性语句。
包名:域名倒着写即可,再加上模块名,并与内部管理类。
其实内部实现就是靠目录结构来做到的。
特别注意:写项目时都要加包,不要使用默认包。
JDK中的主要包
java.lang
-包含一些Java语言的核心类,如String、Math、Integer、System和Thread,提供常用功能。
java.net
-包含执行与网络相关的操作的类。
java.io
-包含能提供多种输入/输出功能的类。
java.util
-包含一些实用工具类,如定义系统特性、使用与日期日历相关的函数。
import
为什么需要import?
如果不适用import,我们如果用到其他包的类时,只能这么写:java.util.Date,代码量太大,不利于编写和维护。通过import可以导入其他包下面的类,从而可以在本类中直接通过类名来调用。
import怎么使用?
import java.util.Date;
import java.util.*;
//导入该包下所有的类。会降低编译速度,但不会降低运行速度。
注意要点:
- java会默认导入 java.lang 包下所有的类,因此这些类我们可以直接使用。
- 如果导入两个同名的类,只能用包名+类名来显示调用相关类:
java.util.Date date = new java.util.Date();
静态导入(了解)
static import 静态导入(JDK 1.5后增加):
静态导入的作用:用于导入指定类的静态属性
如何使用:
import static java.lang.Math.*; //导入Math类的所有静态属性
import static java.lang.Math.PI; //导入Math类的PI属性
然后,我们可以在程序中直接使用: System.out.println(PI);
上面两种导入方式都是为了便于编写代码,提高可维护性。
封装(encapsulation)
封装的作用
我要看电视,只需要按一下开关和换台就可以了。有必要了解电视机内部的结构吗?有必要碰碰显像管吗?
我要开车,….
制造厂家为了方便我们使用电视,方便我们开车,把复杂的内部细节全部封装起来,只给我们暴露简单的接口,比如:电源开关、比如:油门。具体怎么内部实现的,我们不需要操心。
需要让用户知道的暴露出来,不需要让用户了解的全部隐藏起来。这就是封装。
白话:该露的露,该藏的藏
专业:我们程序设计要追求“高内聚,低耦合”。高内聚就是类的内部数据操作细节自己完成,不允许外部干涉;低耦合:仅暴露少量的方法给外部使用。
编程中封装的具体意义:
便于调用者调用。
良好的封装,便于修改内部代码,提高可维护性。
良好的封装,可进行数据完整性检测,保证数据的有效性。
访问控制符
关键字 | 同一个类 | 同一个包中 | 子类 | 所有类 |
---|---|---|---|---|
private | √ | |||
default | √ | √ | ||
protected | √ | √ | √ | |
public | √ | √ | √ | √ |