《Java解惑》(我的笔记)

异常谜题

循环谜题

表达式谜题

字符谜题

类谜题——这个比较熟悉

库谜题——

1,令人疲惫不堪的测验


本谜题将测试你对递归的了解程度。下面的程序将做些什么呢?
public class Workout {
public static void main(String[] args) {
workHard();
System.out.println("It's nap time.");
}
private static void workHard() {
try {
workHard();
} finally {
workHard();
}
}
}
要不是有try-finally 语句,该程序的行为将非常明显:workHard 方法递归地
调用它自身,直到程序抛出StackOverflowError,在此刻它以这个未捕获的异
常而终止。但是,try-finally 语句把事情搞得复杂了。当它试图抛出
StackOverflowError 时,程序将会在finally 语句块的workHard 方法中终止,
这样,它就递归调用了自己。这看起来确实就像是一个无限循环的秘方,但是这
个程序真的会无限循环下去吗?如果你运行它,它似乎确实是这么做的,但是要
想确认的唯一方式就是分析它的行为。
Java 虚拟机对栈的深度限制到了某个预设的水平。当超过这个水平时,VM 就抛
出StackOverflowError。为了让我们能够更方便地考虑程序的行为,我们假设
栈的深度为3,这比它实际的深度要小得多。现在让我们来跟踪其执行过程。
main 方法调用workHard,而它又从其try 语句块中递归地调用了自己,然后它
再一次从其try 语句块中调用了自己。在此时,栈的深度是3。当workHard 方
法试图从其try 语句块中再次调用自己时,该调用立即就会以
StackOverflowError 而失败。这个错误是在最内部的finally 语句块中被捕获
的,在此处栈的深度已经达到了3。在那里,workHard 方法试图递归地调用它自
己,但是该调用却以StackOverflowError 而失败。这个错误将在上一级的
finally 语句块中被捕获,在此处站的深度是2。该finally 中的调用将与相对
应的try 语句块具有相同的行为:最终都会产生一个StackOverflowError。这
似乎形成了一种模式,而事实也确实如此。

 

 

2,切掉类

请考虑下面的两个类:
public class Strange1 {
public static void main(String[] args) {
try {
Missing m = new Missing();
} catch (java.lang.NoClassDefFoundError ex) {
System.out.println("Got it!");
}
}
}
public class Strange2 {
public static void main(String[] args) {
Missing m;
try {
m = new Missing();
} catch (java.lang.NoClassDefFoundError ex) {
System.out.println("Got it!");
}
}
}
Strange1 和Strange2 都用到了下面这个类:
class Missing {
Missing() { }
}
如果你编译所有这三个类,然后在运行Strange1 和Strange2 之前删除
Missing.class 文件,你就会发现这两个程序的行为有所不同。其中一个抛出了
一个未被捕获的NoClassDefFoundError 异常,而另一个却打印出了Got it!

 

 

3,域的问题

static void copy(String src, String dest) throws IOException {
InputStream in = null;
OutputStream out = null;
try {
in = new FileInputStream(src);
out = new FileOutputStream(dest);
byte[] buf = new byte[1024];
int n;
while ((n = in.read(buf)) > 0)
out.write(buf, 0, n);
} finally {
if (in != null) in.close();
if (out != null) out.close();
}
}
这个程序看起来已经面面俱到了。其流域(in 和out)被初始化为null,并且
新的流一旦被创建,它们马上就被设置为这些流域的新值。对于这些域所引用的
流,如果不为空,则finally 语句块会将其关闭。即便在拷贝操作引发了一个
IOException 的情况下,finally 语句块也会在方法返回之前执行。出什么错了
呢?
问题在finally 语句块自身中。close 方法也可能会抛出IOException 异常。如
果这正好发生在in.close 被调用之时,那么这个异常就会阻止out.close 被调
用,从而使输出流仍保持在开放状态。

 

4:不情愿的构造器(递归执行异常)
尽管在一个方法声明中看到一个throws 子句是很常见的,但是在构造器的声明
中看到一个throws 子句就很少见了。下面的程序就有这样的一个声明。那么,
它将打印出什么呢?
public class Reluctant {
private Reluctant internalInstance = new Reluctant();
public Reluctant() throws Exception {
throw new Exception("I'm not coming out");
}
public static void main(String[] args) {
try {
Reluctant b = new Reluctant();
System.out.println("Surprise!");
} catch (Exception ex) {
System.out.println("I told you so");
}
}
}

 

5,优柔寡断(返回false)
下面这个可怜的小程序并不能很好地做出其自己的决定。它的decision 方法将
返回true,但是它还返回了false。那么,它到底打印的是什么呢?甚至,它是
合法的吗?
public class Indecisive {
public static void main(String[] args) {
System.out.println(decision());
}
static boolean decision() {
try {
return true;
} finally {
return false;
}
}
}

 

 

6,JAVA不具有目的确定类型的特性

final long MICROS_PER_DAY = 24 * 60 * 60 * 1000 * 1000;

此种计算会以int类型计算且已经“溢出”了。

正确的写法应该是

final long MICROS_PER_DAY=24L*60*60*1000*1000;

 

7,在处理String和Byte的时候,要特别的小心字符集,尽量做到与平台无关性。

8,混淆的construct

public class Confusing {
private Confusing(Object o) {
System.out.println("Object");
}
private Confusing(double[] dArray) {
System.out.println("double array");
}
public static void main(String[] args) {
new Confusing(null);
}
}

 

Java 的重载解析过程是以两阶段运行的。第一阶段选取所有可获得并且可应用
的方法或构造器。第二阶段在第一阶段选取的方法或构造器中选取最精确的一
个。如果一个方法或构造器可以接受传递给另一个方法或构造器的任何参数,那
么我们就说第一个方法比第二个方法缺乏精确性。

所以这里的Confusing(double[] dArray)比Confusing(Object o)更精确。、

 

9,static都是在编译期确定的。

 

 

10,神论

 从前有一个人,他认为世上只有一只不寻常的狗,所以他写出了如下的类,将它
作为一个单件(singleton)[Gamma95]:
public class Dog extends Exception {
public static final Dog INSTANCE = new Dog();
private Dog() {}
public String toString(){
return "Woof";
}
}
结果证明这个人的做法是错误的。你能够在这个类的外部不使用反射来创建出第
2 个Dog 实例吗?
这个类可能看起来像一个单件,但它并不是。问题在于,Dog 扩展了Exception,
而Exception 实现了java.io.Serializable。这就意味着Dog 是可序列化的
(serializable),并且解序列(deserialization)会创建一个隐藏的构造器。
正如下面的这段程序所演示的,如果你序列化了Dog.INSTANCE,然后对得到的
字节序列(byte sequence)进行解序列,最后你就会得到另外一个Dog。该程
序打印的是false,表示新的Dog 实例和原来的那个实例是不同的,并且它还打
印了Woof,说明新的Dog 实例也具有相应的功能:
import java.io.*;
public class CopyDog{ // Not to be confused with copycat
public static void main(String[] args){
Dog newDog = (Dog) deepCopy(Dog.INSTANCE);
System.out.println(newDog == Dog.INSTANCE);
System.out.println(newDog);
}
// This method is very slow and generally a bad idea!
static public Object deepCopy(Object obj){
try{
ByteArrayOutputStream bos =
new ByteArrayOutputStream();
new ObjectOutputStream(bos).writeObject(obj);
ByteArrayInputStream bin =
new ByteArrayInputStream(bos.toByteArray());
return new ObjectInputStream(bin).readObject();
} catch(Exception e) {
throw new IllegalArgumentException(e);
}
}
}
要订正这个问题,可在Dog 中添加一个readResolve 方法,它可以将那个隐藏的
构造器转变为一个隐藏的静态工厂(static factory),以返回原来那个的Dog
[EJ Items 2,57]。在Dog 中添加了这个方法之后,CopyDog 将打印true 而不是
false,表示那个“复本”实际上就是原来的那个实例:
private Object readResolve(){
// Accept no substitues!
return INSTANCE;
}

 

 

11,不要硬编码目录分隔符 /、\和换行符号 \r\n、\n

 

12,为了避免实现类的命名冲突而发生覆盖或隐藏,父类尽量用private成员变量。

 

override:(只对于一个同名的方法而言)

class Base {
public void f() { }
}
class Derived extends Base {
public void f() { } // overrides Base.f()
}

 

 

hide:(对于一个同名的成员变量、静态方法而言)

class Base {
public static void f() { }
}
class Derived extends Base {
private static void f() { } // hides Base.f()
}

隐藏后可通过超类来访问父类被隐藏的资源。

 

如果有final,final 意味着该方法不能被覆写(对实例方法而言)或者隐藏(对静态方法
而言)[JLS 8.4.3.3]。但对于成员变量,final 意味着该域不能被赋值超过一次,却可以被覆盖或隐藏。

 

 

 

13:

public class Twisted {
private final String name;
Twisted(String name) {
this.name = name;
}
private String name() {
return name;
}
private void reproduce() {
new Twisted("reproduce") {
void printName() {
System.out.println(name());
}
}.printName();
}
public static void main(String[] args) {
new Twisted("main").reproduce();
}
}

本例中的Twisted 类,所有的本地的、内部的、嵌
套的和匿名的类都可以毫无限制地访问彼此的成员[JLS 6.6.1]。这是一个欢乐
的大家庭。匿名类中对于printName
方法的调用必须关联到外围(“main”)实例而不是当前(“reproduce”)实例

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值