通用编程准则

本文是我们名为“ 高级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部分“ 如何以及何时使用Generics”中 ,我们已经在讨论原始类型包装器作为泛型类型参数时看到了它的实际作用。

尽管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知道)脚本支持( 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/358214.shtml

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

相关文章

工厂模式与抽象工厂模式

1、工厂模式&#xff1a;定义了一组创建对象的接口&#xff0c;但是由子类决定要实例化的类是哪一个。工厂方法把类的实例化推迟到子类中。 现在考虑有PizzaStore有一些子类&#xff08;加盟店&#xff09;&#xff0c;他们有可能对pizza的做法进行一些改良&#xff0c;即creat…

数据类型转换为false的有哪些?

看图&#xff0c;看选项你就明白啦

CS通过(CDN+证书)powershell上线详细版

0x00 简介 这个应该叫做域前置技术&#xff1a; 大致图示&#xff1a; 攻击流量通过CDN节点将流量转发到真实的C2服务器CDN节点ip通过识别请求的Host头进行流量转可以有效的躲避一些安全设备&#xff0c;也有这一定的反溯源功能&#xff0c;因为流量都去了CDN上 之前看了一些…

SGU185 Two shortest(最小费用最大流/最大流)

题目求一张图两条边不重复的最短路。 一开始我用费用流做。 源点到1连容量2费用0的边&#xff1b;所有边&#xff0c;连u到v和v到u容量1费用cost的边。 总共最多会增广两次&#xff0c;比较两次求得的费用&#xff0c;然后输出路径。 然而死MLE不过。。 看了题解&#xff0c;是…

那些操作会引起回流(reflow)?

reflow(回流)是指浏览器为了重新渲染部分或者全部的文档,重新计算文档中的元素的位置和几何构造的过程。 因为回流可能导致整个Dom树的重新构造,所以是性能的一大杀手。 以下操作会引起回流&#xff1a; ① 改变窗口大小 ② font-size大小改变 ③ 增加或者移除样式表 …

红蓝对抗之流量加密(Openssl加密传输、MSF流量加密、CS修改profile进行流量加密)

本篇文章转载于:https://blog.csdn.net/q20010619/article/details/122006433?utm_mediumdistribute.pc_relevant.none-task-blog-2defaultbaidujs_utm_term~default-1-122006433-blog-122810834.pc_relevant_default&spm1001.2101.3001.4242.2&utm_relevant_index4 文…

有var d = new Date(‘20xx-m-09‘),可以设置为m+1月份的操作是?

setMonth(n)&#xff0c;这里是0-11分别对应1-12月 d.setDate(n); n表示一个月中的一天的一个数值&#xff08;1 ~ 31&#xff09;: 0 为上一个月的最后一天 -1 为上一个月最后一天之前的一天 如果当月有 31 天: 32 为下个月的第一天 如果当月有 30 天: 32 为下一个月的第二…

如何以及何时使用例外

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

域前置 配置cdn-解决HTTPS-SSL通讯被朔源IP封锁问题

域前置 配置cdn URL -解决HTTPS-SSL通讯被朔源IP封锁问题 https://mp.weixin.qq.com/s/MghFgegdp3l3tFE3hOvcYw

深入理解Java引用类型

深入理解Java引用类型 在Java中类型可分为两大类&#xff1a;值类型与引用类型。值类型就是基本数据类型&#xff08;如int ,double 等&#xff09;&#xff0c;而引用类型,是指除了基本的变量类型之外的所有类型&#xff08;如通过 class 定义的类型&#xff09;。所有的类型在…

值得一谈的鸿蒙2.0,程序员们拿起你们手中的编译器撸一下hello world

一款“面向未来”、面向全场景(移动办公、运动健康、社交通信、媒体娱乐等)的分布式操作系统 。现已开源,名为OpenHarmony。 2019年8月9日,华为在HDC开发者大会上正式发布鸿蒙系统。 2020年9月10日,华为在HDC开发者大会上如约发布鸿蒙 2.0,并面向应用开发者发布Beta版本…

GitHack使用方法

ctf小白刚下载这个&#xff0c;不知道怎么用。现在会用啦就记录一下~ 下载地址&#xff1a;https://github.com/lijiejie/GitHack 下载下来后&#xff0c;通过cmd使用&#xff0c;如下图所示&#xff1a; 总结&#xff08;命令&#xff09;&#xff1a; GitHack.py http://XXXX…

工厂方法模式和抽象工厂模式

有一个抽象的产品类 Product 具体的产品类 Product1 Product2.。。。 工厂方法&#xff1a; 1、创建抽象的工厂类&#xff1a;Creator&#xff1a; public abstract class Creator{ public abstract <T extends Product> T createProduct(Class<T> c); } 2、具体实…

值得一谈的鸿蒙2.0,赶紧撸一下hello world

一款“面向未来”、面向全场景&#xff08;移动办公、运动健康、社交通信、媒体娱乐等&#xff09;的分布式操作系统 。现已开源&#xff0c;名为OpenHarmony。2019年8月9日&#xff0c;华为在HDC开发者大会上正式发布鸿蒙系统。2020年9月10日&#xff0c;华为在HDC开发者大会上…

key 和secret_Java Secret:加载和卸载静态字段

key 和secret总览 首先&#xff0c;很自然地假设静态字段具有特殊的生命周期&#xff0c;并且在应用程序的生命周期中一直存在。 您可以假设它们存在于内存中的特殊位置&#xff0c;例如C或类元信息的perm gen中的内存开始。 但是&#xff0c;得知静态字段驻留在堆上&#xff…

【Jenkins】未授权访问漏洞

一、漏洞介绍 1 ) Jenkins介绍 Jenkins是一个基于Java开发的开源项目&#xff0c;可在Tomcat等流行的servlet容器中运行&#xff0c;也可以独立运行&#xff0c;其功能如下&#xff1a; 用于持续性、自动的构建/测试软件项目监控或跑一些定时任务监控外部调用执行的工作 2 …

java split函数应该注意的问题

split函数的参数是一个String&#xff0c;但是这个String会被解释成一个正则表达式. 比如 "test.txt".split(".").length得到的值是0&#xff0c;因为在正则表达式中点号会被当作一个通配符, 所以得不到结果&#xff0c; 正确的写法应该是 "test.txt&…

解决谷歌浏览器F12打不开调试页面的问题。

最近应用着急上线&#xff0c;需要批量测试&#xff0c;发现F12不好使啦。 解决办法&#xff1a; 1.找到谷歌浏览器&#xff1a;设置-扩展程序-开发者模式。关闭。 2.刷新浏览器或者重启浏览器后&#xff0c;打开需要调试的页面&#xff0c;鼠标点一下地址栏&#xff0c;按F1…

还有另一个报告生成器?

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

ARL资产侦察灯塔系统搭建及使用

ARL资产侦察灯塔系统搭建及使用 ARL&#xff08;Asset Reconnaissance Lighthouse&#xff09;资产侦查灯塔旨在快速发现并整理企业外网资产并为资产构建基础数据库&#xff0c;无需登录凭证或特殊访问即可主动发现并识别资产&#xff0c;让甲方安全团队或者渗透测试人员快速寻…