第一章 面向对象
小白:Java 是一门完全面向对象的编程语言!嗯?什么是面向过程?什么又是面向对象呢?
大佬:在我们正式进入学习这部分前,了解一下面向过程和面向对象这两个概念,对于我们 接下来的学习有很大的好处。别急,下面我就来和你说一说。
面向过程——步骤化
面向过程就是分析出实现需求所需要的步骤,通过函数一步一步实现这些步骤,接着依次调用即可
面向对象——行为化
面向对象是把整个需求按照特点、功能划分,将这些存在共性的部分封装成对象,创建了对象不是为了完成某一个步骤,而是描述某个事物在解决问题的步骤中的行为
小白:面向过程还挺明白的,面向对象也太难理解了吧(哭了)
大佬:我再来举个例子再给你好好说一说
桌球实例 - 帮助理解
我们先提出一个需求:
设计一个桌球游戏(略过开球,只考虑中间过程)
面向过程方式思考:
1.palyer1 击球
2. 实现画面击球效果
3. 判断是否进球及有效
4.palyer2 击球
5. 实现画面击球效果
6. 判断是否进球及有效
7. 返回步骤 1
8. 输出游戏结果
把上面的步骤通过函数一步一步实现,这个需求就完成了。
面向对象方式思考:
经过观察我们可以看到,其实在上面的流程中存在很多 共性 的地方
所以我们将这些共性部分全集中起来,做成一个 通用的结构
- 玩家系统(包括 palyer1 和 palyer2)
- 击球效果系统,负责展示给用户游戏时的画面
- 规则系统,判断是否犯规,输赢等
我们将繁琐的步骤,通过行为、功能,模块化,这就是面向对象,我们甚至可以利用该程序,分别快速实现 8 球和斯诺克的不同游戏(只需要修改规则即可,玩家系统,击球效果系统都是一致的)
面向过程和面向对象的优缺点:
面向过程
优点:性能上它是优于面向对象的 **,因为类在调用的时候需要实例化,开销过大。
缺点:不易维护、复用、扩展 **
用途:单片机、嵌入式开发、Linux/Unix 等对性能要求较高的地方
面向对象
优点:易维护、易复用、易扩展,由于面向对象有 封装 、 继承 、 多态性 的特性,可以设计出 低耦合 的系统,使系统更加灵活、更加易于维护
缺点:性能比面向过程低
低耦合,简单的理解就是说,模块与模块之间尽可能的独立,两者之间的关系尽可能简单,尽量使其独立的完成成一些子功能,这避免了牵一发而动全身的问题。这一部分我们会在面向对象学习结束后进行系统的整理和总结。
只通过教科书后的例题是无法体会到面向过程所存在的问题的,在一些小例程中,面向过程感觉反而会更加的简单,但是一旦面临较大的项目,我们需要编写 N 个功能相似的函数,函数越来越多,代码量越来越多,Bug 之路也就此开始了。
1.1 类和对象
面向对象的思想是如何在 java 展现的呢?就是通过类和对象
类 是一组相关的属性和行为的集合。是一个抽象的概念。
对象 是该类事物的具体表现形式。具体存在的个体。
成员变量 事物的属性
成员方法 事物的行为
上面我们说了这几个概念,那么到底应该怎么理解呢?
类就是对一些具有 共性特征 ,并且 行为相似 的个体的描述。
比如小李和老张都有姓名、年龄、身高、体重等一些 属性,并且两人都能够进行聊天、运动等 相似的行为。
由于这两个人具有这些共性的地方,所以我们把它抽象出来,定义为一个 类——人类,而小李、老王正是这个类中的个体(对象),而个体才是真正具体的存在,光提到人类,你只知道应该有哪些属性行为,但你不知道他具体的一些值,比如你知道他属于“人类”所以他应该有姓名,年龄等属性,但你并不知道他具体叫什么,年龄多大了。而小李和老王这两个具体的对象,却能够实实在在的知道老王今年 30 岁了、身高 175 等值。
结合上面的例子再总结一下:
成员变量就是用来描述这个类的属性的,比如人类应该拥有姓名,年龄等属性
成员方法就是用来描述事物的行为的,比如人类能够聊天、运动等行为
1.1_1 类的定义及使用
我们了解了类和对象的基本定义和概念
下面我们就通过一个实例 来了解到底如定义一个类
A: 类的定义
成员变量:定义格式和普通变量一样,只是位置不同,在 类中,方法外。
成员方法:定义格式和普通方法一样,只是去 掉了 static(后期讲解原因)
B: 创建并且使用类
a: 创建对象格式
类名 对象名 = new 类名();
Eg:Person p = new Person();
b: 如何使用成员变量和成员方法呢
对象名. 成员变量
对象名. 成员方法()
Eg:p.reading();
p.sleeping();
其实我们可以看出来,类的定义还是很简单的,结合了我们前面的知识,只是一些位置上的不同罢了,至于创建对象格式中每部分的意义我们在下面马上就要讲解了(我们还需要补充一些知识点)
我们先来看这么一个话题
经常听说有一个词叫做局部变量,它和类中的成员变量有什么关系吗?
1.1_2 成员变量和局部变量的区别:
(1)在类中的位置不同
成员变量:类中方法外
局部变量:方法定义中或者方法声明上
(2)在内存中的位置不同
成员变量:在堆中
局部变量:在栈中
(3)生命周期不同
成员变量:随着对象的创建而存在,随着对象的消失而消失
局部变量:随着方法的调用而存在,随着方法的调用完毕而消失
(4)初始化值不同
成员变量:有默认值(下面会详讲这一点)
局部变量:没有默认值,必须定义,赋值,然后才能使用
关于初始化问题我们在下面详细讲解,但是我现在还有一个问题,在我们学习 java 中内存分配的时候,有这样一 > 句话,“堆内存用来存放 new 创建的对象和数组”换句话说对象存在于堆中,而成员变量存在于类中而且对象是类 > 的个体,所以成员变量也存在于堆中,那么问题就来了,按照同样的方式推导的时候,则会发现方法也和成员变 > 量一样存在于对象中,岂不就是说,局部变量也存在于堆中呢? 这明显与我们上面的定义有区别
一个类可以创建 n 个不同的对象,当我们 new 一个对象后,这个对象实体,已经在堆上分配了内存空间,由于类 > 的成员变量在不同的对象中各不相同(例如,小李和老王的姓名不同),都需要自己各自的存储空间,所以类的成员变量会随着对象存储在堆中,而由于类的方法是所有对象通用的,所以创建对象时,方法里面的局部变量并没有被创建,只有等到对象使用方法的时候才会被压入栈。
1.1_3 形式参数的问题:
我们知道堆中存放着 new 出来的对象以及数组,两者均为引用类型
在讲数组的相关知识的时候,我们已经讲过了基本类型和数组这一种引用类型,形式参数对实际参数的影响
跳转—第三章 3.3_1 参数传递问题
在我们学习对象后,我们继续来看一下这个问题
基本类型 :形式参数的改变不影响实际参数(值传递)
引用类型:形式参数的改变直接影响实际参数(引用传递)
1.1_4 匿名对象(理解)
我们先来了解一下如何创建匿名对象
(1)简单的理解就是:没有名字的对象
(2)应用场景
A: 调用方法,仅仅只调用一次的时候。
B: 可以作为实际参数传递
好处:匿名对象调用完就是垃圾,可以被垃圾回收器回收,并且这样写比较简化。
注意:如果对一个对象的多个成员进行调用,就必须给这个对象起名字(即上图中的 s),即使用普通创建对象的方法
我们下面来看一个实例来看一下如何具体使用匿名对象
1.1_5 封装的概述和使用
首先我们先来简单举一个例子:
例如:夏天宿舍很热,我们(用户)只需要操作遥控器即可使用空调,并不需要了解空调内部是如何运行的
现在由于知识掌握较少,所以对于封装的概念理解不是很深,不要着急,先过一遍,针对封装的意义及问题我会写一篇具体的文章,现在只需要有一个印象即可。
封装概述:是指隐藏对象的属性和实现细节,仅对外提供公共访问方式
封装好处:
· 隐藏实现细节,提供公共的访问方式
· 提高了代码的复用性
· 提高安全性
封装原则:
· 将不需要对外提供的内容都隐藏起来
· 把属性隐藏,提供公共方法对其访问
思考过程:
通过对象去给成员变量赋值,可以赋值一些非法的数据
这是不合理的。所以在赋值之前应该先对数据进行判断
StudenDemo 是一个测试类,测试类一般只创建对象,调用方法
所以这个判断应该定义在 Student 类中。需要使用逻辑语句
逻辑语句应该定义在方法中。所以在 Student 类中提供一个方法来对数据进行校验
但是如果偏偏不调用方法来赋值,还是直接赋值
这样我们的方法就没有起作用
所以我们必须强制要求使用我的方法,而不能直接调用成员变量
针对这种情况 Java 提供了一个关键字 private
Private:私有的,可以修饰成员变量和成员方法
被 private 修饰的成员只能在本类中访问,所以外界想要操作类中的成员变量就必须通过调用类中的方法来实现
1.1_6 访问修饰符
客户端程序员:即在其应用中使用数据类型的类消费者,他的目标是收集各种用来实现快速应用开发的类。
类创建者:即创建新数据类型的程序员,目标是构建类。
访问控制存在的原因:
a、让客户端程序员无法触及他们不应该触及的部分;
b、允许库设计者可以改变类内部的工作方式而不用担心会影响到客户端程序员
java 的四个关键字:public、protected、default、private
(他们决定了紧跟其后被定义的东西可以被谁使用)
适用范围 < 访问权限范围越小,安全性越高 >
访问权限 | 类 | 包 | 子类 | 其他包 | |
---|---|---|---|---|---|
public | √ | √ | √ | √ | 对任何人都是可用的 |
protect | √ | √ | √ | 继承的类可以访问以及和 private 一样的权限 | |
default | √ | √ | 继承的类可以访问以及和 private 一样的权限 | ||
private | √ | 除类型创建者和类型的内部方法之外的任何人都不能访问的元素 |
1.1_7 private 的应用标准案例
可用 this 关键字进行完善(一般都是使用完善后的)
this 的内容在下面马上介绍
1.1_8 this 关键字的概述和应用
这里的调用只能 通过对象名,这里它应该代表的是 student 的一个对象
this:代表所在类的对象引用
记住: 方法被哪个对象调用,this 就代表哪个对象
适用:局部变量隐藏成员变量(稍后补充)
1.2 构造方法
构造方法和它所在类的名字相同,但构造方法没有返回值。
通常会使用构造方法给一个类的实例变量赋初值,或者执行其它必要的步骤来创建一个完整的对象
怎么理解呢?
当一个对象被创建时候,构造方法用来初始化该对象。
那么什么叫做初始化呢?
我们要知道吗,构造函数又被叫做 构造器 ,它就是为了 初始化类 ,当调用该构造器,会用值去 初始化成员 ,当使用 带参构造 时,会将 参数中的值传递给成员 ,而使用无参构造时,即会用一些 默认的值 来进行成员的初始化
例如:
private String name;
private int age;
public bool flag;
上面的三个成员变量被无参构造进行默认初始化的时候,会被初始化
name = null;age = 0;flag = false;
注意:
A: 如果我们没有给出构造方法,系统将自动提供一个无参构造方法
B: 如果我们给出了构造方法,系统将不再提供默认的无参构造方法
如果这个时候我们还想使用无参构造方法,就必须自己给出,建议永远 给出无参构造方法(所以我们习惯于在类中同时给出无参和带参构造方法)
给成员变量赋值的两种方法:
A:setXxx()
B: 构造方法
讲到这里我们不得不提一下,get、set 方法,在今后写代码的时候,我们需要频繁的用到,其实这里就体现了 封装 , 不让用户直接操作成员 ,可以起到 安全 的作用,具体内容可以看前封装部分的知识
构造方法是为了创建对象时,传入一些必要的参数用来初始化对象。
setter/getter 是为了控制属性可不可以读写
两者不矛盾
之前在讲解类的时候,我们由于缺少一些知识的铺垫,所以我们将类的初始化过程讲一下:
类的初始化过程
Student s = new Student(); 在内存中做了哪些事情?
·加载 Student.class 文件进内存
·在栈内存为 s 开辟空间
·在堆内存为学生对象开辟空间
·对学生对象的成员变量进行默认初始化
·对学生对象的成员变量进行显示初始化
·通过构造方法对学生对象的成员变量赋值
·学生对象初始化完毕,把对象地址赋值给 s 变量
1.3 static 关键字
Static 关键字注意事项
A: 在静态方法中是没有 this 关键字的
静态是随着类的加载而加载,this 是随着对象的创建而 存在的 → 静态比对象先存在
B: 静态方法只能访问静态的成员变量和静态的成员方法
静态方法:
A:成员变量:只能访问静态变量
B:成员方法:只能访问静态成员方法
非静态方法:
A: 成员变量:可以是静态的,也可以是非静态的
B: 成员方法:可以是静态的成员方法,也可以是非静态的成员方法
为什么静态方法不能访问非静态方法呢?
因为静态方法是随着类的加载而加载的,静态是优于对象存在的,你要访问非静态的东西,可是这时候可能它还不存在。
总结起来一句话:静态只能访问静态
1.4 静态变量和成员变量
趁热打铁,我们来对静态变量和成员变量做一些区分
所属不同
·静态变量属于类,所以也称为类变量
·成员变量属于对象。所以也称为实例变量(对象变量)
·内存中位置不同
·静态变量存储于方法区的静态区
·成员变量存储于堆内存
·内存出现时间不一样
·静态变量随着类的加载而加载,随着类的消失而消失
·成员变量随着对象的创建而存在,随着对象的消失而消失
·调用不同·
·静态变量可以通过类名调用,也可以通过对象调用
·成员变量只能通过对象名调用
方式一 是用对象调用成员方法
方式二 是用类调用成员方法(推荐方式二【需要将对应成员方法写为静态的】)
如果不想让用户创建对象调用成员方法:
只需要把构造方法私有,外界就不能创建对象了
在同一个文件夹下,类定义两个文件夹中和一个文件夹中是一样的
1.4 制作文档注释和说明书
(一)
制作文档注释,文档说明书工具解析文档注释
javadoc 工具
D: 格式
javadoc -d 目录 -author -version ArrayTool.java
目录:就可以写一个文件夹的路径
制作帮助文 档出 错:
找不到可以文档化的公共或受保护的类:告诉我们类的权限不够(解决办法:在 class 前面加 public)
(二)
API(Application Programming Interface)
应用程序编程接口(帮助文档)
Jdk 可以帮助我们查阅一些类、方法的详细用法以及参数说明,学会查阅文档也是一项很重要的本领(网络上自行下载)
下面试着自己通过查阅文档使用一个类
Math 类
A: 是针对数学进行操作的类
B: 没有构造方法,因为它的成员都是静态的
C: 产生随机数
public static double random(): [0.0,1.0)
D: 如何产生一个 1 -100 之间的随机数
int number = (int)(Math.random()*100)+1;
E: 猜数字小游戏
1.5 代码块
我们下面来讲解这一部分的最后一个知识点
(1)用 {} 括起来的代码。
(2)分类:
A: 静态代码块
概念: 在 java 类中(方法中不能存在静态代码块)
使用 static 关 键字和{} 声明的代码块:
执行: 静态代码块在类被加载的时候就运行了,而且只运行一次,并且优 先于各种代码块以及构造函数。
作用: 一般情况下,如果有些代码需要在项目启动的时候就执行,这时候 就需要静态代码块。比如一个项目启动需要加载的 很多配置文件等 资源,我们就可以都放入静态代码块中。
对类的数据进行初始化,仅仅只执行一次。
B: 构造代码块
概念:在 java 类中使用 {} 声明的代码块(和静态代码块的 区 别是少了 static 关键字):
执行: 构造代码块在创建对象时被调用,每次创建对象都会调用一 次,但是优先于构造函数执行。
作用: 和构造函数的作用类似,都能对对象进行初始化,并且只要 创建一个对象,构造代码块都会执行一次。但是反过来,构 造函数则不一定每个对象建立时都执行(多个构造函数情况 下,建立对象时传入的参数不同则初始化使用对应的构造函 数)。
把多个构造方法中相同的代码可以放到这里,每个构造方法 执行前,首先执行构造代码块。
C: 局部代码块
用于限定变量的生命周期,及早释放,提高内存利用率。
静态代码块, 构造代码块, 构造方法的顺序问题
·静态代码块 > 构造代码块 > 构造方法
结尾:
如果内容中有什么不足,或者错误的地方,欢迎大家给我留言提出意见, 蟹蟹大家!^_^
如果能帮到你的话,那就来关注我吧!
在这里的我们素不相识,却都在为了自己的梦而努力 ❤
一个坚持推送原创 Java 技术的公众号:理想二旬不止