多类多对象Multiple Classes and Objects

模块化

颗粒化

写代码时, 写的很碎片

但不知道怎么回事最后拼在一起就运行起来了

电影拍摄与剪辑

电影拍摄的时候专心做拍摄的事情

剪辑的人负责将不同电影片段拼凑在一起

工具盒

制造的工具可以被别人使用

有时创建一系列工具, 组成工具盒, 别人并不会用到所有的工具

单类多对象

对象作为参数

需求

double distanceTo(Point target)

心态分析

颗粒化:你在写碎片

拍电影:你在拍片段,参数 剪电影的人 会给你。

资源分析
属性
    this:
    +   x: int 
    +   y: int
参数
    target:
    +   x: int 
    +   y: int 
局部变量
    无 
写代码
1 2 3 4 5 6 7
public class Point { public double distanceTo(Point target) { double dx = this.x - target.x; double dy = this.y - target.y; return Math.sqrt(dx * dx + dy * dy); }}
测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
public class Main { public static void main(String[] args) { new Main().run(); } public void run() { { Point p1 = new Point(4, 5); Point delta = new Point(1, 1); double distance = p1.distanceTo(delta); Console.println(distance); } }}

对象作为返回值

需求

Point flippedPoint()

资源分析

需要返回一个新的点,这个点不是当前 this。所以要造一个新的

1 2 3 4 5 6
public class Point { public Point flippedPoint() { Point result = new Point(); ... }}
属性
    this:
    +   x: int 
    +   y: int
参数
    无
局部变量
    result:
    +   x: int 
    +   y: int 
写代码
1 2 3 4 5 6 7 8
public class Point { public Point flippedPoint() { Point result = new Point(); result.x = -this.x; result.y = -this.y; return result; }}
测试
1 2 3 4 5 6 7 8 9 10 11 12 13
public class Main { public static void main(String[] args) { new Main().run(); } public void run() { { Point p1 = new Point(4, 5); Point result = p1.flippedPoint(); Console.println(result.description()); } }}

对象 和 对象变量Objects and Variables

认知

1 2
int a = 1;Player player = new Player();

player 是对象变量 存的是对象

有对象

有对象变量, 对象变量有值, 值是对象

1
Player player = new Player();
没赋值

有对象变量, 对象变量没值

一个变量没设置值之前, 不能使用该变量

1 2 3!4!
int a;Player player;System.out.println(a); System.out.println(player);
没对象

有对象变量, 对象变量有值, 值是没有对象

1
Player player = null;

赋值 null 就能通过编译

1 2 3 4
int a = 0;Player player = null;System.out.println(a); System.out.println(player);

空指针异常

如果一个对象变量里是 null

调用 . 运算时会导致运行时报错

1 2 3! <4
public void run(){ Player player = null; System.out.println(player.getHp()); NullPointerException}

对空指针进行保护

是否需要进行保护,需要分析业务和设计

使用前
1 2 3 4 5 6 7
public class Point { public double distanceTo(Point target) { double dx = this.x - target.x; double dy = this.y - target.y; return Math.sqrt(dx * dx + dy * dy); }}
使用后
1 2 3+4+5+6 7 8 9 10
public class Point { public double distanceTo(Point target) { if (target == null) { return 0; } double dx = this.x - target.x; double dy = this.y - target.y; return Math.sqrt(dx * dx + dy * dy); }}

关联Association

是什么

不同类的多个对象之间的关系

简单关联

多了类的对象之间可以有关联

1 2 3 4 5
public class Fraction { private int numerator; private int denominator; ...}
1 2 3 4 5 6 7 8 9
public class Rectangle { private int width; private int height; public Fraction widthOverHeight() { return new Fraction(width, height); } ...}

聚合 与 组合Aggregation and Composition

三大关系

关联的具体的一种

对象作为属性

名词

聚合 Aggregation

组合 Composition

有细微差别

前提

Weapon.java

1 2 3 4 5
public class Weapon { private int atk; private int def; ...}

Weapon.java

1 2 3 4 5 6
public class Player { private int atk; private int def; private int hp; ...}
属性关联

Weapon.java

1 2 3 4 5+6 7
public class Player { private int atk; private int def; private int hp; private Weapon weapon; ...}

属性关联下的对象问题

案例

Main.java

1 2 3 4!5 6
public class Main { public static void main(String[] args) { Player player = new Player(); player.getWeapon().getAtk(); }}

会报错

原因

属性声明,并没有赋值,那么按着默认值的规则,会是 null

从而触发空指针异常

解决
内部构建

Weapon.java

1 2 3 4 5*6 7
public class Player { private int atk; private int def; private int hp; private Weapon weapon = new Weapon(); ...}
外部注入

Main.java

1 2 3 4 5 6 7 8 9 10 11 12 13
public class Main { public static void main(String[] args) { // 创建 Player player = new Player(); Weapon weapon = new Weapon(); // 组装 player.setWeapon(weapon); // 运行 player.getWeapon().getAtk(); }}

基于对象属性的函数

需求
1 2 3 4 5 6
public class Player { ... public int totalAtk() { }}
心态分析

颗粒化:你在写碎片

拍电影:你在拍片段,属性 剪电影的人 会给你。

资源分析
属性
    this:
    +   atk: int 
    +   def: int 
    +   hp: int 
    +   weapon: Weapon 
        +   atk: int 
        +   def: int 
参数
    无
局部变量
    无
写代码
1 2 3 4 5 6
public class Player { ... public int totalAtk() { return this.atk + weapon.getAtk(); }}
测试

尝试写出测试代码

组合下的属性值存在性

情况 1

逻辑上一定有值

写实现代码时, 不要关心这个对象是怎么来的

电影拍摄

情况 2

逻辑上不一定有值

默认一定情况下, 这个属性并没有值

写代码时, 就要进行非空验证

非空验证
1 2 3 4 5 6 7 8 9 10
public class Player { ... public int totalAtk() { int atk = this.atk; if (weapon != null) { atk += weapon.getAtk(); } return atk; }}

关联 与 组合 实际应用

设计上使用组合

如果两个类型之间,存在 有 的关系,就需要使用组合 来表达

圆 圆心

Point.java

1 2 3 4
public class Point { private int x; private int y;}

Circle.java

1 2 3 4
public class Circle { private Point center; private int radius;}
账户 默认地址

Address.java

1 2 3 4 5 6
public class Address { private String country; private String province; private String city; private String address;}

Account.java

1 2 3 4
public class Account { private Address defaultAddress; private String defaultPhoneNumber;}
游戏 棋盘 棋子

Token.java

1 2 3
public class Token { private char icon;}

Board.java

1 2 3
public class Board { private Token[][] tokens;}

Game.java

1 2 3
public class Game { private Board board;}

使用 组合 达到类抽离

简单关联 与 组合 的实际应用价值,在类之间的代码重用

使用前

Main.java

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
public class Main { public static void main(String[] args) { new Main().run(); } public void printMenu(){ Console.println("请选择操作"); Console.println("1. 加法"); Console.println("2. 减法"); Console.println("3. 乘法"); Console.println("4. 除法"); Console.println("0. 退出"); } public void run(){ printMenu(); int command = Console.readInt(); while (command != 0){ double value1 = Console.readDouble(); double value2 = Console.readDouble(); double result = 0.0; if (command == 1) { result = value1 + value2; } else if (command == 2) { result = value1 - value2; } else if (command == 3) { result = value1 * value2; } else if (command == 4) { result = value1 / value2; } Console.println(result); printMenu(); command = Console.readInt(); } }}
使用后

Menu.java

1 2 3 4 5 6 7 8 9 10 11
public class Menu { public int getOption() { Console.println("请选择操作"); Console.println("1. 加法"); Console.println("2. 减法"); Console.println("3. 乘法"); Console.println("4. 除法"); Console.println("0. 退出"); return Console.readInt(); }}

Main.java

1 2 3 4 5 6*7 8 9*10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31*32 33 34
public class Main { public static void main(String[] args) { new Main().run(); } private Menu menu = new Menu(); public void run(){ int command = menu.getOption(); while (command != 0){ double value1 = Console.readDouble(); double value2 = Console.readDouble(); double result = 0.0; if (command == 1) { result = value1 + value2; } else if (command == 2) { result = value1 - value2; } else if (command == 3) { result = value1 * value2; } else if (command == 4) { result = value1 / value2; } Console.println(result); int command = menu.getOption(); } }}
使用后 2

其实也可以使用一般关联

Main.java

1 2 3 4 5 6-7 8 9+10 11 12 13
public class Main { public static void main(String[] args) { new Main().run(); } private Menu menu = new Menu(); public void run(){ Menu menu = new Menu(); int command = menu.getOption(); ... }}

Package

问题

可能会有很多很多的类, 可能会有几百上千个, 都在放一起很乱

这些类还有可能重名

什么是

类的目录结构, 文件系统上会有对应的文件夹结构

效果

不同的类文件可以放到不同的包里

操作

创建包

修改包名

命名规则

跟变量命名规则一致

类重名问题

同一个包下 类名 不能重名

不同包下可以出现 重名的类

package

每个类上都要写 package

package 后面跟包路径

包路径声明 必须跟 实际包路径 一致

包路径
包名.包名.包名
操作

使用包路径创建包

修改包名

类路径

包路径.类名

只有类路径才能确定一个类

现在写的
1 2 3 4 5 6 7 8 9
public class Main { public static void main(String[] args) { new Main().run(); } public void run(){ Player player1 = new Player(); }}
其实是
1 2 3 4 5 6 7*8 9
public class Main { public static void main(String[] args) { new Main().run(); } public void run(){ xxx.xxx.Player player1 = new xxx.xxx.Player(); }}
同包内省略

在一个包内,就可以省略

import

假设现在 包结构如下
casePackage
Main.java
entity
Player.java
使用跨包类
1 2 3 4 5 6 7 8 9
public class Main { public static void main(String[] args) { new Main().run(); } public void run(){ xxx.casePackage.entity.Player player1 = new xxx.casePackage.entity.Player(); }}
使用 import
    import 包路径.类名;
    import 包路径.*;

导入后,可以之后省略 类路径里的包路径

1+2 3 4 5 6 7 8 9*10 11
import xxx.casePackage.entity.Player; public class Main { public static void main(String[] args) { new Main().run(); } public void run(){ Player player1 = new Player(); }}
万一

在一段代码里非要用到两个同名的类

那就只能

1 2 3 4 5 6 7 8 9 10
public class Driver { public static void main(String[] args) { new Driver().run(); } public void run(){ xxx.xxx.Player player1 = new xxx.xxx.Player(); yyy.yyy.Player player2 = new yyy.yyy.Player(); }}

自动导入

1
import java.lang.*

这个包下的类都是自动导入

比如

System / String

ZZAX 微信公众

文档一更新,立刻告诉你