四. 对象与类
4.1 面向对象程序设计概述
4.1.4 类之间的关系
- 分类:依赖(uses a),聚合(has a),继承(is a)
-
依赖
- 一个类的方法操纵另一个类的对象
- 最常见的关系,最明显的关系
- 设计中应该遵循尽可能减少依赖,依赖越低,耦合越低
-
聚合
- 一个类中包含着另一个类
4.2 使用预定义类
4.2.1 对象与对象变量
1. 对象变量
- 定义:引用变量的部分
-
注意
- 一个对象变量不包含对象,仅仅引用一个对象
4.3 用户自定义类
4.3.4 构造器
-
规则
- 构造器与类同名
- 每个类可以有一个及以上的构造器
- 构造器可以有 0 至多个参数
- 构造器没有返回值
- 构造器随着 new 操作符的执行被调用,无法通过构造器重新设置实例域
4.3.5 隐式参数与显式参数
- 隐式参数:方法名面前的类对象,使用 this 关键字代表
-
显式参数:传入方法的参数
<!– 实现 –>
public void raise(double ByPercent){double raise = this.salary * ByPercent;} /* conclusion 1.ByPercent 为显式参数 2.this 为隐式参数,代表类对象 */
4.3.6 封装的优点
1. 域访问器
- 定义:返回实例域值的机器
-
组成
- 私有数据域
- 公有的域访问器方法
- 公有的域更改器方法
-
格式:public 修饰,返回值为实例域值
<!– 实现 –>
// 需求:返回薪水值 public class Worker{private int salary;} // 域访问器 public int getSalary(){return salary;}
-
优势
- 支持修改内部实现,不影响其他代码
- 支持执行错误检查 <!– 比如判断传入数据是否为空 –>
-
注意
-
如果需要返回一个可变数据域的拷贝,使用 clone()
<!– 实现 –>
class E{ ... public Date getH(){return (Date)hire.clone();} }
-
4.4 静态域和静态方法
4.4.1 静态域
- 定义:被 static 修饰的域
- 特点:静态域属于类,不属于对象。也就是所有对象共享一个静态域
4.4.2 静态常量
-
常用的静态常量
-
Math.PI
<!– 实现 –>
// 定义 public class Math {public static final double PI = 3.123453454354535345;} // 使用 public class TestMath {public static void main(String[] args) { int l = 5; double circle = l*l*Math.PI; System.out.println(circle); } }
- System.out
-
4.4.3 静态方法
- 定义:不能向对象实施操作的方法(静态方法是没有 this 参数的方法)
-
使用场景
- 方法不需要访问对象状态,所需参数都是通过显示参数提供 <!– 比如 Math.pow–>
- 方法只需要访问类的静态域 <!– 比如返回静态域内容的 get 方法 –>
4.4.4 工厂方法
- 定义:静态方法模式,支持返回预期的对象
-
步骤
- 创建抽象产品类,定义公共的接口 <!– 需要获得工厂中多种对象,返回方法的变量类型能够统一 –>
- 创建具体产品类
- 创建工厂类,创建静态方法来返回具体产品类
-
外部类调用工厂类中的静态方法获得相应的产品对象
<!– 实现 –>
// 抽象产品类,方便产生多种产品类型 abstract class Product {protected abstract void show(); }
// 具体产品类 1 public class Product1 extends Product {@Override public void show() {System.out.println("Product1"); } } // 具体产品类 2 public class Product2 extends Product {@Override public void show() {System.out.println("Product2"); } } // 具体产品类 3 public class Product3 extends Product {@Override public void show() {System.out.println("Product3"); } }
// 工厂类,根据传入参数类型返回相应的对象 public class ProductFatory {public static Product getProduct(String s){switch (s){ default:return null; case "a" :return new Product1(); case "b" :return new Product2(); case "c" :return new Product3();} } }
// 测试类 public class TestFactory {public static void main(String[] args) {ProductFatory.getProduct("a").show(); ProductFatory.getProduct("b").show(); ProductFatory.getProduct("c").show();} } /* output Product1 Product2 Product3 */
-
Pros
- 创建实例和使用实例解耦
-
Cons
- 工厂类出现错误时,整个系统受到影响
- 违背“开放 - 关闭原则”,新增类需要修改工厂逻辑,工厂类将冗杂
4.5 方法参数
-
规则:java 按值调用,方法得到的是所有参数值的拷贝
-
无法修改基本数据类型的值。
<!– 实现 –>
// 修改值的方法 public class Method {public void changeNum(int num){num = num * 3;} } // 测试类 public class TestMethodParam {public static void main(String[] args) { int n = 5; Method method = new Method(); System.out.println(n); method.changeNum(n); System.out.println(n); } } /* output 5 5 */ /* conclusion 因为 num 被初始化为 n 值的拷贝,因此修改 num 不会影响 n 的值 */
- 支持修改对象参数的状态
原因:形参是对 对象引用的拷贝,因此指向的是对象,当发生修改时,会修改对象中的值
- 不支持让对象参数引用一个新的对象
原因:形参是对象引用的拷贝,修改引用无法对原引用造成影响
原理图:
-
4.6 对象构造
4.6.1 重载
1. 方法的签名
-
格式:方法名以及参数类型
<!– 实现 –>
indexOf(int)
4.6.6 调用另一个构造器
-
格式:在类中的构造器中使用 this 关键字调用另一个构造器
<!– 实现 –>
public class TestCons { private int age; private String name; private TestCons(int aAge) {this.age = aAge;} private TestCons(int aAge , String aName) {this(5); this.name = aName; } public static void main(String[] args) {TestCons testCons = new TestCons(4,"Toyz"); System.out.println(testCons.age); System.out.println(testCons.name); } }
4.6.7 初始化数据的方法
-
方法
-
在构造器中设置值
<!– 实现 –>
private TestCons(int aAge) {this.age = aAge;}
-
在声明中赋值
<!– 实现 –>
private int age = 5;
-
在初始化块中赋值(不常见,通常可以在构造器中实现)
<!– 实现 –>
{int age = 5;}
-
-
执行顺序
- 所有数据域初始化为默认值
- 根据在类中的声明顺序,执行初始化语句(上述方法 2)和初始化域(上述方法 3)
- 执行构造器(上述方法 1)
4.10 类设计技巧
- 一定要保证数据私有性
- 一定要对数据初始化
- 不要在类中使用过多的基本类型(使用其他类来代替)
- 不是所有域都需要独立的域访问和修改器
- 对职责过多的类进行分解(不然会导致耦合过高)
- 类名和方法名能够体现职责
- 优先使用不可变的类