day07_认识面向对象

面向过程和面向对象的区别

“面向过程”(Procedure Oriented):是一种以过程为中心的编程思想,简称 OP。“面向过程”也可称之为“面向记录”编程思想,就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了。所以面向过程的编程方式关注点不在“事物”上,而是做这件事分几步,先做什么,后做什么。例如:早晨起来:起床、穿衣、洗漱、上班,只要按照这个步骤来,就能实现“一天”的功能,整个这个过程中关注的是一步一步怎么做,并没有关注“人”这个事物。再例如:开门、调整座椅、系好安全带、踩离合、

启动、挂档、给油,只要按照这个步骤来,车就走了,显然关注点还是在步骤上,只要实现每一步就行,整个过程并没有关注“汽车”这个事物。

“面向对象”(Object Oriented):是一种以对象为中心的编程思想,简称 OO。随着计算机技术的不断提高,计算机被用于解决越来越复杂的问题。一切事物皆对象,通过面向对象的方式,将现实世界的事物抽象成对象。通过面向对象的方法,更利于用人理解的方式对复杂系统进行分析、设计与编程。同时,面向对象能有效提高编程的效率,通过封装技术,可以像搭积木的一样快速开发出一个全新的系统。面向对象将对象作为程序的基本单元,将程序和数据封装其中,以提高软件的重用性、灵活性和扩展性。

使用面向对象编程思想开发系统,在现代开发中会将面向对象贯穿整个过程,一般包括:OOA/OOD/OOP:
  • OOA:面向对象分析(Object-Oriented Analysis)
  • OOD:面向对象设计(Object-Oriented Design)
  • OOP:面向对象编程(Object-Oriented Programming)
面向过程和面向对象有什么关系呢?
  • 面向过程其实是最为实际的一种思考方式,就算是面向对象的方法也是含有面向过程的思想。可以说面向过程是一种基础的方法。它考虑的是实际地实现。一般的面向过程是从上往下步步求精。面向对象主要是把事物给对象化,对象包括属性与行为。当程序规模不是很大时,面向过程的方法还会体现出一种优势。因为程序的流程很清楚,按着模块与函数的方法可以很好的组织。但对于复杂而庞大的系统来说,面向过程显得就很无力了。面向对象的好处之一就是显著的改善了软件系统的可维护性。对于编程语言来说,基于 C 语言的编程是面向过程的,C++只能说一半面向过程一半面向对象,java 语言就是一门完全面向对象的编程语言。因为 java 底层是 C++语言实现的。当然,除了 java 语言之外,还有很多都是完全面向对象的编程语言,例如:C#、Python 等。
面向对象分析方法分析问题的思路和步骤:
  1. 根据问题需要,选择问题所针对的现实世界中的实体。
  2. 从实体中寻找解决问题相关的属性和功能,这些属性和功能就形成了概念世界中的类。
  3. 把抽象的实体用计算机语言进行描述,形成计算机世界中类的定义。即借助某种程序语言,把类构造成计算机能够识别和处理的数据结构。
  4. 将类实例化成计算机世界中的对象。对象是计算机世界中解决问题的最终工具。

面向对象三大特征

  • 封装(Encapsulation)
  • 继承(Inheritance)
  • 多态(Polymorphism)

面向对象的思想概述

软件存在的意义就是为了解决现实世界当中的问题,它必然模拟现实世界,也就是说现实世界中有什么,软件中就对应有什么。类(Class)和对象(Object)是面向对象的核心概念。

那么类和对象分别是什么,它们的区别和联系是什么呢?
  • 类是对一类事物的描述,是抽象的、概念上的定义。
  • 对象是实际存在的该类事物的每个个体,因而也称为实例(instance)。

例如:“沈腾”、“赵本山”、“宋丹丹”都是实际存在的对象,他们都属于“笑星”类,类描述事物的共同特征,那么“笑星”类都有哪些共同特征呢?笑星类都有姓名、性别、年龄等状态信息(属性),他们还有一个共同的行为就是“演出”(方法)。但当具体到某个对象上之后,我们发现姓名是不同的,性别是不同的,年龄也是不同的,演出的效果也是不同的。所以我们在访问姓名、性别、年龄的时候,必须先有笑星对象,通过真实存在的笑星对象去访问他的属性,包括“演出”的时候,只有“笑星”类是不行的,必须先有笑星对象,让笑星对象去执行“演出”这个动作。

  • 通过类可以创建对象,对象又被称为实例(instance),这个过程也可以称为实例化
  • 面向对象程序设计的重点是类的设计 
  • 类的设计,其实就是类的成员的设计
通过以上的描述,我们得知:类 = 属性 + 方法,而属性描述的是状态,方法描述的是行为动作。行为动作以方法的形式存在,那属性以什么形式存在呢?例如:姓名、性别、年龄, 变量用来存储数据。不错,对象的属性以变量形式存在,并且这里所说的变量是我们之前提过的“成员变量当中的实例变量”。为什么是实例变量呢,实例变量就是对象级别的变量,这样的变量要求必须先存在对象,通过对象才能访问。例如:“中国人”这个类,有一个属性是“身份证号”,每一个中国人的“身份证号”都是不一样的,所 以身份证号必须使用一个真实存在的“中国人对象”来访问。不能使用“中国人”这个类去访问身份证号。一个类可以实例化 N 多个对象,假设通过“中国人”这个类创建了 100 个“中国人对象”,那么“身份证号”必然会有 100 个实例变量空间去存储。

总结一下常见的类的成员

  • 现实中的属 性对应类中的成员变量
  • 现实中的行 为:对应类中的成员方法

day07_认识面向对象

类的语法格式

day07_认识面向对象

创建Java自定义类

步骤:
  1. 定义类(考虑修饰符、类名)
  2. 编写类的属性(考虑修饰符、属性类型、属性名、初始化值)
  3. 编写类的方法(考虑修饰符、返回值类型、方法名、形参等)

代码示例

package demo;

/*
 * 要求:
 * (1)创建Person类的对象,设置该对象的name、age和sex属性,调用study方法,
 * 输出字符串“studying”,调用showAge()方法显示age值,
 * 调用addAge()方法给对象的age属性值增加2岁。
 *
 */
public class Person {
    //成员变量
    String name;
    int age;
    /**
     * sex:1 表明是男性
     * sex:0 表明是女性
     */
    int sex;

    //成员方法
    public void study(){
        System.out.println("studying");
    }

    public void showAge(){
        System.out.println("age:" + age);
    }

    public int addAge(int i){
        age += i;
        return age;
    }
}

类的访问机制:

  • 在一个类中的访问机制:类中的方法可以直接访问类中的成员变量。(例外:static方法访问非static,编译不通过。)
  • 在不同类中的访问机制:先创建要访问类的对象,再用对象访问类中定义的成员。

对象的创建和使用

                                              day07_认识面向对象

类定义之后,就可以使用类这个“模板”来创造“对象”了,一个类是可以创建多个对象的哦!怎么创建呢,语法是什么?其实语法格式很简单:new 类名(),这样就可以完成对象的创建了。俗话说,你想要什么 java 都可以给你,想要啥你就 new啥。创建对象完整语法:
  • 类名 对象名 = new 类名();

使用“对象名.对象成员”的方式访问对象成员(包括属性和方法)

使用上面自定义的类,创建对象
package demo;

public class PersonTest {
    public static void main(String[] args) {
        // 创建对象p1
        Person p1 = new Person();
        //给变量赋值
        p1.name = "Tom";
        p1.age = 18;
        p1.sex = 1;

        //调用成员方法
        p1.study();//studying
        p1.showAge();//age:18
        //调用成员方法,把方法的返回值赋值给变量newAge
        int newAge = p1.addAge(2);
        System.out.println(p1.name + "的新年龄为:" + newAge);//Tom的新年龄为:20

        System.out.println(p1.age);//20

        //创建对象p2
        Person p2 = new Person();
        //调用常用方法
        p2.showAge();//age:0
        p2.addAge(10);
        p2.showAge();//age:10

        p1.showAge();//age:20
    }
}

以上代码最初接触的时候,大家肯定会感觉非常陌生,这也是正常的,Person p1 = new Person();实际上和 int i = 10 是类似的,对于 int i = 10 来说,int 是一种基本数据类型,i 是变量名,10 是 int 类型的字面量。那对于 Person p1 = new Person() 来说,其中 Person 是一种引用数据类型,p1 是变量名,new Person()执行之后是一个 Person 类型的对象。大家要注意了,java 语言当中凡是使用 class 关键字定义的类都属于引用数据类型,类名本身就是这种引用数据类型的类型名。

注意:
  • 如果创建了一个类的多个对象,对于类中定义的属性(成员变量),每个对象都拥有各自的一套副本,且互不干扰。
  • 在 java 语言当中,当实例变量没有手动赋值,在创建对象的时候,也就是说在 new 的时候,系统会对实例变量默认赋值,它们的默认值请参考下表:

                                                       day07_认识面向对象

对象创建和使用的深层次解密

为了更好的理解上面的程序,先来看看java 虚拟机是如何管理它的内存的,请看下图:

                                        day07_认识面向对象

程序计数器:

  • 概念:可以看做当前线程所执行的字节码的行号指示器。
  • 特点:线程私有的内存
java 虚拟机栈(重点):
  • 概念:描述的是 java 方法执行的内存模型。(每个方法在执行的时候会创建一个栈帧,用于存储局部变量表,局部变量表存放了编译期可知长度的各种基本数据类型(boolean、byte、char 、 short 、 int 、 float 、 long 、double)、对象引用(reference类型,它不等同于对象本身,是对象在堆内存的首地址操作数栈,动态链接,方法出口等信息。每个方法从调用直至完成的过程,就对应一个栈帧从入栈到出栈的过程。)
  • 特 点 : 线 程 私 有, 生 命 周期 和 线 程 相同 。 这 个 区域 会 出 现 两种 异 常 :*Error 异常: 若线 程请求 的深 度大于 虚拟 机所允 许的 深度 。OutOfMemoryError异常:若虚拟机可以动态扩展,如果扩展是无法申请到足够的内存。
本地方法栈:
  • 概念:它与虚拟机栈所发挥的作用是相似的,区别是 java 虚拟机栈为执行 java 方法服务,而本地方法栈是为本地方法服务。
  • 特点:线程私有,也会抛出两类异常:*Error 和 OutOfMemoryError。
java 堆(重点):
  • 概念:是被所有线程共享的一块区域,在虚拟机启动时创建。
  • 特点:线程共享,存放的是对象实例(所有的对象实例和数组),GC 管理的主要区域。可以处于物理上不连续的内存空间。这一点在Java虚拟机规范中的描述是:所有的对象实例以及数组都要在堆上分配。
⑤ 方法区(重点):
  • 概念:存储已被虚拟机加载的类信息、常量、静态变量,即时编译器编译后的代码等数据。
  • 特点:线程共享的区域,抛出异常 OutOfMemory 异常:当方法区无法满足内存分配需求的时候。
以上所描述内容,有看得懂的,也有看不懂的,例如:线程、本地方法等,这个需要大家在学习后面内容之后,返回来再看一看,那个时候你就全部明白了。针对于目前来说,大家必须要知道 java 虚拟机有三块主要的内存空间,分别是“虚拟机栈(后面简称栈)”、“方法区”、“堆区”,方法区存储类的信息,栈中存储方法执行时的栈帧以及局部变量,堆区中主要存储 new 出来的对象,以及对象内部的实例变量。其中垃圾回收器主要针对的是堆内存,方法区中最先有数据,因为程序执行之前会先进行类加载。栈内存活动最频繁,因为方法不断的执行并结束,不断的进行压栈弹栈操作。将目前阶段需要掌握的内存空间使用一张简单的图表示出来,这个图是大家需要掌握的:

                                                         day07_认识面向对象

大概了解了 java 虚拟机内存分配之后,来看看以下代码在执行过程中,内存是如何变化的:
package demo;

public class StudentTest {
    public static void main(String[] args) {
        int i = 10;
        Student s1 = new Student();
    }
}

以上代码在执行过程中内存的变化如下图所示:

第一步:进行类加载 

day07_认识面向对象

第二步: main 方法调用,给 main 方法分配栈帧(压栈)

day07_认识面向对象

第三步:执行 int i = 10,局部变量

day07_认识面向对象

第四步:执行 new Student(),在堆中创建对象,同时初始化实例变量

day07_认识面向对象

第五步:将堆区中学生对象的内存地址赋值给局部变量 s1 

day07_认识面向对象 

上图所描述内存图有些地方为了帮助大家更好的理解,有些位置画的不是很精确,随着后面内容的学习我们再进一步修改,目前上图已经够大家用了。上图中 i 变量和 s1 变量都是局部变量,都在栈内存当中,只不过 i 变量是基本数据类型 int,而 s1 变量是引用数据类型 Student。上图中堆区当中的称为“对象”,该“对象”内部 no、name、age、sex 都是实例变量/属性,这些变量在 new对象的时候初始化,如果没有手动赋值,系统会赋默认值。上图堆区中“对象”创建完成之后,该对象在堆区当中的内存地址是:0x1111,程序中的“=”将 0x1111 这个堆内存地址赋值给 s1 变量,也就是说 s1 变量保存了堆内存对象的内存地址,我们对于这种变量有一种特殊的称呼,叫做“引用”。也就是说对于 Student s1 = new Student()代码来说,s1 不是对象,是一个引用,对象实际上是在堆区当中,s1 变量持有这个对象的内存地址。java 中没有指针的概念(指针是 C 语言当中的机制),所以 java 程序员没有权利直接操作堆内存,只能通过“引用”去访问堆内存中的对象,例如:s1.no、s1.name、s1.sex、s1.age。访问一个对象的内存,其实就是访问该对象的实例变量,而访问实例变量通常包括两种形式,要么就是读取数据,要么就是修改数据,例如:System.out.println(s1.no)这就是读取数据,s1.no= 100 这就是修改数据。

实例变量执行赋值运算之后的内存图 

day07_认识面向对象

创建多个对象的内存结构图 

day07_认识面向对象

匿名对象

我们也可以不定义对象的句柄,而直接调用这个对象的方法。这样的对象叫做匿名对象。如:

new Person().shout();

使用情况

  • 如果对一个对象只需要进行一次方法调用,那么就可以使用匿名对象。
  • 我们经常将匿名对象作为实参传递给一个方法调用。
上一篇:day07 【Scanner类、Random类、ArrayList类】


下一篇:SCRUM冲刺day07