元数据的力量:如何使用意大利面条式代码
来源:Hackernoon 我们都尝试使用通用方法和已知模式来以最小的努力和最大的影响创建应用程序。我们拥有优秀的库和强大的框架来为我们执行日常操作。我们使用所有这些来只关注业务逻辑。然而,这种追求常常导致我们写出意大利面条般的代码,尤其是在没有现成解决方案的情况下实现一个功能时。在本文中,我想与您分享一个强大的工具,根据我的经验,并非所有开发人员都喜欢它。这个工具存在于大多数编程语言中,并且在许多框架中经常使用——注释。你喜欢意大利面吗?
让我们看一个我几年前遇到的例子。我需要解析 Excel 电子表格以将解析的数据放入数据库中。我还想从数据库中收集一些数据并创建一个电子表格。为了实现,我使用了著名的 Java 库 - Apache POI。该库的 API 使您的工作更加轻松,因为它允许您手动创建工作表、行、单元格和其他元素。这很好,但是当需要生成各种Excel电子表格时,代码就变得完全不可读、不支持。结果,正如通常发生的那样,应用程序的第一个版本结果非常糟糕。该实现由一个数据类组成,该数据类表示一个字符串,其中包含解析所需的所有字段。还有一个解析器,其中 Excel 字段被逐个单元地解析并放入新创建的数据类实例中。起初,该程序运行良好并且满足了要求。当需要进行一些修改时,问题就出现了;代码未被读取。即使是编写这段代码的我也找不到合适的地方来放置新行来实现我需要的新功能。注释中的救援
从此注释意大利面条代码中保存了应用程序。为了摆脱不受支持的代码,我需要将用于确定要解析哪一列、单元格包含的数据类型以及其他所有内容的逻辑移动到不同的位置。为此,我创建了一个注释,在其中指定每个类字段的列名称。在注释中,我还添加了一个变量,允许您选择单元格的颜色和字体。这样,解析类中的代码就大大减少了。只有一个处理器根据从注释中获取的参数动态创建电子表格。这是一场胜利。然后,要对应用程序进行任何更改,我只需创建一个带有注释的类。该解决方案让人想起 Jackson 库,它使用注释解析 JSON,我认为没有必要告诉 Jackson 或类似的库有多方便。@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ColumnExcel {
String name() default "";
int position();
ExcelColumnDataFormat cellTypePattern() default ExcelColumnDataFormat.NONE;
IndexedColors cellColor() default IndexedColors.AUTOMATIC;
ExcelTotalFormula total() default ExcelTotalFormula.NONE;
}
ColumnExcel columnExcel = field.getAnnotation(ColumnExcel.class);
随着应用程序的发展,它收到了一个新的注释,可用于在电子表格中创建一个内部有函数的单元格。可以对各种字段进行乘法、减法,并且可以使用任何常见的 Excel 函数。我还添加了总计行以按列显示总和。我只是通过稍微修改主解析器并简单地向类添加注释来完成这一切。
@ColumnExcel(
name = "Views",
position = 4,
total = ExcelTotalFormula.SUM)
private BigDecimal variableC;
@ColumnExcelFormula(
name = "Conversion",
position = 5,
cellTypePattern = CellDataTypeFormatPattern.PERCENTAGE
)
public String variableD(int rowNumber) {
return new CellAddress(rowNumber, 4).formatAsString() + "*"
+ new CellAddress(rowNumber, 2).formatAsString();
}
@ColumnExcelTotalFormula(position = 4, cellTypePattern = CellDataTypeFormatPattern.RUR)
public static String getVariableCTotalFormula(int firstRowNum, int lastRowNum) {
return "SUM( " + new CellAddress(firstRowNum, 4).formatAsString() + ":"
+ new CellAddress(lastRowNum, 4).formatAsString() + ")";
}
Java 中的垃圾收集 - 它是如何工作的以及它的优点是什么
来源:Dev.to 垃圾收集意味着销毁或清理内存中未使用的对象。Java 会自动处理内存释放,因为一旦创建了对象,它就会使用堆上的一些内存。怎么运行的?
在 Java 之前,最流行的编程语言是 C 或 C++。如果您会说这些语言,那么您应该知道它们手动管理自己的内存。例如,C 具有calloc()、malloc()和realloc()等方法,允许您使用缓冲存储器。您必须确定程序需要多少内存并指定此 API 调用的内容。然后,您可以获取内存缓冲区来创建链表节点或其他东西。当你的程序终止时,在某个时刻,你还负责清理该内存。因此,用 C 编写的大型应用程序会不断分配缓冲区内存,有时会忘记刷新它。这最终会导致内存泄漏和应用程序中出现很多问题。与 C 和 C++ 不同,Java 语言通过称为垃圾收集器的线程提供自动内存管理。其主要目的是通过销毁不可访问的对象来释放堆内存。垃圾收集器始终在后台运行。Java中什么是不可访问的对象?
对象什么时候有机会开始垃圾回收?如果存在无法访问的对象 - 没有活动链接的对象。让我们看一个例子:public static void main(String[] args)
{
// StringBuffer object sb is not eligible for garbage collection
StringBuffer sb = new StringBuffer("Flower Brackets");
System.out.println(sb);
// StringBuffer object sb is eligible for garbage collection
sb = null;
}
在 main 方法中,我创建了一个StringBuffer对象及其引用。此时,StringBuffer对象不符合垃圾回收条件。现在我要将StringBuffer对象设置为“null”。该对象现在符合垃圾回收条件,并成为堆内存中不可访问的对象。也就是说,垃圾收集通常在对象变得不可访问的情况下起作用。这意味着对象通常是在“if 块”或方法的上下文中创建的。因此,一旦方法执行完成,对象就会超出范围,并可以由垃圾收集器处理。由于从旧对象到新对象的引用数量有限,这意味着应用程序中长期存在的对象通常不是新创建的对象。以下是我们应该熟悉的几个术语;其中之一是活体物体。它是应用程序中的一个对象,被同一应用程序中的另一个对象引用。还有一个“死”的物体。死对象是在方法调用期间创建的不可访问的对象,一旦方法调用完成,该对象就会脱离上下文并仅位于堆上。
对象什么时候适合垃圾回收?
如果对象没有任何引用变量,则该对象有资格进行垃圾回收。如何使对象可用于垃圾回收?
下面有几种方法:-
null reference variable Student obj = new Student(); obj = null;
-
re-assign reference variable Student obj1 = new Student(); Student obj2 = new Student(); obj1 = obj2;
-
reate anonymous object new Student();
一旦对象可供垃圾收集器使用,它不会立即被销毁。
-
System.gc()方法
-
Finalize()方法
-
Runtime.getRuntime().gc()方法
public class GarbageCollector
{
public static void main(String[] args)
{
Employee obj1 = new Employee();
Employee obj2 = new Employee();
obj1 = null;
obj2 = null;
System.gc();
}
public void finalize()
{
System.out.println("object garbage collected");
}
}
结果:
对象垃圾收集 对象垃圾收集
Finalize() 方法在对象被清理之前调用。该方法在Object类中定义:
protected void finalize() throws Throwable
-
Finalize方法用于关闭与数据库的连接。
-
该方法由垃圾收集器调用,而不是 JVM。
-
我们需要重写finalize()方法。因为它有一个空的实现。
-
每个对象仅调用一次。
public class Demo
{
public static void main(String[] args)
{
Demo obj1 = new Demo();
Demo obj2 = new Demo();
// nullifying reference variable
obj1 = null;
// nullifying reference variable
obj2 = null;
// running Garbage Collector
Runtime.getRuntime().gc();
}
@Override
protected void finalize() throws Throwable
{
System.out.println("Garbage collector called");
System.out.println("Object garbage collector: " + this);
}
}
结果:
称为对象垃圾收集器的垃圾收集器:Demo@2130772 称为对象垃圾收集器的垃圾收集器:Demo@cd4e940
垃圾收集的好处:
- Java 中的垃圾收集是自动发生的,这使我们免于释放已用内存的额外负担。这使得Java程序的内存更加高效。
- 垃圾收集确保程序完整性。
- 我们不需要编写任何额外的代码,因为垃圾收集器是 JVM 的一部分。
GO TO FULL VERSION