一、面向过程与面向对象
1.1 何谓编程思想
思想是汉语词汇,本义是客观存在反映在人的意识中经过思维活动而产生的结果或形成的观点及观念体系。
首先解释一下“思想”。
先问你个问题:你想做个怎样的人?
可能你会回答:我想做个好人,孝敬父母,尊重长辈,关爱亲朋……你看,这就是思想。这是你做人的思想,或者说,是你做人的原则。
做人有做人的原则,编程也有编程的原则。这些编程的原则呢,就是编程思想。
所谓编程思想,就是指用计算机来解决人们实际问题的思维方式。
好比学习一门课程一样,首先我们应该对课程的基本概念熟悉掌握,然后学习了由定义得出的结论,等到一本书学完后,我们最重要的就是知识体系的构建,而这与编程思想有着极大的联系。
我们在做一件事情的时候,这种方法是合理的:
1、先将一个问题分为一个个小模块,就好比书到章的这一种关系;
2、将一个小模块分为还要小的部分,就好比章到节的这种关系;
3、最终将它们分为不可分割的部分,就好比节到定义与概念这种关系;
4、这就好比我们实现一个程序的功能一样,先考虑大体方向,然后再逐步实现,做到不重不漏。
1.2 面向过程程序设计
面向过程的程序设计(Procedure-Oriented Programming,简称POP)是一种以过程为中心的编程思想。这些都是以什么正在发生为目标进行编程,不同于面向对象的是谁在受影响。与面向对象明显的不同就是封装、继承、类。
特性:模块化 流程化。
优点:性能比面向对象高, 因为类调用时需要实例化,开销比较大,比较消耗资源。单片机、嵌入式开发、Linux/ Unix等一般采用面向过程开发,性能是最重要的因素。
缺点:没有面向对象易维护、易复用、易扩展。
面向过程:根据业务逻辑从上到下写代码。面向过程编程最易被初学者接受,其往往用一长段代码来实现指定功能,开发过程的思路是将数据与函数按照执行的逻辑顺序组织在一起,数据与函数分开考虑。
1.3 面向对象程序设计
面向对象的程序设计(Object Oriented Programming,简称OOP)是按人们认识客观世界的系统思维方式,采用基于对象(实体)的概念建立模型,模拟客观世界分析、设计、实现软件的办法。通过面向对象的理念使计算机软件系统能与现实世界中的系统一一对应。
特性:抽象 封装 继承 多态。
优点:易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统,使系统更加灵活、更加易于维护。
缺点:性能比面向过程低。
1.4 面向过程与面向对象的区别
二者都是一种思想,面向对象是相对于面向过程而言的。
面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了;面向过程,强调的是功能行为,以函数为最小单位,考虑怎么做。
面向对象是把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为;面向对象,将功能封装进对象,强调具备了功能的对象,以类/对象为最小单位,考虑谁来做。
面向过程程序的特点:强调实现功能、算法和一切细节
面向对象程序的特点:强调具备了功能的对象
面向对象更加强调运用人类在日常的思维逻辑中采用的思想方法与原则,如抽象、分类、继承、聚合、多态等。
案例说明:
人把大象装进冰箱:
面向过程:
函数1:打开冰箱() {人站在冰箱前,打开冰箱,冰箱开到30度角的时候,冰箱的灯打开了....}
函数2:储存大象() {大象先迈出左腿,再迈出右腿,考虑冰箱能不能装的下...}
函数3:关闭冰箱() {人站在冰箱前,关闭冰箱,冰箱关到30度角的时候,冰箱的灯熄灭了....}
面向对象:
人{
打开(冰箱) {
冰箱.打开();
}
储存(大象) {
大象.进入(冰箱);
}
关闭(冰箱) {
冰箱.关闭();
}
}
冰箱{
打开() {}
关闭() {}
}
大象{
进入(冰箱) {}
}
面向过程---》面向对象,其实就是执行者到指挥者的一个过渡。
二者相辅相成,并不是对立的。解决复杂的问题,通过面向对象方式方便我们从宏观上把握事物之间的复杂关系,方便我们分析整个系统,具体到微观操作,仍然使用面向过程方式来处理。
怎么理解解决复杂的问题,面向对象更加高效?
举例:现在有一家公司,刚起步;总共4-5个员工,公司业务相对来说也比较少,事情发生了,大家一起做;这样会更加高效,这就是面向过程的方式;但是如果公司规模越来越大,员工四五百人,如果还按照上述的方式来实现,那么显然会非常混乱;那么就势必也划分市场部、采购部、生产部、客服部等并且对每个部门进行职责划分(这就相当于类的设计),当事情发生了,根据业务去找对应部门的人(这就相当于根据类找对象)即可;
也有人说,
面向过程:编年体 (根据年份时间来说明谁谁谁做了什么)
面向对象:纪传体(根据一个人说明他在不同时间做了什么)
1.5 面向对象分析问题的思路和步骤
程序员从面向过程的执行者转化成了面向对象的指挥者。
面向对象分析方法分析问题的思路和步骤:
根据问题需要,选择问题所针对的‘现实世界中的实体’。
从实体中寻找解决问题相关的属性和功能,这些属性和功能就形成了‘概念世界中的类’。
把抽象的实体用计算机语言进行描述,‘形成计算机世界中类的定义’。即借助某种程序语言,把类构造成计算机能够识别和处理的数据结构。
将‘类实例化成计算机世界中的对象’。对象是计算机世界中解决问题的最终工具。
二、Java基本元素:类和对象
世界是由什么构成的?
世界是由概念世界和物质世界组成。概念世界包含所有生命对客观世界的认知以及为记录认知而存在的事物的总和;物质世界,即客观存在,它是指不依赖人的意识并能为人的意识所反映的世界,它包括狭义的自然界和人类社会。
Java是对现实世界的映射,在Java世界里面: 万事万物皆“对象”。
2.1 面向对象的思想概述
类(Class)和对象(Object)是面向对象的核心概念。
类是对一类事物的描述,是抽象的、概念上的定义。
对象是实际存在的该类事物的每个个体,因而也称为实例(instance)。
可以理解为:类 = 汽车设计图;对象 = 实实在在的汽车;
面向对象程序设计的重点是类的设计。
类的设计,其实就是类的成员的设计。
2.1.1 从对象抽取出"类"
抽取出下列对象的属性和方法的共同特征:
分类是人们认识世界的一个很自然的过程,在日常生活中会不自觉地进行分类。
现实世界中的类:人类、猫类、汽车类。。。。。。
现实世界中的对象:张三某人、一只白猫、一辆奔驰汽车。。。
类:从一系列对象中抽取出来相同特征的抽象的概念。对象是类的具体化,即是该类事物实实在在存在的个体。
所以类就是对现实世界事物的抽象定义, 这个抽象定义就可以基本把某事物描述清楚. 要想描述清楚事物, 必须要知道事物有哪些特征(数据, 用变量保存), 有哪些行为(用方法描述), 当某事物的特征和行为都描述清楚后, 我们就认为对这个事物有一个大概的把握.
1、可以将复杂的事情简单化
2、专业的事情交给专业的人去做
3、现代社会,强调合作,面向对象正体现了这一思想(“木桶理论”新解,强调长板效应)
4、计算机是对现实世界的模拟,只有采用面向对象的思想才能更好的模拟世界、服务世界、改造世界。
2.1.2 对象是类的具体化
对象就是一个类的实实在在的实体, 也称为实例, 所以对象(object)也称为实例(instance), 实例就是对象, 对象就是实例.
比如 “学生” 可以是一个类, 因为它描述了学生这一群体事物, 而具体的”3年级的小明” 就是一个对象, 相同的“4年级的小花” 也是一个学生对象.
对比图:
2.1.3 先有对象还是先有类
在面向对象思想里,先有对象还是先有类,乍一看和先有鸡蛋还是先有鸡是一类问题。
现实生活中,先有对象,由对象抽象成类,在程序设计中则是相反的,由抽象的类实例化成对象。
2.1.4 理解“万事万物皆对象”
1.在Java语言范畴中,我们都将功能、结构等封装到类中,通过类的实例化,来调用具体的功能结构
>Scanner,String等
>文件:File
>网络资源:URL
2.涉及到Java语言与前端Html、后端的数据库交互时,前后端的结构在Java层面交互时,都体现为类、对象。
2.1.5 类和对象总结
在Java世界中的类是指的一种数据类型。
在Java中类是代码的载体,所有的代码都要写在类中。
在Java中类包括了:JDK自带的、第三方开发的、自己开发的类。。。
在Java世界中的对象是指的运行在内存(堆)中的类型的个体(实例)。
具有相同特征的一组事物通常为一类,是存在于人脑中的一种抽象定义。
类是一个模型,本身不具备任何的功能。比如蛋糕模子可以制作蛋糕,但不可以吃。
对象是类的具体化,即是该类事物实实在在存在的个体。
类是描述事物(对象)的, 一旦描述清楚, 就可以代表一类事物了, 但是类只是概念, 要想使用实体, 必须要有对象, 但是从时间的先后顺序来讲, 是先有类, 才有的对象, 因为类就像是一个模板, 而对象就像是用这个模板制造出来的产品, 如前面图示所描述的, 汽车设计图是一个模板, 一旦有了这个模板, 就可以使用设计图, 无限制地制造汽车了.
2.2 Java类及类的成员
现实世界的生物体,大到鲸鱼,小到蚂蚁,都是由最基本的‘细胞’构成的。同理,Java代码世界是由诸多个不同功能的‘类’构成的。
Java中用类class来描述事物。事物有大小,颜色,好坏,高低,胖瘦等等静态特征, 而这些特征又可以用数据类型描述,在程序中保存数据的就是变量。事物也有行为动作, 比如吃,跑,跳,睡觉等等, 而这些行为的描述又比变量稍复杂一点, 用方法来描述事物的行为。
常见的类的成员有:
属性: 对象的静态特征
属性——对象具有的各种特征;
每个对象的每个属性都拥有特定值,例如:张浩和李明的年龄、姓名不一样;
对应类中的成员变量;
方法: 对象的动态特征
方法——对象执行的操作,例如:收银员李明的方法:刷卡、打印订单、收银;
对应类中的成员方法;
Field = 属性 = 成员变量 = 实例变量,Method = 函数 = (成员)方法 = 实例方法;
创建类的对象 = 类的实例化 = 实例化类;
2.3 Java类的语法格式
所有Java程序都以类class为组织单元
关键字class定义类类型数据
所有的类都是引用数据类型。
定义一个类的步骤
1、定义类
2、编写类的属性 (成员变量)
3、编写类的方法 (成员方法)
定义类的语法:
修饰符 class 类名{
//定义属性部分
属性1的类型 属性1;
属性2的类型 属性2;
…
属性n的类型 属性n;
//定义方法部分
方法1;
方法2;
…
方法m;
}
说明:
修饰符public,类可以被任意访问
类将现实世界中的概念模拟到计算机程序中
示例代码:
public class Person {
String name; // 在类中声明的变量 name, 此时就是属性, 也称为成员变量, 描述人有名字
int age; // 在类中声明的变量 age, 此时就是属性, 也称为成员变量, 描述人有年龄
public void play() { // 声明方法play(), 也称为成员方法, 表示人会玩
System.out.println("玩的行为");
}
}
2.4 Java类的成员构成
2.4.1 简易版本
public class Person {
// 属性 或 成员变量 或 实例变量
String name;
int age;
boolean isMarried;
// 函数 或 成员方法 或 实例方法
public void walk() {
System.out.println("人走路的行为...");
}
public String disPlay() {
return "名字是:" + name + ",年龄是:" + age + ",结婚否:" + isMarried;
}
}
2.4.2 完整版本
public class Person {
// 属性 或 成员变量 或 实例变量
String name;
int age;
boolean isMarried;
// 构造器 或 构造函数
public Person() {
}
public Person(String name, int age, boolean isMarried) {
this.name = name;
this.age = age;
this.isMarried = isMarried;
}
// 函数 或 成员方法 或 实例方法
public void walk() {
System.out.println("人走路的行为...");
}
public String disPlay() {
return "名字是:" + name + ",年龄是:" + age + ",结婚否:" + isMarried;
}
// 构造代码块
{
name = "张三";
age = 18;
isMarried = true;
}
// 内部类
class Pet {
String name;
int age;
float weight;
}
}
2.5 创建Java自定义类
步骤:
1、定义类(考虑修饰符、类名)
2、编写类的属性(考虑修饰符、属性类型、属性名、初始化值)
3、编写类的方法(考虑修饰符、返回值类型、方法名、形参等)
练习:
定义Person、Animal、ClassRoom、Zoo等类,加以体会。
2.6 面向对象思想落地的实现
1.创建类,设计类的成员
2.创建类的对象
3.通过“对象.属性”或“对象.方法”调用对象的结构
//1.创建类,设计类的成员
class Person {
//属性
String name;
int age = 1;
boolean isMale;
//方法
public void eat() {
System.out.println("人可以吃饭");
}
public void sleep() {
System.out.println("人可以睡觉");
}
public void talk(String language) {
System.out.println("人可以说话,使用的是:" + language);
}
}
//测试类
public class PersonTest {
public static void main(String[] args) {
//2. 创建Person类的对象
Person p1 = new Person();
//Scanner scanner = new Scanner(System.in);
// 3. 调用属性和方法
// 调用对象的结构:属性、方法
// 调用属性:“对象.属性”
p1.name = "Tom";
p1.isMale = true;
System.out.println(p1.name);
// 调用方法:“对象.方法”
p1.eat();
p1.sleep();
p1.talk("Chinese");
}
}
三、Java对象的创建和使用
类定义完成之后,肯定无法直接使用。如果要使用,必须依靠对象,那么由于类属于引用数据类型,所以对象的产生格式(两种格式)如下:
格式一:声明并实例化对象
类名称 对象名称 = new 类名称 () ;
格式二:先声明对象,然后实例化对象:
类名称 对象名称;
对象名称 = new 类名称 () ;
引用数据类型与基本数据类型最大的不同在于:引用数据类型需要内存的分配和使用。所以,关键字new的主要功能就是分配内存空间,也就是说,只要使用引用数据类型,就要使用关键字new来分配内存空间。
当一个实例化对象产生之后,可以按照如下的方式进行类的操作:
对象.属性:表示调用类中的属性;
对象.方法():表示调用类中的方法。
3.1 使用对象操作类
3.1.1 创建对象语法一
public class TestDemo {
public static void main(String args[]) {
// 创建类对象
Person per = new Person();
//操作属性
per.name = "张三";
per.age = 30;
//调用类中的get()方法
per.get();
}
}
class Person {
String name;
int age;
public void get() {
System.out.println("姓名:" + name + ",年龄:" + age);
}
}
运行结果:
姓名:张三,年龄:30
3.1.2 创建对象语法二
以上完成了一个类和对象的操作关系,下面换另外一个操作来观察一下:
public class TestDemo {
public static void main(String args[]) {
// 声明对象
Person per = null;
// 实例化对象
per = new Person();
// 操作属性内容
per.name = "张三";
per.age = 30;
per.get();//调用类中的get()方法
}
}
class Person {
String name;
int age;
public void get() {
System.out.println("姓名:" + name + ",年龄:" + age);
}
}
运行结果:
姓名:张三,年龄:30
那么,问题来了,以上两种不同的实例化方式有什么区别呢?
3.1.3 Java对象内存解析
我们从内存的角度分析。首先,给出两种内存空间的概念:
(1)堆内存: 存放由new创建的对象和数组以及对象的实例变量(属性内容),堆内存需要用new关键字来分配空间。
(2)栈内存: 存放基本类型的变量数据、局部变量和对象的引用,但对象本身不存放在栈中。
对于null的理解:
首先,null不是有效的对象实例,因此没有为它分配内存。它只是一个值,指示对象引用当前不引用对象。
来自JVM规范:
The Java Virtual Machine specification does not mandate a concrete value encoding null.(Java虚拟机规范并没有授权一个具体的值编码null。)
任何情况下,只要看见关键字new,都表示要分配新的堆内存空间,一旦堆内存空间分配了,里面就会有类中定义的属性,并且属性内容都是其对应数据类型的默认值。
于是,上面两种对象实例化对象方式内存表示如下:
两种方式的区别在于①②,第一种声明并实例化的方式实际就是①②组合在一起,而第二种先声明然后实例化是把①和②分步骤来。
另外,如果使用了没有实例化的对象,结果如何?
public class TestDemo {
public static void main(String args[]) {
// 声明对象
Person per = null;
// per = new Person() ;//实例化对象
//操作属性和方法
per.name = "张三";
per.age = 30;
per.get();
}
}
class Person {
String name;
int age;
public void get() {
System.out.println("姓名:" + name + ",年龄:" + age);
}
}
运行结果:
Exception in thread "main" java.lang.NullPointerException
at com.wz.demo01.TestDemo.main(TestDemo.java:15)
此时,程序只声明了Person对象,但并没有实例化Person对象(只有了栈内存,并没有对应的堆内存空间),则程序在编译的时候不会出现任何的错误,但是在执行的时候出现了上面的错误信息。这个错误信息表示的是“NullPointerException(空指向异常)”,这种异常只要是引用数据类型都有可能出现。
3.1.4 对象引用传递分析
如果创建了一个类的多个对象,则每个对象都独立的拥有一套类的属性。(非static的),意味着:如果我们修改一个对象的属性a,则不影响另外一个对象属性a的值。
引用传递的精髓:同一块堆内存空间,可以同时被多个栈内存所指向,不同的栈可以修改同一块堆内存的内容。
下面通过若干个程序,以及程序的内存分配图,来进行代码的讲解。
public class TestDemo {
public static void main(String args[]) {
Person per1 = new Person(); // 声明并实例化对象
per1.name = "张三";
per1.age = 20;
// 这里不是创建新的对象,而是让per2指向per1的地址
Person per2 = per1; // 引用传递
per2.name = "李四";
per1.tell();
}
}
class Person {
String name;
int age;
public void tell() {
System.out.println("姓名:" + name + ",年龄:" + age);
}
}
对应的内存分配图如下:
再来看另外一个:
public class TestDemo {
public static void main(String args[]) {
Person per1 = new Person(); // 声明并实例化对象
Person per2 = new Person();
per1.name = "张三";
per1.age = 20;
per2.name = "李四";
per2.age = 30;
per2 = per1;// 引用传递
per2.name = "王五";
per1.tell();
}
}
class Person {
String name;
int age;
public void tell() {
System.out.println("姓名:" + name + ",年龄:" + age);
}
}
垃圾:指的是在程序开发之中没有任何对象所指向的一块堆内存空间,这块空间就成为垃圾,所有的垃圾将等待GC(垃圾收集器)不定期的进行回收与空间的释放。
3.1.5 Java对象内存深入解析
对象的产生
// 类定义如下
class Person {
int age;
void shout() {
System.out.println("oh, my god !I am "+age);
}
}
Person p1 = new Person();执行完后的内存状态。
对象的使用
class PersonTest {
public static void main(String[] args) {
Person p1 = new Person();
Person p2 = new Person();
p1.age = -30;
p1.shout();
p2.shout();
}
}
//程序运行的内存布局如下图
对象的生命周期
3.2 类的访问机制
在一个类中的访问机制:类中的方法可以直接访问类中的成员变量。(例外:static方法访问非static,编译不通过。)
在不同类中的访问机制:先创建要访问类的对象,再用对象访问类中定义的成员。
3.3 类中的匿名对象
没名字的对象称为匿名对象;
对象的名字按照之前的内存关系来讲,在栈内存之中,而对象的具体内容在堆内存之中保存,这样,没有栈内存指向堆内存空间,就是一个匿名对象。
使用情况:
如果对一个对象只需要进行一次方法调用,那么就可以使用匿名对象。
我们经常将匿名对象作为实参传递给一个方法调用。
public class AnonymityTest {
public static void main(String[] args) {
// 有名对象: 通过变量指向堆内存中的对象的地址
// 通过变量名指向new出来的对象,方法我们调用
Animal animal1 = new Animal();
// 多次使用
animal1.name = "小脑斧";
animal1.age = 6;
animal1.show();
// 匿名对象: 对象内存中的对象没有任何变量指向它
// 两个匿名对象分别调用了一次show();
// new Animal().show();
// new Animal().show();
// 场景一: 如果对一个对象只需要进行一次方法调用,那么就可以使用匿名对象。
new Animal().name = "大花猫";
new Animal().show(); //null 0
// 场景二: 我们经常将匿名对象作为实参传递给一个方法调用。
/*Phone phone = new Phone();
phone.name = "苹果";
phone.price = 8888.88;*/
People people = new People();
people.talk(new Phone());
}
}
class Animal {
String name;
int age;
void show() {
System.out.println("姓名是:" + name + "\t" + age);
}
}
/*
* 人类
* */
class People {
String name;
int age;
/*
* 人类交流的方法
* */
void talk(Phone phone) {
phone.call();
phone.email();
phone.social();
}
}
/*
* 手机类
* */
class Phone {
String name;
double price;
// 打电话
void call() {
System.out.println("用手机打电话");
}
// 发邮件
void email() {
System.out.println("用手机发邮件");
}
// 社交软件
void social() {
System.out.println("用手机里面的社交软件");
}
}
运行结果:
有俩个参数的构造方法
图书的名称:Java开发 图书的价格:89.9
匿名对象由于没有对应的栈内存指向,所以只能使用一次,一次之后就将成为垃圾,并且等待被GC回收释放。