怎样在java中定义一个抽象属性

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介:

怎样在java中定义一个抽象属性


Abstract关键字通常被用于类和方法,用来把某些行为的实现委托给子类。由于Java不支持抽象属性,如果你试图将类属性标记为抽象,将会得到一个编译时错误。

在本教程中,我们将介绍两种定义抽象属性的方法,这些抽象属性可以由子类进行设置,而且不使用Abstract 关键字。

实用案例

假设我们想要实现一个记录事务的日志模块,用来记录特定事务的信息。我们希望这个模块是抽象的,这样我们可以实现不同的日志记录方式,例如:记录到文件或数据库中。

我们的引擎使用预定义的分隔符来连接日志中的信息,并存储在一个String中。具体应该使用哪个分隔符,这将取决于日志记录的规则,例如可以用字符“,”对日志记录中不同部分的信息进行分割。

因此,分隔符看起来对我们的引擎是抽象的,需要由每个日志记录规则明确定义。

下面我提供两种方式,来实现把分隔符的定义委托给子类。

在抽象类中定义带参数的构造函数

在抽象类中定义动态属性的第一种方法是:定义一个参数的构造函数。

所以我们可以这样实现这个引擎:

 
 
  1. // TransactionManager.java 
  2.  
  3. public abstract class TransactionManager { 
  4.     private String separator; 
  5.      
  6.     public TransactionManager(String separator) { 
  7.         this.separator = separator; 
  8.     } 
  9.      
  10.     public abstract void writeTransaction(String result); 
  11.      
  12.     public Transaction startTransaction() 
  13.     { 
  14.         Transaction transaction = new Transaction(System.currentTimeMillis()); 
  15.         return transaction
  16.     } 
  17.      
  18.     public void endTransaction(Transaction t) { 
  19.         long processingTime = System.currentTimeMillis() - t.getStartTime(); 
  20.   
  21.         StringBuilder logBuilder = new StringBuilder(); 
  22.         logBuilder.append(t.getStartTime()); 
  23.         // Notice the use of this.separator 
  24.         logBuilder.append(this.separator); 
  25.         logBuilder.append(processingTime); 
  26.         logBuilder.append(this.separator); 
  27.         logBuilder.append(t.getData()); 
  28.   
  29.         String result = logBuilder.toString(); 
  30.         writeTransaction(result); 
  31.     } 
  32. }  

在抽象类中定义带参数的构造函数时,子类将会被强制定义自己的构造函数并调用super()。 这样我们就能强制separator属性依赖于已使用的日志记录机制。

注意,我们的引擎实现了所有日志机制共有的静态行为:startTransaction(), endTransaction(),同时将动态行为writeTransaction()交给子类去实现。

现在,如果我们想要创建一个事务管理器,用它将日志内容记录到一个文件中,那么可以这样去定义:

 
 
  1. public class TransactionManagerFS extends TransactionManager{ 
  2.   
  3.     // The IDE forces you to implement constructor. 
  4.     public TransactionManagerFS(String separator) { 
  5.         super(separator); 
  6.     } 
  7.         
  8.     @Override 
  9.     public void writeTransaction(String result) { 
  10.         System.out.println("The following transaction has just finished: " ); 
  11.         System.out.println(result); 
  12.     } 
  13. }  

接下来做一个测试,看看代码是怎样工作的

 
 
  1. public static void main(String[] args) throws InterruptedException { 
  2.         // we pass the separator explicitly in the constructor 
  3.         TransactionManager transactionManager = new TransactionManagerFS(","); 
  4.         Transaction transaction = transactionManager.startTransaction(); 
  5.         transaction.setData("This is a test transaction !!"); 
  6.         Thread.sleep(1500); 
  7.         transactionManager.endTransaction(transaction); 
  8.     }  

输出:

 
 
  1. The following transaction has just finished:  
  2. 1502179140689,1501,This is a test transaction !!  

通过getter方法传递分隔符

另外一种实现动态属性的方法是:通过定义一个抽象的getter方法,该方法根据当前的日志记录机制来检索所需的分隔符。在我们的引擎中,当需要要使用分隔符时,可以通过调用这个getter方法得到。

接下来我们将引擎修改成这样:

 
 
  1. public abstract class TransactionManager { 
  2.   
  3.     public abstract String getSeperator(); 
  4.     public abstract void writeTransaction(String result); 
  5.      
  6.     public Transaction startTransaction() 
  7.     { 
  8.         Transaction transaction = new Transaction(System.currentTimeMillis()); 
  9.         return transaction
  10.     } 
  11.      
  12.     public void endTransaction(Transaction t) { 
  13.         long processingTime = System.currentTimeMillis() - t.getStartTime(); 
  14.   
  15.         StringBuilder logBuilder = new StringBuilder(); 
  16.         logBuilder.append(t.getStartTime()); 
  17.         // Notice the use of getSeparator() 
  18.         logBuilder.append(getSeperator()); 
  19.         logBuilder.append(processingTime); 
  20.         logBuilder.append(getSeperator()); 
  21.         logBuilder.append(t.getData()); 
  22.   
  23.         String result = logBuilder.toString(); 
  24.         writeTransaction(result); 
  25.     } 
  26. }  

另外修改TransactionManagerFS如下:

 
 
  1. public class TransactionManagerFS extends TransactionManager{ 
  2.   
  3.     @Override 
  4.     public String getSeperator() { 
  5.         return ","
  6.     } 
  7.         
  8.     @Override 
  9.     public void writeTransaction(String result) { 
  10.         System.out.println("The following transaction has just finished: " ); 
  11.         System.out.println(result); 
  12.     } 
  13. }  

然后,修改main以使用新的实现,并确保得到正确的结果。

 
 
  1. public static void main(String[] args) throws InterruptedException { 
  2.         // The separator is defined implicitly using getSeparator() method of the manager 
  3.         TransactionManager transactionManager = new TransactionManagerFS(); 
  4.         Transaction transaction = transactionManager.startTransaction(); 
  5.         transaction.setData("This is a test transaction !!"); 
  6.         Thread.sleep(1500); 
  7.         transactionManager.endTransaction(transaction); 
  8.     }  

输出:

 
 
  1. The following transaction has just finished: 
  2. 1502179140689,1501,This is a test transaction !!   


作者:疯狂的技术宅

来源:51CTO

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
打赏
0
0
0
0
16485
分享
相关文章
|
1月前
|
《从头开始学java,一天一个知识点》之:方法定义与参数传递机制
**你是否也经历过这些崩溃瞬间?** - 看了三天教程,连`i++`和`++i`的区别都说不清 - 面试时被追问"`a==b`和`equals()`的区别",大脑突然空白 - 写出的代码总是莫名报NPE,却不知道问题出在哪个运算符 🚀 这个系列就是为你打造的Java「速效救心丸」!我们承诺:每天1分钟,地铁通勤、午休间隙即可完成学习;直击痛点,只讲高频考点和实际开发中的「坑位」;拒绝臃肿,没有冗长概念堆砌,每篇都有可运行的代码标本。上篇:《输入与输出:Scanner与System类》 | 下篇剧透:《方法重载与可变参数》。
55 25
|
1月前
|
《从头开始学java,一天一个知识点》之:数组入门:一维数组的定义与遍历
**你是否也经历过这些崩溃瞬间?** - 看了三天教程,连`i++`和`++i`的区别都说不清 - 面试时被追问"`a==b`和`equals()`的区别",大脑突然空白 - 写出的代码总是莫名报NPE,却不知道问题出在哪个运算符 这个系列就是为你打造的Java「速效救心丸」!我们承诺:每天1分钟,地铁通勤、午休间隙即可完成学习;直击痛点,只讲高频考点和实际开发中的「坑位」;拒绝臃肿,没有冗长概念堆砌,每篇都有可运行的代码标本。明日预告:《多维数组与常见操作》。 通过实例讲解数组的核心认知、趣味场景应用、企业级开发规范及优化技巧,帮助你快速掌握Java数组的精髓。
67 23
Java 中数组的多种定义方式
本文深入解析了Java中数组的多种定义方式,涵盖基础的`new`关键字创建、直接初始化、动态初始化,到多维数组、`Arrays.fill()`方法以及集合类转换为数组等高级用法。通过理论与实践结合的方式,探讨了每种定义方法的适用场景、优缺点及其背后的原理,帮助开发者掌握高效、灵活的数组操作技巧,从而编写更优质的Java代码。
40 0
Java最新图形化界面开发技术——JavaFx教程(含UI控件用法介绍、属性绑定、事件监听、FXML)
JavaFX是Java的下一代图形用户界面工具包。JavaFX是一组图形和媒体API,我们可以用它们来创建和部署富客户端应用程序。 JavaFX允许开发人员快速构建丰富的跨平台应用程序,允许开发人员在单个编程接口中组合图形,动画和UI控件。本文详细介绍了JavaFx的常见用法,相信读完本教程你一定有所收获!
3900 2
Java最新图形化界面开发技术——JavaFx教程(含UI控件用法介绍、属性绑定、事件监听、FXML)
Java重复定义变量详解
这段对话讨论了Java中变量作用域和重复定义的问题。学生提问为何不能重复定义变量导致编译错误,老师通过多个示例解释了编译器如何区分不同作用域内的变量,包括局部变量、成员变量和静态变量,并说明了使用`this`关键字和类名来区分变量的方法。最终,学生理解了编译器在逻辑层面检查变量定义的问题。
Java重复定义变量详解
Java属性为什么不能是is开头的boolean
在Java实体类中,阿里规约要求boolean属性不应以is开头。文章通过实际案例分析了isUpdate字段在JSON序列化过程中变为update的问题,并提供了自定义get方法或使用@JSONField注解两种解决方案,建议遵循规约避免此类问题。
134 0
Java属性为什么不能是is开头的boolean
重新定义 Java 对象相等性
本文探讨了Java中的对象相等性问题,包括自反性、对称性、传递性和一致性等原则,并通过LaptopCharger类的例子展示了引用相等与内容相等的区别。文章还介绍了如何通过重写`equals`方法和使用`Comparator`接口来实现更复杂的相等度量,以满足特定的业务需求。
66 3
|
5月前
|
在Java中定义一个不做事且没有参数的构造方法的作用
Java程序在执行子类的构造方法之前,如果没有用super()来调用父类特定的构造方法,则会调用父类中“没有参数的构造方法”。因此,如果父类中只定义了有参数的构造方法,而在子类的构造方法中又没有用super()来调用父类中特定的构造方法,则编译时将发生错误,因为Java程序在父类中找不到没有参数的构造方法可供执行。解决办法是在父类里加上一个不做事且没有参数的构造方法。
|
6月前
|
让星星⭐月亮告诉你,Java NIO之Buffer详解 属性capacity/position/limit/mark 方法put(X)/get()/flip()/compact()/clear()
这段代码演示了Java NIO中`ByteBuffer`的基本操作,包括分配、写入、翻转、读取、压缩和清空缓冲区。通过示例展示了`position`、`limit`和`mark`属性的变化过程,帮助理解缓冲区的工作原理。
82 2
|
6月前
|
Java集合定义其泛型
Java集合定义其泛型
41 1
下一篇
oss创建bucket
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等