JAVA编程中应该注意的问题

Exceiption

  • 使用try-catch-finally时,如果catch子句没有的话,异常有可能会丢失,这是一个非常严重的错误。所以,我们在使用try-catch-finally时,一定要增加一个catch(Exception ex)子句。另外一个策略就是将所有将抛出异常的方法都包装到try-catch中去,而且注意catch不要漏掉你不希望漏掉的异常。

  • 当们知道在我们的方法里面将会有可能抛出一个异常,我们是在这个方法里面就处理,还是处理一些留一些给上面的对象去处理,还是完全交给上面的对象去处理呢?我现在并没有好的标准来分析这个问题。

  • 可以用一个非常简单的方法对“检查型异常”进行包装,使之成为一个“非检查型异常”,而且要需要捕捉的时候能够使用getCause()方法捕捉并处理这个异常,下面是一个例子:

    class WrapCheckedException {
      void throwRuntimeException(int type) {
        try {
          switch(type) {
            case 0: throw new FileNotFoundException();
            case 1: throw new IOException();
            case 2: throw new RuntimeException("Where am I?");
            default: return;
          }
        } catch(Exception e) { // Adapt to unchecked:
          throw new RuntimeException(e);
        }
      }
    }


    class SomeOtherException extends Exception {}

    public class TurnOffChecking { private static Test monitor = new Test();
      public static void main(String[] args) {
        WrapCheckedException wce = new WrapCheckedException();
        // You can call f() without a try block, and let
        // RuntimeExceptions go out of the method:
        wce.throwRuntimeException(3);
        // Or you can choose to catch exceptions:
        for(int i = 0; i < 4; i++)
          try {
            if(i < 3)
              wce.throwRuntimeException(i);
            else
              throw new SomeOtherException();
          } catch(SomeOtherException e) {
              System.out.println("SomeOtherException: " + e);
          } catch(RuntimeException re) {
            try {
              throw re.getCause();
            } catch(FileNotFoundException e) {
              System.out.println(
                "FileNotFoundException: " + e);
            } catch(IOException e) {
              System.out.println("IOException: " + e);
            } catch(Throwable e) {
              System.out.println("Throwable: " + e);
            }
          }
        monitor.expect(new String[] {
          "FileNotFoundException: " +
          "java.io.FileNotFoundException",
          "IOException: java.io.IOException",
          "Throwable: java.lang.RuntimeException: Where am I?",
          "SomeOtherException: SomeOtherException"
        });
      }
    } ///:~


Finally

  • 不要乱用finally。我们很多时候不能确定在什么时候发生清理工作,或是我们不能确定finally会被调用。比如说我现在打开一个文件,然后有一个操作是从文件里面读取一段内容。这个读取的内容可以连续进行多次,直动用户不想用了为止。所以,我们根本不可能知道用户在什么会需要关闭文件这个操作,这个时候我们千万不要把文件关闭的动作放到finally中去执行。




Loop

  • 一个经常出的逻辑错误。

    Here is a common mistake people make when they are trying to do nested iteration over two collections:

    List suits = ...;
    List ranks = ...;
    List sortedDeck = new ArrayList();
    
    // BROKEN - throws NoSuchElementException!
    for (Iterator i = suits.iterator(); i.hasNext(); )
        for (Iterator j = ranks.iterator(); j.hasNext(); )
            sortedDeck.add(new Card(i.next(), j.next()));
    

    Can you spot the bug? Don't feel bad if you can't. Many expert programmers have made this mistake at one time or another. The problem is that the next method is being called too many times on the “outer” collection (suits). It is being called in the inner loop for both the outer and inner collections, which is wrong. In order to fix it, you have to add a variable in the scope of the outer loop to hold the suit:

    // Fixed, though a bit ugly
    for (Iterator i = suits.iterator(); i.hasNext(); ) {
        Suit suit = (Suit) i.next();
        for (Iterator j = ranks.iterator(); j.hasNext(); )
            sortedDeck.add(new Card(suit, j.next()));
    }
    

    So what does all this have to do with the for-each construct? It is tailor-made for nested iteration! Feast your eyes:

    for (Suit suit : suits)
        for (Rank rank : ranks)
            sortedDeck.add(new Card(suit, rank));
    


Array

  • 一个一维数组调用clone()方法是深复制;多维数组调用clone()方法是浅复制。

  • 假定B类是A类的子类,v是一个A类型组组。v能保存B的实例。但是保存B的实例后,v[n]会是一个B类型的句柄,当v[n] = new A();操作时会发生一个运行期异常,但是v = new A[n];还是正确的操作。因为,v依然的类型还是A,只是v[n]的类型改变了。



Nesting interfaces

  • 实现接口的时候,不一定要实现嵌套在里面的接口。

  • 私有接口只有在定义它们的类里面实现。

  • 接口里面内嵌的接口不能是private的。




Unintended recursion

  • 在toString()方法里面用this,经常容易产生一个递归的无限循环。如:
      public String toString() {
        return " InfiniteRecursion address: " + this + "/n";
      }

  • 在任何方法内调用自己也会产生一个递归的无限循环。如:void fo() {this.fo();}




Inner Classes

  • 在内部类除了内部类的宿主类的子类外,其它类都不能直接创建内部类的实例。如果一个内部类被为private,那么即使是它的子类也不能够创建这个内部类的实例。例如:如果B是A的内部类,C继承A,D不继承A。那么我们在C中可以这样创建内部类B的实例:

    A.B b = new A.B()

    在D中不能这样做,在D中要创建内部类B的实例的话可能这样:

    A a = new A();
    A.B b = a.new B();

  • 除类外任意作用域里面的内部类(包括Local Inner Classes局部内部类和Anonymous Inner Classes匿名内部类)的访问范围只局限于它的作用域。如if() {class A {}},这个A类只有在if块内可以访问。使用局部内部类而不使用匿名内部类的唯一原因就是:你需要创建多个那种类的对象。

  • 在同一个地方不同的类里面可以用相同名字的内部类。如A和B里面都可以用内部类C,它们不会冲突。因为访问任何类部类都必须跟它的外部类名字连在一起,如:A.C。

  • 定义匿名内部类时,如果还要用到外面的对象,就必须把这个参数的reference声明为final。如:

    Object test(final String str) {
        return new Object() {
            private String value = str;
        };
    }

  • 要给匿名内部类进行初始化构造,有两个方法:一是实例话对象来初始化匿名内部类,如:new Object(i) {};。这儿对过i来对Object进行初始化,从而达到对这个匿名内部类初始化的效果。二是在匿名内部类的初始化部分来实现,不过这儿只有一个区域,不可能再实现对它的重构。也就是说,只有这一个构造函数。如:

    new Object(final String str) {
        private String value;
    {
        value = str;
        if(value.equals(“Test“))
            value = str + “OK!“;
    }
    //to do something...
    };
  • 内部类可以直接访问他的宿主类的成员。这一点非常的有用。如:

    public class Test {
        Object[] object = new Object[5];
        private class InnerTest {
            public void readObject(int i) {
                return object[i];
            }
        }
    }
  • 普通内部类中不能有static数据,static数据成员和嵌套类(nested classes)。

  • 继承内部类的时候,派生类必须在构造函数中给一个宿主类类型句柄,然后还要进行一点特殊的操作,如:
    class WithInner {
      class Inner {}
    }

    public class InheritInner extends WithInner.Inner {
      //! InheritInner() {} // Won't compile
      InheritInner(WithInner wi) {
        wi.super();
      }
      public static void main(String[] args) {
        WithInner wi = new WithInner();
        InheritInner ii = new InheritInner(wi);
      }
    } ///:~

  • 继承类的内部类跟父类的内部类同名,不会覆盖父类的内部类。他们是两个独立的实例,都有自己的命名空间。如A有一个内部类名字叫C,B是A的子类,B也声明一个内部类名字也叫C,A中的C是用A.C来表示的,而B中的C是用B.C来表示的,他们的名字不会有冲突。我们要求可以在B中通过C extends A.C,这样B.C就成为了A.C的一个子类,可以达到我们的一些特殊的要求。

  • nested classes

    1. 无所宿主类对象就能够创建嵌套类对象。

    2. 不能在嵌套类对象里面访问非static宿主类对象。

    3. 能访问static数据,static数据成员和嵌套类。

  • 为什么要使用内部类

    1. 每个内部类能够独立的继承某个“实现(implementation) ”,因此,内部类不会受“宿主类是否已经继承了别的实现”的约束。如:
      class A {}
      abstract class B {}
      class C extends A {
          B makeB() {
              return new B() {};
          }
      }

      public class MutiImplementation {
          public static void holdA(A a) {}
          public static void holdB(B b) {} 

          public static void main(String[] args) {
              C c = new C();
              holdA(c);
              holdB(c.makeB());
          }
      }

    2. 内部类能够有多个实例,而每个又都可以是它自己的,与宿主对象无关的状态信息。

    3. 一个宿主类能够放上好几个内部类,而每个内部类又都可以以各自不同的方式来实现同一个接口,或实现同一个类。

    4. 内部类对象创建的时间不受宿主类对象的创建的约束。

    5. 内部类不存在让人头晕的“is-a”关系。它是一个独立的实体。



      举例来说:Sequence.java要是没有内部类,我们用它实现一个selector接口,那么你只能说“sequence是一个selector”,于是每个Sequence里面就只能有一个selector。但是用内部类,你可以再定义一个方法getRSelector(),让它返回一个倒过来访问这个序列的selector。内部类提供了这种灵活性。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值