栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

代码整洁之道解读「Java By Comparison - Become a Java Craftsman in 70 Examples」

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

代码整洁之道解读「Java By Comparison - Become a Java Craftsman in 70 Examples」

为了有朝一日等成为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人应该也不会考虑

毕竟人本性是懒得,能一步解决的事情我们绝不会留两步

而且完全没必要呀 这种写法 想不通了 这种代码看看就好



二:让你的代码更简洁 13,避免无意义的比较
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去实现自己想要的功能,而不是写原生代码

避免重复造轮子!!!



三:增强代码的健壮性 17,避免空值异常
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



四:提高代码运行效率 24,避免在循环中做耗时运算
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();
   }
}

解读:

如果流中的数据量足够大,并行流可以加快处速度。



五:改进代码的注释 26,避免没有意义的注释
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

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/327862.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号