【Java编程之---编码规范1】

背景

前段时间关于我们Java后台的加解密算法基本都涉及到了,当然还有可深入的知识点,基于知识广度本人后期再补充加解密算法的相关知识点,后面两期将会重点涉及我们Java日常的规范和经验分享。

数组与List之间的转换

  • Array转换List
    方式一:通过 List list = Arrays.asList(strArray)
    ,将数组转换List后,不能对List增删,只能查改,否则抛异常。
    原因解析:Arrays.asList(strArray)返回值是java.util.Arrays类中一个私有静态内部类java.util.Arrays.ArrayList,它并非java.util.ArrayList类。java.util.Arrays.ArrayList类具有 set(),get(),contains()等方法,但是不具有添加add()或删除remove()方法,所以调用add()方法会报错。

    方式二:通过ArrayList的参数构造 List list = new ArrayList(Arrays.asList(strArray)) 这种方式涉及到数组的多次遍历,在元素数量不大的情况下适合使用,也支持增删改查

    方式三:通过Collections.addAll(arrayList, strArray)方式转换,根据数组的长度创建一个长度相同的List,然后通过Collections.addAll()方法,将数组中的元素转为二进制,然后添加到List中,这是最高效的方法。
    ArrayList< String> arrayList = new ArrayList(strArray.length);
    Collections.addAll(arrayList, strArray);

  • 特殊的数组array转List
    可以使用流stream来将下列3种数组快速转为List,分别是int[]、long[]、double[],其他数据类型比如short[]、byte[]、char[],在JDK1.8中暂不支持。由于这只是一种常用方法的封装,不再纳入一种崭新的数组转List方式,暂时算是java流送给我们的常用工具方法吧。

// 三个基本类型先封装,在处理
List<Integer> intList= Arrays.stream(new int[] { 1, 2, 3, }).boxed().collect(Collectors.toList());
List<Long> longList= Arrays.stream(new long[] { 1, 2, 3 }).boxed().collect(Collectors.toList());
List<Double> doubleList= Arrays.stream(new double[] { 1, 2, 3 }).boxed().collect(Collectors.toList());

// 对于String类型的数据
String[] strArray = {"tom", "jack", "kate"};
List<String> stringList= Stream.of(strArray ).collect(Collectors.toList());
  • List转数组array
    用list的toArray方法,list.size一定要等于arr.length
// newArr与参数是同一个对象
String[] newArr = list.toArray(new String[list.size()]);

集合的初始化与长度

  • 常用的数据容器初始化尽量不要使用匿名内部类比如
List<String> strLsit = new ArrayList(){{add("a");add("b");}}
Set<String> strSet = new HashSet(){{add("a");add("b");}}
Map<String, String>  strMap = new HashMap<String,String>(){{put("a","b");put("a","b");}};

外层大括号实际上是匿名内部类的写法,内层大括号实际上是实例代码块。这种写法不规范会多出额外的内存开销。

  • 建议使用google guava库初始化
List<String> strLsit = Lists.newArrayList("a","b");
Set<String> strSet = Sets.newHashSet("a","b");
// ImmutableMap不可变的Map,数据一旦确定,就不能增删, 想要支持这些功能,需要做一次转换
HashMap<Integer, String> map = Maps.newHashMap(ImmutableMap.of(1, "张三", 2, "李四"));
  • List容器初始化长度问题
    对于List来说,我们一般使用ArrayList,因为底层使用数组实现,默认长度为10,
    扩容模式为 newSize = ((oldSize * 3) / 2) + 1
    比如从默认10开始动态增长的容量数量变化:10->16->25->38->58->88->…
    在这个数组的扩容过程中涉及到数组内存重新分配和数据迁移,对性有也是有一定影响的
    尽量在初始化是定好容量

  • Map容器初始化长度问题
    HashMap的数组长度恒定为2的n次方,也就是说只会为16,32,64,128这种数。即便你给的初始值是13,最后数组长度也会变成16,它会取与你传入的数最近的一个2的n次方的数。比如new HashMap(25), 则其分配长度为32. 当数组占据长度为 currentLen * 0.75 容器继续扩增2倍。
    扩容需要重新申请双倍的内存数组,每个元素同时还需要reHash跃迁新数组的下表,对于大批量的元素就会多次扩容严重影响性能

自循for与while对比

无线循环使用for(;;)而不是while(true)
从效率上看,while(true)每次循环要判断循环条件,for(;;)循环没有判断,理论上节省机器指令
for(;;)编译之后只有一条指令,而while(true)有好几条

使用entrySet遍历Map

使用entrySet遍历Map类集合KV,而不是keySet方式进行遍历
keySet其实是遍历了2次,一次是转为Iterator对象,另一次是从hashMap中取出key所对应的value
而entrySet只是遍历了一次就把key和value都放到了entry中,效率更高
如果是JDK8,使用Map.foreach方法

Map<String,Integer> dataMap = Maps.of("zero", 0, "one", 1, "two", 2, "three", 3, "four", 4);
dataMap.foreach((key, val) -> System.out.println(key + val))

包装类之间用equals,非特殊场景不要使用“==”

由于Integer类有缓存,会复用已有的类对象

public static void main(String[] args){  
   Integer a = 10;  
   Integer b = 10;  
   Integer c = 200;  
   Integer d = 200;  
   System.out.println(a==b);    // true
   System.out.println(c==d);    //false
}  

原来,Integer中有一个静态内部类IntegerCache,在类加载的时候,它会把[-128, 127]之间的值缓存起来,而Integer a = 100这样的赋值方式,会首先调用Integer类中的静态valueOf方法,这个方法会尝试从缓存里取值,如果在这个范围之内就不用重新new一个对象了:

public static Integer valueOf(int i) {  
   if (i >= IntegerCache.low && i <= IntegerCache.high)  
       return IntegerCache.cache[i + (-IntegerCache.low)];  
   return new Integer(i);  
}  

重写equals和hashcode的情况

因为Set存储的是不重复的对象,依据hashCode和equals进行判断,所以Set存储的对象必须重写这两个方法
如果自定义对象作为Map的键,那么必须重写hashCode和equals
String重写了hashCode和equals方法,所以我们可以非常愉快地使用String对象作为key来使用

BigDecimal指定舍入模式

使用小数构建BigDecimal时要用BigDecimal类的BigDecimal(String)构造方法,或使用valueOf方法

        double a = 2.0, b = 1.1;
        System.out.println(a-b); // 会丢失精度
        
        BigDecimal b1 = new BigDecimal(2.0),  b2 = new BigDecimal(1.1);
        BigDecimal res = b1.subtract(b2);
        System.out.println(res.doubleValue());  //会丢失精度
 
        BigDecimal b3 = new BigDecimal("2.0"), b4 = new BigDecimal("1.1");
        BigDecimal res1 = b3.subtract(b4);
        System.out.println(res1.doubleValue());  //不会丢失精度
 
        BigDecimal b5 = BigDecimal.valueOf(2.0), b6 = BigDecimal.valueOf(1.1);
        BigDecimal res2 = b5.subtract(b6);
        System.out.println(res2.doubleValue()); //不会丢失精度

String.format常用功能

功能说明实现方式
字节转HexString.format(“%02x”, bytes))
字符串类型String.format(“你好 %s”,“张三”)
字符类型String.format(“%c %c %c”,‘A’,‘B’,‘C’)
整数类型转HexString.format(“16进制:%x”,122)

变量在内存中的存放位置

  • 局部变量
    基本类型:引用和值都放在栈上
    对象类型:引用放在栈上,对象放在堆上

  • 全局实例变量:
    基本类型:引用和值都放在堆上
    对象类型:引用和对象都放在堆上

  • 全局静态变量:
    基本类型:引用和值放在堆上
    对象类型:引用和值放在堆上

    java7:常量池和类的静态变量放在方法区, java8:常量池和类的静态变量放在堆上

加解密算法更适合char[]而不是String

由于 String 在 Java 中是不可变的,如果你将密码以明文的形式保存成字符串,那么它将一直留在内存中,直到垃圾收集器把它清除。而由于字符串被放在字符串缓冲池中以方便重复使用,所以它就可能在内存中被保留很长时间,而这将导致安全隐患,
因为任何能够访问内存 (memory dump 内存转储) 的人都能清晰的看到文本中的密码,这也是为什么你应该总是使用加密的形式而不是明文来保存密码。
由于字符串是不可变的,所以没有任何方式可以修改字符串的值,因为每次修改都将产生新的字符串,然而如果你使用 char [] 来保存密码,你仍然可以将其中所有的元素都设置为空或者零。
所以将密码保存到字符数组中很明显的降低了密码被窃取的风险。

既然 JVM 有 Full GC,为什么还会出现 OutOfMemoryError

JVM内存分为程序计数器,Java虚拟机栈,本地方法栈,Java堆,方法区,运行时常量池(包含于方法区),直接内存(直接内存并不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域,但是这部分内存也被频繁地使用,而且也可能导致OutOfMemoryError异常出现)。
除了程序计数器之外都是有可能出现OutOfMemoryError异常的,Java堆用于存储对象实例,我们只要不断的创建对象,并且保证GC Roots到对象之间有可达路径来避免垃圾回收机制清除这些对象,就会在对象数量到达最大堆的容量限制后产生内存溢出异常。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值