2005年01月29日

Summary

Polymorphism means “different forms.” In object-oriented programming, you have the same face (the common interface in the base class) and different forms using that face: the different versions of the dynamically bound methods. Feedback

You’ve seen in this chapter that it’s impossible to understand, or even create, an example of polymorphism without using data abstraction and inheritance. Polymorphism is a feature that cannot be viewed in isolation (like a switch statement can, for example), but instead works only in concert, as part of a “big picture” of class relationships. People are often confused by other, non-object-oriented features of Java, like method overloading, which are sometimes presented as object-oriented. Don’t be fooled: If it isn’t late binding, it isn’t polymorphism. Feedback

To use polymorphism—and thus object-oriented techniques—effectively in your programs, you must expand your view of programming to include not just members and messages of an individual class, but also the commonality among classes and their relationships with each other. Although this requires significant effort, it’s a worthy struggle, because the results are faster program development, better code organization, extensible programs, and easier code maintenance. Feedback

2005年01月27日

Inheritance and cleanup

When using composition and inheritance to create a new class, most of the time you won’t have to worry about cleaning up; subobjects can usually be left to the garbage collector. If you do have cleanup issues, you must be diligent and create a dispose( ) method (the name I have chosen to use here; you may come up with something better) for your new class. And with inheritance, you must override dispose( ) in the derived class if you have any special cleanup that must happen as part of garbage collection. When you override dispose( ) in an inherited class, it’s important to remember to call the base-class version of dispose( ), since otherwise the base-class cleanup will not happen. The following example demonstrates this:

//: c07:Frog.java
// Cleanup and inheritance.
import com.bruceeckel.simpletest.*;

class Characteristic {
  private String s;
  Characteristic(String s) {
    this.s = s;
    System.out.println("Creating Characteristic " + s);
  }
  protected void dispose() {
    System.out.println("finalizing Characteristic " + s);
  }
}

class Description {
  private String s;
  Description(String s) {
    this.s = s;
    System.out.println("Creating Description " + s);
  }
  protected void dispose() {
    System.out.println("finalizing Description " + s);
  }
}

class LivingCreature {
  private Characteristic p = new Characteristic("is alive");
  private Description t =
    new Description("Basic Living Creature");
  LivingCreature() {
    System.out.println("LivingCreature()");
  }
  protected void dispose() {
    System.out.println("LivingCreature dispose");
    t.dispose();
    p.dispose();
  }
}

class Animal extends LivingCreature {
  private Characteristic p= new Characteristic("has heart");
  private Description t =
    new Description("Animal not Vegetable");
  Animal() {
    System.out.println("Animal()");
  }
  protected void dispose() {
    System.out.println("Animal dispose");
    t.dispose();
    p.dispose();
    super.dispose();
  }
}

class Amphibian extends Animal {
  private Characteristic p =
    new Characteristic("can live in water");
  private Description t =
    new Description("Both water and land");
  Amphibian() {
    System.out.println("Amphibian()");
  }
  protected void dispose() {
    System.out.println("Amphibian dispose");
    t.dispose();
    p.dispose();
    super.dispose();
  }
}

public class Frog extends Amphibian {
  private static Test monitor = new Test();
  private Characteristic p = new Characteristic("Croaks");
  private Description t = new Description("Eats Bugs");
  public Frog() {
    System.out.println("Frog()");
  }
  protected void dispose() {
    System.out.println("Frog dispose");
    t.dispose();
    p.dispose();
    super.dispose();
  }
  public static void main(String[] args) {
    Frog frog = new Frog();
    System.out.println("Bye!");
    frog.dispose();
    monitor.expect(new String[] {
      "Creating Characteristic is alive",
      "Creating Description Basic Living Creature",
      "LivingCreature()",
      "Creating Characteristic has heart",
      "Creating Description Animal not Vegetable",
      "Animal()",
      "Creating Characteristic can live in water",
      "Creating Description Both water and land",
      "Amphibian()",
      "Creating Characteristic Croaks",
      "Creating Description Eats Bugs",
      "Frog()",
      "Bye!",
      "Frog dispose",
      "finalizing Description Eats Bugs",
      "finalizing Characteristic Croaks",
      "Amphibian dispose",
      "finalizing Description Both water and land",
      "finalizing Characteristic can live in water",
      "Animal dispose",
      "finalizing Description Animal not Vegetable",
      "finalizing Characteristic has heart",
      "LivingCreature dispose",
      "finalizing Description Basic Living Creature",
      "finalizing Characteristic is alive"
    });
  }
} ///:~


Each class in the hierarchy also contains a member objects of types Characteristic and Description, which must also be disposed. The order of disposal should be the reverse of the order of initialization, in case one subobject is dependent on another. For fields, this means the reverse of the order of declaration (since fields are initialized in declaration order). For base classes (following the form that’s used in C++ for destructors), you should perform the derived-class cleanup first, then the base-class cleanup. That’s because the derived-class cleanup could call some methods in the base class that require the base-class components to be alive, so you must not destroy them prematurely. From the output you can see that all parts of the Frog object are disposed in reverse order of creation. Feedback

From this example, you can see that although you don’t always need to perform cleanup, when you do, the process requires care and awareness. Feedback

Order of constructor calls

The order of constructor calls was briefly discussed in Chapter 4 and again in Chapter 6, but that was before polymorphism was introduced. Feedback

A constructor for the base class is always called during the construction process for a derived class, chaining up the inheritance hierarchy so that a constructor for every base class is called. This makes sense because the constructor has a special job: to see that the object is built properly. A derived class has access to its own members only, and not to those of the base class (whose members are typically private). Only the base-class constructor has the proper knowledge and access to initialize its own elements. Therefore, it’s essential that all constructors get called, otherwise the entire object wouldn’t be constructed. That’s why the compiler enforces a constructor call for every portion of a derived class. It will silently call the default constructor if you don’t explicitly call a base-class constructor in the derived-class constructor body. If there is no default constructor, the compiler will complain. (In the case where a class has no constructors, the compiler will automatically synthesize a default constructor.) Feedback

Let’s take a look at an example that shows the effects of composition, inheritance, and polymorphism on the order of construction:

//: c07:Sandwich.java
// Order of constructor calls.
package c07;
import com.bruceeckel.simpletest.*;

class Meal {
  Meal() { System.out.println("Meal()"); }
}

class Bread {
  Bread() { System.out.println("Bread()"); }
}

class Cheese {
  Cheese() { System.out.println("Cheese()"); }
}

class Lettuce {
  Lettuce() { System.out.println("Lettuce()"); }
}

class Lunch extends Meal {
  Lunch() { System.out.println("Lunch()"); }
}

class PortableLunch extends Lunch {
  PortableLunch() { System.out.println("PortableLunch()");}
}

public class Sandwich extends PortableLunch {
  private static Test monitor = new Test();
  private Bread b = new Bread();
  private Cheese c = new Cheese();
  private Lettuce l = new Lettuce();
  public Sandwich() {
    System.out.println("Sandwich()");
  }
  public static void main(String[] args) {
    new Sandwich();
    monitor.expect(new String[] {
      "Meal()",
      "Lunch()",
      "PortableLunch()",
      "Bread()",
      "Cheese()",
      "Lettuce()",
      "Sandwich()"
    });
  }
} ///:~


This example creates a complex class out of other classes, and each class has a constructor that announces itself. The important class is Sandwich, which reflects three levels of inheritance (four, if you count the implicit inheritance from Object) and three member objects. You can see the output when a Sandwich object is created in main( ). This means that the order of constructor calls for a complex object is as follows: Feedback

  1. The base-class constructor is called. This step is repeated recursively such that the root of the hierarchy is constructed first, followed by the next-derived class, etc., until the most-derived class is reached. Feedback
  2. Member initializers are called in the order of declaration. Feedback
  3. The body of the derived-class constructor is called. Feedback

The order of the constructor calls is important. When you inherit, you know all about the base class and can access any public and protected members of the base class. This means that you must be able to assume that all the members of the base class are valid when you’re in the derived class. In a normal method, construction has already taken place, so all the members of all parts of the object have been built. Inside the constructor, however, you must be able to assume that all members that you use have been built. The only way to guarantee this is for the base-class constructor to be called first. Then when you’re in the derived-class constructor, all the members you can access in the base class have been initialized. Knowing that all members are valid inside the constructor is also the reason that, whenever possible, you should initialize all member objects (that is, objects placed in the class using composition) at their point of definition in the class (e.g., b, c, and l in the preceding example). If you follow this practice, you will help ensure that all base class members and member objects of the current object have been initialized. Unfortunately, this doesn’t handle every case, as you will see in the next section. Feedback

Pitfall: “overriding” private methods

Here’s something you might innocently try to do:

//: c07:PrivateOverride.java
// Abstract classes and methods.
import com.bruceeckel.simpletest.*;

public class PrivateOverride {
  private static Test monitor = new Test();
  private void f() {
    System.out.println("private f()");
  }
  public static void main(String[] args) {
    PrivateOverride po = new Derived();
    po.f();
    monitor.expect(new String[] {
      "private f()"
    });
  }
}

class Derived extends PrivateOverride {
  public void f() {
    System.out.println("public f()");
  }
} ///:~


You might reasonably expect the output to be “public f( )”, but a private method is automatically final, and is also hidden from the derived class. So Derived’s f( ) in this case is a brand new method; it’s not even overloaded, since the base-class version of f( ) isn’t visible in Derived. Feedback

The result of this is that only non-private methods may be overridden, but you should watch out for the appearance of overriding private methods, which generates no compiler warnings, but doesn’t do what you might expect. To be clear, you should use a different name from a private base-class method in your derived class. Feedback

Initialization with inheritance

It’s helpful to look at the whole initialization process, including inheritance, to get a full picture of what happens. Consider the following example:

//: c06:Beetle.java
// The full process of initialization.
import com.bruceeckel.simpletest.*;

class Insect {
  protected static Test monitor = new Test();
  private int i = 9;
  protected int j;
  Insect() {
    System.out.println("i = " + i + ", j = " + j);
    j = 39;
  }
  private static int x1 =
    print("static Insect.x1 initialized");
  static int print(String s) {
    System.out.println(s);
    return 47;
  }
}

public class Beetle extends Insect {
  private int k = print("Beetle.k initialized");
  public Beetle() {
    System.out.println("k = " + k);
    System.out.println("j = " + j);
  }
  private static int x2 =
    print("static Beetle.x2 initialized");
  public static void main(String[] args) {
    System.out.println("Beetle constructor");
    Beetle b = new Beetle();
    monitor.expect(new String[] {
      "static Insect.x1 initialized",
      "static Beetle.x2 initialized",
      "Beetle constructor",
      "i = 9, j = 0",
      "Beetle.k initialized",
      "k = 47",
      "j = 39"
    });
  }
} ///:~


The first thing that happens when you run Java on Beetle is that you try to access Beetle.main( ) (a static method), so the loader goes out and finds the compiled code for the Beetle class (this happens to be in a file called Beetle.class). In the process of loading it, the loader notices that it has a base class (that’s what the extends keyword says), which it then loads. This will happen whether or not you’re going to make an object of that base class. (Try commenting out the object creation to prove it to yourself.) Feedback

If the base class has a base class, that second base class would then be loaded, and so on. Next, the static initialization in the root base class (in this case, Insect) is performed, and then the next derived class, and so on. This is important because the derived-class static initialization might depend on the base class member being initialized properly. Feedback

At this point, the necessary classes have all been loaded so the object can be created. First, all the primitives in this object are set to their default values and the object references are set to null—this happens in one fell swoop by setting the memory in the object to binary zero. Then the base-class constructor will be called. In this case the call is automatic, but you can also specify the base-class constructor call (as the first operation in the Beetle( ) constructor) by using super. The base class construction goes through the same process in the same order as the derived-class constructor. After the base-class constructor completes, the instance variables are initialized in textual order. Finally, the rest of the body of the constructor is executed. Feedback

Final methods

There are two reasons for final methods. The first is to put a “lock” on the method to prevent any inheriting class from changing its meaning. This is done for design reasons when you want to make sure that a method’s behavior is retained during inheritance and cannot be overridden. Feedback

The second reason for final methods is efficiency. If you make a method final, you are allowing the compiler to turn any calls to that method into inline calls. When the compiler sees a final method call, it can (at its discretion) skip the normal approach of inserting code to perform the method call mechanism (push arguments on the stack, hop over to the method code and execute it, hop back and clean off the stack arguments, and deal with the return value) and instead replace the method call with a copy of the actual code in the method body. This eliminates the overhead of the method call. Of course, if a method is big, then your code begins to bloat, and you probably won’t see any performance gains from inlining, since any improvements will be dwarfed by the amount of time spent inside the method. It is implied that the Java compiler is able to detect these situations and choose wisely whether to inline a final method. However, it’s best to let the compiler and JVM handle efficiency issues and make a method final only if you want to explicitly prevent overriding.[31] Feedback

final and private

Any private methods in a class are implicitly final. Because you can’t access a private method, you can’t override it. You can add the final specifier to a private method, but it doesn’t give that method any extra meaning. Feedback

This issue can cause confusion, because if you try to override a private method (which is implicitly final), it seems to work, and the compiler doesn’t give an error message:

//: c06:FinalOverridingIllusion.java
// It only looks like you can override
// a private or private final method.
import com.bruceeckel.simpletest.*;

class WithFinals {
  // Identical to "private" alone:
  private final void f() {
    System.out.println("WithFinals.f()");
  }
  // Also automatically "final":
  private void g() {
    System.out.println("WithFinals.g()");
  }
}

class OverridingPrivate extends WithFinals {
  private final void f() {
    System.out.println("OverridingPrivate.f()");
  }
  private void g() {
    System.out.println("OverridingPrivate.g()");
  }
}

class OverridingPrivate2 extends OverridingPrivate {
  public final void f() {
    System.out.println("OverridingPrivate2.f()");
  }
  public void g() {
    System.out.println("OverridingPrivate2.g()");
  }
}

public class FinalOverridingIllusion {
  private static Test monitor = new Test();
  public static void main(String[] args) {
    OverridingPrivate2 op2 = new OverridingPrivate2();
    op2.f();
    op2.g();
    // You can upcast:
    OverridingPrivate op = op2;
    // But you can't call the methods:
    //! op.f();
    //! op.g();
    // Same here:
    WithFinals wf = op2;
    //! wf.f();
    //! wf.g();
    monitor.expect(new String[] {
      "OverridingPrivate2.f()",
      "OverridingPrivate2.g()"
    });
  }
} ///:~


“Overriding” can only occur if something is part of the base-class interface. That is, you must be able to upcast an object to its base type and call the same method (the point of this will become clear in the next chapter). If a method is private, it isn’t part of the base-class interface. It is just some code that’s hidden away inside the class, and it just happens to have that name, but if you create a public, protected, or package-access method with the same name in the derived class, there’s no connection to the method that might happen to have that name in the base class. You haven’t overridden the method; you’ve just created a new method. Since a private method is unreachable and effectively invisible, it doesn’t factor into anything except for the code organization of the class for which it was defined. Feedback

The final keyword

Java’s final keyword has slightly different meanings depending on the context, but in general it says “This cannot be changed.” You might want to prevent changes for two reasons: design or efficiency. Because these two reasons are quite different, it’s possible to misuse the final keyword. Feedback

The following sections discuss the three places where final can be used: for data, methods, and classes. Feedback

Final data

Many programming languages have a way to tell the compiler that a piece of data is “constant.” A constant is useful for two reasons:

  1. It can be a compile-time constant that won’t ever change. Feedback
  2. It can be a value initialized at run time that you don’t want changed. Feedback

In the case of a compile-time constant, the compiler is allowed to “fold” the constant value into any calculations in which it’s used; that is, the calculation can be performed at compile time, eliminating some run-time overhead. In Java, these sorts of constants must be primitives and are expressed with the final keyword. A value must be given at the time of definition of such a constant. Feedback

A field that is both static and final has only one piece of storage that cannot be changed. Feedback

When using final with object references rather than primitives, the meaning gets a bit confusing. With a primitive, final makes the value a constant, but with an object reference, final makes the reference a constant. Once the reference is initialized to an object, it can never be changed to point to another object. However, the object itself can be modified; Java does not provide a way to make any arbitrary object a constant. (You can, however, write your class so that objects have the effect of being constant.) This restriction includes arrays, which are also objects. Feedback

Here’s an example that demonstrates final fields:

//: c06:FinalData.java
// The effect of final on fields.
import com.bruceeckel.simpletest.*;
import java.util.*;

class Value {
  int i; // Package access
  public Value(int i) { this.i = i; }
}

public class FinalData {
  private static Test monitor = new Test();
  private static Random rand = new Random();
  private String id;
  public FinalData(String id) { this.id = id; }
  // Can be compile-time constants:
  private final int VAL_ONE = 9;
  private static final int VAL_TWO = 99;
  // Typical public constant:
  public static final int VAL_THREE = 39;
  // Cannot be compile-time constants:
  private final int i4 = rand.nextInt(20);
  static final int i5 = rand.nextInt(20);
  private Value v1 = new Value(11);
  private final Value v2 = new Value(22);
  private static final Value v3 = new Value(33);
  // Arrays:
  private final int[] a = { 1, 2, 3, 4, 5, 6 };
  public String toString() {
    return id + ": " + "i4 = " + i4 + ", i5 = " + i5;
  }
  public static void main(String[] args) {
    FinalData fd1 = new FinalData("fd1");
    //! fd1.VAL_ONE++; // Error: can't change value
    fd1.v2.i++; // Object isn't constant!
    fd1.v1 = new Value(9); // OK -- not final
    for(int i = 0; i < fd1.a.length; i++)
      fd1.a[i]++; // Object isn't constant!
    //! fd1.v2 = new Value(0); // Error: Can't
    //! fd1.v3 = new Value(1); // change reference
    //! fd1.a = new int[3];
    System.out.println(fd1);
    System.out.println("Creating new FinalData");
    FinalData fd2 = new FinalData("fd2");
    System.out.println(fd1);
    System.out.println(fd2);
    monitor.expect(new String[] {
      "%% fd1: i4 = \\d+, i5 = \\d+",
      "Creating new FinalData",
      "%% fd1: i4 = \\d+, i5 = \\d+",
      "%% fd2: i4 = \\d+, i5 = \\d+"
    });
  }
} ///:~


Since VAL_ONE and VAL_TWO are final primitives with compile-time values, they can both be used as compile-time constants and are not different in any important way. VAL_THREE is the more typical way you’ll see such constants defined: public so they’re usable outside the package, static to emphasize that there’s only one, and final to say that it’s a constant. Note that final static primitives with constant initial values (that is, compile-time constants) are named with all capitals by convention, with words separated by underscores. (This is just like C constants, which is where the convention originated.) Also note that i5 cannot be known at compile time, so it is not capitalized. Feedback

Just because something is final doesn’t mean that its value is known at compile time. This is demonstrated by initializing i4 and i5 at run time using randomly generated numbers. This portion of the example also shows the difference between making a final value static or non-static. This difference shows up only when the values are initialized at run time, since the compile-time values are treated the same by the compiler. (And presumably optimized out of existence.) The difference is shown when you run the program. Note that the values of i4 for fd1 and fd2 are unique, but the value for i5 is not changed by creating the second FinalData object. That’s because it’s static and is initialized once upon loading and not each time a new object is created. Feedback

The variables v1 through v3 demonstrate the meaning of a final reference. As you can see in main( ), just because v2 is final doesn’t mean that you can’t change its value. Because it’s a reference, final means that you cannot rebind v2 to a new object. You can also see that the same meaning holds true for an array, which is just another kind of reference. (There is no way that I know of to make the array references themselves final.) Making references final seems less useful than making primitives final.

2005年01月24日

Summary

In any relationship it’s important to have boundaries that are respected by all parties involved. When you create a library, you establish a relationship with the user of that library—the client programmer—who is another programmer, but one putting together an application or using your library to build a bigger library. Feedback

Without rules, client programmers can do anything they want with all the members of a class, even if you might prefer they don’t directly manipulate some of the members. Everything’s naked to the world. Feedback

This chapter looked at how classes are built to form libraries: first, the way a group of classes is packaged within a library, and second, the way the class controls access to its members. Feedback

It is estimated that a C programming project begins to break down somewhere between 50K and 100K lines of code because C has a single “name space”: names begin to collide, causing an extra management overhead. In Java, the package keyword, the package naming scheme, and the import keyword give you complete control over names, so the issue of name collision is easily avoided. Feedback

There are two reasons for controlling access to members. The first is to keep users’ hands off tools that they shouldn’t touch: tools that are necessary for the internal operations of the data type, but not part of the interface that users need to solve their particular problems. So making methods and fields private is a service to users, because they can easily see what’s important to them and what they can ignore. It simplifies their understanding of the class. Feedback

The second and most important reason for access control is to allow the library designer to change the internal workings of the class without worrying about how it will affect the client programmer. You might build a class one way at first, and then discover that restructuring your code will provide much greater speed. If the interface and implementation are clearly separated and protected, you can accomplish this without forcing users to rewrite their code. Feedback

Access specifiers in Java give valuable control to the creator of a class. The users of the class can clearly see exactly what they can use and what to ignore. More important, though, is the ability to ensure that no user becomes dependent on any part of the underlying implementation of a class. If you know this as the creator of the class, you can change the underlying implementation at will, because you know that no client programmer will be affected by the changes; they can’t access that part of the class. Feedback

When you have the ability to change the underlying implementation, you can freely improve your design. You also have the freedom to make mistakes. No matter how carefully you plan and design, you’ll make mistakes. Knowing that it’s relatively safe to make these mistakes means you’ll be more experimental, you’ll learn more quickly, and you’ll finish your project sooner. Feedback

The public interface to a class is what the user does see, so that is the most important part of the class to get “right” during analysis and design. Even that allows you some leeway for change. If you don’t get the interface right the first time, you can add more methods, as long as you don’t remove any that client programmers have already used in their code. Feedback

Summary

In any relationship it’s important to have boundaries that are respected by all parties involved. When you create a library, you establish a relationship with the user of that library—the client programmer—who is another programmer, but one putting together an application or using your library to build a bigger library. Feedback

Without rules, client programmers can do anything they want with all the members of a class, even if you might prefer they don’t directly manipulate some of the members. Everything’s naked to the world. Feedback

This chapter looked at how classes are built to form libraries: first, the way a group of classes is packaged within a library, and second, the way the class controls access to its members. Feedback

It is estimated that a C programming project begins to break down somewhere between 50K and 100K lines of code because C has a single “name space”: names begin to collide, causing an extra management overhead. In Java, the package keyword, the package naming scheme, and the import keyword give you complete control over names, so the issue of name collision is easily avoided. Feedback

There are two reasons for controlling access to members. The first is to keep users’ hands off tools that they shouldn’t touch: tools that are necessary for the internal operations of the data type, but not part of the interface that users need to solve their particular problems. So making methods and fields private is a service to users, because they can easily see what’s important to them and what they can ignore. It simplifies their understanding of the class. Feedback

The second and most important reason for access control is to allow the library designer to change the internal workings of the class without worrying about how it will affect the client programmer. You might build a class one way at first, and then discover that restructuring your code will provide much greater speed. If the interface and implementation are clearly separated and protected, you can accomplish this without forcing users to rewrite their code. Feedback

Access specifiers in Java give valuable control to the creator of a class. The users of the class can clearly see exactly what they can use and what to ignore. More important, though, is the ability to ensure that no user becomes dependent on any part of the underlying implementation of a class. If you know this as the creator of the class, you can change the underlying implementation at will, because you know that no client programmer will be affected by the changes; they can’t access that part of the class. Feedback

When you have the ability to change the underlying implementation, you can freely improve your design. You also have the freedom to make mistakes. No matter how carefully you plan and design, you’ll make mistakes. Knowing that it’s relatively safe to make these mistakes means you’ll be more experimental, you’ll learn more quickly, and you’ll finish your project sooner. Feedback

The public interface to a class is what the user does see, so that is the most important part of the class to get “right” during analysis and design. Even that allows you some leeway for change. If you don’t get the interface right the first time, you can add more methods, as long as you don’t remove any that client programmers have already used in their code. Feedback

> http://www.ebook007.com
> http://www.bbook.net/gb/books/booksdefault.asp
> http://www.hlepwork.net
> http://www.mycnknow.com/srindex.htm
> http://www.pcbookcn.com/
> http://www.ebooksou.com
> http://bingle.pku.edu.cn
> http://www.xiaomai.org/
> http://www.pcbookcn.com/
> http://www.epubcn.com/
> http://www.id163.com/
> http://www.vvsoft.net/vvbksd/index.asp
> http://free.21cn.com/lobby/zls/indexxux.htm
> http://www.marcocantu.com/
> http://www.taishen.net/ebook/
> http://www.compinfo-center.com/
> http://infocenter.cramsession.com/
> http://www.certifyexpress.com/
> http://www.bl.uk/ 英国图书馆
> http://www.allpm.com/
> http://www.itbook.com.cn/
> http://demos.acfci.cci.fr/
> http://www.vvsoft.com/
> http://www.92show.com/
> http://www.intelinfo.com/
> http://www.nwnetsmart.com/
> http://sunsite.iisc.ernet.in/
> http://www.intelinfo.com/
> http://www.92show.com/
> http://www.intelinfo.com/
> http://www.nwnetsmart.com/
> http://sunsite.iisc.ernet.in/
> http://www.intelinfo.com/
> http://www.apache.org/
> http://www.free-ebooks.net/
> 中华电脑书库 http://www.pcbookcn.com/
> IT人的世界 http://www.tech521.com/
> 非常书城 http://www.mayia.com/downshu/bookcity/index.asp

> http://www.winmag.com.cn/forum/
> http://pcbook.godcn.net/ 帝华咨讯网
> http://www.vckbase.com/  VC知识库
> http://www.helpwork.net  博士网
> www.dev-club.com
> http://www.aspxcn.com/  .NET中华网
> http://www.51itworld.com/ 无忧IT世界
> http://www.netsquare.com/netbook/ 电脑书籍
> http://www.5566.org/prog-.htm  【程 序】
> http://www.tinydust.com/  微尘程序员网站
> http://www.comjoe.com/   中国程序员网
> http://www.ccidnet.com/   赛迪网
> http://www.programmersheaven.com/ Programmers’ Heaven
> http://www.huachu.com.cn/ 华储网
> http://yuyu.6to23.com/ 非常书城
> http://www.china-pub.com 也可以买书
> http://pcbook.51soft.com 无忧书库,网页字体效果极棒!
> http://www.dingbing.com/book/ 
> http://www.eshunet.com e书时空
> http://zjfeng.topcool.net/song.htm 电脑书籍
> http://www.eng.stu.edu.cn/ebook/ 电脑书籍
> http://www.godcn.net/school/ 
> http://gshulkin.hypermart.net/books/FreeVBBook.html 

> China: http://javachina.yeah.net/design
> China: http://louisxiv.home.chinaren.com
> China: http://proxy.iini.com/eckel/
> China: http://www.jiangnan.edu.cn
> China: http://www.lelf.net/lelf/download/tij/
> ftp.gargoylewiz.dynip.com
> ftp.gargoylewiz.dynip.com
> ftp.gargoylewiz.dynip.com
> ftp://128.32.208.109/.NWNK-FxP/_vti_pvt/_vti_pub
> ftp://141.133.239.239/.%20scanned%20and%20uploaded/by%20blue
> ftp://166.111.132.3
> ftp://166.111.160.4
> ftp://166.111.164.147/
> ftp://166.111.174.33
> ftp://203.198.80.44/test/ilifebackup/24Jun2000/.%20%20%20tagged%20by%20p/Bookz
> ftp://209.196.62.156/_vti_pvt/.pmbd
> ftp://209.204.119.253/temp/damoak/
> ftp://216.0.51.50/%7Etmp/.%20for/%7E/ftf/by%20icon/-=Books%20&amp;%20TutorialZ=-
> ftp://216.149.46.228/scan/by/damoak/
> ftp://216.167.56.173/tmp/damoak/cisco/
> ftp://216.233.144.210/%7E/%7Etagged%20by%20fietsbeller/
> ftp://61.156.23.10/2/.EM/m2w/study/
> ftp://cicd.hfut.edu.cn/
> ftp://dl3.51soft.com:8384/c_c021.zip《C++高级参考手册》
> ftp://ftp.bj.software.chinese.com/software/program/book/1a35.zip
> ftp://ftp.bupt.edu.cn/pub1/e-book/computer/
> ftp://ftp.pim.tsinghua.edu.cn
> ftp://ftp.sjtu.edu.cn/document
> ftp://ftp.tsinghua.edu.cn/pub/
> ftp://ftp.tup.tsinghua.edu.cn/C++/    钱能《c++程序设计教程》那本书的书后习题源码
> ftp://ftp.xjtu.edu.cn/pub/document
> ftp://ftp.xjtu.edu.cn/pub/document
> ftp://keygen:exam@24.92.14.2:21
> ftp://maestro.den.disa.mil
> ftp://win2k:abc123@63.236.67.26/
> ftp://win2k:win2000@63.236.67.49/
> ftp://wlbookwl:2531382@ftp.diy.myrice.com/book/dosint.zip
> ftp://wlbookwl:2531382@ftp.diy.myrice.com/book/qianC/001.zip 《钱能C++程序设计》(1)
> ftp://wlbookwl:2531382@ftp.diy.myrice.com/book/qianC/002.zip  《钱能C++程序设计》(2)> http://www.helpwork.net/book/html/user/index.asp
> http://www.helpwork.net/downdata/book/programming/vc/book/vc++2/invcpp5.chm
> http://www.hktk.com/soft/program/book/index.html
> http://www.hyedu.com
> http://www.jjhou.com
> http://www.kubao.com
> http://www.laohu.net/
> http://www.linuxdoc.org/LDP/lpg/node1.html
> http://www.mayia.com/downshu/book/c_c015.zip
> http://www.mayia.com/downshu/bookcity/index.asp
> http://www.mcsdunion.com
> http://www.mcsebraindumps.com
> http://www.mcsetutor.com/freestuff.html
> http://www.mikezqz.myetang.com/computeronline.htm
> http://www.monumental.com/boat/computerbooks.html
> http://www.net-sky.com/Computers_and_Internet/Education/
> http://www.nopayweb.com
> http://www.pcbookcn.com
> http://www.pchome.net/~/LJ/book/book3.htm
> http://www.qdit.com.cn/software/programm/
> http://www.realvb.bizland.com/
> http://www.rokinc.cjb.net
> http://www.startvweb.com/Computers_and_Internet
> http://www.vr3d.com/personal.HTM
> http://www.vvsoft.net/vvbkss/vvbkss.asp
> http://www.winsite.com/hot/winnt.html
> http://www.wuyou.org/wlbookwl/windowshxbc.zip  《windows核心编程》
> http://www.wuyou.org/wlbookwl/windowsp5.zip 《Windows程序设计第五版》
> http://www8.pconline.com.cn
> http://yangnan.126.com/  ****白菜乐园
> http://zhanet.myrice.com/  *****化境
> http://zhwm.myrice.com/pcbook/index.htm
> http://zjfeng.csw.cnshare.net
> http://zjfeng.csw.cnshare.net
> http://zjfeng.csw.cnshare.net
> http://zjfeng.topcool.net/index.html
> ftp://delphi:delphi@ftp.xxtax.gov.cn/
> ms html workshop
> nihao.dlut.edu.cn/web/book
> www.3322.net/~shii/TNT/crk_a.htm
> www.helpwork.net
> www.tech521.com
> www.mycnknow.com
> http://www.readease.com/
> http://wenxue.lycos.com.cn/
> http://www.taishen.net/ebook/
> http://www.helpwork.com/
> http://extend.hk.hi.cn/~kehubu/VBbooks.htm
> http://ojava.uhome.net/
> http://www.mycnknow.com/
> www.guxiang.com/epubcn
> http://www.pcbookcn.com/
> http://www.epubcn.com/
> http://free.21cn.com/lobby/zls/indexxux.htm
> http://wlbookwl.myrice.com/c2book.htm
> www.yuyux.com
> http://www.ebooksou.com/
> http://www.ez-search.com
> http://home.eshunet.com
> http://www.3stonebook.com
> http://www.mypcera.com/book
> http://www.hao123.com/book.htm
> ftp://166.111.164.147/
> http://www.rokinc.cjb.net
> http://books.zone.ru
> http://members.spree.com/education/jackyhft/
> http://www.realvb.bizland.com/
> http://lib.wuhee.edu.cn/book/computer.asp
> http://zhanet.myrice.com/ *****化境
> http://it.263.net/b/book/
> ftp://win2k:abc123@63.236.67.26/
> ftp://216.233.144.210/%7E/%7Etagged%20by%20fietsbeller/
> http://zjfeng.csw.cnshare.net
> http://vg.dyndns.org/ebook/
> http://www.pchome.net/~/LJ/book/book3.htm
> http://www.mcsebraindumps.com
> http://www.mcsdunion.com
> http://www.kubao.com
> ftp.gargoylewiz.dynip.com
> http://delphideveloper.myrice.com/
> http://www.freedrive.com/ASP/PostFolderShortcut.asp?fsc=14365878
> ftp://maestro.den.disa.mil
> http://rokinc.narod.ru
> http://home.cyberarmy.com/micromaniz/
> http://www.helpwork.com/
> http://www.chinathink.net/edown/default.asp
> http://www.bbook.net/gb/books/booksdefault.asp
> http://www.hlepwork.net/
> http://www.mycnknow.com/srindex.htm
> http://www.pcbookcn.com/
> http://www.ebooksou.com/
> http://bingle.pku.edu.cn
> ftp://dlking:good_guy@coolwoo.dlking.com/
> http://www.xiaomai.org/
> http://www.pcbookcn.com/
> http://www.epubcn.com/
> http://www.id163.com/
> http://www.vvsoft.net/vvbksd/index.asp
> http://free.21cn.com/lobby/zls/indexxux.htm
> http://www.marcocantu.com/
> http://www.taishen.net/ebook/