以下是个人总结的一些代码习惯问题和优化,单独一个也许不起眼,但堆积起来,就让一个项目代码变成一座屎山。
1.滥用`public`修饰符
? 重要性:滥用`public`修饰符可能导致类的成员变量或方法被不恰当地公开,增加不必要的暴露和模块间的耦合。? 避免方法:默认使用`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.字符串拼接使用`+`
? 重要性:在循环中使用`+`进行字符串拼接可能导致性能问题,因为每次拼接都会创建新的字符串对象。? 避免方法:使用`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.在多线程中共享可变状态
? 重要性:在多线程环境中共享可变状态可能导致线程安全问题。? 避免方法:使用不可变对象或通过同步机制来管理状态。
举个正例:
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.在集合中使用原始类型
? 重要性:使用原始类型可能导致`ClassCastException`。? 避免方法:使用泛型来避免类型转换问题。
举个正例:
List 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.使用硬编码字符串
? 重要性:硬编码字符串使得代码难以维护和国际化。? 避免方法:使用常量或资源文件来管理字符串。
举个正例:
public static final String ADMIN = "admin";
if (userType.equals(ADMIN)) {
? // do something
}
举个反例:
if (userType.equals("admin")) {
? // do something
}
6.不使用日志框架
? 重要性:不使用日志框架可能导致调试和问题跟踪困难。? 避免方法:使用如 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.错误使用浮点数进行比较题
? 错误使用浮点数进行比较? 重要性:直接比较浮点数可能导致逻辑错误,因为浮点数的精度问题。? 避免方法:使用误差范围进行比较。
举个正例:
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.循环中创建对象
? 重要性:在循环中创建对象可能导致性能问题和内存浪费。? 避免方法:将对象初始化移到循环外部。
举个正例:
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.过度依赖静态变量
? 重要性:过度依赖静态变量可能导致线程不安全。? 避免方法:限制静态变量的使用,优先使用实例变量或线程本地变量。
举个正例:
private int counter = 0;
举个反例:
public static int counter = 0;
10.不规范的命名
? 重要性:不规范的命名降低代码可读性。? 避免方法:遵循命名规范,如 CamelCase。
举个正例:
public void getData() { }
举个反例:
public void GETdata() { }
11.滥用反射
? 重要性:滥用反射可能导致性能差且不安全。? 避免方法:仅在必要时使用反射。
举个正例:
List list = new ArrayList<>();
举个反例:
Class> clazz = Class.forName("java.util.ArrayList");
Object obj = clazz.newInstance();
12.在集合中存放过多元素
? 重要性:存放过多元素可能导致内存溢出。? 避免方法:设置合理的容量或限制集合大小。
举个正例:
List list = new ArrayList<>(1000); // 添加有限数量的元素
举个反例:
List list = new ArrayList<>();
for (int i = 0; i < Integer.MAX_VALUE; i++) {
list.add(i);
}
13.使用双重检查锁定时未声明`volatile`
? 重要性:未声明`volatile`可能导致线程安全问题。? 避免方法:使用`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.直接暴露内部数据结构
? 重要性:直接暴露内部数据结构容易破坏封装性。? 避免方法:提供不可变集合或深拷贝。
举个正例:
public List getItems() {
? return Collections.unmodifiableList(items);
}
举个反例:
public List getItems() {
? return items;
}
15. 滥用`instanceof`
? 重要性:滥用`instanceof`可能导致代码难以维护。? 避免方法:使用多态。
举个正例:
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`中使用可变键
? 重要性:使用可变键可能导致数据丢失或行为异常。? 避免方法:使用不可变对象作为键。
举个正例:
Map map = new HashMap<>();
String key = "constantKey";
map.put(key, "value");
System.out.println(map.get(key)); // 返回 value
举个反例:
Map, String> map = new HashMap<>();
List key = new ArrayList<>();
map.put(key, "value");
key.add("newKey");
System.out.println(map.get(key)); // 可能返回 null
17.直接调用线程的`run()`方法
? 重要性:直接调用`run()`方法可能导致逻辑错误,原因详见备注1。
? 避免方法:使用`start()`方法启动线程。
举个正例:
Thread thread = new Thread(() -> System.out.println("Running"));
thread.start();
举个反例:
Thread thread = new Thread(() -> System.out.println("Running"));
thread.run();
18.滥用`System.exit()`
? 重要性:滥用`System.exit()`可能导致资源未释放。? 避免方法:避免使用`System.exit()`,正常流程中结束程序。
举个正例:
if (error) {
? throw new RuntimeException("程序出现错误");
}
举个反例:
if (error) {
? System.exit(1);
}
19.在循环中频繁调用`get`方法
? 重要性:频繁调用`get`方法可能导致性能问题。? 避免方法:使用局部变量存储返回值。
举个正例:
List list = new ArrayList<>();
int size = list.size();
for (int i = 0; i < size; i++) {
? System.out.println(list.get(i));
}
举个反例:
List list = new ArrayList<>();
for (int i = 0; i < list.size(); i++) {
? System.out.println(list.get(i));
}
20.在同步代码块中使用`String`锁
? 重要性:使用`String`锁可能引发死锁。? 避免方法:使用私有的锁对象。
举个正例:
代码private final Object lock = new Object();
synchronized (lock) {
System.out.println("Locked");
}
举个反例:
synchronized ("lock") {
? System.out.println("Locked");
}
21.使用过时的类或方法
? 重要性:使用过时的类或方法可能存在安全问题或性能低下。? 避免方法:使用现代替代方案。
举个正例:
LocalDate date = LocalDate.of(202
举个反例:
Date date = new Date(2022, 1, 1);
22.未对集合进行空检查
? 重要性:未进行空检查可能引发`NullPointerException`。? 避免方法:使用`Objects.isNull`或`Objects.nonNull`检查。
举个正例:
import java.util.Objects;
List list = null;
if (Objects.nonNull(list)) {
??? System.out.println(list.size());
}
//或者使用Java 8 的 Optional 类来避免空检查:
import java.util.List;
import java.util.Optional;
List list = null;
Optional.ofNullable(list).ifPresent(l -> System.out.println(l.size()));
举个反例:
List list = null;
System.out.println(list.size());
23.递归调用未限制深度
? 重要性:未限制深度可能导致栈溢出。? 避免方法:添加递归终止条件或改为迭代。
举个正例:
void recursive(int depth) {
??? if (depth == 0) return;
??? recursive(depth - 1);
}
举个反例:
void recursive() {
??? recursive();
}
备注:
一,线程使用run方法执行的问题
1. 生命周期管理:
? 当你创建一个线程并调用其 run() 方法时,这个线程并没有被当作一个线程来执行,而是作为一个普通的对象方法调用。这意味着线程的生命周期并没有被正确管理,它没有被放置在线程调度器的控制之下。
? 线程的启动和执行应该由 Java 虚拟机(JVM)的线程调度器来管理。通过调用 start() 方法而不是 run() 方法,线程会被正确地放入 JVM 的线程调度队列中,按照线程的优先级和调度策略被执行。
2. 并发执行:
? start() 方法会创建一个新的线程,并在这个新线程中调用 run() 方法,从而实现并发执行。这意味着主线程和新线程可以同时运行,这是多线程编程的核心优势之一。
? 如果直接调用 run() 方法,就不会创建新的线程, run() 方法的执行会阻塞当前线程,直到 run() 方法执行完成,这失去了并发执行的意义。
3. 线程局部变量:
? 在多线程环境中,线程局部变量(ThreadLocal)是每个线程独有的,它们通过 start() 方法启动的线程来隔离和管理。
? 如果直接调用 run() 方法,线程局部变量可能不会按预期工作,因为实际上并没有创建新线程,因此无法保证变量的线程安全性和隔离性。
4. 异常处理:
? 当线程通过 start() 方法启动时,在其 run() 方法中抛出的任何未捕获异常都会作为线程的运行时异常被捕获,并且可以通过 getStackTrace() 方法从外部获取堆栈跟踪,这有助于调试。
? 如果直接调用 run() 方法,任何在 run() 方法中抛出的异常都会导致当前线程(可能是主线程)崩溃,这可能导致程序的其他部分无法正常执行,并且可能使得异常处理更加困难。
如果不注意以上相关规范,写出这样一座屎山代码,对后续接手的人来说,是多大的伤害啊!
本文暂时没有评论,来添加一个吧(●'◡'●)