zoukankan      html  css  js  c++  java
  • 编写高质量代码:改善Java程序的151个建议 --[106~117]

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

    动态代理可以使代理模式更加灵活
    interface Subject {
        // 定义一个方法
        public void request();
    }
    
    // 具体主题角色
    class RealSubject implements Subject {
        // 实现方法
        @Override
        public void request() {
            // 实现具体业务逻辑
        }
    
    }
    
    class SubjectHandler implements InvocationHandler {
        // 被代理的对象
        private Subject subject;
    
        public SubjectHandler(Subject _subject) {
            subject = _subject;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args)
                throws Throwable {
            // 预处理
            System.out.println("预处理...");
            //直接调用被代理的方法
            Object obj = method.invoke(subject, args);
            // 后处理
            System.out.println("后处理...");
            return obj;
        }
    
    }
    

    动态代理使用场景:

    public static void main(String[] args) {
            //具体主题角色,也就是被代理类
            Subject subject = new RealSubject();
            //代理实例的处理Handler
            InvocationHandler handler =new SubjectHandler(subject);
            //当前加载器
            ClassLoader cl = subject.getClass().getClassLoader();
            //动态代理
            Subject proxy = (Subject) Proxy.newProxyInstance(cl,subject.getClass().getInterfaces(),handler);
            //执行具体主题角色方法
            proxy.request();
        }
    

    不用显式创建代理类即实现代理的功能,例如可以在被代理的角色执行前进行权限判断,或者执行后进行数据校验。

    使用反射增加装饰模式的普适性

    反射让模板方法模式更强大

    提倡异常封装

    class MyException extends Exception {
        // 容纳所有的异常
        private List<Throwable> causes = new ArrayList<Throwable>();
    
        // 构造函数,传递一个异常列表
        public MyException(List<? extends Throwable> _causes) {
            causes.addAll(_causes);
        }
    
        // 读取所有的异常
        public List<Throwable> getExceptions() {
            return causes;
        }
    }
    

    具体调用如下

    public void doStuff() throws MyException {
            List<Throwable> list = new ArrayList<Throwable>();
            // 第一个逻辑片段
            try {
                // Do Something
            } catch (Exception e) {
                list.add(e);
            }
            // 第二个逻辑片段
            try {
                // Do Something
            } catch (Exception e) {
                list.add(e);
            }
            // 检查是否有必要抛出异常
            if (list.size() > 0) {
                throw new MyException(list);
            }
        }
    

    不要在finally块中处理返回值

    在finally代码块中处理返回值,这是考试和面试中经常出现的题目。虽然可以以此来出考试题,但在项目中绝对不能再finally代码块中出现return语句,这是因为这种处理方式非常容易产生" 误解 ",会误导开发者。
    finally块中处理返回值会产生的问题:

    1. 覆盖了try代码块中的return返回值
    2. 屏蔽异常
    public static void doSomeThing(){
            try{
                //正常抛出异常
                throw new RuntimeException();
            }finally{
                //告诉JVM:该方法正常返回
                return;
            }
        }
    	
    public static void main(String[] args) {
            try {
                doSomeThing();
            } catch (RuntimeException e) {
                System.out.println("这里是永远不会到达的");
            }
        }
    

    不要在构造函数中抛出异常

    异常的机制有三种:

    • Error类及其子类表示的是错误,它是不需要程序员处理也不能处理的异常,比如VirtualMachineError虚拟机错误,ThreadDeath线程僵死等。
    • RunTimeException类及其子类表示的是非受检异常,是系统可能会抛出的异常,程序员可以去处理,也可以不处理,最经典的就是NullPointException空指针异常和IndexOutOfBoundsException越界异常。
    • Exception类及其子类(不包含非受检异常),表示的是受检异常,这是程序员必须处理的异常,不处理则程序不能通过编译,比如IOException表示的是I/O异常,SQLException表示的数据库访问异常。
    • 构造函数中抛出异常:
      • 构造函数中抛出错误是程序员无法处理的
      • 构造函数不应该抛出非受检异常
      • 加重了上层代码编写者的负担
      • 后续代码不会执行
    • 构造函数尽可能不要抛出受检异常
      • 导致子类膨胀
      • 违背了里氏替换原则
    • 子类构造函数扩展受限

    使用Throwable获得栈信息

    class Foo {
        public static boolean method() {
            // 取得当前栈信息
            StackTraceElement[] sts = new Throwable().getStackTrace();
            // 检查是否是methodA方法调用
            for (StackTraceElement st : sts) {
                if (st.getMethodName().equals("methodA")) {
                    return true;
                }
            }
            throw new RuntimeException("除了methodA方法外,该方法不允许其它方法调用");
        }
    }
    

    Throwable源码

    public class Throwable implements Serializable {
        private static final StackTraceElement[] UNASSIGNED_STACK = new StackTraceElement[0];
        //出现异常记录的栈帧
        private StackTraceElement[] stackTrace = UNASSIGNED_STACK;
        //默认构造函数
        public Throwable() {
            //记录栈帧
            fillInStackTrace();
        }
        //本地方法,抓取执行时的栈信息
        private native Throwable fillInStackTrace(int dummy);
    
        public synchronized Throwable fillInStackTrace() {
            if (stackTrace != null || backtrace != null /* Out of protocol state */) {
                fillInStackTrace(0);
                stackTrace = UNASSIGNED_STACK;
            }
            return this;
        }
    
    }
    

    在出现异常时(或主动声明一个Throwable对象时),JVM会通过fillInStackTrace方法记录下栈帧信息,然后生成一个Throwable对象,这样我们就可以知道类间的调用顺序,方法名称及当前行号等了。获得栈信息可以对调用者进行判断,然后决定不同的输出。

    异常只为异常服务

    异常只能用在非正常的情况下,不能成为正常情况下的主逻辑。
    比如如下代码是不建议的:

    //判断一个枚举是否包含String枚举项
        public static <T extends Enum<T>> boolean Contain(Class<T> clz,String name){
            boolean result = false;
            try{
                Enum.valueOf(clz, name);
                result = true;
            }catch(RuntimeException e){
                //只要是抛出异常,则认为不包含
            }
            return result;
        }
    

    多使用异常,把性能问题放一边

    如下业务逻辑比较清晰,正常代码和异常代码分离、能快速查找问题(栈信息快照)等,虽然性能比较差。

    public void login(){
            try{
                //正常登陆
            }catch(InvalidLoginException lie){
                //    用户名无效
            }catch(InvalidPasswordException pe){
                //密码错误的异常
            }catch(TooMuchLoginException){
                //多次登陆失败的异常
            }
        }
    
  • 相关阅读:
    如何创建支持64位的安装程序
    SharePoint Server 2013开发之旅(四):配置工作流开发和测试环境
    SharePoint Server 2013开发之旅(三):为SharePoint Server配置App开发、部署、管理环境
    SharePoint Server 2013开发之旅(二):使用在线的开发人员网站进行SharePoint App开发
    SharePoint Server 2013开发之旅(一):新的开发平台和典型开发场景介绍
    在WPF应用程序中利用IEditableObject接口实现可撤销编辑的对象
    一个在ASP.NET中利用服务器控件GridView实现数据增删改查的例子
    关于未捕获异常的处理(WPF)
    牛刀小试:使用Reactive Extensions(Rx),对短时间内多次发生的事件限流
    如何对SharePoint网站进行预热(warmup)以提高响应速度
  • 原文地址:https://www.cnblogs.com/androidsuperman/p/9469693.html
Copyright © 2011-2022 走看看