代码规范(二)——java篇

51.

try {

newMAC = PosCryptor.getMAC(PosServerConfig.getKeyHost(),PosServerConfig.getKeyPost(),

entNo, posNo, operNo,requestMsgTypeId, newBody);

LOGGER.debug("newMAC:"+LoUtils.byte2HexStr(newMAC));

}catch (UnknownHostException e){

throw new PosRunTimeException("keyserver 连接异常!",e);

}catch (IOException e){

throw new PosRunTimeException("keyserver io异常!",e);

}catch (Exception e){

throw new PosRunTimeException("keyserver格式解析异常!",e);

}

建议:参考第19节。

建议:org.apache.commons.codec.binary.Hex

52.

@Service

public class XxxServiceImpl implements XxxService {

private static Object invokeGet(Object o, String fieldName) throws Exception {

String[] fieldNames = fieldName.split("[.]");

for (String fn : fieldNames) {

o = o.getClass().getMethod(fn).invoke(o, new Object[0]);

}

return o;

}

建议:通用的static函数,尽量放在Util里,尽量减少XxService的长度。

建议:分隔string,不应该用“.”,因为它在regex里有特殊意义。应该是逗号“,”。

org.springframework.beans.propertyeditors.StringArrayPropertyEditor.

DEFAULT_SEPARATOR就是“,”

建议:这个函数的本身意义,就有很大毛病:

(A)变量o被赋值多次,却被当成结果只返回一次。

(B)变量o既作为输入参数,又作为输出参数。

(C)变量o前后的意义都不同。

输入时的意义是:“某个普通对象”,将要被调用。

输出时的意义是:“某个普通对象”调用某method的结果。

53. 有人说:持续集成,只是编译打包就行了,不需要跑自动化测试。

建议:参考第38节。

55.

建议:在循环里搞删除,影响性能,应该使用sql批量删除,输入的参数为:”1,2,3”

建议:return行,返回:1或0,这是C风格,不是Java风格。

Java风格是:使用Exception:public void myDeleteFunction(String str) throws Exception

建议:扔掉try-catch。参考第19节。

建议:经过了以上3个和第19节的建议,这个函数只需要1行就行了:

msgPrivateMapper.deleteXxx (ids);

56.

建议:所有的能跑的东东,都应该写在单元测试(junit)里,而不是main里。

建议:有大量的注释(//或/* … */),就是错误。

要么删除;要么解开,改变成为一个个的单独的junit test method。

那么,在持续构建里,就能总是能把那些功能跑到,总是能自我检查尽早检查。

建议:main()最后1行,不需要显式调用System.exit(0)。

58.

建议:把重用多次的’N’、’Y’做成常量。

59.

建议:del、add、update,都是相同性质的事务,Spring默认的行为就够用了。

所以,rollback-for和no-rollback-for这2项,可以删除。

60. 有人说:代码写到一坨了,没法搞单独的TestCase。

那就说明:这个程序比较差,是紧耦合的。好的是:松散耦合。所以,要重构了。

解耦的最好方式是:接口+组合。例如:

public class MyBusiness {

private  MyBusniess00  bn00;

private  MyBusniess01  bn01;

private  MyBusniess02  bn02;

……

//  setter & getter

}

这也是Spring最常用的套路。

附带提个常识:

如果某程序有好的单元测试和覆盖率,那就肯定说明:它的结构是好的,松散耦合的。

61. 如何对Java程序做格式化?

建议:这是个来自于Spring源码里的文件,用Eclipse导入:

Preferences—>Java—>Code Style—>Formatter-->Import

62. 某项目有600个class,修改了2个,影响了些啥?

如果有完善的单元测试,跑跑,很快就知道影响了些啥,就能快速修改,极大的增强信心。

所以,如果你按照XP/TDD的核心思想(单元测试)做程序,将会越做越顺!

63. 源码里有192.168.1.xxx,如何评价?

如果这个IP是自己的,下班就关机了……

(A)那么,夜间Jenkins服务器跑CI(持续构建)/每日构建(Daily Build),肯定失败。

(B)别人从SVN拉下来项目,跑,也肯定失败。

所以,这个IP绝不应该是自己的。

65. 如何查找性能瓶颈?

DeveloperA负责项目P,最核心的功能B,性能总数字是100个/12秒。

里面有这些东东:通讯协议解析C,业务D,业务E,业务F

请他把这4项的耗时的具体数字和TestCase拿出来,他没有。

请他把业务全部扔掉,只把C的耗时的具体数字和TestCase拿出来,他没有。

建议:较好的查找性能瓶颈的路线:

(A)设定个固定次数,例如:600,还可以设置大些,后面会多次用到这个。

设定基线,即:assert(…); 每次跑完,必须要能看见绿条(Green Bar)作为结果。

(B)单独把核心的功能B跑600次。

(C)单独把C/D/E/F分别跑600次。

(D)看看C/D/E/F里,谁耗时最多,就最先优化谁。每修改一次,就重新跑一趟。

搞好了这项,往往达到70%的效果。

(E)再优化耗时第2多的。搞好了这项,又增加了20%的效果。

优化某项,里面又有多个组件R/S/T,如何分辨性能瓶颈?

建议:重复之前的路线,针对R/S/T都建立TestCase……各个击破。

67. 跟性能最密切的程序段,可能是些啥?

WebApp里的Filter、Inteceptor,普通App的协议的解析和反解析……

是每次必须经过的路线。

针对这些,必须有专门的TestCase,反复跑,反复优化。

69.

建议:HashMap是有关算法的,肯定要内建hash表和计算hash。

如果key是纯粹的数字,而且很小,那就直接做成数组/List,数组的index就是key。

数组是直接存取,不需要计算hash,更快。

建议:既然用了Spring,就有了BeanFactory,那就用它建立Singleton,不要自己手写getInstance()了。例如:

<bean id="dataSources" class="org.springframework.beans.factory.config.ListFactoryBean">

<property name="targetListClass">

<value>java.util.ArrayList</value>

</property>

<property name="sourceList">

<list>

<ref bean="dataSource_0" />

<ref bean="dataSource_1" />

有人说:“做到spring里面去,效果是一样,还是这样好使用”

建议:列几项软件原则:

(A)“不做code,少做code。”  “代码行数越少越好。”

(B)朝着稳定的方向依赖。

完成同一个功能,是用Spring框架搞得稳定呢?还是用自己写的code搞得稳定呢?

70.

Map<Integer,String> messageBody = new LinkedHashMap<Integer,String>();

messageBody.put(3,termTradeCode);

messageBody.put(42,entNoAsc);

messageBody.put(62, "0012"+versionsAsc);

建议:

LinkedHashMap有复杂的算法,尽量回避。

给Map显式的分配空间,尽量大些,这里分配256就足够了。

List<Pair<K, V>>,只是简单的顺序的存取,很实用。参考第29节和上节。

或者,用数组,更快。

71. 有人说:我做的程序,无法测试性能。还有人说:我做的这个框架,必须带些业务才能测试性能,无法单独测试纯粹的性能。

建议:

(A) 所有程序都能测试出来性能。如果无法测试出来,或者测试出的数字差,说明:

这个程序或者测试程序有毛病,需要改善。

(B) 业务是业务,框架是框架,它们之间应该是弱耦合的,针对接口编程的,能替换其它实现类的。它们都是能单独测试的,还能用上针对接口的Mock的东东。

72.

这个极品工程里,只有几个class,每个class还占了一个package。

建议:

(A) 不要瞎成立maven工程。

对于不确定是否需要独立的工程,先都放在一个里,便于修改。

这个cache-api工程,可以合并到common工程里。如果code太烂,干脆就废除掉。

(B) 静态方法调用要回避,应该做成实例形式,便于搞Object的模拟和测试。

75.

建议:

(A)权威的证明资料:《J2EE Development without EJB》

我们自建的MyException,都应该extends RuntimeException,而非checked Exception。

(B)重复的、临时的、不伦不类的、命名意义模糊的、小范围使用的class,赶快删除吧。

76.

建议:PowerDesigner等工具生成的code,也要用眼睛看3遍。明显臃肿的,要删除。

77.

建议:

(A)用void代替boolean。如果出毛病了失败了,就抛出RuntimeException。

(B)从try开始的几行,只保留Ecms所在的一行就够了。

(C)带有@Test 的method,都应该声明throws Exception,为了里面尽量少写try-catch。

78.

建议:

(A) else-if,要有else { throw new RuntimeException(…) }

(B) Java7,switch( string )

79.

建议:用标准的JavaDoc方式,这还能避免某些源码覆盖率工具的解析错误。

80.

后面request在cache里的值,精确的覆盖了前面request在cache里的值。

81.

建议1:request应该是个傻傻的的简单的数据容器,里面怎么有个ServerMainHandler?

ServerMainHandler调用request,是正常的,还能反过来调用,就是错误。

建议2:handlerExecute(...)明显是搞业务的,里面怎么会放个Spring单态类?

应该通过set/get的方式注入Spring单态类才对,不用每次在参数里传入。

82.

aWeb项目的有多个spring的xml配置,可能跟b.jar、c.jar里的发生重名冲突。

如今,公司测试环境OS上多个app,配置都统一在一个目录里,配置文件更加容易冲突。

建议:配置文件应该统一命名为projectName_config.xml,projectName_config.properties

不应该有applicationContext*.xml这种文件名字了。

83.

建议:把package级的javadoc写在.java里,jdk和开源软件都走这种标准路线了。

84.

建议:继承,只是OO的特性,跟常量(Constants)没有关系。

应该杜绝常量类的继承。还应该杜绝常量接口。

85.

建议:

org.apache.commons.lang3.StringUtils

public static boolean isNotBlank(CharSequence cs)

public static boolean isNotEmpty(CharSequence cs)

86.

建议:common有太多的小模块了,全部加起来也只430KB。

这会浪费dev很多的编译和查错时间。

应该合并为1个模块。

87.

建议:

(A)最明显的是57行—65行,连续出现3次相同的code。应该重新整理if-else。

(B)36行,看看spring源码,initFilterBean()里面是空的。说明:此类不需要spring。

原则:性能要紧之处(例如:Filter),继承层级越少越好,设置为final也好。

所以,这里可以扔掉spring的父类,实现最基本的Filter接口就行了。

(C)42行,用str.contains(…)

(D)多个if-else,就必须要有足够的自动化TC覆盖到每个分支,否则,会出多个bug。

(E)Filter的参数,也应该来自于spring工厂。参考第96节。

88.

疯狂的if-else,这来自于Apache Continuum,是个反OO的典型范例。

前人蹉跌,后人知警。

89.

建议 1:第1行,返回的数据容器,应该是个普通的List/Map,怎么是JSONArray?

List/Map是性能最高的,JSONArray性能肯定偏低。

JSONArray是个偏重的容器,包含了List,还有些跟数据容器没有关系的东东。

对于网络传输和解析,这些都是缺陷。

建议 2:map的value,被前面的for循环多次赋值,实际却只用了最后的一次赋值!!!

而且,Map的Key顺序是不确定的,最后得到的value,也是不确定的!!!

建议 3:高亮的行,JSONArray.parseArray(…);

参数里应该是普通String才对,实际却是jsonArray.toString()!!!

倒腾几番,参数jsonArray完全没有排上用途,结果还是个String。

那干脆做成Map<String, Integer>就更简单了。

建议 4:网络传输,格式可能是json/xml/自定义string/binary/……。

在发送端和接收端,解析出来的都应该是基本的Pojo/Model。

如果用Pojo/Model,修改了传输信息格式,jsonàbinary,不会修改传输类(DTO)。

它转换到各种格式,是最容易的,也是最开放的。

如果用JSONArray,jsonàbinary,将会修改大量的传输类(DTO)。

建议 5:原则:朝着稳定的方向依赖。

是List/Map稳定呢?还是开源的(可能近几年没人管理或移交给他人的)fastjson稳定呢?

建议 6:避免用Map,避免hash计算,多用List。

就算用Map是对的,Map的key也应该是Integer、String、Class等,绝不是JSONArray。

建议 7:从原意看来,Map里只有一个元素(Entry),key是list,value是int。

既然信息只有两样,不用Map,恰好可以用apache-commons Pair<List, Integer>。

建议 8:从原意看来,如果要携带更多信息,可以自定义class来搞。

建议 9:综上所述,Server端Client端都做得很奇葩。

以后dev再遇到这种code,应该立即提出来。

越早暴露就越好,bug越少,性能越高,今后修改越少,越节省公司的成本。

建议 10:

在内网里,ServerA从ServerB取得比较稳定的数据,可以把吞吐量搞大些。例如:

多数情况,总共都只有几百条,那就每次取2000条数据缓存于ServerA的session里。

避免每次取10条取多次而增加request次数。

90.

建议 1:成员变量Random被多线程访问,会出错。

建议 2:验证码,应该避免长得像的字符:

(A)数字1、L的小写和i的大写。

(B)数字0和字母o大写。

(C)数字2和字母z。

建议 3:成员变量都应该显式声明为final,扔掉private。

91.

建议:这种情况,“可读性”比“性能和硬编码”更重要。

String.format(“12%s45%s”, 3, 6);

92.

建议:常用的英文词汇,搞简写就行了。

transactionàtx

account-->acc

currency-->ccy

level-->lv

message-->msg

amount-->amt

commandàcmd

buffer-->buf

receive-->rcv

service-->svc

93. 跑·不跑

今天是2014/12/31,这几个的最后更新日期,竟然是146天之前!!!

建议:测试类,肯定是经常增加内容的。例如:

(A)测试组报了bug,dev就应该增加几个TC(TestCase)了。

(B)有了新需求,应该增加几个TC了。

(C)程序的某段,也许有更好的更快的做法,要做个试验吗?增加TC了。

(D)dev估计到某块可能出毛病,那么今后肯定会出毛病。增加TC了。

(E)需要几个性能测试?增加TC了。

程序,就是用来跑的,必须经常的跑,才能检查出健康状况。

持续构建,把工程里几万行code跑几遍几十遍,就是经常的“跑”。

本图,就是典型的“不跑”。

94. 外部配置·main程序(jar)

在spring xml里配置相对的.properties文件路径:

<context:property-placeholder location="file:../../hsserv/config/pos.properties" />

这样做的好处是:

(A)只要把同一套配置编辑好了,今后发布N次jar,都不需要修改配置了,

减少了修改次数,这条会让测试人员和部署人员轻松很多。

(B)配置文件放在专门的目录里,设置权限,

只让有权限的人员操作,禁止他人的读写,安全。

(C)在一个OS实例里,appA、appB、appC能共用相同的配置。

(D)相对路径总是优于绝对路径。

(E)不需要设置OS级别的系统变量classpath,不需要设置tomcat的classpath。

打包的指令:mvn -DskipTests install -P deploy

在point1-0.0.1-SNAPSHOT.jar/META-INF/ MANIFEST.MF里,有两个attribute:

Class-Path: . config/ ../

Main-Class: com.gy.pos.Main

这表明:,classpath包括了:

当前路径,当前的config里的路径,上一级路径。

有些.xml要单独的拷贝出来,避免出现重复的配置,这用到了maven-resources-plugin。

它们必须在classpath里,而且必须在jar外面,而且还能被别的app共用。

完整的配置:

运行程序的指令:java -jar point1-0.0.1-SNAPSHOT.jar

path就是jar所在的目录

如果.xml还要被webapp(.war)共用,这么设置:

<Class-Path>. /opt/xml_conf/ /opt/lib/a.jar</Class-Path>

注意:它们之间的分隔符是空格,不是“;”或“:”

ecms_config.xml等放在/opt/xml_conf/里,就能被找到了。

95. 外部配置·web程序(war)

如果maven web项目的某子模块用了这个配置,在pom.xml里屏蔽掉。

在打war包的时候,子模块的jar就 不会 有这个文件了。

然后,在dist或aggregator子模块里修改spring配置:

<context:property-placeholder location="file:../../hsserv/config/posweb_config.properties" />

或者:

<context:property-placeholder location="classpath:posweb_config.properties" />

这是最好的方案,保持spring的xml配置在多种环境dev/test/uat/prod都保持一致。

当然,需要先在OS级别设置系统变量:

CLASSPATH=.:/opt/xml_conf:../config
export CLASSPATH

注意:它们之间的分隔符是“:”,不是空格。

注意:这是全局性的,如果OS跑几个app,它们的类路径有同名的配置文件,可能会冲突。

最典型的例子:common-log-0.0.1.jar里有log4j.xml,会覆盖pos-server.jar的log4j.xml。

所以,pom.xml的打包功能,要排除掉这些文件。另外,也只能用人工来提防这个了。

有些是无法用classpath的,例如:log4j.xml里设置log文件,那就只能用文件路径了:

<param name="File" value="/opt/hsserv/log/myProj/debug.log" />

事实上,这个值跟classpath也是完全没有关系的。

96. 外部配置·把spring的配置值传递给Filter

首先,确认几样东东的执行顺序:

(1)ContextListener(标准ServletContextListener、Spring ContextLoaderListener)

(2)Filter

(3)Servlet

所以,在Filter之前,Spring的ctx就已经组装好。

Filter做init()的时候,就能拿到Spring的配置值(myProperties)了。

<util:properties id="my_config" location="classpath:my_config.properties" />

以上2行都是spring的标准api。

通用的Util.getConfigBean()还能在 任何时候任何地点 使用,这就足够灵活了。

97. 外部配置·一种配置有3套配置值

怎么会有3套配置值?后面还能覆盖前面的值?像“传染病”有了传递关系?

对于长期的维护,保持唯一性,很重要。

对于长期的维护,配置文件,越少越好。

再例如:

*-config.properties里有个开关runMock=on

*-config-test.properties里没有设置这个开关

*-config-prod.properties里没有(忘记了)设置这个开关

那么,在prod环境,用的就是开发环境的on!实际上我们需要的是off!

所以,建议删除此xml的*-test.properties、*-prod.properties。

98. 外部配置·自定义的扩展类

目前,多个app用的是公司common里的:GyPropertyPlaceholderConfigurer,

作用:把多个.properties内容合并为一个大的,能取得配置值。

(A)这样搞,配置值会冲突。

曾经有人说:“制定一个命名方式比较好,比如用子系统标识做前缀”。

其实,“在每个配置文件里面,做监督检查”是很难的。

如同公司规定“禁止全体员工吃牛肉”,很难实现和监督。

(B)config.properties、notice-config.properties、acl-config.properties等等,

本来都是独立的,就不应该在此class里合并。

99. 外部配置·多个配置文件·推荐的路线

优点:

1 不需要自己创建class、interface跟Spring做整合。符合大原则:Don’t Write Code。

2 每个配置文件都保持了独立性,都能用Spring getBean()方式得到。

Properties  posConf  = ctx.getBean("pos_PosConf", Properties.class);

里面的Key Value的命名也是最宽松的,随便倒腾都行,不会影响别的.properties。

3 多个配置之间,永远不会有覆盖的事情。

100. 外部配置·全局配置·局部配置

如果公共classpath里已经有了some_config.properties,

但我的app不用那个,只用自己专有的some_config.properties,怎么办?

很简单,把some_config换个新名字some_config_for_myproj。

各种局部配置都可以这么做。

时间: 2024-10-10 08:59:17

代码规范(二)——java篇的相关文章

关于自动化测试框架,所需代码技能,Java篇——参数配置与读取.

前言: 说在前边.像我这种假期不出去浪,在这里乖乖写文章研究代码的人,绝壁不是因为爱学习,而是自己不知道去哪玩好,而且也不想玩游戏,看电视剧什么的,结果就无聊到看代码了-- 至于如何解读代码,请把它当做一门语言,况且它就是语言 ,计算机的,那就当做是外国人的语言,可以翻译成汉语的! 例:system.out.print(" ") 翻译:系统.输出.打印(内容).如是说! 本文介绍: Properties.Csv.Excel.JDBC 初级架构所需代码之 参数配置与读取--propert

Alibaba Java Coding Guidelines,以后我的Java代码规范,就靠它了

前言 对于Java代码规范,业界有统一的标准,不少公司对此都有一定的要求.但是即便如此,庞大的Java使用者由于经验很水平的限制,未必有规范编码的意识,而且即便经验丰富的老Java程序员也无法做到时刻将规范牢记于心.所以对于代码规范扫描工具,一经问世就广受青睐,阿里巴巴出品的Alibaba Java Coding Guidelines(阿里巴巴Java代码规约扫描,以下简称为AJCG)插件便是其中之一.与很多其它流行的同类工具(如FindBugs,Lint等)相比,它更专注于Java代码规范,而

MyEclipse中阿里JAVA代码规范插件(P3C)的安装及使用

JAVA代码规范插件(P3C)是阿里巴巴2017年10月14日在杭州云栖大会上首发的,使之前的阿里巴巴JAVA开发手册正式以插件形式公开走向业界.插件的相关信息及安装包都可以在GitHub(https://github.com/alibaba/p3c)上获取.目前插件实现了开发手册中的53条规则,只能满足较为基本的代码检测,后继应该会更加完善. MyEclipse中阿里JAVA代码规范插件(P3C)的安装跟通常的插件安装方法相同,有三种方法. 安装方法: 方法一.MyEclipse中在线安装 1

阿里巴巴Java开发手册正确学习姿势是怎样的?刷新代码规范认知

很多人都知道,阿里巴巴在2017发布了<阿里巴巴Java开发手册>,前后推出了很多个版本,并在后续推出了与之配套的IDEA插件和书籍. 相信很多Java开发都或多或少看过这份手册,这份手册有7个章节,覆盖了编程规约.异常日志.单元测试.安全规约.MySQL数据库.工程结构以及设计规约等方面. 这份规约可以说是覆盖了Java开发的方方面面,如果还有人没看的话,强烈建议大家好好看看,并且仔细研读. 手册中,有那么一些规则,是比较容易理解的.比如一些变量命名规范,有另外一些规则,是不太容易理解的,背

JAVA代码规范笔记(下)

声明 17.推荐一行一个声明,因为这样有利于写注释.换句话说,下面的声明方法中, int level; // indentation level int size; // size of table 要优于: int level, size; 绝对不要将变量和方法的声明放在同一行,例如: long dbaddr, getDbaddr(); // WRONG! 不要将不同类型的变量的声明放在同一行,例如: int foo, fooarray[]; //WRONG! 注意:上面的例子中,变量与标识符

这篇文章不错——代码规范

该文章来自于阿里巴巴技术协会(ATA)精选文章. 个人经历 对我代码质量影响最大的是在一家外资企业,在这家公司我觉得有以下几个方面做的很不错. 团队编码风格统一 统一到什么程度? 不看代码作者,你很难区分代码是谁写的(在目前公司一些团队也能达到这个标准). 个人观点: 这样做有什么好处?团队中每个人阅读代码都很容易,减少很多沟通,维护成本( 代码阅读的次数远远大于变更的次数),并且心情非常愉悦.有人肯定觉得愉悦有点夸张,举个栗子: 有一些代码,如果不是由于与工作内容有关联,你是否有种这辈子都不情

C#与Java对比学习:类型判断、类与接口继承、代码规范与编码习惯、常量定义(转载)

C#与Java对比学习:类型判断.类与接口继承.代码规范与编码习惯.常量定义 类型判断符号: C#:object a;  if(a is int) { }  用 is 符号判断 Java:object a; if(a instanceof Integer) { } 用 instanceof 符号判断 类与接口的继承: C#:public class MDataRow : List<MDataCell>, IDataRecord, ICustomTypeDescriptor Java:publi

JAVA技术开发规范(4)——JASMP平台的开发代码规范

4.关于代码规范 代码规范对程序员的重要性: 1.在软件开发生命周期中,有80%的时间是用在维护上 2.几乎所有的软件维护者都不是创使人 3.迭代式开发,必须要有清晰.可读性强的代码,以便于快速迭代 4.敏捷开发时,对应文档的输出不是减少,而是更加实用,因此,可交付的易读的代码更为重要了 5.协同协同,必需确保在编码时遵循统一的规范 编码规范是必需的,每个参与人必需严格遵循该规范进行编码. 4.1.命名规范 基本原则: 1.采用英文全称进行命名 2.保持各层级名称基本统一 3.命名英文单词用全称

Java&amp;Android代码规范

项目中直接导入Square的代码风格文件.(不导入Google的原因是Square同时提供了Java和Android两套统一风格,Google只提供了一套) Square Code Styles Goolge Code Styles 导入后会有很多好处,如注释规范化,import规范,方法排序,统一自动断行(PC屏幕较宽,建议断行字符数设置为120[File->Setting–>Code Style,右边Right Margin Columns])等 而在编码保存或提交时还能自动格式化代码,删

java代码解析二维码

java代码解析二维码一般步骤 本文采用的是google的zxing技术进行解析二维码技术,解析二维码的一般步骤如下: 一.下载zxing-core的jar包: 二.创建一个BufferedImageLuminanceSource类继承LuminanceSource,此类在google的源码中有,但是为了使用方便,下面有此类的源码,可以直接复制使用: private final BufferedImage image; private final int left; private final