• 编写高质量代码:改善Java程序的151个建议 --[36~51]


    编写高质量代码:改善Java程序的151个建议 --[36~51]

    工具类不可实例化

    工具类的方法和属性都是静态的,不需要生成实例即可访 问,而且JDK也做了很好的处理,由于不希望被初始化,于是就设置了构造函数private的访问权限,表示出了类本身之外,谁都不能产生一个实例:

    class UtilsClazz{
        public UtilsClazz(){
            throw new Error("Don't instantiate "+getClass());
        }
    }
    
    避免对象的浅拷贝

    super.clone()的拷贝规则:

    1. 基本类型:如果变量是基本类型,则拷贝其值。比如int、float等
    2. 对 象:如果变量是一个实例对象,则拷贝其地址引用,也就是说此时拷贝出的对象与原有对象共享该实例变量,不受访问权限的控制,这在Java中是很疯狂的,因 为它突破了访问权限的定义:一个private修饰的变量,竟然可以被两个不同的实例对象访问,这让java的访问权限体系情何以堪。
    3. String字符串:这个比较特殊,拷贝的也是一个地址,是个引用,但是在修改时,它会从字符串池(String pool)中重新生成新的字符串,原有的字符串对象保持不变,在此处我们可以认为String是一个基本类型。
    使用序列化对象的拷贝

    要求要拷贝的对象必须实现了Serializable接口。

    import java.io.ByteArrayInputStream;
    import java.io.ByteArrayOutputStream;
    import java.io.IOException;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    import java.io.Serializable;
    
    public final class CloneUtils {
        private CloneUtils() {
            throw new Error(CloneUtils.class + " cannot instance ");
        }
    
        // 拷贝一个对象
        public static <T extends Serializable> T clone(T obj) {
            // 拷贝产生的对象
            T cloneObj = null;
            try {
                // 读取对象字节数据
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                ObjectOutputStream oos = new ObjectOutputStream(baos);
                oos.writeObject(cloneObj);
                oos.close();
                // 分配内存空间,写入原始对象,生成新对象
                ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
                ObjectInputStream ois = new ObjectInputStream(bais);
                // 返回新对象, 并做类型转换
                cloneObj = (T) ois.readObject();
                ois.close();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return cloneObj;
    
        }
    
    }
    

    通过CloneUtils工具进行对象的深拷贝了,用词方法进行对象拷贝时需要注意两点:

    对象的内部属性都是可序列化的:如果有内部属性不可序列化,则会抛出序列化异常,这会让调试者很纳闷,生成一个对象怎么回出现序列化异常呢?从这一点考虑,也需要把CloneUtils工具的异常进行细化处理。
    注 意方法和属性的特殊修饰符:比如final,static变量的序列化问题会被引入对象的拷贝中,这点需要特别注意,同时 transient变量(瞬态变量,不进行序列化的变量)也会影响到拷贝的效果。当然,采用序列化拷贝时还有一个更简单的方法,即使用Apache下的 commons工具包中SerializationUtils类,直接使用更加简洁.

    package org.apache.commons.lang3;
    
     import java.io.ByteArrayInputStream;
     import java.io.ByteArrayOutputStream;
     import java.io.IOException;
     import java.io.InputStream;
     import java.io.ObjectInputStream;
     import java.io.ObjectOutputStream;
     import java.io.ObjectStreamClass;
     import java.io.OutputStream;
     import java.io.Serializable;
     import java.util.HashMap;
     import java.util.Map;
    
     public class SerializationUtils
     {
       public SerializationUtils() {}
    
       //克隆一个序列化对象
       public static <T extends Serializable> T clone(T object)
       {
    
       }
       //执行串行往返也就是执行序列化或者反序列化
       public static <T extends Serializable> T roundtrip(T msg)
       {
         return (Serializable)deserialize(serialize(msg));
       }
       //讲一个对象序列化并且输出到输出流中
       public static void serialize(Serializable obj, OutputStream outputStream)
       {
         Validate.isTrue(outputStream != null, "The OutputStream must not be null", new Object[0]);
         try { ObjectOutputStream out = new ObjectOutputStream(outputStream);Throwable localThrowable2 = null;
           try { out.writeObject(obj);
           }
           catch (Throwable localThrowable1)
           {
             localThrowable2 = localThrowable1;throw localThrowable1;
           } finally {
             if (out != null) if (localThrowable2 != null) try { out.close(); } catch (Throwable x2) { localThrowable2.addSuppressed(x2); } else out.close();
           } } catch (IOException ex) { throw new SerializationException(ex);
         }
       }
       //将一个对象序列化并返回字节数组
       public static byte[] serialize(Serializable obj)
       {
         ByteArrayOutputStream baos = new ByteArrayOutputStream(512);
         serialize(obj, baos);
         return baos.toByteArray();
       }
       //讲一个输入流反序列化成一个对象
       public static <T> T deserialize(InputStream inputStream)
       {
       }
       //将一个字节数组反序列化成一个对象
       public static <T> T deserialize(byte[] objectData)
       {
         Validate.isTrue(objectData != null, "The byte[] must not be null", new Object[0]);
         return deserialize(new ByteArrayInputStream(objectData));
       }
       static class ClassLoaderAwareObjectInputStream
         extends ObjectInputStream
       {
         private static final Map<String, Class<?>> primitiveTypes = new HashMap();
         private final ClassLoader classLoader;
    
         static {
           primitiveTypes.put("byte", Byte.TYPE);
           primitiveTypes.put("short", Short.TYPE);
           primitiveTypes.put("int", Integer.TYPE);
           primitiveTypes.put("long", Long.TYPE);
           primitiveTypes.put("float", Float.TYPE);
           primitiveTypes.put("double", Double.TYPE);
           primitiveTypes.put("boolean", Boolean.TYPE);
           primitiveTypes.put("char", Character.TYPE);
           primitiveTypes.put("void", Void.TYPE);
         }
    
         ClassLoaderAwareObjectInputStream(InputStream in, ClassLoader classLoader)
           throws IOException
         {
           super();
           this.classLoader = classLoader;
         }
    
         protected Class<?> resolveClass(ObjectStreamClass desc)
           throws IOException, ClassNotFoundException
         {
           String name = desc.getName();
           try {
             return Class.forName(name, false, this.classLoader);
           } catch (ClassNotFoundException ex) {
             try {
               return Class.forName(name, false, Thread.currentThread().getContextClassLoader());
             } catch (ClassNotFoundException cnfe) {
               Class<?> cls = (Class)primitiveTypes.get(name);
               if (cls != null) {
                 return cls;
               }
               throw cnfe;
             }
           }
         }
       }
     }
    
    覆写equals方法必须覆写hashCode方法
    class Person{
    
       @Override
        public int hashCode() {
            return new HashCodeBuilder().append(name).toHashCode();
        }   
    
    }
    

    其中HashCodeBuilder是org.apache.commons.lang.builder包下的一个哈希码生成工具;
    org.apache.commons.lang.builder包下相关资料参考学习:
    http://www.blogjava.net/19851985lili/articles/95448.html

    推荐覆写toString方法(ToStringBuilder或者自己重写toString())
    使用package-info类为包服务
    1. 声明友好类和包内访问常量:这个比较简单,而且很实用,比如一个包中有很多内部访问的类或常量,就可以统一放到package-info类中,这样很方便,便于集中管理,可以减少友好类到处游走的情况;
    class PkgClazz {
            public void test() {
            }
        }
        
        class PkgConstant {
            static final String PACKAGE_CONST = "ABC";
        }
    
    1. 为在包上提供注解提供便利:比如我们要写一个注解(Annotation),查看一下包下的对象,只要把注解标注到package-info文件中即可,而且在很多开源项目中也采用了此方法,比如struts2的@namespace、hibernate的@FilterDef等。
    2. 提供包的整体注释说明:如果是分包开发,也就是说一个包实现了一个业务逻辑或功能点或模块或组件,则该包需要一个很好的说明文档,说明这个包是做什么用的,版本变迁历史,与其他包的逻辑关系等,package-info文件的作用在此就发挥出来了,这些都可以直接定义到此文件中,通过javadoc生成文档时,会吧这些说明作为包文档的首页,让读者更容易对该包有一个整体的认识。当然在这点上它与package.html的作用是相同的,不过package-info可以在代码中维护文档的完整性,并且可以实现代码与文档的同步更新。
    不要主动进行垃圾回收
  • 相关阅读:
    vue生命周期
    vue input 循环渲染问题
    Node express post 大小设置
    webpack 好文章
    知识点的总结
    jsplumb 使用总结
    理解es6 中 arrow function的this
    分块编码(Transfer-Encoding: chunked)
    CGI的工作原理
    JS数组循环的性能和效率分析(for、while、forEach、map、for of)
  • 原文地址:https://www.cnblogs.com/androidsuperman/p/9430044.html
Copyright © 2020-2023  润新知