Ericson's blog Time is limited, To be a better better man

Java异常处理

Java将异常分为两种,Checked异常和Runtime异常,Java认为Checked异常都是可以在编译阶段被处理的异常,所以它强制程序处理所有的Checked异常;而Runtime异常则无须处理。

Java的异常处理机制可以让程序具有极好的容错性,让程序更加健壮。当程序运行出现意外情形时,系统会自动生成一个Exception对象来通知程序,从而实现将“业务功能实现代码”和“错误处理代码”分离,提供更好的可读性。异常类的继承关系如下图所示:

粉红色的代表Checked Exception,其必须被try-catch语句块所捕获;绿色的异常是运行时异常,是Unchecked Exception,不需要捕获。Java把所有的非正常情况分为两种:异常和错误,它们都继承Throwable父类。Error错误,一般是指与虚拟机相关的问题,如系统崩溃、虚拟机错误、动态链接失败等。finally语句块中负责处理一些资源释放,如果finally里含有return语句,则try里的return语句将会失效。

策略模式

策略模式:它定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的客户。 策略模式 Context类用来保存一个Strategy类,在Context类中调用Strategy类的算法。如下是策略结构的代码:

[Java Code]

public class Context {
    Strategy strategy;
    public Context(Strategy strategy){
        this.strategy=strategy;
    }
    public void ContextInterface(){
        strategy.AlgorithmInterface();
    }
    public static void main(String[] args){
        Context context;
        context=new Context(new ConcreteStrategyA());
        context.ContextInterface();
        context=new Context(new ConcreteStrategyB());
        context.ContextInterface();
        context=new Context(new ConcreteStrategyC());
        context.ContextInterface();
    }
}
public abstract class Strategy {
    public abstract void AlgorithmInterface();
}
public class ConcreteStrategyA extends Strategy {
    @Override
    public void AlgorithmInterface() {
        System.out.println("算法A实现");
    }
}
public class ConcreteStrategyB extends Strategy {
    @Override
    public void AlgorithmInterface() {
        System.out.println("算法B实现");
    }
}
public class ConcreteStrategyC extends Strategy {
    @Override
    public void AlgorithmInterface() {
        System.out.println("算法C实现");
    }
}

策略模式是一种定义一系列算法的方法,从概念上来看,所有这些算法完成的都是相同的工作,只是实现不同,它可以以相同的方式调用所有的算法,减少了各种算法类与使用算法类之间的耦合。策略模式的Strategy类层次为Context定义了一系列的可供重用的算法或行为。继承有助于析取出这些算法中的公共功能。另一个策略模式的优点是简化了单元测试,因为每个算法都有自己的类,可以通过自己的接口单独测试。

策略模式封装了变化,策略模式就是用来封装算法的,但在实践中,我们发现可以用它来封装几乎任何类型的规则,只要在分析过程中听到需要在不同时间应用不同的业务规则,就可以考虑使用策略模式处理这种变化的可能性。

使用场景   假设现在要设计一个贩卖各类书籍的电子商务网站的购物车系统。一个最简单的情况就是把所有货品的单价乘上数量,但是实际情况肯定比这要复杂。比如,本网站可能对所有的高级会员提供每本20%的促销折扣;对中级会员提供每本10%的促销折扣;对初级会员没有折扣。

  根据描述,折扣是根据以下的几个算法中的一个进行的:

  算法一:对初级会员没有折扣。

  算法二:对中级会员提供10%的促销折扣。

  算法三:对高级会员提供20%的促销折扣。

  使用策略模式来实现的结构图如下: simple

泛型

在没有泛型之前,一旦把一个对象“丢进”Java集合中,集合就会忘记对象的类型,把所有对象当成Object类型处理,当程序从集合中取出对象后,就需要进行强制类型转换,这种强制类型转换不仅使代码臃肿,而且容易引起ClassCastException异常。

Java7以后提供了一个菱形语法,在Java7以前,泛型的声明必须前后都指定类型,如下所示:

List strList=new ArrayList();

菱形语法的声明如下:

List strList=new ArrayList<>();

编译器会自动推断出后面的类型。所谓泛型,就是允许在定义类、接口、方法时使用类型形参,这个类型形参将在声明变量、创建对象、调用方法时动态地指定(即传入实际的类型参数,也可称为类型实参)。泛型示例如下代码所示:
[Java Code]

class Apple<E> {
        private E info;

        public Apple(E info) {
            this.info = info;
        }

        public void setInfo(E info) {
            this.info = info;
        }

        public E getInfo() {
            return this.info;
        }
    }

当创建带泛型声明的自定义类,为该类定义构造器时,构造器名还是原来的类名,不要增加泛型声明。当创建了带泛型声明的接口、父类之后,可以为接口创建实现类,或从该父类派生子类,但需要指出的是,当使用这些接口、父类时不能再包含类型形参。如果想从Apple类中派生一个子类,需要指定Apple的实际形参类型(也可以不指定,但会看到编译器警告,泛型参数会被默认当成Object来处理)。

public class A extends Apple<String>

不管泛型的实际类型参数是什么,它们在运行是总有同样的类(class),在Java中,在内存中只占用一块内存空间,因此在静态方法、静态初始化块或者静态变量的声明和初始化中不允许使用类型参数。

类型通配符:
如果Foo是Bar的一个子类型(子类或者子接口),而G是具有泛型声明的类或接口,G并不是G的子类型。数组和泛型有所不同,假设Foo是Bar的一个子类型(子类或者子接口),那么Foo[]依然是Bar[]的子类型,但是G不是G的子类型。类型通配符是一个问号?,将一个问号作为类型实参传给List集合,写作:List<?>。如果规定限定是某父类的子类时可以使用如下通配符:List<? extends Father>。如果规定限定某子类的父类可以使用如下通配符:List<? super Son>。把泛型变量赋值给非泛型变量,会擦除泛型变量中的类信息,转为原生类型。

Maximal Rectangle

Problem: Maximal Rectangle

Solve:
解法一:在(i,j)处进行矩形搜索,首先横向搜索,再纵向搜索,搜索方式如下图所示:

搜索方式

搜索代码如下:
[Java Code]

public int maxRectangle(char[][] matrix, int row, int col) {
        int minWidth = Integer.MAX_VALUE;
        int maxArea = 0;
        for (int i = row; i < matrix.length && matrix[i][col] == '1'; i++) {
            int width = 0;
            while (col + width < matrix[row].length && matrix[i][col + width] == '1') {
                width++;
            }
            if (width < minWidth) {
                minWidth = width;
            }
            int area = minWidth * (i - row + 1);
            if (area > maxArea)
                maxArea = area;
        }
        return maxArea;
    }

然后对矩形中所有点使用上述搜索,就可以找出最大的矩形面积,代码如下:
[Java Code]

public int maximalRectangle(char[][] matrix) {
        int max = 0;
        for (int i = 0; i < matrix.length; i++) {
            for (int j = 0; j < matrix[0].length; j++) {
                max = Math.max(max, maxRectangle(matrix, i, j));
            }
        }
        return max;
    }

解法二:从上往下,按行搜索,每行内容加上它上面的所有行就组成的一个个直方图,所以对每一行而言,都对应一个直方图,所以问题就转换为了求直方图中最大的矩形面积。示意图如下所示:

直方图

具体讲解请参考直方图博客 代码如下所示:
[Java Code]

public int maximalRectangle(char[][] matrix) {
        if (matrix.length == 0 || matrix[0].length == 0) return 0;
        int[] dp = new int[matrix[0].length];
        int max = 0;
        for (int i = 0; i < matrix.length; i++) {
            for (int j = 0; j < matrix[0].length; j++) {
                if (matrix[i][j] == '1') {
                    dp[j] += matrix[i][j] - '0';
                } else {
                    dp[j] = 0;
                }
            }
            max = Math.max(maxRectangle(dp), max);
        }
        return max;
    }

    public int maxRectangle(int[] dp) {
        Deque<Integer> stack = new ArrayDeque<Integer>();
        int n = dp.length, area = 0;
        for (int i = 0; i < n; i++) {
            while (stack.size() > 0 && dp[stack.peek()] > dp[i]) {
                int h = dp[stack.peek()];
                stack.pop();
                int l = stack.size() == 0 ? -1 : stack.peek();
                area = Math.max(area, h * (i - l - 1));
            }
            stack.push(i);
        }
        while (stack.size() > 0 && dp[stack.peek()] > 0) {
            int h = dp[stack.peek()];
            stack.pop();
            int l = stack.size() == 0 ? -1 : stack.peek();
            area = Math.max(area, h * (dp.length - 1 - l));
        }
        return area;
    }

UML Test

This is my uml test page!