深入理解Java类和对象的关系

如果要说清楚对象和类的关系,不可避免的要提到C++,因为Java从时间线上来说,是C和C++之后的一门语言,很多Java Coder 也是因为厌烦了C++的一些特性,进而从事于Java开发的,所以以下内容会利用C++的一部分知识来对比,但是所需知识很低,只要学习过一部分C语言就可以理解。

image-20240126115456039

概念层面:

类是构建对象的模板或蓝图【这个概念只需要有个印象即可,不需要多余的思考和研究】

理解层面:

1)静动角度

对于类和对象产生模糊或者概念不清楚的主要原因:就是因为对类和对象的静态和动态在不同情况下的区别不清楚。大家在学习Java 的时候绝大多数情况直接学习语法,在类中直接new 对象,这种就导致直接模糊了 类代码和类对象的概念。

学习过映射和类加载器的朋友们都知道,类在虚拟机中是以类对象的形式存在的,从理解的角度就可以这样说,类本身也是一个对象,这是在动态的情况下,什么是动态的情况,就是在程序运行的过程中。但是在静态的情况下,类是以文件或者说代码的形式被编写的,这是混淆的根源。

不去掺杂过多的东西,用一句话去说就是:类在编写时是静态的代码,在程序运行时在虚拟机中是动态的对象。

那么什么叫做动态的对象?

对象都是new 出来的,直接的感觉他像是一个盒子,盒子有名字,被装了数据,如果详细的去区分,对象动态的”体现形式“可以说成是三个角度:状态,行为,和标识。

在动态的情况下,状态这个词可以理解为:某一个时刻对象的情况,这个情况体现在对象此刻实例变量的数据是的多少。就是说这个瞬间,切片中对象中的数据是多少。

行为这个词很好理解,就是对象做了什么,说白了就是他的方法。

而标识则是说如何辨别有相同行为相同状态的不同对象。

这个三个角度在静态时如何体现??

就是类在定义时的实例变量,方法,和equals方法,但是这里因为没有对象,所以不能称为对象

将静动两态的对象和类连接起来靠一个概念:类其实是一个特殊的对象类型。

我们说了类在虚拟机中动态的存在形式也是以对象的形式,所以可以通过类名来调用静态的方法和变量,因为类本身作为一个对象就已经存在在了虚拟机中,所以可以直接调用。

而对象在没被创建前,并不存在,不具有动态的特性,而当new 的代码被调用时,就会根据它所基于类对象的状态和行为数据:具有什么变量?具有什么方法?来创建一个实例,从此对象就被创建出来,拥有了动态性。

2)抽象具体角度

换个角度去说,对象是类的实例,类没有状态,行为也不会被触发,但是一旦实例化,类就有了状态,方法就可以被调用

3)类类关系

类和类之间的关系可以直接用三个词描写,可在静态的思维下理解:

user-a:调用访问其他类

has-a:w2内含其他类,如作为变量

is-a:继承

在动态的情况下,对象也是这样的关系。

4)封装:

相对于OOP三大基本特性其他两个:继承和多态,封装其实我认识是最重要的概念。

对于封装初步在理解的时候就是privat 一个variable 然后设置get和set方法,为什么要这样做,有个大概的想法,就是不让其他的对象直接获取数据,修改数据,限制其他类的调用和使用,仅此而已,但这只是封装的体现形式,他有更深层次的含义。

我们考虑3个情况:

1)如果没有对实例变量进行private 修饰,那么多个线程在操作这个对象的时候,就会出现读后写或者写后读的矛盾问题。那么即使是要做线程安全的处理,就需要将整个类进行线程安全,但如果限制使用只能通过set和get方法,那么只需要方法进行线程安全的处理。

2)如果我们自己写出一个Util ,提供给其他程序员使用,创建Util 对象时,内部的两个变量被赋值,且在用户操作方法时需要调用,如果不设置成private,用户就会直接修改这个变量,导致方法调用与预期不同,进而导致调试困难。

3)如果我们自己写一个Util,提供给其他程序员使用,只给外部提供两个方法,但是内部需要若干变量和方法,比如一个算法必须分成三个方法,而我们不希望外界去调用到这些变量和方法就可以将他设置为private

也就是说,我们作为代码的设计者,代码多数情况不只是给我们用的,而是会提供给别人,为了让他们通过固定的方式调用,所以需要封装。

其次封装还代表着我们可以限制给固定的人调用:通过访问修饰符的形式,将方法限制在固定范围内,范围之外人不可调用方法,进一步保证了安全性。

同时作为代码的设计者,我们在向外提供代码让别人使用时,实际开发过程中别人只需要知道,传入什么参数,获得什么结果即可,具体的实现过程可以向外隐藏,如:

在Service 层向外提供服务接口的时候,具体Service实现类中使用的是JDBC还是Mybatis都与调用者无关,换个角度在持久层向上提供服务的时候,我的持久层使用的是什么类型的数据库,调用者也不需要知道。

即:通过封装隐藏实现细节,实现黑盒模型。

其实这本身也是低耦合和高内聚的实现要求即:仅暴露少量的接口给外部和数据操作细节自己完成,不允许外部干涉

综上封装的作用就体现在了:

  1. 安全性

  2. 隐藏实现细节

  3. 统一接口

  4. 便于修改代码

5)变量和对象

初学者在学习Java的时候经常会混淆这个概念,但是学C语言的时候却不会混淆这个概念,因为C语言中有一个特殊的东西:指针,进而就会产生疑问:Java是通过什么方式进行参数传递的?

常见的传递方式是两种:按值传递和按引用传递

而Java总是采用按值传递,获得的是参数的拷贝【这句话总是容易引起歧义】具体要看两个角度:

  1. 1.在传递类型是基本类型:int,double时,方法定义的是局部变量,所以无法修改传入参数的值,只能以返回值的方式修改参数。

    private int increase(int x){
    	x =x+1;
        ruturn x;
    }
    private void test(){
        int x = 1;
        this.increase(x); 
        //x 为1
        x = this.increase(x);
        //x 为2
    }
    
  2. 2.在传递的参数为对象类型,如Student 的实例s1,传入时在方法中修改对象却可以修改数值,这因为在传入的参数中,传入的是对象的引用,这里并不是说拷贝创建了一个新的对象,而是将对象的引用赋值给了局部变量,所以操作的还是原对象:

    public void increase(Student s1){
        int temp = s1.getX();
        temp++;
        s1.setX(temp);
    }
    //x = 2
    

    这段代码在C++中其实应是传入一个对象的引用指针

    public void increase(Student& s1)
    

这里回到主题,Student student 这里的student不是对象,只是一个变量,只有当student 变量真正引用到一个student类型的对象时,它才能代表这个对象,这样就能理解空指针异常。

本篇关键词:静动关系,状态和行为,封装限制,变量不等于对象

  • 22
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值