【JavaSE】类和对象

面向对象概述

面向对象编程(简称OOP),其核心思想就是参照现实中的事物,将事物的属性特征、行为特征抽象出来,使用类来表示,当涉及到一个具体的实例时,就将类进行实例化,使用一个对象来表示。

简而言之,计算机是一个不能独立思考的物品。当使用者赋予某种权利的同时,计算机才能获得并使用该种权利。因此程序员想要计算机呈现某种事物时,就需要将此种事物的特征给抽象出来,而这些特征就会构造成一个整体,这个整体程序员使用类来形容。于是,当使用者进行使用时,直接去调用它的特征即可。此时需要考虑的一个问题就是每个人拥有相同的特征,但是特征的内容不尽相同,例如人们都拥有身份证,但是内容却不相同。对于此类问题,既然某种事物拥有相同的特征,那么就使用这种特征来表示这类事物,对于这类事物的个体,将这些特征实例化,每个具体的个体使用一个对象来表示即可。

举例:对与CSDN这个系统来说,其核心事物就是用户,因此可以将其抽象成类:用户类。对于用户类来说,其属性特征就是用户名、用户密码以及文章数等其他,其行为特征就是看文章、写文章以及修改文章等其他。对于我们使用者来说,都是用户,因此我们的属性特征和行为特征正是上述内容,但是其特征的内容对于每个人来说都不相同,因此一个用户使用CSDN时,都会使用上述事物来构建成一个对象,每个用户属于一个对象。所谓的类和对象,就是面向对象的核心概念。

类和对象

类和对象是面向对象的核心概念。对于Java来说,面向对象的程序设计思想贯穿始终,无论是现在学习的JavaSE,还是后续学习的JavaEE,都离不开类和对象。

什么是类?

类是具有相同特征事物的抽象描述,是抽象的、概念上的定义。

什么是对象?

对象是实际存在的该类事物的具体个体,是具体的。因此也被称为实例。

类的成员概述

面向对象程序设计的重点是类的设计;

类的设计,其实就是类的成员的设计。

  •  现实世界中的生物体,大到鲸鱼、小到蚂蚁,都是由最基本的细胞构成的。同理,Java代码世界是由诸多个不同功能的类构成的。
  • 现实生物世界中的细胞又是由什么构成的呢?细胞核、细胞质 ... 
  • Java中用类class来描述事物也是如此。类,是一组相关属性和行为的集合,这也是类两个最基本的成员之一。

        a. 属性:该类事物的状态信息。对应类中的成员变量。

        b. 行为:该类事物要做什么操作,或者基于事物的状态能做什么。对应类中的成员方法。

public class Person {

    // 属性,也被称为成员变量
    public String name; // 姓名
    public int age; // 年龄
    public char gender; // 性别

    // 行为,也被称为成员方法
    public void sayHi() {
        System.out.println("早上好!");
    }

    public void sing() {
        System.out.println("我非常喜欢唱歌!");
    }

    public void dance() {
        System.out.println("我非常喜欢跳舞!");
    }
    
}

类的实例化

类表示的是一类事物的特征,而对象就是类实例化的产物。

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

面向对象完成具体功能的三步骤

步骤一:创建类,并设计类的内部成员。

步骤二:创建类的对象。

步骤三:通过对象,调用其内部的属性或方法。

内存解析

JVM中使用到的内存区域

5213f4418310e120f9d23649305e6f87.png

虚拟机栈:用于存储局部变量等。局部变量表存放了编译器可知长度的各种基本数据类型和对象引用(reference类型,它不等同于对象本身,是对象在堆内存的首地址)。方法执行完,自动释放。

堆:此内存的唯一目的就是存放对象实例,几乎所有的对象实例都在这里进行分配内存。在Java虚拟机中的描述是,所有的对象实例以及数组都要在堆上分配。

方法区:用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。

创建类的一个对象

903be0512ed243a0b8ecc45c3f293d51.png

创建类的多个对象

a58f1afba73c44e1b1320460774b0ee8.png

通过上述的内存解析,可以看出,对象和数组的内存位置大致相同。两者都是在虚拟机栈中存放数组名/类名的地址,然后地址再对应到堆内存中存放具体的数据。对于这种存放方式,导致的一个问题就是如果不new一个新的空间,那么可能存在几个变量对应的都是一个地址,即同一个对象。这使得若多个变量对应一个地址,那么进行修改时,其他变量的值也会同时被改变。

创建类的多个对象时,每个对象在堆空间中会有一个对象实体。每个对象实体中保存着一份类的属性,也就是成员变量、这里就包含着一个成员变量和局部变量的区别:成员变量会有一个默认初始值,初始值的大小和数组的一致;局部变量则没有默认初始值。

属性/成员变量

成员变量是类的成员之一,描述的是事物的状态信息。例如在用户类中的用户名、密码以及年龄等都属于成员变量。

变量的分类

按照数据类型来分:基本数据类型(8种),引用数据类型(6种)。

按照变量在类中声明的位置不同:成员变量,局部变量(方法内、方法形参、构造器内、构造器形参以及代码块内)。

cb9029ef5765925ab4b9216ae5b7518b.png

成员变量 VS 局部变量

概念

成员变量:在方法体外、类体内声明的变量。

局部变量:在方法体内部等位置声明的变量。

public class User {

    // 成员变量
    private String username; // 姓名
    private String password; // 密码
    private int age; // 年龄
    private String gender; // 性别

    public void sleep(int hour) { // 形参,相当于是局部变量
        System.out.println(username + "的睡眠时间是" + hour + "小时!");
    }

    public void eat() {
        String food = "鱼香肉丝"; // 局部变量
        System.out.println("我最喜欢吃的菜是" + food + "!");
    }
    
}

相同点

1. 变量声明的格式相同:数据类型   变量名   =   变量值;

2. 变量都有其有效的作用域,出了作用域就失效了;

3. 变量必须先声明,后赋值,再使用。

不同点

1. 声明的位置不同

    成员变量:方法体外,类体内。

    局部变量:声明在方法内、方法形参、构造器内、构造器形参、代码块内。

2. 内存位置不同

    成员变量:随着对象的创建,存储在堆内存中。

    局部变量:存储在栈空间中。

3. 生命周期不同

    成员变量:随着对象的创建而创建,随着对象的消亡而消亡。

    局部变量:随着方法对应的栈帧入栈,局部变量会在栈中进行分配,随着方法对应的栈帧出栈,局部变量消亡。

4. 作用域不同

    成员变量:在整个类的内部都是有效的。

    局部变量:仅限于声明此变量所在的方法(或构造器内、或代码块内)。

5. 权限修饰符不同

    成员变量:可以使用权限修饰符。

    局部变量:不可以使用权限修饰符。

6. 默认值不同

    成员变量:存在初始默认值。

    局部变量:没有初始默认值。

    换句话说,如果没有给成员变量进行显式初始化赋值,则会有默认初始化值;而使用局部变量之前,必须要显式的赋值,否则报错。

方法

成员方法是类的成员之一,描述的是该类事物对应的操作。例如用户类可以修改自己的信息,可以进行吃饭的动作等行为。在Java中,方法是必定包含在某个类中的,即使程序的入口:main方法也是包含在Main类中,因此方法即是成员方法(个人理解)。

方法 / 函数的理解

方法是用来完成某个功能操作。在某些语言中也被称为函数或者过程。

通俗的讲:方法是对某个功能的封装。例如在某个游戏中,攻击是一个重复性极高的操作,如果每次攻击都写一段新的代码。显而易见,代码将会非常冗余。但是如果进行封装,将攻击的操作封装成一个方法,当使用攻击操作时,直接调用方法,此时代码就会变得简练许多。

将功能封装为方法的目的是,可以实现代码调用、减少冗杂、优化代码。

Java中方法不能独立存在,所有方法必须定义在类里。

方法声明的格式

修饰符 返回类型 方法名(形参列表) throws 异常列表 { // 方法头

    // 方法体

}

1. 一个完整的方法 = 方法头 + 方法体

2. 修饰符不仅包括权限修饰符,还包括其他例如static、abstract、native、final、sychronized等。

方法调用的内存解析

public static void main(String[] args) {
	Person person = new Person();
	person.name = "王彬泽";
	person.age = 21;
	person.gender = '男';
	
	person.eat();
	
	person.sleep(6);
	
	person.interests("编程"); 
} 

7af5be7796cb4d84b6c1416e6390af9e.png

由于main方法是程序的入口,因此对于方法调用来说,一定是从main方法开始,最后从main方法结束。学过数据结构的对栈都有一定的了解——先后进出,虚拟机栈也是如此。程序开始时,main方法先进入虚拟机栈中,由于new了一个Person类,因此会在相应堆内存中分配空间。调用eat方法,那么eat方法就会进入虚拟机栈中,当eat方法调用完毕之后,出栈。后续操作仿照进行。sleep方法进栈,并且自身还带着hour局部变量,功能完成之后出栈。然后interests方法进栈出栈,最后main方法出栈,程序执行完毕。

方法重载(overload)

概念

在同一个类中,允许存在一个以上的同名方法,只要他们的参数列表不同即可。参数列表不同,意味着参数类型和参数个数的不同。

特点

只看参数列表,且参数列表必须不同(参数类型和参数个数)。调用时,根据方法参数列表的不同来区别。

如何判断两个方法是否相同?

方法名相同,且形参列表相同。

编译器是如何确定调用某个具体的方法?

先通过方法名确定了一波重载的方法,进而通过不同的形参列表,确定具体的方法。

形参个数可变的方法

JDK5.0

使用场景

在调用方法时,可能会出现形参的类型是确定的,但是参数的个数不确定。

格式

(参数类型   ...   参数名)

说明

1. 该方法在调用时,针对可变的形参赋的实参值可以是0个、1个 ... N个。

2. 该方法重载有其他方法时,先针对确定参数个数的方法进行调用,如果没有再调用形参个数不确定的方法。

3. 该方法不能重载数据类型相同的数组方法。因为从某一方面论证来说该方法的形参本质就是数组。在使用时,使用方法也和数组的使用方法一致。

4. 若某方法存在参数个数可变的形参,那么必须将此形参放在最后一位,并且参数个数可变的形参在一个方法中只能存在一个。

5. 如下图:若存在多个参数,那么多个参数类型一致的重载方法不能存在,编译器不知道调用哪个方法合适。例如方法一、三和四,参数类型都是int,那么调用print时,编译器认为方法一三四都可以调用,但没有一个最合适的方法,因而报错。

182d68716a5b5bb374c131eeb518d13f.png

使用举例

ba4f3579f2750345f6fb5f99ab8c3986.png

方法的值传递机制

对于方法内声明的局部变量来说,如果出现赋值操作:

如果是基本数据类型的变量,则将此变量保存的数据值传递出去;

如果是引用数据类型的变量,则将此变量保存的地址值传递出去。

基本概念

形参:在定义方法时,方法名后面括号中声明的变量称为形式参数,简称形参。

实参:在调用方法时,方法名后面括号中使用的变量/值/表达式称为实际参数,简称实参。

方法参数的传递机制

值传递。

规则

实参给形参赋值的过程:

如果形参是基本数据类型的变量,则将实参保存的数据值赋给形参;

如果形参是引用数据类型的变量,则将实参保存的地址值赋给形参。

内存解析

形参是基本数据类型的变量:

d1057befedd04fbd9934f68e6046d8e4.png

5cc67462ba95493288ffb8d1a565db5c.png

如图所示,由于形参是基本数据类型的变量。因此只是将变量值赋给a与b,并非地址,这也导致最终m和n并不会进行交换。

形参是引用数据类型的变量:

public class Data {

    public int a;
    public int b;

}
public class Test {

    public static void main(String[] args) {
        Data data = new Data();
        data.a = 10;
        data.b = 20;
        Test.swap(data);
    }

    public static void swap(Data data) {
        int temp = data.a;
        data.a = data.b;
        data.b = temp;
    }

}

44723dbbfb824f119cbb2dc92c3d197f.png

如图所示,由于形参是引用数据类型的变量。因此会在堆内存中分配空间,当给swap方法传递形参时,传递的是地址,从而使得操作的是同一块内存空间,最后的结果也使得值成功交换。

构造器

构造器又称为构造方法,但是构造方法和方法是没有关联的。

作用

new对象,并同时给对象的相关属性赋值。

格式

权限修饰符 方法名(形参列表) {
        

}

说明 

1. 创建类之后,在没有自定义构造器的情况下,系统会提供一个空参的构造器,并且构造器的权限和类的权限相同。

2. 创建类之后,如果程序员已经自定义了构造器,那么系统将不再会提供默认构造器。

3. 一个类中可以声明多个构造器,彼此之间形成重载。

this关键字

this的理解

this可以表示当前对象(在方法中使用时)或者正在创建的对象(在构造器中使用时)。

this可以调用的结构

属性、方法、构造器。

this可以可以使用的位置

1. 针对方法内使用的情况(准确的说是非static修饰的方法)

通过对象调用方法,可以在方法内调用当前对象的属性或者其他方法。此时,我们可以在属性或其他方法前使用”this.属性/方法“,表示当前属性或方法所属的对象。一般可以不使用,但是当方法的形参和属性名相同时,就必须使用”this.属性“进行区分。使用this修饰的表示属性,没有使用this修饰的表示局部变量。

2. 针对构造器内使用的情况

通过构造器创建对象时,可以在构造器内调用正在创建方法的对象或者属性。和方法内使用的情况类似,如果没有重名的情况出现,那么可以忽略”this.属性“,反之就必须使用”this.属性“

通过构造器创建对象时,还可以在构造器内调用其他构造器。使用的结构为“this(形参列表)”

        a. 调用时构造器时,不能调用自身构造器,否则会形成死递归。

        b. 调用其他构造器时,必须声明在首行且只能有一个。

        c. 如果一个类中声明了n个构造器,那么最多有n-1个构造器中可以调用其他构造器。

对于类和对象的叙述就到这里,下篇文章就正式步入面向对象三大特性的介绍,应该是分别用三篇文章进行介绍,这样即可以有较多的篇幅来对每一个特性进行解释,也可以文章使脉络清晰,读起来更加流程。 

  • 20
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

王彬泽

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值