如果要说清楚对象和类的关系,不可避免的要提到C++,因为Java从时间线上来说,是C和C++之后的一门语言,很多Java Coder 也是因为厌烦了C++的一些特性,进而从事于Java开发的,所以以下内容会利用C++的一部分知识来对比,但是所需知识很低,只要学习过一部分C语言就可以理解。
概念层面:
类是构建对象的模板或蓝图【这个概念只需要有个印象即可,不需要多余的思考和研究】
理解层面:
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都与调用者无关,换个角度在持久层向上提供服务的时候,我的持久层使用的是什么类型的数据库,调用者也不需要知道。
即:通过封装隐藏实现细节,实现黑盒模型。
其实这本身也是低耦合和高内聚的实现要求即:仅暴露少量的接口给外部和数据操作细节自己完成,不允许外部干涉
综上封装的作用就体现在了:
-
安全性
-
隐藏实现细节
-
统一接口
-
便于修改代码
5)变量和对象
初学者在学习Java的时候经常会混淆这个概念,但是学C语言的时候却不会混淆这个概念,因为C语言中有一个特殊的东西:指针,进而就会产生疑问:Java是通过什么方式进行参数传递的?
常见的传递方式是两种:按值传递和按引用传递
而Java总是采用按值传递,获得的是参数的拷贝【这句话总是容易引起歧义】具体要看两个角度:
-
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.在传递的参数为对象类型,如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类型的对象时,它才能代表这个对象,这样就能理解空指针异常。
本篇关键词:静动关系,状态和行为,封装限制,变量不等于对象