面向对象程序设计概述
- 早期的 Pascal 语言采用 算法+数据结构=程序 的视角,把算法放第一位,数据结构是第二位的,而 OOP 相反,把数据放在第一位,然后再考虑操作数据的算法
- Java 编写的所有代码都位于某个类的内部
- 实现封装的关键在于绝对不能让类中的方法直接地访问其他类的实例域,程序仅通过对象的方法给对象数据发送消息
- 对象的行为(behavior),对象的状态(state),对象标识(identity)
- OOP 从设计类开始,没有类就无法做任何事情,然后再往每个类中添加方法,在分析问题的过程中寻找名词,而方法对应着动词
- 依赖(uses-a),聚合(has-a),继承(is-a)
使用预定义类
- 一个对象变量并没有实际包含一个对象,而仅仅引用一个对象,对象变量设置为null表明这个变量目前没有引用任何对象
- Date, LocalDate.now(), LocalDate.of()
- 访问器方法(只访问对象而不修改对象,accessor method)、更改器方法(修改了对象,mutator method)
用户自定义类
- 构造器只能伴随着new操作符的执行被调用,构造器没有返回值,public ClassName(...) {...}
- 不要编写返回引用可变对象的访问器方法,访问器方法应该只读,如果返回可变对象就破坏了封装性;在经验上,如果需要返回一个可变数据域的拷贝,就应该使用clone
- final 实例域,static 静态域(属于类,不属于任何对象),
静态域与静态方法
方法参数
- Java 方法总是采用按值调用,方法得到的是参数值的一个拷贝,不会改变原基本类型的入参,但对象引用类型,拷贝后的引用变量指向的是和原变量的同一个对象,此时可以改变原对象
-
// 参数x,y是入参a,b的拷贝,此时是引用的拷贝 // 交换了x,y变量所指向的对象,并不会改变变量a,b所指向的对象 // 而且方法结束后x,y会被销毁,该方法做了无用功 public static void swap(Employee x, Employee y) { Employee temp = x; x = y; y = temp; }
Java 的对象引用是按值传递的
对象构造
- 返回类型不是方法签名的一部分,方法签名只包括方法名+参数类型
-
// 构造器参数命名加一个前缀 a 是一个好的习惯,一眼能清晰地看懂 public Employee(String aName, double aSalary) { name = aName; salary = aSalary; } // 借助 this public Employee(String name, String salary) { this.name = name; this.salary = salary; } // 初始化块,只要构造类的对象,这些块就会被执行 public Employee { private static int nextId; private int id; private String name; private double salary; // 对类的静态域进行初始化,在类第一次加载的时候,将会进行静态域的初始化 static { Random generator = new Random(); nextId = generator.nextInt(1000); } // object initialization block { id = nextId; nextId++; } public Employee(String aName, double aSalary) { // } }
this关键字还可以用在调用另一个构造器方法时,this("Employee 1", s);
- Java 有自动垃圾回收,不支持析构器。Java 的 finalize 方法将在垃圾回收器清除对象之前调用,在实际应用中不能依赖于使用 finalize 方法回收任何短缺的资源,因为不确定 finalize 何时才能被调用。可参考 Runtime.addShutdownHook(),如果某个资源需要在使用完毕后立刻被关闭,那么就需要由人工来管理
包
- 使用包的主要原因是确保类名的唯一性,从编译器的角度来看,嵌套的包之间没有任何关系,每一个都拥有独立的类集合
- 默认包(default package)是一个没有名字的包
- 如果包与目录不匹配,虚拟机就找不到类
- 类的成员变量默认为包可见,必须显式地标记为private
- Java 不允许加载用户自定义的、包名以“java.”开始的类,可以通过包密封(package sealing)机制来解决包的安全性问题,将包进行密封之后,就不能再向这个包添加类了
类路径
- 类的路径必须与包名匹配
- 类路径(class path)是所有包含雷文杰的路径的集合,在 UNIX 环境中用:分隔类路径
- 虚拟机查找类,先查找在运行时目录 jre/lib 和 jre/lib/ext 下能否找到类文件,然后再查找类路径下面的目录
- 编译器定位文件,先检查包含这个类的包,并查询所有的import指令,确定其中是否包含了被引用的类
- java -classpath 设置类路径
文档注释
- 包注释:提供一个package.html文件,在标记<body>...</body>之间的所有文本都会被抽取出来;或者提供一个package-info.java的文件,以一个初始注释块开始 /** ... */,后面跟一个packge xxx; 的声明语句
- javadoc -d docDirectory *.java
类设计技巧
- 一定要保证数据私有
- 一定要对数据初始化
- 不要在类中使用过多的基本类型,用一个封装的类来代替多个相关的基本类型的使用
- 不是所有的域都需要独立的域访问器和域更改器
- 将职责更多的类进行分解
- 类名和方法名要能够体现它们的职责
- 优先使用不可变的类(LocalDate 类以及java.time包中的其他类是不可变的——返回的是已修改的新对象,更改对象的问题在于多线程的安全问题)