Java编程的那些屎山代码分析之二(java 代码)

createh53个月前 (02-01)技术教程22

以下是个人总结的一些代码习惯问题和优化,单独一个也许不起眼,但堆积起来,就让一个项目代码变成一座屎山。



1.滥用`public`修饰符

o 重要性:滥用`public`修饰符可能导致类的成员变量或方法被不恰当地公开,增加不必要的暴露和模块间的耦合。o 避免方法:默认使用`private`或`package-private`访问控制符,仅当需要外部访问时提供公共的 getter 和 setter 方法。

举个正例:

public class MyClass {
  private int value;
  public int getValue() { return value; }
  public void setValue(int value) { this.value = value; }
}

举个反例:

public class MyClass {
  public int value;
}



2.字符串拼接使用`+`


o 重要性:在循环中使用`+`进行字符串拼接可能导致性能问题,因为每次拼接都会创建新的字符串对象。o 避免方法:使用`StringBuilder`或`String.format`来提高性能。

举个正例:

StringBuilder result = new StringBuilder();
for (int i = 0; i < 1000; i++) {
  result.append(i);
}

举个反例:

String result = "";
for (int i = 0; i < 1000; i++) {
  result += i;
}



3.在多线程中共享可变状态


o 重要性:在多线程环境中共享可变状态可能导致线程安全问题。o 避免方法:使用不可变对象或通过同步机制来管理状态。

举个正例:

import java.util.concurrent.atomic.AtomicInteger;

class Counter {
  private AtomicInteger count = new AtomicInteger(0);
  public int increment() {
    return count.incrementAndGet();
  }
}

举个反例:

class Counter {
  int count = 0;
}



4.在集合中使用原始类型

o 重要性:使用原始类型可能导致`ClassCastException`。o 避免方法:使用泛型来避免类型转换问题。

举个正例:

List<String> list = new ArrayList<>();
list.add("test");
String str = list.get(0);

举个反例:

List list = new ArrayList();
list.add("test");
String str = (String) list.get(0);



5.使用硬编码字符串

o 重要性:硬编码字符串使得代码难以维护和国际化。o 避免方法:使用常量或资源文件来管理字符串。

举个正例:

public static final String ADMIN = "admin";
if (userType.equals(ADMIN)) {
  // do something
}

举个反例:

if (userType.equals("admin")) {
  // do something
}



6.不使用日志框架

o 重要性:不使用日志框架可能导致调试和问题跟踪困难。o 避免方法:使用如 SLF4J 或 Log4j 等日志框架。

举个正例:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

private static final Logger logger = LoggerFactory.getLogger(MyClass.class);
logger.error("错误信息");

举个反例:

System.out.println("错误信息");



7.错误使用浮点数进行比较题

o 错误使用浮点数进行比较o 重要性:直接比较浮点数可能导致逻辑错误,因为浮点数的精度问题。o 避免方法:使用误差范围进行比较。

举个正例:

if (Math.abs(0.1 + 0.2 - 0.3) < 1e-9) {
  System.out.println("相等");
}

举个反例:

if (0.1 + 0.2 == 0.3) {
  System.out.println("相等");
}



8.循环中创建对象

o 重要性:在循环中创建对象可能导致性能问题和内存浪费。o 避免方法:将对象初始化移到循环外部。

举个正例:

StringBuilder sb = new StringBuilder();
for (int i = 0; i < 100; i++) {
  sb.append(i);
}

举个反例:

for (int i = 0; i < 100; i++) {
  StringBuilder sb = new StringBuilder();
}



9.过度依赖静态变量

o 重要性:过度依赖静态变量可能导致线程不安全。o 避免方法:限制静态变量的使用,优先使用实例变量或线程本地变量。

举个正例:

private int counter = 0;

举个反例:

public static int counter = 0;



10.不规范的命名

o 重要性:不规范的命名降低代码可读性。o 避免方法:遵循命名规范,如 CamelCase。

举个正例:

public void getData() { }

举个反例:

public void GETdata() { }



11.滥用反射

o 重要性:滥用反射可能导致性能差且不安全。o 避免方法:仅在必要时使用反射。

举个正例:

List<String> list = new ArrayList<>();

举个反例:

Class<?> clazz = Class.forName("java.util.ArrayList");
Object obj = clazz.newInstance();



12.在集合中存放过多元素

o 重要性:存放过多元素可能导致内存溢出。o 避免方法:设置合理的容量或限制集合大小。

举个正例:

List<Integer> list = new ArrayList<>(1000); // 添加有限数量的元素

举个反例:

List<Integer> list = new ArrayList<>();
for (int i = 0; i < Integer.MAX_VALUE; i++) {
  list.add(i);
}



13.使用双重检查锁定时未声明`volatile`

o 重要性:未声明`volatile`可能导致线程安全问题。o 避免方法:使用`volatile`或枚举单例模式。

举个正例:

class Singleton {
  private static volatile Singleton instance;
  public static Singleton getInstance() {
    if (instance == null) {
      synchronized (Singleton.class) {
        if (instance == null) {
          instance = new Singleton();
        }
      }
    }
    return instance;
  }
}

举个反例:

class Singleton {
  private static Singleton instance;
  public static Singleton getInstance() {
    if (instance == null) {
      synchronized (Singleton.class) {
        if (instance == null) {
          instance = new Singleton();
        }
      }
    }
    return instance;
  }
}



14.直接暴露内部数据结构

o 重要性:直接暴露内部数据结构容易破坏封装性。o 避免方法:提供不可变集合或深拷贝。

举个正例:

public List<String> getItems() {
  return Collections.unmodifiableList(items);
}

举个反例:

public List<String> getItems() {
  return items;
}



15. 滥用`instanceof`

o 重要性:滥用`instanceof`可能导致代码难以维护。o 避免方法:使用多态。

举个正例:

abstract class Shape {
  abstract void draw();
}
class Circle extends Shape {
  void draw() {
    System.out.println("Drawing Circle");
  }
}
Shape shape = new Circle();
shape.draw();

举个反例:

if (obj instanceof Circle) {
  Circle circle = (Circle) obj;
  circle.draw();
}



16.在`HashMap`中使用可变键

o 重要性:使用可变键可能导致数据丢失或行为异常。o 避免方法:使用不可变对象作为键。

举个正例:

Map<String, String> map = new HashMap<>();
String key = "constantKey";
map.put(key, "value");
System.out.println(map.get(key)); // 返回 value

举个反例:

Map<List<String>, String> map = new HashMap<>();
List<String> key = new ArrayList<>();
map.put(key, "value");
key.add("newKey");
System.out.println(map.get(key)); // 可能返回 null



17.直接调用线程的`run()`方法

o 重要性:直接调用`run()`方法可能导致逻辑错误,原因详见备注1。


o 避免方法:使用`start()`方法启动线程。

举个正例:

Thread thread = new Thread(() -> System.out.println("Running"));
thread.start();

举个反例:

Thread thread = new Thread(() -> System.out.println("Running"));
thread.run();



18.滥用`System.exit()`

o 重要性:滥用`System.exit()`可能导致资源未释放。o 避免方法:避免使用`System.exit()`,正常流程中结束程序。

举个正例:

if (error) {
  throw new RuntimeException("程序出现错误");
}

举个反例:

if (error) {
  System.exit(1);
}



19.在循环中频繁调用`get`方法

o 重要性:频繁调用`get`方法可能导致性能问题。o 避免方法:使用局部变量存储返回值。

举个正例:

List<Integer> list = new ArrayList<>();
int size = list.size();
for (int i = 0; i < size; i++) {
  System.out.println(list.get(i));
}

举个反例:

List<Integer> list = new ArrayList<>();
for (int i = 0; i < list.size(); i++) {
  System.out.println(list.get(i));
}



20.在同步代码块中使用`String`锁

o 重要性:使用`String`锁可能引发死锁。o 避免方法:使用私有的锁对象。

举个正例:

代码private final Object lock = new Object();
synchronized (lock) {
  System.out.println("Locked");
}

举个反例:

synchronized ("lock") {
  System.out.println("Locked");
}



21.使用过时的类或方法

o 重要性:使用过时的类或方法可能存在安全问题或性能低下。o 避免方法:使用现代替代方案。

举个正例:

LocalDate date = LocalDate.of(202

举个反例:

Date date = new Date(2022, 1, 1);



22.未对集合进行空检查


o 重要性:未进行空检查可能引发`NullPointerException`。o 避免方法:使用`Objects.isNull`或`Objects.nonNull`检查。

举个正例:

import java.util.Objects;

List<String> list = null;
if (Objects.nonNull(list)) {
    System.out.println(list.size());
}
//或者使用Java 8 的 Optional 类来避免空检查:
import java.util.List;
import java.util.Optional;
List<String> list = null;
Optional.ofNullable(list).ifPresent(l -> System.out.println(l.size()));

举个反例:

List<String> list = null;
System.out.println(list.size());



23.递归调用未限制深度

o 重要性:未限制深度可能导致栈溢出。o 避免方法:添加递归终止条件或改为迭代。

举个正例:

void recursive(int depth) {
    if (depth == 0) return;
    recursive(depth - 1);
}

举个反例:

void recursive() {
    recursive();
}




备注:

一,线程使用run方法执行的问题

1. 生命周期管理:

o 当你创建一个线程并调用其 run() 方法时,这个线程并没有被当作一个线程来执行,而是作为一个普通的对象方法调用。这意味着线程的生命周期并没有被正确管理,它没有被放置在线程调度器的控制之下。

o 线程的启动和执行应该由 Java 虚拟机(JVM)的线程调度器来管理。通过调用 start() 方法而不是 run() 方法,线程会被正确地放入 JVM 的线程调度队列中,按照线程的优先级和调度策略被执行。


2. 并发执行:

o start() 方法会创建一个新的线程,并在这个新线程中调用 run() 方法,从而实现并发执行。这意味着主线程和新线程可以同时运行,这是多线程编程的核心优势之一。

o 如果直接调用 run() 方法,就不会创建新的线程, run() 方法的执行会阻塞当前线程,直到 run() 方法执行完成,这失去了并发执行的意义。

3. 线程局部变量:

o 在多线程环境中,线程局部变量(ThreadLocal)是每个线程独有的,它们通过 start() 方法启动的线程来隔离和管理。

o 如果直接调用 run() 方法,线程局部变量可能不会按预期工作,因为实际上并没有创建新线程,因此无法保证变量的线程安全性和隔离性。

4. 异常处理:

o 当线程通过 start() 方法启动时,在其 run() 方法中抛出的任何未捕获异常都会作为线程的运行时异常被捕获,并且可以通过 getStackTrace() 方法从外部获取堆栈跟踪,这有助于调试。

o 如果直接调用 run() 方法,任何在 run() 方法中抛出的异常都会导致当前线程(可能是主线程)崩溃,这可能导致程序的其他部分无法正常执行,并且可能使得异常处理更加困难。



如果不注意以上相关规范,写出这样一座屎山代码,对后续接手的人来说,是多大的伤害啊!


相关文章

Python之面向对象:私有属性是掩耳盗铃还是恰到好处

引言声明,今天的文章中没有一行Python代码,更多的是对编程语言设计理念的思考。上一篇文章中介绍了关于Python面向对象封装特性的私有属性的相关内容,提到了Python中关于私有属性的实现是通过“...

思考:Java对象之生(java中对象的概念的理解)

内存、性能是程序永恒的话题,实际开发中关于卡顿、OOM也经常是打不完的两只老虎,关于卡顿、OOM的定位方法和工具比较多,这篇文章也不打算赘述了,本章主要是来整理一下JVM的内存模型以及Java对象的生...

java匿名内部类的定义以及使用场景

匿名内部类定义 匿名内部类是Java中一种没有显式声明名称的内部类。它们在创建时被同时声明和实例化,通常用于创建一次性使用的类。它们的特点是:无名称: 无法像普通类一样被其他代码引用。一次性: 通常只...

java 核心技术-12版 卷Ⅰ- 6.1.4 静态和私有方法

原文6.1.4 静态和私有方法在 Java 8 中,允许在接口中增加静态方法。理论上讲,没有任何理由认为这是不合法的。只是这似乎有违于将接口作为抽象规范的初衷。目前为止,通常的做法都是将静态方法放在伴...

用GitHub创建自己的Maven私有仓库

【Github上创建仓库】首先,在GitHub上创建自己的仓库(mvn-repo):【配置本地setting文件】找到本地的maven settings文件,配置server:有两种选择,可以选择配置...

终于搞懂了 Java 8 的内存结构,再也不纠结方法区和常量池了

java8内存结构图虚拟机内存与本地内存的区别Java虚拟机在执行的时候会把管理的内存分配成不同的区域,这些区域被称为虚拟机内存,同时,对于虚拟机没有直接管理的物理内存,也有一定的利用,这些被利用却不...