Java谜题畅读版之更多的库谜题

谜题78:反射的污染

这个谜题举例说明了一个关于反射的简单应用。这个程序会打印出什么呢?
  1. importjava.util.*;
  2. importjava.lang.reflect.*;
  3. publicclassReflector{
  4. publicstaticvoidmain(String[]args)throwsException{
  5. Set<String>s=newHashSet<String>();
  6. s.add("foo");
  7. Iteratorit=s.iterator();
  8. Methodm=it.getClass().getMethod("hasNext");
  9. System.out.println(m.invoke(it));
  10. }
  11. }
如果你跑这个程序,会出现下面的错误:
Exception in thread "main" java.lang.IllegalAccessException:
Class test.puzzlers.p8.Reflector can not access a member of class java.util.HashMap$HashIterator with modifiers "public final"
这个错误让人看不懂: 既然是public的,就应该可以访问.
问题并不在于该方法的访问级别(access level),而在于该方法所在的类型的访问级别。迭代器java.util.HashMap.KeyIterator是私有的嵌套类,它是默认的包访问权限, 访问位于其他包中的非公共类型的成员是不合法的.
下面的程序同样违反了这个规则:
  1. packagelibrary;
  2. publicclassApi{
  3. staticclassPackagePrivate{}
  4. publicstaticPackagePrivatemember=newPackagePrivate();
  5. }
  6. packageclient;
  7. importlibrary.Api;
  8. classClient{
  9. publicstaticvoidmain(String[]args){
  10. System.out.println(Api.member.hashCode());
  11. }
  12. }
实际上,这个问题并不会在普通的非反射的访问中出现,因为api调用者如果不通过反射的话,是无法touch到私有的嵌套类.

谜题79:这是狗的生活

下面的程序看起来很简单, 创建一个宠物, 然后它就开始吃玩睡吃玩睡...
  1. publicclassPet{
  2. publicfinalStringname;
  3. publicfinalStringfood;
  4. publicfinalStringsound;
  5. publicPet(Stringname,Stringfood,Stringsound){
  6. this.name=name;
  7. this.food=food;
  8. this.sound=sound;
  9. }
  10. publicvoideat(){
  11. System.out.println(name+":Mmmmm,"+food);
  12. }
  13. publicvoidplay(){
  14. System.out.println(name+":"+sound+""+sound);
  15. }
  16. publicvoidsleep(){
  17. System.out.println(name+":Zzzzzzz...");
  18. }
  19. publicvoidlive(){
  20. //finalPetme=this;
  21. newThread(){
  22. publicvoidrun(){
  23. while(true){
  24. eat();
  25. play();
  26. sleep();
  27. //me.sleep();
  28. }
  29. }
  30. }.start();
  31. }
  32. publicstaticvoidmain(String[]args){
  33. newPet("Fido","beef","Woof").live();
  34. }
  35. }
看出来了吗?在Thread里面调用了sleep.
修正的方法就如同我注释调的一样.

谜题80:更深层的反射

下面这个程序通过打印一个由反射创建的对象来产生输出。那么它会打印出什么呢?
  1. publicclassOuter{
  2. publicstaticvoidmain(String[]args)throwsException{
  3. newOuter().greetWorld();
  4. }
  5. privatevoidgreetWorld()throwsException{
  6. System.out.println(Inner.class.newInstance());
  7. }
  8. publicclassInner{
  9. publicStringtoString(){
  10. return"Helloworld";
  11. }
  12. }
  13. }
如果运行这个程序, 它会报错:
Exception in thread "main" InstantiationException: Outer$Inner
at java.lang.Class.newInstance0(Class.java:335)
at java.lang.Class.newInstance(Class.java:303)
at Outer.greetWorld(Outer.java:7)
at Outer.main(Outer.java:3)

为什么会抛出这个异常呢?从5.0版本开始, 关于Class.newInstance的文档叙述道:如果那个Class对象“代表了一个抽象类(abstract class),一个接口(interface),一个数组类(array class),一个原始类型(primitive type),或者是空(void);或者这个类没有任何空的[也就是无参数的]构造器, 就抛出InstantiationException异常.
在这个例子中,Outer.Inner没有空的构造器,因为对与非静态的内部类, 它的任何一个构造函数在编译的时候会被隐藏地,多传一个参数进去, 就是它的外部类的实例.
因为内部类不能直接实例化, 一个常规的内部类实例化过程是这样的:Outer outer = new Outer(); Outer.Inner inner = outer.new Inner();
对本例来说, 也不是没有办法通过newInstance, 只是需要得到那个特殊的构造函数即可:
  1. privatevoidgreetWorld()throwsException{
  2. Constructorc=Inner.class.getConstructor(Outer.class);
  3. System.out.println(c.newInstance(Outer.this));
  4. }
由于本例的Inner实际上不需要外部的实例, 所以可以把它声明为静态内部类.

谜题81:烧焦到无法识别

下面这个程序看起来是在用一种特殊的方法做一件普通的事。那么,它会打印出什么呢?
  1. publicclassGreeter{
  2. publicstaticvoidmain(String[]args){
  3. Stringgreeting="HelloWorld";
  4. for(inti=0;i<greeting.length();i++)
  5. System.out.write(greeting.charAt(i));
  6. }
  7. }
这个程序本身没有问题,问题处在缓冲上. System.out是PrintStream的实例, 而且被创建为自动刷新的;这意味着当一个字节数组(byte array)被写入,或者某个println方法被调用,或者一个换行字符或字节(‘\n’)被写入之后,PrintStream类型的flush方法就会被自动地调用。
但是,唯独write(byte)方法在没有遇到\n是不会自动刷新的.

谜题83:诵读困难者的一神论

下面的程序是单例模式, 请问有没有办法创建出多于1个的不同的实例出来? 提醒一下, 熟悉单例模式的可能认为这个程序是线程不安全的, 但是这个程序不需要考虑通过多线程凑巧.
  1. publicclassDogextendsException{
  2. publicstaticfinalDogINSTANCE=newDog();
  3. privateDog(){}
  4. publicStringtoString(){
  5. return"Woof";
  6. }
  7. }
看程序:
  1. importjava.io.*;
  2. publicclassDogextendsException{
  3. //privatestaticfinallongserialVersionUID=-7156412195888553079L;
  4. publicstaticfinalDogINSTANCE=newDog();
  5. privateDog(){}
  6. publicStringtoString(){
  7. return"Woof";
  8. }
  9. publicstaticvoidmain(String[]args)throwsIOException,ClassNotFoundException{
  10. Dogd=Dog.INSTANCE;
  11. ByteArrayOutputStreambro=newByteArrayOutputStream();
  12. ObjectOutputStreamoout=newObjectOutputStream(bro);
  13. oout.writeObject(d);
  14. ObjectInputStreamoin=newObjectInputStream(newByteArrayInputStream(bro.toByteArray()));
  15. Dogd1=(Dog)oin.readObject();
  16. System.out.println(d1==d);
  17. }
  18. }

谜题84:被粗暴地中断

在下面这个程序中,一个线程试图中断自己,然后检查中断是否成功。它会打印什么呢?
  1. publicclassSelfInterruption{
  2. publicstaticvoidmain(String[]args){
  3. Thread.currentThread().interrupt();
  4. if(Thread.interrupted()){
  5. System.out.println("Interrupted:"+
  6. Thread.interrupted());
  7. }else{
  8. System.out.println("Notinterrupted:"+
  9. Thread.interrupted());
  10. }
  11. }
  12. }
它打印的是 Interrupted: false。
但是, 你如果debug一下这个程序, 你会发现, 他走进了if这个块.
问题处在Thread.interrupted(), 这个静态方法会在查阅线程状态之后, 清除状态.

谜题85:惰性初始化

下面的程序很懒惰, 它连静态域的初始化工作都不想自己做, 而是交给一个后台线程去做, 它会打印什么?
  1. publicclassLazy{
  2. privatestaticbooleaninitialized=false;
  3. static{
  4. Threadt=newThread(newRunnable(){
  5. publicvoidrun(){
  6. initialized=true;
  7. }
  8. });
  9. t.start();
  10. try{
  11. t.join();
  12. }catch(InterruptedExceptione){
  13. thrownewAssertionError(e);
  14. }
  15. }
  16. publicstaticvoidmain(String[]args){
  17. System.out.println(initialized);
  18. }
  19. }
先理解一下这个程序的工作流程: 主线程启动后, 发现需要读取Lazy.initialized, 它发现Lazy并没有初始化, 于是开始初始化它(调用<clinit>()方法), 它开启一个子线程并启动它去初始化, 然后主线程调用join等待初始化结束.
问题的关键是当一个线程访问一个类的某个成员的时候,它会去检查这个类是否已经被初始化。结果有4种可能的情况 :
这个类尚未被初始化。 //于是开始初始化
这个类正在被当前线程初始化:这是对初始化的递归请求。//继续初始化
这个类正在被其他线程而不是当前线程初始化。 //挂起并等待其他线程初始化它完毕

这个类已经被初始化。 // 可以使用

所以, 最终主线程等待子线程运行结束, 而子线程也在等待主线程初始化Lazy结束. 死锁了.

你可能感兴趣的:(java)