七、类与对象

news2024/11/18 19:56:33

文章目录

  • 类与对象
    • 1.1 自定义类
    • 1.2 第一个类
    • 1.3 private变量
    • 1.4 变量默认值
    • 1.5 构造方法
    • 1.6 类和对象的生命周期

类与对象

本文为书籍《Java编程的逻辑》1和《剑指Java:核心原理与应用实践》2阅读笔记

将客观世界中存在的一切可以描述的事物称为对象(实体),即“万物皆对象”。例如,楼底下卖烧饼的大姐、楼下KFC服务员、楼下停的共享单车,桌子上的水杯,中午的外卖订单等,都可以称为对象。这些众多的对象有些是有相同的属性和功能(或行为)的,按照属性和功能(或行为)等对它们进行分类、抽象,就形成了概念世界中的类。类实际上就是根据生活经验总结抽取出来的概念产物,相当于一组对象的模型。类和对象关系如下图所示。

如上图,类和对象我们可以理解为类等于抽象概念的人,对象等于实实在在的某个人。类是抽象的,对象是具体的,类相当于创建对象的蓝图。例如汽车,类与对象的关系就如汽车设计图与汽车实物的关系。面向对象的程序实现需要通过类创建对应的实例化对象,来对应客观世界中的实体。

1.1 自定义类

一个类由其包含的属性以及该类可以进行的操作组成,属性又可以分为是类本身具有的属性,还是一个具体实例具有的属性,同样,操作也可以分为是类型本身可以进行的操作,还是一个具体实例可以进行的操作。这样,一个类就主要由 4 4 4部分组成:

  1. 类本身具有的属性,通过类变量体现。
  2. 类本身可以进行的操作,通过类方法体现。
  3. 类实例具有的属性,通过实例变量体现。
  4. 类型实例可以进行的操作,通过实例方法体现。

类变量和实例变量都叫成员变量,也就是类的成员,类变量也叫静态变量或静态成员变量。类方法和实例方法都叫成员方法,也都是类的成员,类方法也叫静态方法。

1、类变量类方法

类型本身具有的属性通过类变量体现,经常用于表示一个类型中的常量。比如Math类,定义了两个数学中常用的常量,如下所示:

   public static final double E = 2.718281828459045;
   public static final double PI = 3.141592653589793;

E E E表示数学中自然对数的底数,自然对数在很多学科中有重要的意义; P I PI PI表示数学中的圆周率 π \pi π。要使用类变量,可以直接通过类名访问,如Math.PI。这两个变量的修饰符也都有public staticpublic表示外部可以访问,static表示是类变量。与public相对的是private,表示变量只能在类内被访问。与static相对的是实例变量,没有static修饰符。这里多了一个修饰符final final在修饰变量的时候表示常量,即变量赋值后就不能再修改了。使用final可以避免误操作,比如,如果有人不小心将Math.PI的值改了,那么很多相关的计算就会出错。另外,Java编译器可以对final变量进行一些特别的优化。所以,如果数据赋值后就不应该再变了,就加final修饰符。表示类变量的时候,static修饰符是必需的,但publicfinal都不是必需的。

2、类方法

类型本身的方法通过类方法体现,经常用于表示一个类型中的方法。比如Math中定义的一些数学函数,要使用这些函数,直接在前面加Math.即可,例如Math.abs(-1)返回 1 1 1​。这些函数都有相同的修饰符:public staticstatic表示类方法,也叫静态方法,与类方法相对的是实例方法。实例方法没有static修饰符,必须通过实例或者对象调用,而类方法可以直接通过类名进行调用,不需要创建实例。public表示这些函数是公开的,可以在任何地方被外部调用。与public相对的是private。如果是private,则表示私有,这个函数只能在同一个类内被别的函数调用,而不能被外部的类调用。在Math类中,有一个函数Random initRNG就是private的,这个函数被public的方法random调用以生成随机数,但不能在Math类以外的地方被调用。将函数声明为private可以避免该函数被外部类误用,调用者可以清楚地知道哪些函数是可以调用的,哪些是不可以调用的。类实现者通过private函数封装和隐藏内部实现细节,而调用者只需要关心public就可以了。可以说,通过private封装和隐藏内部实现细节,避免被误操作,是计算机程序的一种基本思维方式。

3、实例变量和实例方法

实例变量表示具体的实例所具有的属性,实例方法表示具体的实例可以进行的操作。如果将微信订阅号看作一个类型,那“人民日报”订阅号就是一个实例,订阅号的头像、功能介绍、发布的文章可以看作实例变量,而修改头像、修改功能介绍、发布新文章可以看作实例方法。实例变量和实例方法是每个实例独有的,虽然可能和其它类似的实例一样,但是不是同一个东西。比如,一个人,都有头发,但是每个人的头发都是不一样的。

实例方法和类方法的主要区别如下:

  1. 类方法只能访问类变量,不能访问实例变量,可以调用其他的类方法,不能调用实例方法。
  2. 实例方法既能访问实例变量,也能访问类变量,既可以调用实例方法,也可以调用类方法。

1.2 第一个类

我们定义一个简单的类来表示直角坐标系中的一个点,代码如下:

package com.ieening;

public class ClassTest {
    public static void main(String[] args) {
        Point point; // 注释4
        point = new Point(); // 注释5
        System.out.println("Point:x=" + point.x + ",y=" + point.y); // 注释9
        point.x = 3; // 注释6
        point.y = 4; // 注释6
        System.out.println("Point:x=" + point.x + ",y=" + point.y); // 注释7
        System.out.println(point.distance()); // 注释8
    }
}

/**
 * Point
 */
class Point { // 注释1

    public int x; // 注释2
    public int y; // 注释2

    public double distance() { // 注释3
        return Math.sqrt(x * x + y * y);
    }
}
  1. 注释1:定义了名为Point的类(class Point)。
  2. 注释2:类中定义了两个两个实例变量(public int x; public int y;)表示。
  3. 注释3:类中定义一个实例方法(public double distance)。
  4. 注释4:Point p声明了一个变量,这个变量叫p,是Point类型的,这个变量和数组变量是类似的,都有两块内存:一块存放实际内容,一块存放实际内容的位置。声明变量本身只会分配存放位置的内存空间,这块空间还没有指向任何实际内容,像这种只保存实际内容位置的变量,也叫做引用类型的变量。
  5. 注释5:创建了一个实例或对象,然后赋值给了Point类型的变量p,它至少做了两件事:
    1. 分配内存,以存储新对象的数据,对象数据包括这个对象的属性,具体包括其实例变量xy
    2. 给实例变量设置默认值,int类型默认值为 0 0 0
  6. 注释6:给public属性赋值;
  7. 注释7:使用public属性,格式为:<对象变量名>.<成员名>;
  8. 注释8:调用实例方法distance,并输出结果,语法形式是:<对象变量名>.<方法名>;
  9. 注释9:实例变量都会有默认值,int默认值为0

本例中,我们通过对象直接操作了其内部数据xy,这是一个不好的习惯,一般而言,不应该将实例变量声明为public,而是private,并且只应该通过对象的方法对实例变量进行操作。这也是为了减少误操作,直接访问变量没有办法进行参数检查和控制,而通过方法修改,可以在方法中进行检查。

1.3 private变量

一般不应该将实例变量声明为public,下面我们修改一下类的定义,将实例变量定义为private,然后通过实例方法来操作变量,比如获取变量值以及修改变量值。修改后,代码如下:

package com.ieening;

public class ClassTest {
    public static void main(String[] args) {
        Point point;
        point = new Point();
        System.out.println("Point:x=" + point.getX() + ",y=" + point.getY());
        point.setX(3);
        point.setY(4);
        System.out.println("Point:x=" + point.getX() + ",y=" + point.getY());
        System.out.println(point.distance());
    }
}

/**
 * Point
 */
class Point {

    private int x;
    private int y;

    public int getX() {
        return x;
    }

    public void setX(int x) {
        this.x = x;
    }

    public int getY() {
        return y;
    }

    public void setY(int y) {
        this.y = y;
    }

    public double distance() {
        return Math.sqrt(x * x + y * y);
    }
}

上面代码中,我们加了 4 4 4个方法,setⅩ/setY用于设置实例变量的值,getⅩ/getY用于获取实例变量的值,这就是私有属性的SetterGetter方法。

这里面需要介绍的是this这个关键字。this表示当前实例,在语句this.x=x;中,this.x表示实例变量x,而右边的x表示方法参数中的x。在实例方法中,有一个隐含的参数,这个参数就是this,没有歧义的情况下,可以直接访问实例变量,在这个例子两个变量setter方法中,因为实例属性和方法参数相同,所以需要通过加上this来消除歧义。这 4 4 4个方法看上去是非常多余的,直接访问变量不是更简洁吗?而且函数调用是有成本的。在这个例子中,意义确实不太大,实际上,Java编译器一般也会将对这几个方法的调用转换为直接访问实例变量,而避免函数调用的开销。但在很多情况下,通过函数调用可以封装内部数据,避免误操作,我们一般还是不将成员变量定义为public

1.4 变量默认值

实例变量都有默认值,但是如果我们想修改该默认值该怎么办呢?如下面代码,如果希望修改这个默认值,有两种方法:

  1. 注释1:可以在定义变量的同时就赋值;
  2. 注释2:将代码放入初始化代码块中,代码块用{}包围;
package com.ieening;

public class ClassTest {
    public static void main(String[] args) {
        ......
    }
}

/**
 * Point
 */
class Point {

    private int x=6; // 注释1
    private int y;
    
    {
        y=8; // 注释2
    }
    

    ......
}

上述代码中,x的默认值设为了 1 1 1, y的默认值设为了 2 2 2​。在新建一个对象的时候,会先调用这个初始化,然后才会执行构造方法中的代码,关于构造方法,我们稍后介绍。静态变量也可以这样初始化:

    static int STATIC_ONE = 1;
    static int STATIC_TWO;
    static {
        STATIC_TWO = 2;
    }

语句外面包了一个static {},这叫静态初始化代码块。静态初始化代码块在类加载的时候执行,这是在任何对象创建之前,且只执行一次。

1.5 构造方法

在初始化对象的时候,前面我们都是直接对每个变量赋值,有一个更简单的方式对实例变量赋初值,就是构造方法,先看下面代码。在Point类定义中增加如下代码:

package com.ieening;

public class ClassTest {
    public static void main(String[] args) {
        ......
    }
}

/**
 * Point
 */
class Point {

    private int x;
    private int y;

    Point() { // 注释1
        this(0, 0);
    }

    Point(int x, int y) { // 注释2
        this.x = x;
        this.y = y;
    }

    public int getX() {
        return x;
    }

    ......

}

注释1和注释2,这两个就是构造方法,构造方法可以有多个。不同于一般方法,构造方法有一些特殊的地方:

  1. 名称是固定的,必须与类名相同。这也容易理解,靠这个用户和Java系统就都能容易地知道哪些是构造方法。
  2. 没有返回值,也不能有返回值。构造方法隐含的返回值就是实例本身。
  3. 与普通方法一样,构造方法也可以重载。第二个构造方法是比较容易理解的,使用this对实例变量赋值。我们解释下第一个构造方法,this(0,0)的意思是调用第二个构造方法,并传递参数0,0,我们前面解释说this表示当前实例,可以通过this访问实例变量,这是this的第二个用法,用于在构造方法中调用其他构造方法。这个this调用必须放在第一行,这个规定也是为了避免误操作。构造方法是用于初始化对象的,如果要调用别的构造方法,先调别的,然后根据情况自己再做调整,而如果自己先初始化了一部分,再调别的,自己的修改可能就被覆盖了。

这个例子中,不带参数的构造方法通过this(0,0)又调用了第二个构造方法,这个调用是多余的,因为xy的默认值就是 0 0 0,不需要再单独赋值,我们这里主要是演示其语法。

我们来看下如何使用构造方法,代码如下:

public class ClassTest {
    public static void main(String[] args) {
        Point point;
        point = new Point(3, 4); // 注释1
        System.out.println("Point:x=" + point.getX() + ",y=" + point.getY());
        System.out.println(point.distance());
    }
}

注释1中调用构造方法,就可以将实例变量xy的值设为 3 3 3 4 4 4。前面我们介绍new Point()的时候说,它至少做了两件事,一件是分配内存,另一件是给实例变量设置默认值,这里我们需要加上一件事,就是调用构造方法。调用构造方法是new操作的一部分。通过构造方法,可以更为简洁地对实例变量进行赋值。

关于构造方法,下面我们讨论两个细节概念:一个是默认构造方法;另一个是私有构造方法。

1、默认构造方法

默认构造方法每个类都至少要有一个构造方法,在通过new创建对象的过程中会被调用。但构造方法如果没什么操作要做,可以省略。Java编译器会自动生成一个默认构造方法,也没有具体操作。但一旦定义了构造方法,Java就不会再自动生成默认的,具体什么意思呢?在这个例子中,如果我们只定义了第二个构造方法(带参数的),则下面语句:Point p = new Point();就会报错,因为找不到不带参数的构造方法。为什么Java有时候自动生成,有时候不生成呢?在没有定义任何构造方法的时候,Java认为用户不需要,所以就生成一个空的以被new过程调用;定义了构造方法的时候,Java认为用户知道自己在干什么,认为用户是有意不想要不带参数的构造方法,所以不会自动生成。

2、私有构造方法

构造方法可以是私有方法,即修饰符可以为private,为什么需要私有构造方法呢?大致可能有这么几种场景:

  1. 不能创建类的实例,类只能被静态访问,如MathArrays类,它们的构造方法就是私有的。
  2. 能创建类的实例,但只能被类的静态方法调用。有一种常见的场景:类的对象有但是只能有一个,即单例(单个实例)。在这种场景中,对象是通过静态方法获取的,而静态方法调用私有构造方法创建一个对象,如果对象已经创建过了,就重用这个对象。
  3. 只是用来被其他多个构造方法调用,用于减少重复代码。

1.6 类和对象的生命周期

了解了类和对象的定义与使用,下面我们再从程序运行的角度理解下类和对象的生命周期。

在程序运行的时候,当第一次通过new创建一个类的对象时,或者直接通过类名访问类变量和类方法时,Java会将类加载进内存,为这个类分配一块空间,这个空间会包括类的定义、它的变量和方法信息,同时还有类的静态变量,并对静态变量赋初始值。类加载进内存后,直到程序结束一般都不会释放。一般情况下,类只会加载一次,所以静态变量在内存中只有一份。当通过new创建一个对象的时候,对象产生,在内存中,会存储这个对象的实例变量值,每做new操作一次,就会产生一个对象,就会有一份独立的实例变量。每个对象除了保存实例变量的值外,可以理解为还保存着对应类型即类的地址,这样,通过对象能知道它的类,访问到类的变量和方法代码。实例方法可以理解为一个静态方法,只是多了一个参数this。通过对象调用方法,可以理解为就是调用这个静态方法,并将对象作为参数传给this。对象的释放是被Java用垃圾回收机制管理的,大部分情况下,我们不用太操心,当对象不再被使用的时候会被自动释放。

具体来说,对象和数组一样,有两块内存,保存地址的部分分配在栈中,而保存实际内容的部分分配在堆中。栈中的内存是自动管理的,函数调用入栈就会分配,而出栈就会释放。堆中的内存是被垃圾回收机制管理的,当没有活跃变量指向对象的时候,对应的堆空间就可能被释放,具体释放时间是Java虚拟机自己决定的。活跃变量就是已加载的类的类变量,以及栈中所有的变量。


  1. 马俊昌.Java编程的逻辑[M].北京:机械工业出版社,2018. ↩︎

  2. 尚硅谷教育.剑指Java:核心原理与应用实践[M].北京:电子工业出版社,2023. ↩︎

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1435169.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

浏览器提示ERR_SSL_KEY_USAGE_INCOMPATIBLE解决

ERR_SSL_KEY_USAGE_INCOMPATIBLE报错原因 ERR_SSL_KEY_USAGE_INCOMPATIBLE 错误通常发生在使用 SSL/TLS 连接时,指的是客户端和服务器之间进行安全通信尝试失败,原因是证书中的密钥用途(Key Usage)或扩展密钥用途(Extended Key Usage, EKU)与正在尝试的操作不兼容。这意味…

性能评测|虚拟化和裸金属 K8s 哪个性能更好?

本文重点 整体而言&#xff0c;SKS&#xff08;虚拟机 Kubernetes&#xff09;可以达到裸金属 Kubernetes 性能的 82% – 96%&#xff0c;满足绝大部分场景下生产容器应用的性能需求。更多虚拟化与裸金属 Kubernetes 架构、特性、适用场景与性能对比&#xff0c;欢迎阅读文末电…

【算法】枚举——蓝桥杯、日期统计、特殊日期(位数之和)、2023、特殊日期(倍数)、跑步锻炼

文章目录 蓝桥杯日期统计特殊日期&#xff08;位数之和&#xff09;2023特殊日期&#xff08;倍数&#xff09;跑步锻炼 蓝桥杯 日期统计 日期统计 如果暴力枚举100个数的八次循环那就是1016次运算&#xff0c;时间复杂度太高了&#xff0c;好在前四次的2023是确定的&#xf…

Mybatis中的sql-xml延迟加载机制

Mybatis中的sql-xml延迟加载机制 hi&#xff0c;我是阿昌&#xff0c;今天记录一下关于Mybatis中的sql-xml延迟加载机制 一、前言 首先mybatis技术本身就不多介绍&#xff0c;说延迟加载机制之前&#xff0c;那要先知道2个概念&#xff1a; 主查询对象关联对象 假设咱们现…

人工智能福利站,初识人工智能,图神经网络学习,第二课

&#x1f3c6;作者简介&#xff0c;普修罗双战士&#xff0c;一直追求不断学习和成长&#xff0c;在技术的道路上持续探索和实践。 &#x1f3c6;多年互联网行业从业经验&#xff0c;历任核心研发工程师&#xff0c;项目技术负责人。 &#x1f389;欢迎 &#x1f44d;点赞✍评论…

UML之在Markdown中使用Mermaid绘制类图

1.UML概述 UML&#xff08;Unified modeling language UML&#xff09;统一建模语言&#xff0c;是一种用于软件系统分析和设计的语言工具&#xff0c;它用于帮助软件开发人员进行思考和记录思路。 类图是描述类与类之间的关系的&#xff0c;是UML图中最核心的。类图的是用于…

SpringBoot实战第三天

今天主要完成了&#xff1a; 新增棋子分类 棋子分类列表 获取棋子分类详情 更新棋子分类 更新棋子分类和添加棋子分类_分组校验 新增棋子 新增棋子参数校验 棋子分类列表查询(条件分页) 先给出分类实体类 Data public class Category {private Integer id;//主键IDNot…

mysql 批量查询取每一组最新一条数据

AI回答 需求 根据车牌号查询最新的一条交车记录的‘合同号’ &#xff0c;与上面需要类似&#xff0c;这里只需要查询‘合同号’这个字段 方式1 直接把需要查询的字段加上contract_no&#xff0c;直接查&#xff0c;不用子查询 SELECT number_plate,id,contract_no, MAX( …

❤ React18 环境搭建项目与运行(地址已经放Gitee开源)

❤ React项目搭建与运行 环境介绍 node v20.11.0 react 18.2 react-dom 18.2.0一、React环境搭建 第一种普通cra搭建 1、检查本地环境 node版本 18.17.0 检查node和npm环境 node -v npm -v 2、安装yarn npm install -g yarn yarn --version 3、创建一个新的React项目…

SSRF漏洞给云服务元数据带来的安全威胁

文章目录 前言元数据服务威胁1.1 Metadata元数据1.2 RAM资源管理角色1.3 STS 临时凭据利用1.4 CF云环境利用框架1.5 元数据安全性增强 TerraformGoat2.1 永久性AccessKey2.2 SSRF靶场环境搭建2.3 腾讯云CVM配角色2.4 接管腾讯云控制台 SSRF组合拳案例3.1 上传图片功能SSRF3.2 文…

vue3-内置组件-Transition

基于状态变化的过渡和动画&#xff08;常用&#xff09; 建议多看几遍~~。然后动手去写写&#xff0c;学编程只有多动手才能有感觉。 内置组件: 它在任意别的组件中都可以被使用&#xff0c;无需注册。 Vue 提供了两个内置组件&#xff0c;可以帮助你制作基于状态变化的过渡和动…

Jmeter 01 -概述线程组

1、Jmeter:概述 1.1 是什么&#xff1f; Jmeter是Apache公司使用Java 开发的一款测试工具 1.2 为什么&#xff1f; 高效、功能强大 模拟一些高并发或多次循环等特殊场景 1.3 怎么用&#xff1f; 下载安装 1、下载jmeter&#xff0c;解压缩2、安装Java环境&#xff08;jmet…

【保姆级教程|YOLOv8改进】【5】精度与速度双提升,使用FasterNet替换主干网络

《博主简介》 小伙伴们好&#xff0c;我是阿旭。专注于人工智能、AIGC、python、计算机视觉相关分享研究。 ✌更多学习资源&#xff0c;可关注公-仲-hao:【阿旭算法与机器学习】&#xff0c;共同学习交流~ &#x1f44d;感谢小伙伴们点赞、关注&#xff01; 《------往期经典推…

为什么(如何)从 Java 8/11 迁移到 Java 21,从 Spring Boot 2 迁移到最新的 Spring Boot 3.2 ?

介绍 如果您的工作配置与 Java 有一定的关系&#xff0c;您一定已经注意到 了Java 最新稳定版本 Java 21 引起了很多关注。 这个新版本引入了一些未来的功能&#xff0c;改进了之前引入/孵化的一些突破性功能&#xff0c;弃用了多余的功能&#xff0c;并删除了一些错误。它使…

Halcon 缺陷检测

文章目录 开闭运算&#xff0c;腐蚀膨胀的亮点问题灰度图像的开闭运算&#xff0c;腐蚀膨胀的亮点问题算子二值化算子 Halcon blob特征处理的方法检测缺陷Halcon Blob特征差分的方法检测缺陷Halcon 极坐标变换&#xff08;环形先转换坐标&#xff09;blob特征Halcon Blob局部二…

PySpark(四)PySpark SQL、Catalyst优化器、Spark SQL的执行流程

目录 PySpark SQL 基础 SparkSession对象 DataFrame入门 DataFrame构建 DataFrame代码风格 DSL SQL SparkSQL Shuffle 分区数目 DataFrame数据写出 Spark UDF Catalyst优化器 Spark SQL的执行流程 PySpark SQL 基础 PySpark SQL与Hive的异同 Hive和Spark 均是:“分…

2月5日作业

1.请编程实现哈希表的创建存储数组(12,24,234,234,23,234,23),输入key查找的值&#xff0c;实现查找功能 #include<stdio.h> #include<string.h> #include<stdlib.h> #include<math.h> typedef int datatype; typedef struct node {datatype data;str…

JenkinsGitLab完成自动化构建部署

关于GitLab安装:GitLab安装-CSDN博客 Docker中安装GitLab:Docker下安装GitLab-CSDN博客 安装JenKins Jenkins官网:Jenkins 中文版:Jenkins 安装时候中文页面的war包下不来 在英文页面 记得装JDK8以上 JenKins使用java写的 运行JenKins需要JDK环境 我这里已经装好了 将下…

VXLAN:虚拟化网络的强大引擎

1.什么是VXLAN VXLAN&#xff08;Virtual eXtensible Local Area Network&#xff0c;虚拟扩展局域网&#xff09;&#xff0c;是由IETF定义的NVO3&#xff08;Network Virtualization over Layer 3&#xff09;标准技术之一&#xff0c;是对传统VLAN协议的一种扩展。VXLAN的特…

华为云GaussDB在新零售云转型上的摸索实验

新零售的“云化” 阿里研究院曾经提到过一个理念&#xff1a; 零售的本质是无时无刻不为消费者提供超出预期的“内容”。 这个理念其实不难理解&#xff0c;想要留住消费者&#xff0c;靠大家都能提供的“内容”显然是行不通的。超出预期&#xff0c;才能吸引消费者的“消费…