通用编程_通用编程准则

通用编程

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

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

目录

1.简介 2.可变范围 3.类字段和局部变量 4.方法参数和局部变量 5.装箱和拆箱 6.接口 7.琴弦 8.命名约定 9.标准库 10.不变性 11.测试 12.接下来是什么 13.下载源代码

1.简介

在本部分的教程中,我们将继续讨论Java良好编程风格和健壮设计的一般原理。 我们已经在本教程的前面部分中看到了其中一些原则,但是在此过程中将引入许多新的实用建议,以提高您作为Java开发人员的技能。

2.可变范围

在本教程的第3部分“ 如何设计类和接口”中 ,我们讨论了如何将可见性和可访问性应用于类和接口成员,从而限制了它们的范围。 但是,我们尚未讨论在方法实现中使用的局部变量。

在Java语言中,每个局部变量(一旦声明)都有一个作用域。 从声明位置到声明的方法(或代码块)的末尾,该变量变得可见。因此,只有一条规则可遵循:将局部变量声明为靠近其所在的位置尽可能使用。 让我们看一些典型的例子:

for( final Locale locale: Locale.getAvailableLocales() ) {// Some implementation here
}try( final InputStream in = new FileInputStream( "file.txt" ) ) {// Some implementation here
}

在这两个代码段中,局部变量的范围都限于它们在其中声明的执行块。一旦块结束,局部变量将超出范围,并且不再可见。 看起来简洁明了,但是随着Java 8的发布和lambda的引入,使用局部变量的许多众所周知的习惯用法已经过时了。 让我们重写前面示例中的for-each循环,改为使用lambda:

Arrays.stream( Locale.getAvailableLocales() ).forEach( ( locale ) -> {// Some implementation here}
);

局部变量成为函数的参数,该函数本身作为forEach方法的参数传递。

3.类字段和局部变量

Java中的每个方法都属于某个类(如果是Java 8,则将该接口声明为default ,则属于某个接口)。 这样,在方法实现中使用的局部变量与类成员之间就有名称冲突的可能性。 Java编译器可以从范围中选择正确的变量,尽管它可能不是打算使用的开发者。 为了向开发人员提示发生此类冲突(警告,突出显示等)的时间,现代Java IDE进行了大量工作,但在开发时最好考虑一下。 让我们看一下这个例子:

public class LocalVariableAndClassMember {private long value;public long calculateValue( final long initial ) {long value = initial;        value *= 10;value += value;return value;}}

该示例看起来很简单,但是有一个陷阱。 方法calculateValue引入一个具有名称value的局部变量,并以此隐藏具有相同名称的类成员。 第08行应该将类成员和局部变量相加,但是它所做的却大不相同。 正确的版本可能如下所示(使用关键字this ):

public class LocalVariableAndClassMember {private long value;public long calculateValue( final long initial ) {long value = initial;        value *= 10;value += this.value;        return value;}}

天真的实现,但它突出了重要的问题,在某些情况下,调试和故障排除可能需要数小时。

4.方法参数和局部变量

Java开发人员经常遇到的另一个陷阱是使用方法参数作为局部变量。 Java允许使用不同的值重新分配非final方法参数(但是,它对原始值没有任何影响)。 例如:

public String sanitize( String str ) {if( !str.isEmpty() ) {str = str.trim();}str = str.toLowerCase();return str;
}

它不是一段漂亮的代码,但足以说明问题:将方法参数str重新分配给另一个值(基本上用作局部变量)。 在所有情况下(无一例外),可以并且应该避免这种模式(例如,通过将方法参数声明为final )。 例如:

public String sanitize( final String str ) {String sanitized = str;if( !str.isEmpty() ) {sanitized = str.trim();}sanitized = sanitized.toLowerCase();return sanitized;
}

即使遵循引入局部变量的代价,遵循此简单规则的代码也更易于遵循和推理。

5.装箱和拆箱

装箱和拆箱都是Java语言中用于在原始类型(如intlongdouble )之间转换为各自的原始类型包装程序(如IntegerLongDouble )的相同技术的名称。 在本教程的第4部分“ 如何以及何时使用泛型”中 ,我们已经在将原始类型包装器作为泛型类型参数讨论时看到了它的作用。

尽管Java编译器试图通过执行自动装箱来尽力隐藏这些转换,但有时它会使情况变得更糟并导致意外的结果。 让我们看一下这个例子:

public static void calculate( final long value ) {// Some implementation here
}
final Long value = null;calculate( value );

上面的代码段可以很好地编译,但是当Longlong之间的转换发生时,它将在第02行抛出NullPointerException 。 这里的建议是更喜欢使用原始类型(但是,正如我们已经知道的那样,并非总是可能的)。

6.接口

在本教程的第3部分“ 如何设计类和接口”中 ,我们讨论了基于接口和基于契约的开发,并着重强调了这样的事实,即接口应尽可能优先于具体类。 本节的目的是通过展示真实的示例来说服您再有时间首先考虑接口。

接口不依赖于任何特定的实现(默认方法是一个例外)。 它们只是合同,因此在履行合同的方式上提供了很多自由和灵活性。 当实施涉及外部系统或服务时,这种灵活性变得越来越重要。 让我们看一下以下简单接口及其可能的实现:

public interface TimezoneService {TimeZone getTimeZone( final double lat, final double lon ) throws IOException;
}
public class TimezoneServiceImpl implements TimezoneService {@Overridepublic TimeZone getTimeZone(final double lat, final double lon) throws IOException {final URL url = new URL( String.format("http://api.geonames.org/timezone?lat=%.2f&lng=%.2f&username=demo",lat, lon) );final HttpURLConnection connection = ( HttpURLConnection )url.openConnection();connection.setRequestMethod( "GET" );connection.setConnectTimeout( 1000 );connection.setReadTimeout( 1000 );connection.connect();int status = connection.getResponseCode();if (status == 200) {// Do something here}return TimeZone.getDefault();}
}

上面的代码段演示了典型的接口/实现模式。 该实现使用外部HTTP服务( http://api.geonames.org/ )来检索特定位置的时区。 但是,由于联系方式是由界面驱动的,因此很容易引入另一种使用数据库或什至平面文件的实现。 这样,接口可以极大地帮助设计可测试的代码。 例如,在每次测试运行时调用外部服务并不总是可行的,因此提供替代的虚拟实现(也称为存根或模拟)是有意义的:

public class TimezoneServiceTestImpl implements TimezoneService {@Overridepublic TimeZone getTimeZone(final double lat, final double lon) throws IOException {return TimeZone.getDefault();}
}

此实现可在需要TimezoneService接口的每个地方使用,从而将测试方案与对外部组件的依赖隔离开来。

在Java标准集合库中封装了许多适当使用接口的出色示例。 CollectionListSet ,所有这些接口都有几种实现的支持,当倾向于使用合同时,这些实现可以无缝且可互换地替换,例如:

public static< T > void print( final Collection< T > collection ) {for( final T element: collection ) {System.out.println( element );}
}
print( new HashSet< Object >( /* ... */ ) );
print( new ArrayList< Integer >( /* ... */ ) );
print( new TreeSet< String >( /* ... */ ) );
print( new Vector< Long >( /* ... */ ) );

7.琴弦

字符串是Java以及大多数编程语言中使用最广泛的类型之一。 Java语言通过本机支持串联和比较,大大简化了字符串常规操作。 另外,Java标准库提供了许多不同的类来提高字符串操作的效率,这就是我们将在本节中讨论的内容。

在Java中,字符串是不可变的对象,以UTF-16格式表示。 每次连接字符串(或执行任何修改原始字符串的操作)时,都会创建String类的新实例。 因此,连接操作可能会变得非常无效,从而导致创建许多中间字符串实例(通常来说,会生成垃圾)。

但是Java标准库提供了两个非常有用的类,旨在促进字符串操作: StringBuilderStringBuffer (它们之间的唯一区别是StringBuffer是线程安全的,而StringBuilder不是)。 让我们看看使用这些类之一的几个示例:

final StringBuilder sb = new StringBuilder();for( int i = 1; i <= 10; ++i ) {sb.append( " " );sb.append( i );
}sb.deleteCharAt( 0 );
sb.insert( 0, "[" );
sb.replace( sb.length() - 3, sb.length(), "]" );

尽管建议使用StringBuilder / StringBuffer来操作字符串,但是在连接两个或三个字符串的简单情况下,它看起来可能会过分杀伤,因此可以使用常规+运算符代替,例如:

String userId = "user:" + new Random().nextInt( 100 );

通常,直接连接的更好替代方法是使用字符串格式设置,并且Java标准库也通过提供静态帮助器方法String.format来提供帮助。 它支持一组丰富的格式说明符,包括数字,字符,日期/时间等(有关完整参考,请访问官方文档 )。 让我们通过示例来探索格式化的力量:

String.format( "%04d", 1 );                      -> 0001
String.format( "%.2f", 12.324234d );             -> 12.32
String.format( "%tR", new Date() );              -> 21:11
String.format( "%tF", new Date() );              -> 2014-11-11
String.format( "%d%%", 12 );                     -> 12%

String.format方法提供了一种干净方便的方法来从不同的数据类型构造字符串。 值得一提的是,某些现代Java IDE能够根据传递给String.format方法的参数来分析格式规范,并在检测到任何不匹配时向开发人员发出警告。

8.命名约定

Java作为一种语言并不能强迫开发人员严格遵守任何命名约定,但是社区已经开发了一套易于遵循的规则,这些规则使Java代码在标准库和其他任何Java项目中看起来都统一。

  • 软件包名称小写形式输入: org.junitcom.fasterxml.jacksonjavax.json
  • 类,枚举,接口注释名称大写字母键入: StringBuilderRunnable@Override
  • 方法字段名static final除外)以驼峰形式输入: isEmptyformataddAll
  • 静态最终字段枚举常量名称大写形式键入, 并用下划线 “ _” MIN_RADIXLOGMIN_RADIXINSTANCE
  • 局部变量方法参数名称驼峰式键入: strnewLengthminimumCapacity
  • 泛型类型参数名称通常以大写形式表示为一个字符TUE

通过遵循这些简单的约定,您正在编写的代码将与其他任何库或框架看起来简洁明了,并没有区别,给人的印象是它是由同一人创作的(约定真正起作用的罕见情况之一)。

9.标准库

无论您从事哪种Java项目,Java标准库都是您最好的朋友。 是的,很难不同意它们有一些粗糙的边缘和奇怪的设计决策,但是在99%的情况下,这是专家编写的高质量代码。 值得学习。

每个Java版本都为现有库带来了许多新功能(可能会淘汰旧功能),并增加了许多新库。 Java 5带来了在java.util.concurrent包下协调的新并发库。 Java 6提供了(很少为人所知)脚本支持( javax.script包)和Java编译器API(在javax.tools包下)。 Java 7对java.util.concurrent了很多改进,在java.nio.file包下引入了新的I / O库,并通过java.lang.invoke包支持了动态语言。 最后,Java 8提供了一个期待已久的日期/时间API,该API在java.time程序包下java.time

Java作为平台正在不断发展,紧跟这一发展非常重要。 每当您打算将第三方库或框架引入您的项目时,请确保Java标准库中没有所需的功能(实际上,有许多专门的高性能算法实现要优于标准库中的实现)但在大多数情况下,您实际上并不需要它们)。

10.不变性

不变性遍及本教程,在这一部分中,它提醒您:请认真对待不变性。 如果您正在设计的类或正在实现的方法可以提供不变性保证,那么它几乎可以在任何地方使用,而不必担心会被并发修改。 这将使您作为开发人员的生活更加轻松(并希望您的队友的生活也更加轻松)。

11.测试

测试驱动开发(TDD)实践在Java社区中非常流行,这提高了所编写代码的质量标准。 与所有这些考虑TDD在桌子上带来的好处,这是可悲的,观察到Java标准库不包含任何测试框架或棚架今天的。

尽管如此,测试已成为现代Java开发中必不可少的部分,在本节中,我们将介绍使用出色的JUnit框架的一些基础知识。 本质上,在JUnit中 ,每个测试都是关于预期对象状态或行为的一组断言。

编写出色的测试的秘诀在于使它们简短而简单,一次只测试一件事。 作为练习,让我们编写一组测试来验证“ 字符串 ”部分中的String.format函数是否返回了所需的结果。

package com.javacodegeeks.advanced.generic;import static org.junit.Assert.assertThat;
import static org.hamcrest.CoreMatchers.equalTo;import org.junit.Test;public class StringFormatTestCase {@Testpublic void testNumberFormattingWithLeadingZeros() {final String formatted = String.format( "%04d", 1 );assertThat( formatted, equalTo( "0001" ) );}@Testpublic void testDoubleFormattingWithTwoDecimalPoints() {final String formatted = String.format( "%.2f", 12.324234d );assertThat( formatted, equalTo( "12.32" ) );}
}

测试看起来非常可读,其执行是实例化。 如今,普通的Java项目包含数百个测试用例,为开发人员提供了有关正在开发的回归或功能的快速反馈。

12.接下来是什么

本教程的这一部分完成了与Java编程实践和指南相关的一系列讨论。 在下一部分中,我们将通过探索Java异常的世界,它们的类型,使用方式和使用时间来返回到语言功能。

13.下载源代码

这是关于“通用编程准则”的课程,是“高级Java”课程的课程。 您可以在此处下载源代码: AdvancedJavaPart7

翻译自: https://www.javacodegeeks.com/2015/09/general-programming-guidelines.html

通用编程

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

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

相关文章

python发送邮件outlook_通过Python发送Outlook电子邮件?

I am using Outlook 2003. What is the best way to send email (through Outlook 2003) using Python? 解决方案 For a solution that uses outlook see TheoretiCALs answer below. Otherwise, use the smtplib that comes with python. Note that this will require your e…

Python3实现快速排序 通俗易懂

Python3实现快速排序快速排序 是一种交换排序&#xff0c;属于分治算法。 思路&#xff1a; 对于要排序的元素集合&#xff0c;指定集合的第一个元素为基准点&#xff0c;通过一系列的扫描和交换(见下面的代码)&#xff0c;让基准点左边的元素比基准点小&#xff0c;让基准点右…

C++队列queue用法详解(超详细)

点击蓝字关注我们一、定义queue是一种容器转换器模板&#xff0c;调用#include< queue>即可使用队列类。二、queue初始化queue<Type, Container> (<数据类型&#xff0c;容器类型>&#xff09;初始化时必须要有数据类型&#xff0c;容器可省略&#xff0c;省…

redis nosql_Redis教程:NoSQL键值存储

redis nosql课程大纲 Redis是使用ANSI C编写的&#xff0c;具有可选持久性的开源&#xff0c;网络化&#xff0c;内存中键值数据存储。根据DB-Engines.com的月度排名&#xff0c;Redis是最受欢迎的键值存储。 其名称意为“远程词典服务器”。 Redis是BSD许可的&#xff0c;并且…

python随机抽取人名_python实现艾宾浩斯背单词功能,实现自动提取单词、邮件发送,再也不用担心背单词啦...

&#xfeff;已经完成了利用python爬虫实现定时QQ邮箱推送英文文章&#xff0c;辅助学习英语的项目&#xff0c;索性就一口气利用python多做一些自动化辅助英语学习的项目&#xff0c;对自己的编程能力和英文水评也有一定的帮助&#xff0c;于是在两天的努力下&#xff0c;我完…

用不到125行C语言代码就可以编写一个简单的16位虚拟机?

点击蓝字关注我们一位国外的软件工程师分享了这么一篇博文&#xff1a;Writing a simple 16 bit VM in less than 125 lines of C&#xff08;用不到 125 行 C 语言编写一个简单的 16 位虚拟机&#xff09;。博文地址&#xff1a;https://www.andreinc.net/2021/12/01/writing-…

用一个程序生成另一个程序_还有另一个报告生成器?

用一个程序生成另一个程序如果您具有业务应用程序开发的经验&#xff0c;那么很可能会遇到要求该应用程序具有灵活的报告机制的需求。 我工作的公司主要专注于开发业务解决方案&#xff0c;而报告是必不可少的&#xff0c;实际上&#xff0c;它必须包含我们开发的所有企业系统的…

CocosCreator1.x实现水流动的效果

CocosCreator1.x实现水流动的效果Cocos Creator版本&#xff1a;1.10.2 运行结果&#xff1a;(H5和原生都支持) 场景: 脚本&#xff1a; HelloWorld.js&#xff1a; let shader require(shader);cc.Class({extends: cc.Component,properties: {water: cc.Node,waterNorm…

python爬虫xpath教程_使用 Xpath 进行爬虫开发

使用 Xpath 进行爬虫开发 Xpath( XML Path Language, XML路径语言)&#xff0c;是一种在 XML 数据中查找信息的语言&#xff0c;现在&#xff0c;我们也可以使用它在 HTML 中查找需要的信息。 既然谈到 Xpath 是一门语言&#xff0c;当然它就会有自己的一些特定的语法。我们这里…

java使用泛型后消除泛型_如何以及何时使用泛型

java使用泛型后消除泛型本文是我们名为“ 高级Java ”的学院课程的一部分。 本课程旨在帮助您最有效地使用Java。 它讨论了高级主题&#xff0c;包括对象创建&#xff0c;并发&#xff0c;序列化&#xff0c;反射等。 它将指导您完成Java掌握的旅程&#xff01; 在这里查看 &a…

用C语言写烟花,给心中的那个人看!

点击蓝字关注我们前言程序员不懂浪漫? 大错特错&#xff01;今天就让你们看看什么是程序员的浪漫&#xff01;你向窗外看烟花&#xff0c;我在窗边看你&#xff0c;这时&#xff0c;你比烟花好看的多&#xff0c;你的眼眸倒映满天的烟火&#xff0c;我的瞳孔倒映你温柔的脸庞…

js实现阶乘算法的三种方法

js实现阶乘算法的三种方法// 非递归写法 function f(n) {if (0 n) {return 1;}let res 1;for (let i 1; i < n; i) {res * i;}return res; }// 递归写法 function g(n) {if (0 n) {return 1;}return n*g(n-1); }// 动态规划写法 let dp []; function h(n) {if (n < …

手把手教你做一个线程池--C语言版

点击蓝字关注我们1、线程池原理我们使用线程的时候就去创建一个线程&#xff0c;这样实现起来非常简便&#xff0c;但是就会有一个问题&#xff1a;如果并发的线程数量很多&#xff0c;并且每个线程都是执行一个时间很短的任务就结束了&#xff0c;这样频繁创建线程就会大大降低…

oracle 48小时内_缺血性脑梗死后48小时内使用阿替普酶能够降低脑损伤程度

一项刊登在影响因子7.6杂志Neurology上题为“Effect of IV alteplase on the ischemic brain lesion at 24–48 hours after ischemic stroke”的研究报告中&#xff0c;来自爱丁堡大学的科学家们发现&#xff0c;alteplase与病变可视性的短期进展降低相关。在荟萃分析中&#…

基于按位与的 就散策略_比较散列策略

基于按位与的 就散策略总览 编年史有很多用于哈希的实现&#xff0c;包括City和Murmur。 它也有自己的香草哈希&#xff0c;但是如何测试呢&#xff1f; 什么是香草哈希&#xff1f; Vanilla Hash设计得尽可能简单&#xff0c;并且针对Orthogonal Bits测试进行了优化&#xff…

js实现数组降维算法[不准用Array.prototype.flat的api]

js实现数组降维算法[不准用Array.prototype.flat的api]// target要降维的元素&#xff0c;n降维阶数&#xff0c;newArr存储结果的新数组 function f(target, n 1, newArr []) {if (n < 0) {newArr.push(target);return;}if (!Array.isArray(target)) {newArr.push(target…

python编程能力等级测试_青少年编程能力等级测评-Python编程二级试卷

青少年编程能力等级测评 Python 编程二级试卷 一、单项选择题&#xff08;共 20 题&#xff0c;每题分&#xff0c;共 50 分&#xff09; 1. 运行下方代码段&#xff0c;输出的结果是&#xff08; &#xff09; 。 a() print(type(a)) A &#xff0e;&#xff1b; B &#xff0…

MySQL夺命16问,你能坚持到第几问?

点击蓝字关注我们1、数据库三大范式是什么&#xff1f;第一范式&#xff1a;每个列都不可以再拆分。第二范式&#xff1a;在第一范式的基础上&#xff0c;非主键列完全依赖于主键&#xff0c;而不能是依赖于主键的一部分。第三范式&#xff1a;在第二范式的基础上&#xff0c;非…

美图手机投射功能在哪_在Java 8中进行投射(还有其他功能?)

美图手机投射功能在哪将实例转换为设计不良的类型。 尽管如此&#xff0c;在某些情况下没有其他选择。 从第一天开始&#xff0c;执行此功能就已成为Java的一部分。 我认为Java 8提出了对这种古老技术稍加改进的需求。 静态铸造 Java中最常见的转换方法如下&#xff1a; 静态…

js箭头函数和普通函数区别

js箭头函数和普通函数区别实验环境&#xff1a;nodejs v12.16.1 箭头函数不能作为构造函数&#xff0c;而普通函数可以 箭头函数没有原型&#xff0c;而普通函数有 箭头函数return可以省略语句块。(如果>右边不是语句块&#xff0c;则代表return右边的表达式或对象) 箭…