为了有朝一日等成为Java大师,传奇人物
决定挤个时间解读一下这篇大作吧
书名《Java By Comparison - Become a Java Craftsman in 70 Examples》
顺便感谢下经理吧 不然我都不知道还有这本书
一,提高代码的可读性 1,避免反向思考
public class Laboratory {
Microscope microscope;
Result analyze(Sample sample) {
if (microscope.isInorganic(sample)) {
return Result.INORGANIC;
} else {
return analyzeOrganic(sample);
}
}
private Result analyzeOrganic(Sample sample) {
if (!microscope.isHumanoid(sample)) {
return Result.ALIEN;
} else {
return Result.HUMANOID;
}
}
}
改造后:
public class Laboratory {
Microscope microscope;
Result analyze(Sample sample) {
if (microscope.isOrganic(sample)) {
return analyzeOrganic(sample);
} else {
return Result.INORGANIC;
}
}
private Result analyzeOrganic(Sample sample) {
if (!microscope.isHumanoid(sample)) {
return Result.HUMANOID;
} else {
return Result.ALIEN;
}
}
}
解读:
如果sample是无机的,则返回无机,如果是有机的,则进行判断
改为如果是有机的,则进行判断,如果是无机的,则返回无机
之前的给人感觉头轻脚重,后面的看起来更舒坦,可能这就是“避免反向思考的真谛”
代码也是一件艺术品,论如何创造艺术
(不过改造后的逻辑好像有点问题:如果不是人,则返回人;否则如果是人,则返回外星人)
2,简化表达式
public class SpaceShip {
Crew crew;
FuelTank fuelTank;
Hull hull;
Navigator navigator;
OxygenTank oxygenTank;
boolean willCrewSurvive() {
return hull.holes == 0 &&
fuelTank.fuel >= navigator.requiredFuelToEarth() &&
oxygenTank.lastsFor(crew.size) > navigator.timeToEarth();
}
}
改造后:
public class SpaceShip {
Crew crew;
FuelTank fuelTank;
Hull hull;
Navigator navigator;
OxygenTank oxygenTank;
boolean willCrewSurvive() {
boolean hasEnoughResources = hasEnoughFuel() && hasEnoughOxygen();
return hull.isIntact() && hasEnoughResources;
}
private boolean hasEnoughOxygen() {
return oxygenTank.lastsFor(crew.size) > navigator.timeToEarth();
}
private boolean hasEnoughFuel() {
return fuelTank.fuel >= navigator.requiredFuelToEarth();
}
}
解读:
第一种代码可能会给新手一种编程高深的感觉,但其实就是各种无意义的拼凑而使代码的可读性大大降低
第二种进行解耦,每个方法只做一件事,将复杂的代码进行拆分,大大增强了代码的可读性,值得参考
第一种代码我以前刷LeetCode的时候经常看见,那些将各种想法编凑成一句,使每道题用一句话就能解决,看起来技术高深,实际上进行欺骗自己,没有可读性的代码可以看做垃圾,要是交接,别人看都不想看,更可能心里对代码作者一顿操作
3,用空行来区分逻辑
public enum DistanceUnit {
MILES, KILOMETERS;
static final double MILE_IN_KILOMETERS = 1.60934;
static final int IDENTITY = 1;
static final double KILOMETER_IN_MILES = 1 / MILE_IN_KILOMETERS;
double getConversionRate(DistanceUnit unit) {
if (this == unit) {
return IDENTITY;
}
if (this == MILES && unit == KILOMETERS) {
return MILE_IN_KILOMETERS;
} else {
return KILOMETER_IN_MILES;
}
}
}
改造后:
public enum DistanceUnit {
MILES, KILOMETERS;
static final double MILE_IN_KILOMETERS = 1.60934;
static final int IDENTITY = 1;
static final double KILOMETER_IN_MILES = 1 / MILE_IN_KILOMETERS;
double getConversionRate(DistanceUnit unit) {
if (this == unit) {
return IDENTITY;
}
if (this == MILES && unit == KILOMETERS) {
return MILE_IN_KILOMETERS;
} else {
return KILOMETER_IN_MILES;
}
}
}
解读:
第一种给人的第一印象有没有让人内心抽搐的感觉,有没有,反正我是有的,可读性极差,说实话,这种代码看的我内脏疼
第二种明显就是个艺术品嘛,每个层次加了空行是整个类看起来特别清爽,值得效仿,值得学习
4,用代码块区分业务逻辑
public class BoardComputer {
CruiseControl cruiseControl;
void authorize(User user) {
Objects.requireNonNull(user);
if (user.isUnknown()) {
cruiseControl.logUnauthorizedAccessAttempt();
} else if (user.isAstronaut()) {
cruiseControl.grantAccess(user);
} else if (user.isCommander()) {
cruiseControl.grantAccess(user);
cruiseControl.grantAdminAccess(user);
}
}
}
改造后:
public class BoardComputer {
CruiseControl cruiseControl;
void authorize(User user) {
Objects.requireNonNull(user);
if (user.isUnknown()) {
cruiseControl.logUnauthorizedAccessAttempt();
return;
}
if (user.isAstronaut()) {
cruiseControl.grantAccess(user);
} else if (user.isCommander()) {
cruiseControl.grantAccess(user);
cruiseControl.grantAdminAccess(user);
}
}
}
解读:
很显然,第一种将不同逻辑里面的代码全部放到if else判断中去了,看起来代码本身是没什么问题的,但是艺术本身就是精益求精
代码二根据逻辑进行分开,软件工程本身就是这样,遵循“高内聚,低耦合”,方便以后的重构等等
一个好的设计就和自己的家一样,应该不会有人将马桶设计到自己床旁边,厕所摆个电视吧
5,用常量替换魔术数字
public class CruiseControl {
private double targetSpeedKmh;
void setPreset(int speedPreset) {
if (speedPreset == 2) {
setTargetSpeedKmh(16944);
} else if (speedPreset == 1) {
setTargetSpeedKmh(7667);
} else if (speedPreset == 0) {
setTargetSpeedKmh(0);
}
}
void setTargetSpeedKmh(double speed) {
targetSpeedKmh = speed;
}
}
改造后:
public class CruiseControl {
static final int STOP_PRESET = 0;
static final int PLANETARY_SPEED_PRESET = 1;
static final int CRUISE_SPEED_PRESET = 2;
static final double CRUISE_SPEED_KMH = 16944;
static final double PLANETARY_SPEED_KMH = 7667;
static final double STOP_SPEED_KMH = 0;
private double targetSpeedKmh;
void setPreset(int speedPreset) {
if (speedPreset == CRUISE_SPEED_PRESET) {
setTargetSpeedKmh(CRUISE_SPEED_KMH);
} else if (speedPreset == PLANETARY_SPEED_PRESET) {
setTargetSpeedKmh(PLANETARY_SPEED_KMH);
} else if (speedPreset == STOP_PRESET) {
setTargetSpeedKmh(STOP_SPEED_KMH);
}
}
void setTargetSpeedKmh(double speed) {
targetSpeedKmh = speed;
}
}
解读:
这个建议感触良多,因为自己之前一直喜欢一些数字配个注释,自己觉得更显而易见,但之前真实项目中遇到了这种情况,全部要求我定义个常量,但我看到一排Constants.XXX的时候麻木了,我觉得数字加上注释更能好的理解业务,而不是一排看不懂的英文字母,可能是我英语不好的原因吧,或者说这是外国人的建议,英语是他们的母语,而他们又不太擅长数学,可能他们看到数字的感觉就像我看到一排英文字母的感觉
但是没办法,公司的规范,代码的规范,这个世界上有规范是好事情,更方便的交流和统一,而不是凭借个人喜好你写数字我用自定义的常量,建议大家平时代码的书写中尽量用自定义常量,养成一个好的规范
但是,没有人能够阻挡我对数字的热爱,打心底来说我个人方面还是喜欢数字的,但是为了世界代码的规范,有些事情不得不为之
6,用枚举代替常量
public class CruiseControl2 {
static final int STOP_PRESET = 0;
static final int PLANETARY_SPEED_PRESET = 1;
static final int CRUISE_SPEED_PRESET = 2;
static final double CRUISE_SPEED_KMH = 16944;
static final double PLANETARY_SPEED_KMH = 7667;
static final double STOP_SPEED_KMH = 0;
private double targetSpeedKmh;
void setPreset(int speedPreset) {
if (speedPreset == CRUISE_SPEED_PRESET) {
setTargetSpeedKmh(CRUISE_SPEED_KMH);
} else if (speedPreset == PLANETARY_SPEED_PRESET) {
setTargetSpeedKmh(PLANETARY_SPEED_KMH);
} else if (speedPreset == STOP_PRESET) {
setTargetSpeedKmh(STOP_SPEED_KMH);
}
}
void setTargetSpeedKmh(double speed) {
targetSpeedKmh = speed;
}
}
改造后:
public class CruiseControl2 {
private double targetSpeedKmh;
void setPreset(SpeedPreset speedPreset) {
Objects.requireNonNull(speedPreset);
setTargetSpeedKmh(speedPreset.speedKmh);
}
void setTargetSpeedKmh(double speed) {
targetSpeedKmh = speed;
}
}
enum SpeedPreset {
STOP(0), PLANETARY_SPEED(7667), CRUISE_SPEED(16944);
final double speedKmh;
SpeedPreset(double speedKmh) {
this.speedKmh = speedKmh;
}
}
解读:
枚举代替常量的优势百度一搜一大把了,大家可以去看看相关的文章,这里给出几点概括:
1,枚举常量更简单
2,枚举常量属于稳态型
3,枚举具有内置方法
4,枚举可以自定义方法
7,用字符Format取代字符相加
public class Mission {
Logbook logbook;
LocalDate start;
void update(String author, String message) {
LocalDate today = LocalDate.now();
String month = String.valueOf(today.getMonthValue());
String formattedMonth = month.length() < 2 ? "0" + month : month; Strin
g entry = author.toUpperCase() + ": [" + formattedMonth + "-" +
today.getDayOfMonth() + "-" + today.getYear() + "](Day " + (ChronoU
nit.DAYS.between(start, today) + 1) + ")> " + message + System.lineSeparator();
logbook.write(entry);
}
}
改造后:
public class Mission {
Logbook logbook;
LocalDate start;
void update(String author, String message) {
LocalDate today = LocalDate.now();
String entry = String.format("%S: [%tm-% %s%n",
author, today,
ChronoUnit.DAYS.between(start, today) + 1, message);
logbook.write(entry);
}
}
解读:
我从未想过还可以第一种写法,自我学java以来,因为都是自学的,所以没有看过这种写法,太神奇了,这么写肯定伤脑吧,果断第二种呀,别人创造出来的工具就要学会用,不要创富造轮子,能提升自己工作效率的工具就是好工具
没事多学习学习别人创造出来的工具吧 努力提升自己
8,使用Java命名规范
public class Rover {
static final double WalkingSpeed = 3;
final String SerialNumber;
double MilesPerHour;
Rover(String NewSerialNumber) {
SerialNumber = NewSerialNumber;
}
void Drive() {
MilesPerHour = WalkingSpeed;
}
void Stop() {
MilesPerHour = 0;
}
}
改造后:
public class Rover {
static final double WALKING_SPEED = 3;
final String serialNumber;
double milesPerHour;
Rover(String serialNumber) {
serialNumber = serialNumber;
}
void drive() {
milesPerHour = WALKING_SPEED;
}
void stop() {
milesPerHour = 0;
}
}
解读:
java开发规范–命名规范
这仅仅举了几个简单的例子,具体大家可以看看阿里开发手册第一章,详细的介绍了关于这些应该怎么做
我第一次看阿里手册的时候那真的叫恍然大明白
这里十分推荐看阿里手册不少于三遍!!!
这两个小小的例子也太局限了
9,避免单字母变量
public class Inventory {
List sl = new ArrayList<>();
boolean isInStock(String n) {
Supply s = new Supply(n); int l=0;
int h = sl.size() - 1;
while (l <= h) {
int m = l + (h - l) / 2;
int c = sl.get(m).compareTo(s);
if (c < 0) {
l = m + 1;
} else if (c>0) {
h = m - 1;
} else {
return true;
}
}
return false;
}
}
改造后:
public class Inventory {
List sortedList = new ArrayList<>();
boolean isInStock(String name) {
Supply supply = new Supply(name);
int low = 0;
int high = sortedList.size() - 1;
while (low <= high) {
int middle=low+(high-low)/2;
int comparison = sortedList.get(middle).compareTo(supply);
if (comparison < 0) {
low = middle + 1;
} else if (comparison > 0) {
high = middle - 1;
} else {
return true;
}
}
}
}
解读:
如果全是单个字母,根本不知道这段程序说了些什么,而用个单词就不一样了
显然知道这块代码是什么意思
开发中应禁止使用单字母
好的程序应该是不看注释就应该知道做了什么
而不是给出大量注释去给这段程序做出解释
(多看阿里手册!)
10,避免缩写
public class Logbook {
static final Path DIR = Paths.get("/var/log");
static final Path CSV = DIR.resolve("stats.csv");
static final String GLOB = "*.log";
void createStats() throws IOException {
try (DirectoryStream dirStr =
Files.newDirectoryStream(DIR, GLOB);
BufferedWriter bufW = Files.newBufferedWriter(CSV)) {
for (Path lFile : dirStr) {
String csvLn = String.format("%s,%d,%s",
lFile,
Files.size(lFile),
Files.getLastModifiedTime(lFile));
bufW.write(csvLn);
bufW.newline();
}
}
}
}
改造后:
public class Logbook {
static final Path LOG_FOLDER = Paths.get("/var/log");
static final Path STATISTICS_CSV = LOG_FOLDER.resolve("stats.csv");
static final String FILE_FILTER = "*.log";
void createStatistics() throws IOException {
try (DirectoryStream logs =
Files.newDirectoryStream(LOG_FOLDER, FILE_FILTER);
BufferedWriter writer =
Files.newBufferedWriter(STATISTICS_CSV)) {
for (Path log : logs) {
String csvLine = String.format("%s,%d,%s",
log,
Files.size(log),
Files.getLastModifiedTime(log));
writer.write(csvLine);
writer.newline();
}
}
}
}
解读:
改造前的代码定义的静态变量看起来像个天数一样,当我们在写代码时,定义的变量应该见名知意,而不是看跟没看一样,万全不知道写的是什么
改造后就不一样了,定义的每个变量都是知道是什么意思,知道了为什么会这么定义(要是英语比较好的话,像我百度翻译停不下来)
11,避免过度命名
public class MainSpaceShipManager {
AbstractRocketPropulsionEngine abstractRocketPropulsionEngine;
INavigationController navigationController;
boolean turboEnabledFlag;
void navigateSpaceShipTo(PlanetInfo planetInfo) {
RouteData data = navigationController.calculateRouteData(planetInfo);
LogHelper.logRouteData(data);
abstractRocketPropulsionEngine.invokeTask(data, turboEnabledFlag);
}
}
改造后:
public class SpaceShip {
Engine engine;
Navigator navigator;
boolean turboEnabled;
void navigateTo(Planet destination) {
Route route = navigator.calculateRouteTo(destination);
Logger.log(route);
engine.follow(route, turboEnabled);
}
}
解读:
给类,属性或者方法命名的时候应尽量简介,毕竟简介之美的道理大家都会懂
做产品也是一样,尽量保持简介美,而不是花里胡哨的功能一大堆
12,尽早检查
public class CruiseControl {
static final double SPEED_OF_LIGHT_KMH = 1079252850;
static final double SPEED_LIMIT = SPEED_OF_LIGHT_KMH;
private double targetSpeedKmh;
void setTargetSpeedKmh(double speedKmh) {
if (speedKmh < 0) {
throw new IllegalArgumentException();
} else if (speedKmh <= SPEED_LIMIT) {
targetSpeedKmh = speedKmh;
} else {
throw new IllegalArgumentException();
}
}
}
改造后:
public class CruiseControl {
static final double SPEED_OF_LIGHT_KMH = 1079252850;
static final double SPEED_LIMIT = SPEED_OF_LIGHT_KMH;
private double targetSpeedKmh;
void setTargetSpeedKmh(double speedKmh) {
if (speedKmh < 0 || speedKmh > SPEED_LIMIT) {
throw new IllegalArgumentException();
}
targetSpeedKmh = speedKmh;
}
}
解读:
说实话,我实在想不通代码一为什么会这么写,一个步骤能解决的为什么要拆呢
这种就不符合常理了,大部分刚刚学java人应该也不会考虑
毕竟人本性是懒得,能一步解决的事情我们绝不会留两步
而且完全没必要呀 这种写法 想不通了 这种代码看看就好
public class Laboratory {
Microscope microscope;
Result analyze(Sample sample) {
if (microscope.isInorganic(sample) == true) {
return Result.INORGANIC;
} else {
return analyzeOrganic(sample);
}
}
private Result analyzeOrganic(Sample sample) {
if (microscope.isHumanoid(sample) == false) {
return Result.ALIEN;
} else {
return Result.HUMANOID;
}
}
}
改造后:
public class Laboratory {
Microscope microscope;
Result analyze(Sample sample) {
if (microscope.isInorganic(sample)) {
return Result.INORGANIC;
} else {
return analyzeOrganic(sample);
}
}
private Result analyzeOrganic(Sample sample) {
if (!microscope.isHumanoid(sample)) {
return Result.ALIEN;
} else {
return Result.HUMANOID;
}
}
}
解读:
别说,说实话,我还真这么写过,当组长问我为什么这么写时,我当时就很懵说:这是我写的吗,天哪,我为什么会写出这玩意,那时候还是个低级的选手
所以可能这条道理很多人都知道,但是不要写的时候,脑子突然抽筋,不知道在想些什么,所以这条注意就好了
14,直接返回结果
public class Astronaut {
String name;
int missions;
boolean isValid() {
if (missions < 0 || name == null || name.trim().isEmpty()) {
return false;
} else {
return true;
}
}
}
改造后:
public class Astronaut {
String name;
int missions;
boolean isValid() {
return missions < 0 || name == null || name.trim().isEmpty();
}
}
解读:
尽量保持代码的简介
看看改造后的多省事呀 简洁 明了
15,用foreach代替for
public class LaunchChecklist {
List checks = Arrays.asList("Cabin Pressure",
"Communication",
"Engine");
Status prepareForTakeoff(Commander commander) {
for (int i = 0; i < checks.size(); i++) {
boolean shouldAbortTakeoff = commander.isFailing(checks.get(i));
if (shouldAbortTakeoff) {
return Status.ABORT_TAKE_OFF;
}
}
return Status.READY_FOR_TAKE_OFF;
}
}
改造后:
public class LaunchChecklist {
List checks = Arrays.asList("Cabin Pressure",
"Communication",
"Engine");
Status prepareForTakeoff(Commander commander) {
for (String check : checks) {
boolean shouldAbortTakeoff = commander.isFailing(checks);
if (shouldAbortTakeoff) {
return Status.ABORT_TAKE_OFF;
}
}
return Status.READY_FOR_TAKE_OFF;
}
}
解读:
能用foreach的尽量用foreach,减少普通for循环
除非用到需要根据下标去处理数据的
之前也写了一篇关于for和高级for的区别,大家可以去看看
16,更多的使用Java API
public class Inventory {
private List supplies = new ArrayList<>();
int getQuantity(Supply supply) {
if (supply == null) {
throw new NullPointerException("supply must not be null");
int quantity = 0;
for (Supply supplyInStock : supplies) {
if (supply.equals(supplyInStock)) {
quantity++;
}
}
return quantity;
}
}
改造后:
public class Inventory {
private List supplies = new ArrayList<>();
int getQuantity(Supply supply) {
Objects.requireNonNull(supply, "supply must not be null");
return Collections.frequency(supplies, supply);
}
}
解读:
既然别人已经造好了轮子出来,我们为什么还要继续不行呢
尽量扩展自己的知识,学习更多的APi去实现自己想要的功能,而不是写原生代码
避免重复造轮子!!!
public class Inventory {
void writeMessage(String message, Path location) throws IOException {
if (Files.isDirectory(location)) {
throw new IllegalArgumentException("The path is invalid!");
}
if (message.trim().equals("") || message == null) {
throw new IllegalArgumentException("The message is invalid!");
}
String entry = LocalDate.now() + ": " + message;
Files.write(location, Collections.singletonList(entry),
StandardCharsets.UTF_8,
StandardOpenOption.CREATE,
StandardOpenOption.APPEND);
}
}
改造后:
public class Inventory {
void writeMessage(String message, Path location) throws IOException {
if (message == null || message.trim().isEmpty()) {
throw new IllegalArgumentException("The message is invalid!");
}
if (location == null || Files.isDirectory(location)) {
throw new IllegalArgumentException("The path is invalid!");
}
String entry = LocalDate.now() + ": " + message;
Files.write(location, Collections.singletonList(entry),
StandardCharsets.UTF_8,
StandardOpenOption.CREATE,
StandardOpenOption.APPEND);
}
}
解读:
写代码应该尽量保持严谨,而不是到处都有隐藏的bug,比如空指针使我们最常见的
记得刚开始实习的时候写几行代码到处都是空指针,不过现在不会了
这块代码的判空其实可以用:Objects.isnull方法
18:避免switch陷阱
public class BoardComputer {
CruiseControl cruiseControl;
void authorize(User user) {
Objects.requireNonNull(user);
switch (user.getRank()) {
case UNKNOWN:
cruiseControl.logUnauthorizedAccessAttempt();
case ASTRONAUT:
cruiseControl.grantAccess(user);
break;
case COMMANDER:
cruiseControl.grantAccess(user);
cruiseControl.grantAdminAccess(user);
break;
}
}
}
改造后:
public class BoardComputer {
CruiseControl cruiseControl;
void authorize(User user) {
Objects.requireNonNull(user);
switch (user.getRank()) {
case UNKNOWN:
cruiseControl.logUnauthorizedAccessAttempt();
break;
case ASTRONAUT:
cruiseControl.grantAccess(user);
break;
case COMMANDER:
cruiseControl.grantAccess(user);
cruiseControl.grantAdminAccess(user);
break;
}
}
}
解读:
平时会经常用到Switch,应尽量正确是使用,比如代码一就少了个breal; 这样的后果是不堪设想的
注意:这段switch还不䭧完美,可以考虑加上default来防止最终没有条件匹配的情况,也可以
用{}把代码包起来,防止作用域的污染。
19,永远使用代码块(代码少并不永远代表更好的代码)
public class BoardComputer {
CruiseControl cruiseControl;
void authorize(User user) {
Objects.requireNonNull(user);
if (user.isUnknown())
cruiseControl.logUnauthorizedAccessAttempt();
if (user.isAstronaut())
cruiseControl.grantAccess(user);
if (user.isCommander())
cruiseControl.grantAccess(user);
cruiseControl.grantAdminAccess(user);
}
}
改造后:
public class BoardComputer {
CruiseControl cruiseControl;
void authorize(User user) {
Objects.requireNonNull(user);
if (user.isUnknown()) {
cruiseControl.logUnauthorizedAccessAttempt();
}
if (user.isAstronaut()) {
cruiseControl.grantAccess(user);
}
if (user.isCommander()) {
cruiseControl.grantAccess(user);
cruiseControl.grantAdminAccess(user);
}
}
}
解读:
这条建议阿里规范也提到过,而且是明确规定的规范
不加括号的代码只会影响下面的一行,如果有更多的代码,则不会在判断的条件当中
我之前看的很多书就会有这种代码,极其影响可读性,导致我觉得这本书的水平也很一般
如果有if判断,必须要加括号,这条是强制性的
20,避免ConcurrentModifificationException错误
public class Inventory {
private List supplies = new ArrayList<>();
void disposeContaminatedSupplies() {
for (Supply supply : supplies) {
if (supply.isContaminated()) {
supplies.remove(supply);
}
}
}
}
改造后:
public class Inventory {
private List supplies = new ArrayList<>();
void disposeContaminatedSupplies() {
Iterator iterator = supplies.iterator();
while (iterator.hasNext()) {
if (iterator.next().isContaminated()) {
iterator.remove();
}
}
}
}
解读:
像这种需要判断的,或者是否为空的平时就影响多判断一下,现在idea都是很智能的,一旦出现可能会发生异常或者空指针之类的情况都会给出提示,我们只需要根据提示去改进
自己也要多细心,没事判断一下,万一上线项目遇到问题了,可不只是被产品经理骂这么轻松了
负罪感是很高的
21,捕捉特定的异常
public class TransmissionParser {
static Transmission parse(String rawMessage) {
if (rawMessage != null && rawMessage.length() != Transmission.MESSAGE_LENGTH) {
throw new IllegalArgumentException("Bad message received!");
}
String rawId = rawMessage.substring(0, Transmission.ID_LENGTH);
String rawContent = rawMessage.substring(Transmission.ID_LENGTH);
try {
int id = Integer.parseInt(rawId);
String content = rawContent.trim();
return new Transmission(id, content);
} catch (Exception e) {
throw new IllegalArgumentException("Bad message received!");
}
}
}
改造后:
public class TransmissionParser {
static Transmission parse(String rawMessage) {
if (rawMessage != null
&& rawMessage.length() != Transmission.MESSAGE_LENGTH) {
throw new IllegalArgumentException("Bad message received!");
}
String rawId = rawMessage.substring(0, Transmission.ID_LENGTH);
String rawContent = rawMessage.substring(Transmission.ID_LENGTH);
try {
int id = Integer.parseInt(rawId);
String content = rawContent.trim();
return new Transmission(id, content);
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Bad message received!");
}
}
}
解读:
捕获异常当然是捕获最精确的了,不然天天捕获一个Exception有什么意义呢
22,尽量用不可变代替可变
public class Distance {
DistanceUnit unit;
double value;
Distance(DistanceUnit unit, double value) {
this.unit = unit;
this.value = value;
}
static Distance km(double value) {
return new Distance(DistanceUnit.KILOMETERS, value);
}
void add(Distance distance) {
distance.convertTo(unit); value += distance.value;
}
void convertTo(DistanceUnit otherUnit) {
double conversionRate = unit.getConversionRate(otherUnit);
unit = otherUnit;
value = conversionRate * value;
}
}
改造后:
public class Distance {
final DistanceUnit unit;
final double value;
Distance(DistanceUnit unit, double value) {
this.unit = unit;
this.value = value;
}
Distance add(Distance distance) {
return new Distance(unit, value + distance.convertTo(unit).value);
}
Distance convertTo(DistanceUnit otherUnit) {
double conversionRate = unit.getConversionRate(otherUnit);
return new Distance(otherUnit, conversionRate * value);
}
}
解读:
这种就相当于给属性加一个private,让外界禁止访问
对数据进行封装
不过我很少看到属性是final修饰符的,一般都是private
可能是我太年轻了吧
那就多看点书扩充自己视野吧
23,用Optional代替Null
public class Communicator {
Connection connectionToEarth;
void establishConnection() {
// used to set connectionToEarth, but may be unreliable
}
Connection getConnectionToEarth() {
return connectionToEarth;
}
}
改造后:
public class Communicator {
Connection connectionToEarth;
void establishConnection() {
// used to set connectionToEarth, but may be unreliable
}
Optional getConnectionToEarth() {
return Optional.ofNullable(connectionToEarth);
}
}
调用实例:
communicationSystem.getConnectionToEarth()
.ifPresent(connection ->
connection.send("Houston, we got a problem!") );
解读:
这条信息是必然的,没有人会希望自己的代码跑着跑着来个空指针,所以这必然需要优化
难道在代码中大量的实现判空吗,每过一会儿,去检查这个变量是否为空吗
那可真是麻烦,虽然我以前也是这么做的
java1.8提供了一个可以解决这个问题最好的方案,那就是Optional类
这里就不具体说为什么了,否则需要扯大量的相关内容
大家可以去百度搜搜:Option代替null
public class Inventory {
private List supplies = new ArrayList<>();
List find(String regex) {
List result = new linkedList<>();
for (Supply supply : supplies) {
if (Pattern.matches(regex, supply.toString())) {
result.add(supply);
}
}
return result;
}
}
改造后:
public class Inventory {
private List supplies = new ArrayList<>();
List find(String regex) {
List result = new linkedList<>();
Pattern pattern = Pattern.compile(regex);
for (Supply supply : supplies) {
if (pattern.matcher(supply.toString()).matches()) {
result.add(supply);
}
}
return result;
}
}
见解:
这玩意我真的是颇有感悟,印象特别深,因为从不注意到十分在意
意思就是说for循环里面的判断能在外面处理完成就不要在里面解决,否则每次循环都要跑一遍,岂不是要把编译器给累死,给他省点事吧,它作为报答,会给你省下很多时间的
这个虽然看起来很简单,很是很重要,希望大家多多注意
25,利用Java8的parallel
public class Inventory {
List supplies;
long countDifferentKinds() {
return supplies.stream()
.sequential() // this can be omitted
.filter(Supply::isUncontaminated)
.map(Supply::getName)
.distinct()
.count();
}
}
改动后:
public class Inventory {
List supplies;
long countDifferentKinds() {
return supplies.stream()
.parallel()
.filter(Supply::isUncontaminated)
.map(Supply::getName)
.distinct()
.count();
}
}
解读:
如果流中的数据量足够大,并行流可以加快处速度。
class Inventory {
// Fields (we only have one)
Listsupplies=newArrayList<>(); // The list of supplies.
// Methods
int countContaminatedSupplies() {
// TODO: check if field is already initialized (not null)
int contaminatedCounter = 0; // the counter
// No supplies => no contamination
for (Supply supply : supplies) { // begin FOR
if (supply.isContaminated()) {
contaminatedCounter++; // increment counter!
} // End IF supply is contaminated
} // End FOR
// Returns the number of contaminated supplies.
return contaminatedCounter; // Handle with care!
}
} // End of Inventory class
改动后:
class Inventory {
Listsupplies=newArrayList<>();
// Methods
int countContaminatedSupplies() {
if (supplies == null || supplies.isEmpty()) {
// No supplies => no contamination
return 0;
}
int contaminatedCounter = 0;
for (Supply supply : supplies) {
if (supply.isContaminated()) {
contaminatedCounter++;
}
}
return contaminatedCounter;
}
}
解读:
这个就不用说的,好的代码都是不需要注释的
一些没用的注释放在上面基本上没有任何意义
27,移除注释掉的代码
public class LaunchChecklist {
List checks = Arrays.asList(
"Cabin Leak",
// "Communication", // Do we actually want to talk to Houston? "Engine"
,
"Hull",
// "Rover", // We won't need it, I think...
"OxygenTank"
//"Supplies"
);
Status prepareLaunch(Commander commander) {
for (String check : checks) {
boolean shouldAbortTakeoff = commander.isFailing(check);
if (shouldAbortTakeoff) {
//System.out.println("REASON FOR ABORT: " + item);
return Status.ABORT_TAKE_OFF; }
}
return Status.READY_FOR_TAKE_OFF;
}
}
改造后:
public class LaunchChecklist {
List checks = Arrays.asList(
"Cabin Leak",
"Hull",
"OxygenTank"
);
Status prepareLaunch(Commander commander) {
for (String check : checks) {
boolean shouldAbortTakeoff = commander.isFailing(check);
if (shouldAbortTakeoff) {
return Status.ABORT_TAKE_OFF; }
}
return Status.READY_FOR_TAKE_OFF;
}
}
解读:
有本书叫做程序员的整洁之道,上面说了为什么要这么做,以及怎么做,基本上包括了所有的这些知识
这些关于注释的很好理解,看看就好,以后就知道要这么做了
28,用方法代替注释
public class FuelSystem {
List tanks = new ArrayList<>();
int getAverageTankFillingPercent() {
double sum = 0;
for (double tankFilling : tanks) {
sum += tankFilling;
}
double averageFuel = sum / tanks.size();
// round to integer percent
return Math.toIntExact(Math.round(averageFuel * 100));
}
}
改造后:
public class FuelSystem {
List tanks = new ArrayList<>();
int getAverageTankFillingPercent() {
double sum = 0;
for (double tankFilling : tanks) {
sum += tankFilling;
}
double averageFuel = sum / tanks.size();
return roundToIntegerPercent(averageFuel);
}
static int roundToIntegerPercent(double value) {
return Math.toIntExact(Math.round(value * 100));
}
}
见解:
一般能用代码解决的问题就不要用注释去解决
依旧是那句话,好的代码是不用注释的,这些代码看看就好,知道哪些是对的,哪些是错的
29,用文档解释缘由
class Inventory {
private List list = new ArrayList<>();
void add(Supply supply) {
list.add(supply);
Collections.sort(list);
}
boolean isInStock(String name) {
// fast implementation
return Collections.binarySearch(list, new Supply(name)) != -1;
}
}
改造后:
class Inventory {
// Keep this list sorted. See isInStock().
private List list = new ArrayList<>();
void add(Supply supply) {
list.add(supply);
Collections.sort(list);
}
boolean isInStock(String name) {
return Collections.binarySearch(list, new Supply(name)) != -1;
}
}
解读:
需要文档注释的一定不能少,给出详细的解释
意思就是无用注释直接不要,需要的注释那你就给详细一点
举例说明:
public class Supply {
static final Pattern CODE =
Pattern.compile("^S\d{5}\\(US|EU|RU|CN)\.[a-z]+$");
}
改动后:
public class Supply {
static final Pattern SUPPLY_CODE =
Pattern.compile("^S\d{5}\\(US|EU|RU|CN)\.[a-z]+$");
}
学习!!!
2021,10,16



