Java
大约 7 分钟
Java
目录
对象和类
简概
OOP简概
- 面向对象程序设计(简称OOP)
- 在OOP中,不必关心对象的具体实现,只要能够满足用户的需求即可
- 传统的结构化程序设计
- 通过设计一系列的过程(即算法)来求解问题。一旦确定了这些过程,就要开始考虑存储数据的方式。
- 这就是Pascal语言的设计者Niklaus Wirth将其著作命名为《算法+数据结构=程序》(Algorithms+Data Structures=Programs,Prentice Hall,1975)的原因。
- 在Wirth命名的书名中,算法是第一位的,数据结构是第二位的,这就明确地表述了程序员的工作方式。
- 传统的过程化程序设计,必须从顶部的main函数开始编写程序。在面向对象程序设计时没有所谓的“顶部”。对于学习OOP的初学者来说常常会感觉无从下手。答案是:首先从设计类开始,然后再往每个类中添加方法。
- 选择
- 对于一些规模较小的问题,将其分解为过程的开发方式比较理想。而面向对象更加适用于解决规模较大的问题。
- 要想实现一个简单的Web浏览器可能需要大约2000个过程,这些过程可能需要对一组全局数据进行操作。 采用面向对象的设计风格,可能只需要大约100个类,每个类平均包含20个方法 后者更易于程序员掌握,也容易找到bug。假设给定对象的数据出错了,在访问过这个数据项的20个方法中查找错误要比在2000个过程中查找容易得多。
类简概
- 三个主要特性
- 对象的行为(behavior)——可以对对象完成哪些操作,或者可以对对象应用哪些方法
- 对象的状态(state)——当调用那些方法时,对象会如何相应
- 对象的标识(identity)——如何区分具有相同行为与状态的不同对象
- 类、构造(construct)、实例(instance)
- 是构造对象的模板或蓝图。由类构造对象的过程称为创建类的实例
- 封装(encapsulation,有时称为数据隐藏)
- 形式上看,封装不过是将数据和行为组合在一个包中,并对对象的使用者隐藏了数据的实现方式
- 关键在于绝对不能让类中的方法直接地访问其他类的实例域。程序仅通过对象的方法与对象数据进行交互
- 实例域(instance field)、方法(method)
- 对象中的数据称为实例域,操纵数据的过程称为方法
- 状态(state)
- 对于每个特定的类实例(对象)都有一组特定的实例域值。这些值的集合就是这个对象的当前状态
- 继承(inheritance)
- 可以通过扩展一个类来建立另外一个新的类。扩展一个已有的类时,这个扩展后的新类具有所扩展的类的全部属性和方法。过程称为继承
- Object
- 事实上,在Java中,所有的类都源自于一个“神通广大的超类”,它就是Object。
类之间的关系
- 依赖(dependence),即“uses-a”关系
- 应该尽可能地将相互依赖的类减至最少。用软件工程的术语来说,就是让类之间的耦合度最小。
- 聚合(aggregation),即“has-a”关系
- 聚合关系意味着类A的对象包含类B的对象
- 有些方法学家不喜欢聚合这个概念,而更加喜欢使用“关联”这个术语。从建模的角度看,这是可以理解的。但对于程序员来说,“has-a”显得更加形象。
- 继承(inheritance),即“is-a”关系
- 一种用于表示特殊与一般关系的
- UML符号
- 很多程序员采用UML(Unified Modeling Language,统一建模语言)绘制类图,用来描述类之间的关系。
类(class)
使用预定义类
并不是所有的类都具有面向对象特征。例如Math类只封装了功能,它不需要也不必隐藏数据。
与C/C++不同:外壳类
- 用Java编写的所有代码都位于某个类的内部(包括main函数)
- 而C/C++、Python可以有代码在外面
使用预设类(通过Data类介绍)
定义类
Date birthday = new Date();
String s = new Date().toString();
创建一个新对象
对象不等于对象变量。一个对象变量并没有实际包含一个对象,而仅仅引用一个对象
在对象与对象变量之间存在着一个重要的区别,例如:
Date deadline; // dones't refer to any object
s = deadline.toString; // not yet
deadline = new Date(); // 必须首先初始化变量deadline或引用一个已经存在的对象
对象的指针、引用、内存相关
与C/C++不同:指针还是引用?
很多人错误地认为Java对象变量与C++的引用类似。然而,在C++中没有空引用,并且引用不能被赋值。可以将Java的对象变量看作C++的对象指针
Date birthday; // Java
// 等同于
Date* birthday; // C++
// 不同于
Date& birthday; // C++,不可空引用、不可被赋值
与C/C++不同:存储空间
所有的Java对象都存储在堆中。当一个对象包含另一个对象变量时,这个变量依然包含着指向另一个堆对象的指针。
如果使用一个没有初始化的指针,运行系统将会产生一个运行时错误,而不是生成一个随机的结果。同时,不必担心内存管理问题,垃圾收集器将会处理相关的事宜。
自定义类
主力类(workhorse class)
通常,这些类没有main方法,却有自己的实例域和实例方法。要想创建一个完整的程序,应该将若干类组合在一起,其中只有一个类有main方法
定义类
类定义
class ClassName
{
field1; // 实例域
field2;
constructor1; // 构造器
constructor2;
method1; // 方法
method2;
}
例如
class Employee
{
// instance field
private String name;
private double salary;
private Localdate hireDay;
// constructor
public Employee(String n, double s, int year, int month, int day)
{
name = n;
salary = s;
hireDay = LocalDate.of(year, moth, day);
}
// a method
public String getName()
{
return name;
}
}
使用
james = new Employee("James Bond", 100000, 1950, 1, 1);
类设计技巧
1.一定要保证数据私有
- 不要破坏封装性。需要编写一个访问器方法或更改器方法。即使出现bug也易于检测
2.一定要对数据初始化
- Java不对局部变量进行初始化,但是会对对象的实例域进行初始化。最好不要依赖于系统的默认值
3.不要在类中使用过多的基本类型
例如,用一个称为Address的新的类替换以下的实例域
private String street; private String city; private String state; private int zip;
4.不是所有的域都需要独立的域访问器和域更改器
- 不想被知道的实例域就不需要域访问器和域更改器
5.将职责过多的类进行分解
- 设计模式
6.类名和方法名要能够体现它们的职责
7.优先使用不可变的类
- 例如LocalDate类以及java.time包中的其他类是不可变的——没有方法能修改对象的状态
- 更改对象的问题在于,如果多个线程试图同时更新一个对象,就会发生并发更改。其结果是不可预料的
- 当然,并不是所有类都应当是不可变的