javaSE_08_认识面向对象丶对象的创建和使用

知识框架

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

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

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

使用面向对象编程思想开发系统,在现代开发中会将面向对象贯穿整个过程,

一般包括:OOA/OOD/OOP:
  • OOA:面向对象分析(Object-Oriented Analysis)
  • OOD:面向对象设计(Object-Oriented Design)
  • OOP:面向对象编程(Object-Oriented Programming)
面向过程和面向对象有什么关系呢?
  • 面向过程其实是最为实际的一种思考方式,就算是面向对象的方法也是含有面向过程的思想。可以说面向过程是一种基础的方法。它考虑的是实际地实现。一般的面向过程是从上往下步步求精。面向对象主要是把事物给对象化,对象包括属性与行为。当程序规模不是很大时,面向过程的方法还会体现出一种优势。因为程序的流程很清楚,按着模块与函数的方法可以很好的组织。但对于复杂而庞大的系统来说,面向过程显得就很无力了。

面向对象分析方法分析问题的思路和步骤:

  • 根据问题需要,选择问题所针对的现实世界中的实体。
  • 从实体中寻找解决问题相关的属性和功能,这些属性和功能就形成了概念世界中的类。
  • 把抽象的实体用计算机语言进行描述,形成计算机世界中类的定义。即借助某种程序 语言,把类构造成计算机能够识别和处理的数据结构。
  • 将类实例化成计算机世界中的对象。对象是计算机世界中解决问题的最终工具。

面向对象的三大特征

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

类和对象的概念

软件存在的意义就是为了解决现实世界当中的问题,它必然模拟现实世界,也就是说现实世界中有什么,软件中就对应有什么。面向对象编程思想中关注点是“对象”或者“事物”,那么在编程语言当中要想创建对象则必须先有类,那么类和对象分别是什么,它们的区别和联系是什么呢?

什么是类

类:是一组相关属性和行为的集合。可以看成是一类事物的模板,使用事物的属性特征和行为特征来描述该类事物。类是现实世界当中具有共同特征的事物进行抽象形成的模板或概念。而对象是实际存在的个体。例如:“汽车”就是一个类(所有的汽车都有方向盘、发动机、都能形式,这是它们的共同特征),“你家的那个汽车”就是一个真实存在的对象。通过类可以创建对象,对象又被称为实例(instance),这个过程也可以称为实例化。对象1、2、3 具有共同特征,进行抽象形成了类,所以从对象到类称为抽象。
  • 现实中的属性:对应类中的成员变量
  • 现实中的行为:对应类中的成员方法

什么是对象

对象:是一类事物的具体体现。对象是类的一个实例,必然具备该类事物的属性和行为。
  • 对象的属性以变量形式存在,并且这里所说的变量是成员变量当中的实例变量。实例变量就是对象级别的变量,这样的变量要求必须先存在对象,通过对象才能访问。例如:“中国人”这个类,有一个属性是“身份证号”,每一个中国人的“身份证号”都是不一样的,所以身份证号必须使用一个真实存在的“中国人对象”来访问。不能使用“中国人”这个类去访问身份证号。一个类可以实例化 N 多个对象,假设通过“中国人”这个类创建了 100 个“中国人对象”,那么“身份证号”必然会有 100 个实例变量空间去存储。
  • 对象的行为以方法形式表示,并且这里所说的方法是成员方法。对象调用方法时,根据对象中方法标记(地址值),去类中寻找方法信息。成员方法多个对象共用的一份。当多个对象的引用指向同一个内存空间(变量所记录的地址值是一样的)只要有任何一个对象修改了内存中的数据,随后,无论使用哪一个对象进行数据获取,都是修改后的数据。

类与对象的关系

  • 类是对一类事物的描述,是抽象的。
  • 对象是一类事物的实例,是具体的。
  • 类是对象的模板,对象是类的实体。

类的定义

类的语法格式

  

类的成员之一:属性

  

示例:

public class Person {
    private int age; //声明private变量 age
    public String name = "Lila"; //声明public变量 name
}

变量的分类:成员变量与局部变量

变量根据定义位置的不同,我们给变量起了不同的名字。在方法体外,类体内声明的变量称为成员变量。 在方法体内部声明的变量称为局部变量。

                            

如下图所示:

 

成员变量(属性)和局部变量的区别?

        

创建Java自定义类

步骤:

  1. 定义类(考虑修饰符、类名)
  2. 编写类的属性(考虑修饰符、属性类型、属性名、初始化值)
  3. 编写类的方法(考虑修饰符、返回值类型、方法名、形参等)
package demo01;
 
/*
    手机类:
        类名:
        手机(Phone)
        成员变量:
        品牌(brand)
        价格(price)
        成员方法:
        打电话(call)
        发短信(sendMessage)
 */
public class Phone {
    //成员变量
    String brand;
    int price;
 
    //成员方法
    public void call() {
        System.out.println("打电话");
    }
 
    public void sendMessage() {
        System.out.println("发短信");
    }
}

知识框架

对象的创建和使用

常情况下,一个类并不能直接使用,需要根据类创建一个对象,才能使用。

1.导包:也就是指出需要使用的类,在什么位置。

  • 格式:import 包名称.类名称;

对于和当前类属于同一个包的情况,可以省略导包语句不写。

2. 创建对象

  • 格式:类名称 对象名 = new 类名称();

3. 使用分为两种情况:(也就是,想用谁,就用对象名点儿谁)

  • 使用成员变量:对象名.成员变量名
  • 使用成员方法:对象名.成员方法名(参数)

我们要创建一个对象首先必须创建一个类。我们现在创建对象使用上面定义的类

package demo01;
 
/*
    创建对象
        格式:类名 对象名 = new 类名();
        范例:Phone p = new Phone();
    使用对象
        1:使用成员变量
            格式:对象名.变量名
            范例:p.brand
        2:使用成员方法
            格式:对象名.方法名()
            范例:p.call()
 */
public class PhoneTest {
    public static void main(String[] args) {
        //创建对象
        Phone p = new Phone();
 
        //使用成员变量
        System.out.println(p.brand);//null
        System.out.println(p.price);//0
 
        p.brand = "小米";
        p.price = 2999;
 
        System.out.println(p.brand);//小米
        System.out.println(p.price);//2999
 
        //使用成员方法
        p.call();//打电话
        p.sendMessage();//发短信
    }
}

注意事项:

  • 如果成员变量没有进行赋值,那么将会有一个默认值。如下图所示:

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

为了更好的理解上面的程序,先来看看java 虚拟机是如何管理它的内存的,请看下图: 
程序计数器:
  • 概念:可以看做当前线程所执行的字节码的行号指示器。
  • 特点:线程私有的内存
java 虚拟机栈(重点):
  • 概念:描述的是 java 方法执行的内存模型。(每个方法在执行的时候会创建一个栈帧,用于存储局部变量表,操作数栈,动态链接,方法出口等信息。每个方法从调用直至完成的过程,就对应一个栈帧从入栈到出栈的过程。)
  • 特 点 : 线 程 私 有, 生 命 周期 和 线 程 相同 。 这 个 区域 会 出 现 两种 异 常 :StackOverflowError 异常: 若线 程请求 的深 度大于 虚拟 机所允 许的 深度 。OutOfMemoryError 异常:若虚拟机可以动态扩展,如果扩展是无法申请到足够的内存。
本地方法栈:
  • 概念:它与虚拟机栈所发挥的作用是相似的,区别是 java 虚拟机栈为执行 java 方法服务,而本地方法栈是为本地方法服务。
  • 特点:线程私有,也会抛出两类异常:StackOverflowError 和 OutOfMemoryError。
java 堆(重点):
  • 概念:是被所有线程共享的一块区域,在虚拟机启动时创建。
  • 特点:线程共享,存放的是对象实例(所有的对象实例和数组),GC 管理的主要区域。可以处于物理上不连续的内存空间。
方法区(重点):
  • 概念:存储已被虚拟机加载的类信息、常量、静态变量,即时编译器编译后的代码等数据。
  • 特点:线程共享的区域,抛出异常 OutOfMemory 异常:当方法区无法满足内存分配需求的时候。

以上所描述内容,有看得懂的,也有看不懂的,例如:线程、本地方法等,这个需要大家在学习后面内容之后,返回来再看一看,那个时候你就全部明白了。针对于目前来说,大家必须要知道 java 虚拟机有三块主要的内存空间,分别是“虚拟机栈(后面简称栈)”、“方法区”、“堆区”,方法区存储类的信息,栈中存储方法执行时的栈帧以及局部变量,堆区中主要存储 new 出来的对象,以及对象内部的实例变量。其中垃圾回收器主要针对的是堆内存,方法区中最先有数据,因为程序执行之前会先进行类加载。栈内存活动最频繁,因为方法不断的执行并结束,不断的进行压栈弹栈操作。将目前阶段需要掌握的内存空间使用一张简单的图表示出来,这个图是大家需要掌握的:

大概了解了 java 虚拟机内存分配之后,来看看以下代码在执行过程中
public class StudentTest {
    public static void main(String[] args) {
        int i = 10;
        Student s1 = new Student();
    }
}

内存发生的变化:

总结一下

  • 上图中 i 变量和 s1 变量都是局部变量,都在栈内存当中,只不过 i 变量是基本数据类型 int,而 s1 变量是引用数据类型 Student。
  • 上图中堆区当中的称为“对象”,“该对象”内部 no、name、age、sex 都是实例变量/属性,这些变量在 new对象的时候初始化,如果没有手动赋值,系统会赋默认值。上图堆区中“对象”创建完成之后,该对象在堆区当中的内存地址是:0x1111,程序中的“=”将 0x1111 这个堆内存地址赋值给 s1 变量,也就是说 s1 变量保存了堆内存对象的内存地址,我们对于这种变量有一种特殊的称呼,叫做“引用”。也就是说对于 Student s1=new Student()代码来说,s1 不是对象,是一个引用,对象实际上是在堆区当中,s1 变量持有这个对象的内存地址。
  • 如果创建了一个类的多个对象,对于类中定义的属性,每个对象都拥有各自的一套副本,且互不干扰。

空指针异常

java.lang.NullPointerException 被称为空指针异常,在 java 编程当中属于很常见的异常,接下来研究一下以上程序执行过程的内存图是如何变化的。请看下图:

以上代码的 ball.color。但是程序在运行阶段会通过 ball 引用查找堆内存当中的对象,因为 color 是实例变量,该变量存储在 java 对象内部,当 ball=null 执行之后表示“引用 ball”不再保存 java对象的内存地址,换句话说通过 ball 引用已经无法找到堆内存当中的 java 对象了,对于程序来说这个时候就没有办法正常访问了,这种情况下就会发生空指针异常。总之,当一个“空的引用”去访问“对象相关/实例相关”数据的时候,此时一定会发生空指针异常。 

方法调用时参数的传递问题

方法在调用的时候参数是如何传递的呢?其实在调用的时候参数传递给方法,这个过程就是赋值的过程,参数传递和“赋值规则”完全相同,只不过参数传递在代码上看不见“=”运算符。我们先来深入的研究一下“赋值规则”吧! 
我们知道“赋值”运算的时候实际上和变量的数据类型无关,无论是基本数据类型还是引用数据类型,一律都是将变量中保存的“值”复制一份,然后将复制的这个“值”赋上去。他们的区别在于,如果是基本数据类型则和堆内存当中的对象无关,如果是引用数据类型由于传递的这个值是 java 对象的内存地址,所以会导致两个引用指向同一个堆内存中的 java 对象,通过任何一个引用去访问堆内存当中的对象,此对象内存都会受到影响。

方法参数传递基本类型

  • 基本数据类型的参数,形式参数的改变,不影响实际参数

依据:

  • 每个方法在栈内存中,都会有独立的栈空间,方法运行结束后就会弹栈消失

                                     

代码举例

public class Demo {
 
        public static void main(String[] args) {
            int number = 100;
            System.out.println("调用change方法前:" + number);//调用change方法前:100
            change(number);
            System.out.println("调用change方法后:" + number);//调用change方法后:100
        }
 
        public static void change(int number) {
            number = 200;
        }
    }
 

方法参数传递引用类型

结论:

  • 对于引用类型的参数,形式参数的改变,影响实际参数的值

依据:

  • 引用数据类型的传参,传入的是地址值,内存中会造成两个引用指向同一个内存的效果,所以即使方法 弹栈,堆内存中的数据也已经是改变后的结果。当多个对象的引用指向同一个内存空间(变量所记录的地址值是一样的)只要有任何一个对象修改了内存中的数据,随后,无论使用哪一个对象进行数据获取,都是修改后的数据。 

代码示例

public class Demo {
 
    public static void main(String[] args) {
        int[] arr = {10, 20, 30};
        System.out.println("调用change方法前:" + arr[1]);//调用change方法前:20
        change(arr);
        System.out.println("调用change方法后:" + arr[1]);//调用change方法后:200
    }
 
    public static void change(int[] arr) {
        arr[1] = 200;
    }
 
}

当实例变量是一个引用

其实只要记住一点,任何“引用”当中存储一定是对象的内存地址,“引用”不一定只是以局部变量的形式存在,类当中有什么就可以“.”什么。

构造方法 Constructor

构造方法是类中特殊的方法,通过调用构造方法来完成对象的创建,以及对象属性的初始化操作。 

构造方法的作用:

  • 当一个对象被创建时候,构造方法用来初始化该对象给对象的成员变量赋初始值。

构造方法怎么定义,请看以下的语法格式: 

根据参数不同,构造器可以分为如下两类:

  • 隐式无参构造器(系统默认提供)
  • 显式定义一个或多个构造器(无参、有参)

注 意:

  • Java语言中,每个类都至少有一个构造器
  • 默认构造器的修饰符与所属类的修饰符一致
  • 一旦显式定义了构造器,则系统不再提供默认构造器
  • 一个类可以创建多个重载的构造器,具体调用哪个构造方法,那要看调用的时候传递的实际参数列表符合哪个构造方法了
  • 父类的构造器不可被子类继承
  • 构造方法名和类名一致。 
  • 构造方法返回值类型不需要写,写上就报错,包括 void 也不能写
  • 无参数构造方法又叫做缺省构造器,或者默认构造方法。
  • 一般在开发中为了方便编程,建议程序员手动的将无参数构造方法写上,因为不写无参数构造方法的时候,这个默认的构造方法很有可能就不存在了,另外也是因为无参数构造方法使用的
    频率较高。

代码演示

package com.wrg;
 
/*
 *  自定义的Student类.成员变量,name age
 *  要求在 new Person的同时,就指定好name,age的值
 *  实现功能,利用方法去实现, 构造方法,构造器 Constructor
 *  作用: 在new 的同时对成员变量赋值, 给对象的属性初始化赋值  new Person 对属性 name,age赋值
 *
 *  构造方法的定义格式
 *    权限  方法名(参数列表){
 *    }
 *    方法的名字,必须和类的名字完全一致
 *    构造方法不允许写返回值类型  , void 也不能写
 *
 *    构造方法在什么时候,运行呢, 在new 的时候,自动执行
 *    只运行一次,仅此而已
 *
 *    每个class必须拥有构造方法,构造方法不写也有
 *    编译的时候,javac, 会自动检查类中是否有构造方法
 *    如果有,就这样的
 *    如果没有,编译器就会自动添加一个构造方法
 *     编译器自动添加的无参构造方法:
 *    自己手写了构造方法,编译的时候,不会自动添加构造方法!
 */
public class Student {
    private String name;
    private int age;
 
    //定义出Student类的构造方法
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
        //System.out.println("我是一个空参数构造方法");
    }
 
    //定义出Student类的无参构造方法
    public Student() {
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public int getAge() {
        return age;
    }
 
    public void setAge(int age) {
        this.age = age;
    }
 
 
}

构造方法和一般方法区别

  • 构造方法在对象创建时就执行了,而且只执行一次。
  • 一般方法是在对象创建后,需要使用时才被对象调用,并可以被多次调用。

注意:

  • 可以在类的构造器中使用"this(形参列表)"的方式,调用本类中重载的其他的构造器!
  • 明确:构造器中不能通过"this(形参列表)"的方式调用自身构造器
  • 如果一个类中声明了n个构造器,则最多有 n - 1个构造器中使用了 "this(形参列表)"
  • "this(形参列表)"必须声明在类的构造器的首行!
  • 在类的一个构造器中,最多只能声明一个"this(形参列表)"

总结:属性赋值过程

截止到目前,我们讲到了很多位置都可以对类的属性赋值。现总结这几个位 置,并指明赋值的先后顺序。

赋值的位置:

  • ① 默认初始化,实例变量没有手动赋值的时候,实际上系统会默认赋值,实例变量是在构造方法执行的过程中完成初始化的,完成赋值的。
  • ② 显式初始化
  • ③ 构造器中初始化
  • ④ 通过“对象.属性“或“对象.方法”的方式赋值

赋值的先后顺序: ① - ② - ③ - ④

原文地址:https://www.cnblogs.com/wurengen/p/13252832.html