微认证之华为企业级JAVA编程规范(华为java开发规范)

网友投稿 1240 2022-05-30

对软件开发人员来说,此规范可以保证软件产品的质量,可以作为和其他程序员沟通的标准,若编码规则是建立在广泛的共识之上,更有利于产品的发展。

一、"JAVA基础语句"编程规范

例外:如果enum类型的switch语句包含该类型的所有可能值的显示案例,则该类型的switch语句可以省略default语句组。IDE或其他静态分析工具能够在缺少时,发出警告。

不包含default语句的代码:

private void switchMethodNoDefault(int number) {

switch (number) {

case 1:System.out.println("This is branch 1!");break;

case 2:System.out.println("This is branch 2!");break;

}

}          这个switch就包含两个分支

通过程序执行的结果,可以发现,参数是4的情况,程序没有任何输出,也就是说,程序没有覆盖到参数是4的情况。这种代码是不好的,程序是不完整的。

2.switch的条件变量是枚举类型

enum类型的switch语句如果包含该类型的所有可能值的显式案例,则该类型的switch语句可以省略该default语句组。

如果没有break语句,switch语句会从满足条件的地方(即与switch(表达式)括号中表达式匹配的case)开始执行,直到switch结构结束。

3.如果要结束switch语句怎么办?

程序在执行完选中的分支后,要跳出整个switch语句,需要使用break

总结:当switch后面表达式的值和case语句后的值相同时,switch结构从该位置开始向下执行,直到遇到break语句或者switch语句块结束

4. if-else if-else语句: if-else if-else语句是多条件分支语句,即根据多个条件来控制程序执行的流程。

if-else if语句,最后应以else分支作为结尾。

所以建议,多个else if条件组合的判断逻辑,往往会出现开发人员忽略的分支,需要设置else默认操作(类似于switch-case语句要有default分支)

5.浮点数不能为循环变量,精度问题会导致产生意想不到的结果

一个浮点数由三部分组成︰符号位S、指数部分E(阶码)以及尾数部分M(如下)。

Float   S--------E-------M  1位-----8位-----23位(共32位)

Double    S--------E-------M   1位-----11位----52位(共64位)

其中尾数部分为有效数字

计算机是用二进制来保持数据的, 在java中,浮点数2000000000f == 2000000045等式成立。

2000000000表示为单精度浮点数,求∶其对应的浮点数2000000000.0的二进制表示。

IEEE二进制浮点数算术标准(IEEE 754 )

IEEE754约定单精度指数偏移量为127,所以2000000000使用IEEE754标准表示时,指数为30+127=157 ,即:10011101,

IEEE754约定单精度尾数长度为23,所以2000000000使用IEEE754标准表示时,尾数为:11011100110101100101000

2000000000是正数,所以符号位是0。把符号位,阶码和尾数连起来︰

最后得到32位浮点数在内存中的二进制存储格式为∶

0 10011101 11011100110101100101000

2000000050和2000000000的二进制相同,所以,2000000000f == 2000000050

总结︰浮点数据在计算机中不是精确的数据,浮点变量具有不确定性,所以用来当循环的条件,很可能得不到预期结果。因此,不建议用浮点数做循环变量!

6. 在Java程序中,不同的基本数据类型的数据之间经常需要进行相互转换。我们建议,明确地进行类型转换,不要依赖隐式类型转换。

自动类型/隐式类型转换

由系统自动完成的类型转换。从存储范围小的类型到存储范围大的类型。

强制类型转换

从存储范围大的类型到存储范围小的类型。该类类型转换很可能存在精度的损失。

需要精确计算时,怎么办?

需要精确计算时,使用BigDecimal

总结∶明确地进行类型转换,不要依赖隐式类型转换,通过这种方式,程序员表明他知道所涉及的不同类型,并且混合是有意的。以免意外地浮点数转换截取,导致误差逐步放大。

二、 "类的使用"编程规范

1.使用类名调用静态方法,而不要使用实例来调用。

package BlueBridge;

public class Dog {

public static void main(String [] args) {

Dog dog = new Dog();

Dog basenjiDog = new Basenji();

dog.run();

basenjiDog.run();

dog.eat();

basenjiDog.eat();

}

public static void eat() {

System.out.println("Animal eat");

}

public void run() {

System.out.println("Animal running");

}

}

class Basenji extends Dog{

public static void eat() {

System.out.println("Basenji eat");

}

public void run() {

System.out.println("Basenji running");

}

}

运行结果:

Animal running

Basenji running

Animal eat

Animal eat

分析一下原因∶使用实例调用静态方法时,调用的静态方法是声明类型的静态方法,与实例的实际类型无关。当父类和子类有同名静态方法时,声明父类变量引用子类实例,调用该静态方法时调用的是父类的静态方法,而非子类的静态方法。

父类和子类有同名非静态方法时,子类方法将重写父类方法;

【当子类的创建方式为 父类声明 子类实现时】

父类和子类有同名 静态方法时,父类方法将隐藏字类方法。

总结︰使用对象调用类中的静态方法,可能会导致与预期的结果不一致的情况,明确的使用类名调用静态方法不容易造成混淆。

2.避免在无关的变量或无关的概念之间重用名字,避免隐藏(hide)、遮蔽(shadow)、和遮掩(obscure)

OOP---面向对象的思想

总结:

override 存在于子类和父类之间。 子类中的【实例方法】可以覆写 override 其在超类中能够访问(非私有)的同签名的实例方法(非静态方法)。

一个实例方法可以覆写( override )在其超类中可访问到(非private )的具有相同签名的实例方法(非static ),从而使能了动态分派( dynamic dispatch );换句话说,VM将基于实例的运行期类型来选择要调用的覆写方法。覆写的好处在于子类可以根据需要,定义特定于自己的行为。

重载(overload)存在于类内部

什么是重载?

Object 一组相似行为用相同名命名

具体而言比如一个计算工具类 求和两个集合的大小 求和两个数组的大小 求和集合和数组的大小

总结︰

overload存在于某个类之间。 某个类中的【某个方法】可以重载 overload 同类中的另一个具有相同命名和不同签名的方法。

在某个类中的方法可以重载( overload )另一个方法,只要它们具有相同的名字和不同签名。由调用所指定的重载方法是在编译期选定的。重载歧义或混淆的场景∶   可变参数   包装类型,例如int与Integer,这时应该修改方法名称,如果是构造方法,则委托到不同名的静态方法。

不恰当的使用重命名的方式有三种:

1.隐藏(hide)----子类与父类间

说明:一个属性、静态方法或内部类可以分别隐藏(hide)在其超类中可访问到的具有相同名字(对方法而言就是相同的方法签名)的所有属性、方法或内部类。上述成员被隐藏后,将阻止其被继承:

2.遮蔽(shadow)----类内部

一个变量、方法或类可以分别遮蔽(shadow)在类内部具有相同名字的变量、方法或类。如果一个实体被遮蔽了,那么就无法用简单名引用到它:

3.遮掩(obscure)----类内部

一个变量可以遮掩具有相同名字的一个类,只要它们都在同一个范围内︰如果这个名字被用于变量与类都被许可的范围,那么它将引用到变量上。相似地,一个变量或一个类型可以遮掩一个包。遮掩是唯一一种两个名字位于不同的名字空间的名字重用形式,这些名字空间包括:变量、包、方法或类型。如果一个类或一个包被遮掩了,那么你不能通过其简单名引用到它,除非是在这样一个上下文环境中,即语法只允许在其名字空间中出现一种名字。

3.子类覆写父类方法时,应加上@Override 注释

什么是方法的覆写︰

如果父类的方法,不能满足子类的要求,子类可以覆写父类继承的方法,当调用方法的时候会优先调用子类的方法。

语法规则︰

1.返回值类型     2.方法名

3.参数列表(类型和个数)   都要与继承父类的方法相同,才叫方法的覆写。

Annotation(注解)是JDK1.5及以后版本引入的。它可以用于创建文档,跟踪代码中的依赖性,甚至执行基本编译时检查。

@Override

它的作用是对覆盖超类中方法的方法进行标记,如果被标记的方法没有实际覆盖超类中的方法,则编译器会发出错误警告。

总结︰加上@Override注解的好处是,如果覆写时因为疏忽,导致子类方法的参数同父类不一致,编译时会报错,使问题在编译期就被发现。如果父类修改了方法的定义造成子类不再覆写父类方法,也能使问题在编译期尽早被发现。

4.哈希集合存储对象的相关规则

将对象存入HashSet,或作为Key存入HashMap(或HashTable)后,必须确保对象的hashcode值不变,避免因为hashcode值变化导致不能从集合内删除该对象

HashSet简介

HashSet是set 接口的实现,不允许元素重复  元素不重复是基于HashMap实现  非线程安全的

对于Hash集合(HashMap ,HashSet等)而言,对象的hashcode至关重要,在hash集合内查找该对象完全依赖此值。如果一个对象存入Hash集合后,修改了参与计算hashcode有关的字段,那么修改后的hashcode的值就与最初存储进来的hashcode的值不同了,结果就是无法在集合内找到该对象,进而不能删除该对象,最终导致内存泄漏。

当一个对象被存储在Hash集合中后,不要修改与计算hashcode有关的字段。

5.在向下类型转换前用Instanceof进行判断

向上类型转换(自动类型转换),是从小类型到大类型的转换

向下类型转换(强制类型转换),是从大类型到小类型的转换

向上(无风险)     向下(有风险)

总结:没有判断直接进行向下类型转换,可能会因类型不匹配而导致运行期异常,在强制转换之前使用instanceof进行判断,确认转换操作可行,可以避免类型转换的安全性问题。

6.使用集合的toArray()方法将集合转为数组

怎样将集合转为数组呢?

如果他们之间能够转换,应该是把集合中的元素一个一个取出来,再一个一个放到数组中

集合类有没有方便的方法可以用呢?

toArray

1.public Object[] toArray()       按适当顺序(从第一个到最后一个元素)返回包含此列表中所有元素的数组。

Object[] 不可以转化为String[]; 需要进行每个元素的单独转换

toArray

2.public T[] toArray(T[] a)

按适当顺序(从第一个到最后一个元素) 返回包含此列表中所有元素的数组; 返回数组的运行时类型是指定数组的运行时类型。如果指定的数组能容纳列表,则将该列表返回此处。否则,将分配一个具有指定数组的运行时类型和此列表大小的新数组。

3.get() method

使用toArray方法,代码更加简洁。

Objecr[] toArray()方法

运行程序可能抛出异常:java.lang.ClassCastException

T[] toArray(T[] a)方法  如果a数组的长度小于集合元素的长度,则重新分配空间复制并返回,否则使用a数组复制并返回

使用toArray带参方法

1.入参分配的数组空间不够大时,toArray方法内部将重新分配内存空间,并返回新数组地址﹔例如: new String[0]

⒉.如果数组元素大于实际所需,下标为[ list.size()]的数组元素和后面的元素将被置为null,其它数组元素保持原值,这个设置可以使方法调用者从返回的数组中检测到null时就知道后面已经没有list元素对象了。所以,最好将方法入参数组大小定义与集合元素个数一致( new String[list.size()] )。

怎样进行数组复制呢?

第一种方法:使用for循环进行数组复制

使用System.arraycopy()进行数组复制

System.arraycopy()作用

从指定源数组中复制一个数组,复制从指定的位置开始,到目标数组的指定位置结束。

src:源数组;  srcPos:复制源数组的起始位置;

dest:目的数组;  destPos:目的数组放置的起始位置;    length:复制的长度。

第二种方法:使用System.arraycopy()进行数组复制

两种复制方法的对比:

【1】目标数组是个小数组,长度在100以内

【2】目标是个中等数组,长度以千为单位。

【3】目标数组是个大型数组,长度以万为单位。

总结:

原始数组长度比较小的时候,几百以内,for循环表现十分优异,并随着数组长度的增加,效率越来越低,因此,for循环适合于小型数组。

原始数组长度比较大的时候,以千或者以万为单位,这时候本地方法System.arraycopy ( )方法的优势体现出来了。

因此,需要根据操作的数组的长度,灵活地选择数组复制方式,会使得我们的程序得到性能的略微提升。

三、“异常处理”编程规范

1.在异常条件下,保证释放已持有的锁

ReentrantLock(重入锁)

可重入的互斥锁,是一种递归无阻塞的同步机制。它可以等同于synchronized的使用,但是ReentrantLock提供了比synchronized更强大、灵活的锁机制,可以减少死锁发生的概率。

ReentrantLock类能够实现线程之间同步互斥

ReentrantLock类具有完全互斥排他的效果,即同一时间只有一个线程在执行

ReentrantLock.lock()方法后面的任务。

微认证之华为企业级JAVA编程规范(华为java开发规范)

出现异常应该怎么办?

出现异常也要释放掉持有的锁。

总结:一个线程中没有正确释放持有的锁会使其他线程无法获取该锁对象,导致阻塞。在发生异常时,要确保程序正确释放当前持有的锁。

Lock接口使用的标准形式:

//创建锁

Lock lock = new ReentranLock();

...

lock.lock();

try{

//进行必要操作

//捕获异常,并在必要时恢复不变性条件

} finally{

//释放锁

lock.unlock();

}

}

2.防止通过异常泄露敏感信息

异常行为

什么样的信息是敏感的?   一般是由系统的安全策略来决定的

传递异常时:  未对敏感信息过滤  信息泄露  攻击者发起攻击

典型的敏感信息包括  个人社保号 口令 秘钥等等

如果在传递异常的时候没有对其中的敏感信息进行过滤,常常会导致信息泄露,而这可能帮助攻击者尝试发起进一步的攻击,比如攻击者可以通过构造恶意的输入参数来发掘应用的内部

不管是异常中的文本消息还是异常本身的类型都有可能泄露敏感信息

敏感信息泄露的来源:

文本消息:例如  FileNotFoundException异常会透露文件系统的结构信息

异常类型:通过异常本身的类型,可以得知所请求的文件不存在

解决办法:对敏感的异常消息和敏感的异常类型进行过滤。

3.在finally块中不要使finally块非正常结束

finally是什么?

1.java的一种异常处理机制    2. 对java异常处理模型的最佳补充   3.使代码总会执行,而不管有无异常发生

在finally块中使用 return  break 和continue 会使finally块非正常结束

造成的影响是即使在try块或catch中抛出了异常,也会因为finally非正常结束而导致无法抛出。

1. 感谢老师们的教学与课件

2. 欢迎各位同学一起来交流学习心得^_^

3. 在线课程、沙箱实验、认证、论坛和直播,其中包含了许多优质的内容,推荐了解与学习。

Java 数据结构

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:用 Golang 实现基于 Redis 的安全高效 RPC 通信(用一生去爱你)
下一篇:MySQL Server Startup Script(mysql安装教程)
相关文章