类之间的关系
类之间常见的三种关系:依赖、聚合、继承
依赖:
如果在一个类方法中,存在操作另一个类对象的行为,那么这种情况就是依赖。
我们应该降低依赖,也就是降低耦合度,进而提高程序的安全性,因为如果需要修改一个类,但是此类的依赖过多,那么所有的类都需要检查修改,当这些类修改之后,那么与这些类相关的类也需要修改,可以想象是多么的麻烦。
聚合:
在一个类的对象中,有另一个类对象充当其实例数据,也就是一个对象中包含另一个对象,就称为聚合。
在有些地方,也被叫做关联,但是不建议。
继承:
用于表示一个类是另一个类扩展而来的,只是增加一些自己的属性及方法。
对象
对象是一个类的具体表现,我们用语言对客观世界进行描述,进而将其抽象为一个可描述的类别,也就是类,对于其中的一个具体事物就是一个对象,比如人就是一个类,然后小明就是一个具体的人,就是一个对象,这个重点不在这里,在:
对象被创建在heap内存中,而对象的具体地址空间被记录下来,存放在一个对象类型的变量中,也就是我们常说的对象变量就是存放了一个对象的地址引用,而不是一个真正的对象,而这个变量被存放在Stack内存中。
final
对于此关键字并不陌生,一般用来定义一个常量,我们常说的一个常量一般定义出来,在第一次被初始化赋值后就不可以再改变,我们有时也会见到,确实是用final定义的,但是其值可以改变,这里的原因是final关键字定义的值确实不能再改变,但是如果final关键字定义的是一个可变对象的对象变量,这个对象确实不改变,但是其指向的空间可以改变(因为是可变对象),所以会让人误以为final修饰的空间值发生了改变。
static
static关键字非常重要,用static修饰的通常被称作是静态的,这样的信息是属于全局的,是可以直接用类名直接访问,比如,一个类并没有实例化对象,但是static的信息还是在那里,因为这个信息属于整个类,并不是属于一个特定的对象,表示全局共享信息:如Math.PI或者System.out都属于这类信息,阅读System类会发现,虽然常量out是由final修饰的,但是其中有一个setOut方法可以修改out流的类型,这是因为setOut是一个本地方法,而不是用Java语言实现的,本地方法可以绕过Java语言的存取控制机制,但是不提倡这样处理。
静态方法是一种不能向对象实施操作的方法
由于静态的东西属于整个类,所以在类加载的时候就加载了,但是没有对象,其他非静态的是之后加载的,所以静态的东西不能操作一个比自己出生还迟的东西,因为会找不到这个东西而报错。
静态工厂方法
这种方法主要用来操作一些非对象的操作,主要有获取对象,在没有对象的时候我们可以直接用类调用,然后获取一个对象,对于初始化的对象根据要求的不同,创建的对象不同,在这种模式下,可以将不同的用处的对象创建方法区分,而在传统模式的重载下,会出现一些混乱,所以提倡这种做法,在单例模式中有这样的思想体现。
参数传递
一直以来有一个问题,就是在传递的时候到底是值传递还是引用传递,在C++中有区分,但是,在Java中所有的传递都是值传递,所谓的引用传递其实也是值传递,只是底层的传递方法没有展现出来,让人误以为是引用传递,
先来说一下传递过程,对于方法调用时候,首先方法会拷贝一份,然后在这个地址空间中进行修改,比如:
我们现在传进来的是一个数字变量,在传进方法之后,方法将这个数字拷贝了一份,然后对数字进行修改,但是方法外部的这个变量依然没有变化;
我们现在传进去一个引用,在相同的方式下,进去之后,方法将这个引用会拷贝一份,但是这个时候,外部的引用和内部的引用指向的是同一个地址空间,所以,方法内部做出改变,外部也会改变;
对比两次的传递,会发现,不管最后的值有没有发生变化,都是值传递。
实例域初始化
一般,在类中对实例域初始化有三种方法(实例域就是属性域):
- 在构造器中设置值
- 在声明时直接赋值
- 利用初始化块进行赋值(这种方法感觉没必要,虽然达到了效果,但是如果这样所有的对象都是同一个值,还不如直接声明的时候赋值)
Java类的设计技巧
- 一个类中的所有数据应该私有,不能被外界访问。
- 一定要对数据进行初始化,对于实例域,不要依赖于系统的默认值,自己进行初始化。
- 不要在类中使用过多的基本数据类型,这个度需要自己把控,多少才算多。
- 不是所有的域都需要域访问器和域更改器
- 将职责过多的类进行分解,这个度也需要自己把控。
- 类名和方法名要能体现他们的职责
- 优先使用不可变得类(这样可以防止并发的访问,如果可变,那么并发访问的数据是不确定的)
- 一个类中的所有数据都应该私有化,而对于方法而言,不要编写返回引用可变对象的访问器方法,因为这样会导致原本封装好的数据泄露给外界,破坏了程序的封装性。