一个Socket服务器样板程序

这是一个非常好的Socket服务器样板程序,这个socket服务器可以为你建立指定的监听端口、客户端请求响应机制等一些服务器所具备的基本框架

/*
 * Copyright (c) 2000 David Flanagan.  All rights reserved.
 * This code is from the book Java Examples in a Nutshell, 2nd Edition.
 * It is provided AS-IS, WITHOUT ANY WARRANTY either expressed or implied.
 * You may study, use, and modify it for any non-commercial purpose.
 * You may distribute it non-commercially as long as you retain this notice.
 * For a commercial use license, or to purchase the book (recommended),
 * visit http://www.davidflanagan.com/javaexamples2.
 */

import java.io.*;
import java.net.*;
import java.util.*;

/**
 * This class is a generic framework for a flexible, multi-threaded server.
 * It listens on any number of specified ports, and, when it receives a 
 * connection on a port, passes input and output streams to a specified Service
 * object which provides the actual service.  It can limit the number of
 * concurrent connections, and logs activity to a specified stream.
 **/
public class Server {
    /**
     * A main() method for running the server as a standalone program.  The
     * command-line arguments to the program should be pairs of servicenames
     * and port numbers.  For each pair, the program will dynamically load the
     * named Service class, instantiate it, and tell the server to provide
     * that Service on the specified port.  The special -control argument
     * should be followed by a password and port, and will start special
     * server control service running on the specified port, protected by the
     * specified password.
     **/
    public static void main(String[] args) {
        try {
            if (args.length < 2)  // Check number of arguments
                throw new IllegalArgumentException("Must specify a service");
            
            // Create a Server object that uses standard out as its log and
            // has a limit of ten concurrent connections at once.
            Server s = new Server(System.out, 10);

            // Parse the argument list
            int i = 0;
            while(i < args.length) {
                if (args[i].equals("-control")) {  // Handle the -control arg
                    i++;
                    String password = args[i++];
                    int port = Integer.parseInt(args[i++]);
      // add control service
                    s.addService(new Control(s, password), port);
                } 
                else {
                    // Otherwise start a named service on the specified port.
                    // Dynamically load and instantiate a Service class
                    String serviceName = args[i++];
                    Class serviceClass = Class.forName(serviceName); 
                    Service service = (Service)serviceClass.newInstance();
                    int port = Integer.parseInt(args[i++]);
                    s.addService(service, port);
                }
            }
        }
        catch (Exception e) { // Display a message if anything goes wrong
            System.err.println("Server: " + e);
            System.err.println("Usage: java Server " +
          "[-control  ] " +
          "[  ... ]");
            System.exit(1);
        }
    }

    // This is the state for the server
    Map services;                   // Hashtable mapping ports to Listeners
    Set connections;                // The set of current connections
    int maxConnections;             // The concurrent connection limit
    ThreadGroup threadGroup;        // The threadgroup for all our threads
    PrintWriter logStream;          // Where we send our logging output to

    /**
     * This is the Server() constructor.  It must be passed a stream 
     * to send log output to (may be null), and the limit on the number of
     * concurrent connections.  
     **/
    public Server(OutputStream logStream, int maxConnections) { 
        setLogStream(logStream);
        log("Starting server");
        threadGroup = new ThreadGroup(Server.class.getName());
 this.maxConnections = maxConnections;
        services = new HashMap();
 connections = new HashSet(maxConnections);
    }
    
    /** 
     * A public method to set the current logging stream.  Pass null
     * to turn logging off
     **/
    public synchronized void setLogStream(OutputStream out) {
        if (out != null) logStream = new PrintWriter(out);
        else logStream = null;
    }

    /** Write the specified string to the log */
    protected synchronized void log(String s) { 
        if (logStream != null) {
            logStream.println("[" + new Date() + "] " + s);
            logStream.flush();
        }
    }
    /** Write the specified object to the log */
    protected void log(Object o) { log(o.toString()); }
    
    /**
     * This method makes the server start providing a new service.
     * It runs the specified Service object on the specified port.
     **/
    public synchronized void addService(Service service, int port)
 throws IOException
    {
        Integer key = new Integer(port);  // the hashtable key
        // Check whether a service is already on that port
        if (services.get(key) != null) 
            throw new IllegalArgumentException("Port " + port +
            " already in use.");
        // Create a Listener object to listen for connections on the port
        Listener listener = new Listener(threadGroup, port, service);
        // Store it in the hashtable
        services.put(key, listener);
        // Log it
        log("Starting service " + service.getClass().getName() + 
     " on port " + port);
        // Start the listener running.
        listener.start();
    }
    
    /**
     * This method makes the server stop providing a service on a port.
     * It does not terminate any pending connections to that service, merely
     * causes the server to stop accepting new connections
     **/
    public synchronized void removeService(int port) {
        Integer key = new Integer(port);  // hashtable key
        // Look up the Listener object for the port in the hashtable
        final Listener listener = (Listener) services.get(key);
        if (listener == null) return;
        // Ask the listener to stop
        listener.pleaseStop();
        // Remove it from the hashtable
        services.remove(key);
        // And log it.
        log("Stopping service " + listener.service.getClass().getName() + 
     " on port " + port);
    }
    
    /** 
     * This nested Thread subclass is a "listener".  It listens for
     * connections on a specified port (using a ServerSocket) and when it gets
     * a connection request, it calls the servers addConnection() method to
     * accept (or reject) the connection.  There is one Listener for each
     * Service being provided by the Server.
     **/
    public class Listener extends Thread {
        ServerSocket listen_socket;    // The socket to listen for connections
        int port;                      // The port we're listening on
        Service service;               // The service to provide on that port
        volatile boolean stop = false; // Whether we've been asked to stop

        /**
  * The Listener constructor creates a thread for itself in the
  * threadgroup.  It creates a ServerSocket to listen for connections
  * on the specified port.  It arranges for the ServerSocket to be
  * interruptible, so that services can be removed from the server.
  **/
        public Listener(ThreadGroup group, int port, Service service) 
     throws IOException
 {
            super(group, "Listener:" + port);      
            listen_socket = new ServerSocket(port);
            // give it a non-zero timeout so accept() can be interrupted
            listen_socket.setSoTimeout(600000);
            this.port = port;
            this.service = service;
        }

        /** 
  * This is the polite way to get a Listener to stop accepting
  * connections
  ***/
        public void pleaseStop() {
            this.stop = true;              // Set the stop flag
            this.interrupt();              // Stop blocking in accept()
     try { listen_socket.close(); } // Stop listening.
     catch(IOException e) {}
        }
        
        /**
  * A Listener is a Thread, and this is its body.
  * Wait for connection requests, accept them, and pass the socket on
  * to the addConnection method of the server.
  **/
        public void run() {
            while(!stop) {      // loop until we're asked to stop.
                try {
                    Socket client = listen_socket.accept();
                    addConnection(client, service);
                } 
                catch (InterruptedIOException e) {} 
                catch (IOException e) {log(e);}
            }
        }
    }
 
    /**
     * This is the method that Listener objects call when they accept a
     * connection from a client.  It either creates a Connection object 
     * for the connection and adds it to the list of current connections,
     * or, if the limit on connections has been reached, it closes the 
     * connection. 
     **/
    protected synchronized void addConnection(Socket s, Service service) {
 // If the connection limit has been reached
 if (connections.size() >= maxConnections) {
     try {
  // Then tell the client it is being rejected.
  PrintWriter out = new PrintWriter(s.getOutputStream());
  out.print("Connection refused; " +
     "the server is busy; please try again later.\n");
  out.flush();
  // And close the connection to the rejected client.
  s.close();
  // And log it, of course
  log("Connection refused to " +
      s.getInetAddress().getHostAddress() +
      ":" + s.getPort() + ": max connections reached.");
     } catch (IOException e) {log(e);}
 }
 else {  // Otherwise, if the limit has not been reached
     // Create a Connection thread to handle this connection
     Connection c = new Connection(s, service);
     // Add it to the list of current connections
     connections.add(c);
     // Log this new connection
     log("Connected to " + s.getInetAddress().getHostAddress() +
  ":" + s.getPort() + " on port " + s.getLocalPort() +
  " for service " + service.getClass().getName());
     // And start the Connection thread to provide the service
     c.start();
 }
    }

    /**
     * A Connection thread calls this method just before it exits.  It removes
     * the specified Connection from the set of connections.
     **/
    protected synchronized void endConnection(Connection c) {
 connections.remove(c);
 log("Connection to " + c.client.getInetAddress().getHostAddress() +
     ":" + c.client.getPort() + " closed.");
    }

    /** Change the current connection limit */
    public synchronized void setMaxConnections(int max) {
 maxConnections = max;
    }

    /**
     * This method displays status information about the server on the
     * specified stream.  It can be used for debugging, and is used by the
     * Control service later in this example.
     **/
    public synchronized void displayStatus(PrintWriter out) {
 // Display a list of all Services that are being provided
 Iterator keys = services.keySet().iterator();
 while(keys.hasNext()) {
     Integer port = (Integer) keys.next();
     Listener listener = (Listener) services.get(port);
     out.print("SERVICE " + listener.service.getClass().getName()
        + " ON PORT " + port + "\n");
 }
 
 // Display the current connection limit
 out.print("MAX CONNECTIONS: " + maxConnections + "\n");

 // Display a list of all current connections
 Iterator conns = connections.iterator();
 while(conns.hasNext()) {
     Connection c = (Connection)conns.next();
     out.print("CONNECTED TO " +
        c.client.getInetAddress().getHostAddress() +
        ":" + c.client.getPort() + " ON PORT " +
        c.client.getLocalPort() + " FOR SERVICE " +
        c.service.getClass().getName() + "\n");
 }
    }

    /**
     * This class is a subclass of Thread that handles an individual
     * connection between a client and a Service provided by this server.
     * Because each such connection has a thread of its own, each Service can
     * have multiple connections pending at once.  Despite all the other
     * threads in use, this is the key feature that makes this a
     * multi-threaded server implementation.
     **/
    public class Connection extends Thread {
        Socket client;     // The socket to talk to the client through
        Service service;   // The service being provided to that client
 
        /**
  * This constructor just saves some state and calls the superclass
  * constructor to create a thread to handle the connection.  Connection
  * objects are created by Listener threads.  These threads are part of
  * the server's ThreadGroup, so all Connection threads are part of that
  * group, too.
  **/
        public Connection(Socket client, Service service) {
            super("Server.Connection:" +
    client.getInetAddress().getHostAddress() +
    ":" + client.getPort());
            this.client = client;
            this.service = service;
        }
 
        /**
  * This is the body of each and every Connection thread.
  * All it does is pass the client input and output streams to the
  * serve() method of the specified Service object.  That method is
  * responsible for reading from and writing to those streams to
  * provide the actual service.  Recall that the Service object has
  * been passed from the Server.addService() method to a Listener
  * object to the addConnection() method to this Connection object, and
  * is now finally being used to provide the service.  Note that just
  * before this thread exits it always calls the endConnection() method
  * to remove itself from the set of connections
  **/
        public void run() {
            try { 
                InputStream in = client.getInputStream();
                OutputStream out = client.getOutputStream();
                service.serve(in, out);
            } 
            catch (IOException e) {log(e);}
            finally { endConnection(this); }
        }
    }
    
    /**
     * Here is the Service interface that we have seen so much of.  It defines
     * only a single method which is invoked to provide the service.  serve()
     * will be passed an input stream and an output stream to the client.  It
     * should do whatever it wants with them, and should close them before
     * returning.
     *
     * All connections through the same port to this service share a single
     * Service object.  Thus, any state local to an individual connection must
     * be stored in local variables within the serve() method.  State that
     * should be global to all connections on the same port should be stored
     * in instance variables of the Service class.  If the same Service is
     * running on more than one port, there will typically be different
     * Service instances for each port.  Data that should be global to all
     * connections on any port should be stored in static variables.
     *
     * Note that implementations of this interface must have a no-argument 
     * constructor if they are to be dynamically instantiated by the main()
     * method of the Server class.
     **/
    public interface Service {
        public void serve(InputStream in, OutputStream out) throws IOException;
    }

    /**
     * A very simple service.  It displays the current time on the server
     * to the client, and closes the connection.
     **/
    public static class Time implements Service {
        public void serve(InputStream i, OutputStream o) throws IOException {
            PrintWriter out = new PrintWriter(o);
            out.print(new Date() + "\n");
            out.close();
            i.close();
        }
    }
    
    /**
     * This is another example service.  It reads lines of input from the
     * client, and sends them back, reversed.  It also displays a welcome
     * message and instructions, and closes the connection when the user 
     * enters a '.' on a line by itself.
     **/
    public static class Reverse implements Service {
        public void serve(InputStream i, OutputStream o) throws IOException {
            BufferedReader in = new BufferedReader(new InputStreamReader(i));
            PrintWriter out = 
                new PrintWriter(new BufferedWriter(new OutputStreamWriter(o)));
            out.print("Welcome to the line reversal server.\n");
            out.print("Enter lines.  End with a '.' on a line by itself.\n");
            for(;;) {
                out.print("> ");
                out.flush();
                String line = in.readLine();
                if ((line == null) || line.equals(".")) break;
                for(int j = line.length()-1; j >= 0; j--)
                    out.print(line.charAt(j));
                out.print("\n");
            }
            out.close();
            in.close();
        }
    }
    
    /**
     * This service is an HTTP mirror, just like the HttpMirror class
     * implemented earlier in this chapter.  It echos back the client's
     * HTTP request
     **/
    public static class HTTPMirror implements Service {
        public void serve(InputStream i, OutputStream o) throws IOException {
            BufferedReader in = new BufferedReader(new InputStreamReader(i));
            PrintWriter out = new PrintWriter(o);
            out.print("HTTP/1.0 200 \n");
            out.print("Content-Type: text/plain\n\n");
            String line;
            while((line = in.readLine()) != null) {
                if (line.length() == 0) break;
                out.print(line + "\n");
            }
            out.close();
            in.close();
        }
    }
    
    /**
     * This service demonstrates how to maintain state across connections by
     * saving it in instance variables and using synchronized access to those
     * variables.  It maintains a count of how many clients have connected and
     * tells each client what number it is
     **/
    public static class UniqueID implements Service {
        public int id=0;
        public synchronized int nextId() { return id++; }
        public void serve(InputStream i, OutputStream o) throws IOException {
            PrintWriter out = new PrintWriter(o);
            out.print("You are client #: " + nextId() + "\n");
            out.close();
            i.close();
        }
    }
    
    /**
     * This is a non-trivial service.  It implements a command-based protocol
     * that gives password-protected runtime control over the operation of the 
     * server.  See the main() method of the Server class to see how this
     * service is started.  
     *
     * The recognized commands are:
     *   password: give password; authorization is required for most commands
     *   add:      dynamically add a named service on a specified port
     *   remove:   dynamically remove the service running on a specified port
     *   max:      change the current maximum connection limit.
     *   status:   display current services, connections, and connection limit
     *   help:     display a help message
     *   quit:     disconnect
     *
     * This service displays a prompt, and sends all of its output to the user
     * in capital letters.  Only one client is allowed to connect to this 
     * service at a time.
     **/
    public static class Control implements Service {
        Server server;             // The server we control
        String password;           // The password we require
        boolean connected = false; // Whether a client is already connected
 
        /**
  * Create a new Control service.  It will control the specified Server
  * object, and will require the specified password for authorization
  * Note that this Service does not have a no argument constructor,
  * which means that it cannot be dynamically instantiated and added as
  * the other, generic services above can be.
  **/
        public Control(Server server, String password) {
            this.server = server;
            this.password = password;
        }

        /**
  * This is the serve method that provides the service.  It reads a
  * line the client, and uses java.util.StringTokenizer to parse it
  * into commands and arguments.  It does various things depending on
  * the command.
  **/
        public void serve(InputStream i, OutputStream o) throws IOException {
            // Setup the streams
            BufferedReader in = new BufferedReader(new InputStreamReader(i));
            PrintWriter out = new PrintWriter(o);
            String line;  // For reading client input lines
     // Has the user has given the password yet?
            boolean authorized = false; 

            // If there is already a client connected to this service, display
            // a message to this client and close the connection.  We use a
            // synchronized block to prevent a race condition.
            synchronized(this) { 
                if (connected) { 
                    out.print("ONLY ONE CONTROL CONNECTION ALLOWED.\n");
                    out.close();
                    return;
                }
                else connected = true;
            }

     // This is the main loop: read a command, parse it, and handle it
            for(;;) {  // infinite loop
                out.print("> ");           // Display a prompt
                out.flush();               // Make it appear right away
                line = in.readLine();      // Get the user's input
                if (line == null) break;   // Quit if we get EOF.
                try {
                    // Use a StringTokenizer to parse the user's command
                    StringTokenizer t = new StringTokenizer(line);
                    if (!t.hasMoreTokens()) continue;  // if input was empty
                    // Get first word of the input and convert to lower case
                    String command = t.nextToken().toLowerCase(); 
                    // Now compare to each of the possible commands, doing the
                    // appropriate thing for each command
                    if (command.equals("password")) {  // Password command
                        String p = t.nextToken();      // Get the next word
                        if (p.equals(this.password)) { // Is it the password?
                            out.print("OK\n");         // Say so
                            authorized = true;         // Grant authorization
                        }
                        else out.print("INVALID PASSWORD\n"); // Otherwise fail
                    }
                    else if (command.equals("add")) {  // Add Service command
                        // Check whether password has been given
                        if (!authorized) out.print("PASSWORD REQUIRED\n"); 
                        else {
                            // Get the name of the service and try to
                            // dynamically load and instantiate it.
                            // Exceptions will be handled below
                            String serviceName = t.nextToken();
                            Class serviceClass = Class.forName(serviceName);
                            Service service;
                            try {
    service = (Service)serviceClass.newInstance();
       }
                            catch (NoSuchMethodError e) {
                                throw new IllegalArgumentException(
             "Service must have a " +
      "no-argument constructor");
                            }
                            int port = Integer.parseInt(t.nextToken());
                            // If no exceptions occurred, add the service
                            server.addService(service, port);
                            out.print("SERVICE ADDED\n");    // acknowledge
                        }
                    }
                    else if (command.equals("remove")) { // Remove service
                        if (!authorized) out.print("PASSWORD REQUIRED\n");
                        else {
                            int port = Integer.parseInt(t.nextToken()); 
                            server.removeService(port); // remove the service
                            out.print("SERVICE REMOVED\n"); // acknowledge
                        }
                    }
                    else if (command.equals("max")) { // Set connection limit
                        if (!authorized) out.print("PASSWORD REQUIRED\n");
                        else {
                            int max = Integer.parseInt(t.nextToken()); 
                            server.setMaxConnections(max); 
                            out.print("MAX CONNECTIONS CHANGED\n");
                        }
                    }
                    else if (command.equals("status")) { // Status Display
                        if (!authorized) out.print("PASSWORD REQUIRED\n");
   else server.displayStatus(out);
                    }
                    else if (command.equals("help")) {  // Help command
                        // Display command syntax.  Password not required
                        out.print("COMMANDS:\n" + 
      "\tpassword \n" +
      "\tadd  \n" +
      "\tremove \n" +
      "\tmax \n" +
      "\tstatus\n" +
      "\thelp\n" + 
      "\tquit\n");
                    }
                    else if (command.equals("quit")) break; // Quit command.
                    else out.print("UNRECOGNIZED COMMAND\n"); // Error
  }
                catch (Exception e) {
                    // If an exception occurred during the command, print an
                    // error message, then output details of the exception.
                    out.print("ERROR WHILE PARSING OR EXECUTING COMMAND:\n" +
         e + "\n");
                }
            }
            // Finally, when the loop command loop ends, close the streams
            // and set our connected flag to false so that other clients can
            // now connect.
            connected = false;
            out.close();
            in.close();
        }    
    }
}


<淘宝热门商品:
 

¥:27.00 

◆男童装女童装◆6-15元◆

0货号559◆女童装 纯棉线线衣(1-5岁)

 

鲜花速递/蛋糕配送/园艺花艺 

瑞锦记锦缎喜糖袋〓婚礼特制●上等材质●流行韩


来源:程序员网

小小豆叮

通过JDBC连接oracle数据库的十大技

Java数据库连接(JDBC)API是一系列能够让Java编程人员访问数据库的接口,各个开发商的接口并不完全相同。在使用多年的Oracle公司的JDBC后,我积累了许多技巧,这些技巧能够使我们更好地发挥系统的性能和实现更多的功能。   1、在客户端软件开发中使用Thin驱动程序   在开发Java软件方面,Oracle的数据库提供了四种类型的驱动程序,二种用于应用软件、applets、servlets等客户端软件,另外二种用于数据库中的Java存储过程等服务器端软件。在客户机端软件的开发中,我们可以选择OCI驱动程序或Thin驱动程序。OCI驱动程序利用Java本地化接口(JNI),通过Oracle客户端软件与数据库进行通讯。Thin驱动程序是纯Java驱动程序,它直接与数据库进行通讯。为了获得最高的性能,Oracle建议在客户端软件的开发中使用OCI驱动程序,这似乎是正确的。但我建议使用Thin驱动程序,因为通过多次测试发现,在通常情况下,Thin驱动程序的性能都超过了OCI驱动程序。   2、关闭自动提交功能,提高系统性能   在第一次建立与数据库的连接时,在缺省情况下,连接是在自动提交模式下的。为了获得更好的性能,可以通过调用带布尔值false参数的Connection类的setAutoCommit()方法关闭自动提交功能,如下所示:   conn.setAutoCommit(false);   值得注意的是,一旦关闭了自动提交功能,我们就需要通过调用Connection类的commit()和rollback()方法来人工的方式对事务进行管理。   3、在动态SQL或有时间限制的命令中使用Statement对象   在执行SQL命令时,我们有二种选择:可以使用PreparedStatement对象,也可以使用Statement对象。无论多少次地使用同一个SQL命令,PreparedStatement都只对它解析和编译一次。当使用Statement对象时,每次执行一个SQL命令时,都会对它进行解析和编译。这可能会使你认为,使用PreparedStatement对象比使用Statement对象的速度更快。然而,我进行的测试表明,在客户端软件中,情况并非如此。因此,在有时间限制的SQL操作中,除非成批地处理SQL命令,我们应当考虑使用Statement对象。   此外,使用Statement对象也使得编写动态SQL命令更加简单,因为我们可以将字符串连接在一起,建立一个有效的SQL命令。因此,我认为,Statement对象可以使动态SQL命令的创建和执行变得更加简单。 4、利用helper函数对动态SQL命令进行格式化   在创建使用Statement对象执行的动态SQL命令时,我们需要处理一些格式化方面的问题。例如,如果我们想创建一个将名字O'Reilly插入表中的SQL命令,则必须使用二个相连的“''”号替换O'Reilly中的“'”号。完成这些工作的最好的方法是创建一个完成替换操作的helper方法,然后在连接字符串心服用公式表达一个SQL命令时,使用创建的helper方法。与此类似的是,我们可以让helper方法接受一个Date型的值,然后让它输出基于Oracle的to_date()函数的字符串表达式。   5、利用PreparedStatement对象提高数据库的总体效率   在使用PreparedStatement对象执行SQL命令时,命令被数据库进行解析和编译,然后被放到命令缓冲区。然后,每当执行同一个PreparedStatement对象时,它就会被再解析一次,但不会被再次编译。在缓冲区中可以发现预编译的命令,并且可以重新使用。在有大量用户的企业级应用软件中,经常会重复执行相同的SQL命令,使用PreparedStatement对象带来的编译次数的减少能够提高数据库的总体性能。如果不是在客户端创建、预备、执行PreparedStatement任务需要的时间长于Statement任务,我会建议在除动态SQL命令之外的所有情况下使用PreparedStatement对象。   6、在成批处理重复的插入或更新操作中使用PreparedStatement对象   如果成批地处理插入和更新操作,就能够显著地减少它们所需要的时间。Oracle提供的Statement和 CallableStatement并不真正地支持批处理,只有PreparedStatement对象才真正地支持批处理。我们可以使用addBatch()和executeBatch()方法选择标准的JDBC批处理,或者通过利用PreparedStatement对象的setExecuteBatch()方法和标准的executeUpdate()方法选择速度更快的Oracle专有的方法。要使用Oracle专有的批处理机制,可以以如下所示的方式调用setExecuteBatch(): PreparedStatement pstmt3D null; try { ((OraclePreparedStatement) pstmt).setExecuteBatch(30); ... pstmt.executeUpdate(); }   调用setExecuteBatch()时指定的值是一个上限,当达到该值时,就会自动地引发SQL命令执行,标准的executeUpdate()方法就会被作为批处理送到数据库中。我们可以通过调用PreparedStatement类的sendBatch()方法随时传输批处理任务。   7、使用Oracle locator方法插入、更新大对象(LOB)   Oracle的PreparedStatement类不完全支持BLOB和CLOB等大对象的处理,尤其是Thin驱动程序不支持利用PreparedStatement对象的setObject()和setBinaryStream()方法设置BLOB的值,也不支持利用setCharacterStream()方法设置CLOB的值。只有locator本身中的方法才能够从数据库中获取LOB类型的值。可以使用PreparedStatement对象插入或更新LOB,但需要使用locator才能获取LOB的值。由于存在这二个问题,因此,我建议使用locator的方法来插入、更新或获取LOB的值。   8、使用SQL92语法调用存储过程   在调用存储过程时,我们可以使用SQL92或Oracle PL/SQL,由于使用Oracle PL/SQL并没有什么实际的好处,而且会给以后维护你的应用程序的开发人员带来麻烦,因此,我建议在调用存储过程时使用SQL92。   9、使用Object SQL将对象模式转移到数据库中   既然可以将Oracle的数据库作为一种面向对象的数据库来使用,就可以考虑将应用程序中的面向对象模式转到数据库中。目前的方法是创建Java bean作为伪装的数据库对象,将它们的属性映射到关系表中,然后在这些bean中添加方法。尽管这样作在Java中没有什么问题,但由于操作都是在数据库之外进行的,因此其他访问数据库的应用软件无法利用对象模式。如果利用Oracle的面向对象的技术,可以通过创建一个新的数据库对象类型在数据库中模仿其数据和操作,然后使用JPublisher等工具生成自己的Java bean类。如果使用这种方式,不但Java应用程序可以使用应用软件的对象模式,其他需要共享你的应用中的数据和操作的应用软件也可以使用应用软件中的对象模式。   10、利用SQL完成数据库内的操作   我要向大家介绍的最重要的经验是充分利用SQL的面向集合的方法来解决数据库处理需求,而不是使用Java等过程化的编程语言。   如果编程人员要在一个表中查找许多行,结果中的每个行都会查找其他表中的数据,最后,编程人员创建了独立的UPDATE命令来成批地更新第一个表中的数据。与此类似的任务可以通过在set子句中使用多列子查询而在一个UPDATE命令中完成。当能够在单一的SQL命令中完成任务,何必要让数据在网上流来流去的?我建议用户认真学习如何最大限度地发挥SQL的功能。 <淘宝热门商品:
 

198.00 元 

08秋款欧洲正版G-star双排扣夹克外套 超好

 

80.00 元  

托玛琳厂家直营店

托玛琳预防富贵病;脂降压降糖减肥 活效清肠杯 一馈赠佳品限量版


来源:程序员网

小小豆叮

诺基亚推出手机Java经纪人服务

【赛迪网讯】诺基亚最近向市场投放了一个新的具有Java功能的移动电话系列,此举充分利用它对Sun微系统公司Web平台的投资,开辟了一项针对无线通信运营商的Java经纪人服务。这个经纪人网络集线器可以供运营商向客户提供一系列的J2ME应用软件。 诺基亚称,在美国和世界各地已经有一批运营商在测试这项服务,其目标是将全球范围内的J2ME软件开发商和移动通信运营商联系在一起。公司还准备让开发商有一个统一的地址来得到他们所需要的这些工具、测试服务和分销渠道。 诺基亚的Java经纪人服务为全球软件开发商开辟了一个崭新的市场,否则的话他们有可能得不到这样的机会。 J2ME网络服务平台是由Sun开发的,它提供的应用软件包括游戏和电子商务应用,以及针对无线设备的影像内容,这些移动设备包括下一代可上网移动电话以及PDA等。 在Sun于本周举办的第七届年度性JavaOne开发商大会上,Sun说有大约300万软件开发人员投身于对Java技术的改进,年内将有15家制造商提供的7500万台带有Java功能的手机上市。 为了满足这方面的需求,Sun根据它的Java规范申请(JSR)计划,于本周初发布了一套网络服务工具包,专门供移动电话、PDA和其他小型设备使用。 诺基亚的经纪人服务听上去很像是摩托罗拉3月25日公布的J2ME运营商级服务器。摩托罗拉说,它将在北美洲建设基于GSM、CDMA和TDMA技术的Java网络平台,可以供任何类型的手机使用。 诺基亚最近在推出5款带有J2ME平台的手机之后,还对自己的产品评估网络进行了扩充,使其能够容纳J2ME应用软件,同时还新成立了两个新的测试机构。 诺基亚还说,国家软件测试实验室(NSTL)以及Cap Gemini Ernst & Young公司正在同芬兰的VTT公司一道对Symbian操作系统和J2ME应用软件进行评估与测试。 <淘宝热门商品:
 

 

【杭州商盟】快美影像中心

 

¥:48.00 

纤体·丽颜New Concept茶坊 纯天然美容瘦身品位花饮

超级瘦腿!(迷迭香柠檬马鞭草)袋泡瘦腿茶 1月量2盒 包快递哦!


来源:程序员网

小小豆叮

使用设计模式改善程序结构

设计模式是对特定问题经过无数次经验总结后提出的能够解决它的优雅的方案。但是,如果想要真正使设计模式发挥最大作用,仅仅知道设计模式是什么,以及它是如何实现的是很不够的,因为那样就不能使你对于设计模式有真正的理解,也就不能够在自己的设计中正确、恰当的使用设计模式。本文试图从另一个角度(设计模式的意图、动机)来看待设计模式,通过这种新的思路,设计模式会变得非常贴近你的设计过程,并且能够指导、简化你的设计,最终将会导出一个优秀的解决方案。 1、介绍 在进行项目的开发活动中,有一些设计在项目刚刚开始工作的很好,但是随着项目的进展,发现需要对已有的代码进行修改或者扩展,导致这样做的原因主要有:新的功能需求的需要以及对系统进一步理解。在这个时候,我们往往会发现进行这项工作比较困难,即使能完成也要付出很大的代价。此时,一个必须要做的工作就是要对现有的代码进行重构(refactoring),通过重构使得我们接下来的工作变得相对容易。 重构就是在不改变软件系统代码的外部行为的前提下,改善它的内部结构。重构的目标就是使代码结构更加合理,富有弹性,能够适应新的需求、新的变化。对于特定问题给出优美解决方案的设计模式往往会成为重构的目标,而且一旦我们能够识别出能够解决我们问题的设计模式,将会大大简化我们的工作,因为我们可以重用别人已经做过的工作。但是在我们的原始设计和最终可能会适用于我们的设计模式间的过渡并不是平滑的,而是有一个间隙。这样的结果就是:即使我们已经知道了很多的设计模式,面对我们的实际问题,我们也没有一个有效的方法去判断哪一个设计模式适用于我们的系统,我们应该去怎样应用它。 造成上述问题的原因往往是由于过于注重设计模式所给出的解决方案这个结果,而对于设计模式的意图,以及它产生的动机却忽略了。然而,正是设计模式的意图、动机促使人们给出了一个解决一类问题的方案这个结果,设计模式的动机、意图体现了该模式的形成思路,所以更加贴近我们的实际问题,从而会有效的指导我们的重构历程。本文将通过一个实例来展示这个过程。 在本文中对例子进行了简化,这样做是为了突出问题的实质并且会使我们的思路更加清晰。思路本身才是最重要、最根本的,简化了的例子不会降低我们所展示的思路、方法的适用性。 2、问题描述 一个完善的软件系统,必须要对出现的错误进行相应的处理,只有这样才能使系统足够的健壮,我准备以软件系统中对于错误的处理为例,来展示我所使用的思路、方法。 在一个分布式的网管系统中,一个操作往往不会一定成功,常常会因为这样或者那样的原因失败,此时我们就要根据失败的原因相应的处理,使错误的影响局限在最小的范围内,最好能够恢复而不影响系统的正常运行,还有一点很重要,那就是在对错误进行处理的同时,一定不要忘记通知系统的管理者,因为只有管理者才有能力对错误进行进一步的分析,从而查找出错误的根源,从根本上解决错误。 下面我就从错误处理的通告管理者部分入手,开始我们的旅程。假定一个在一个分布式环境中访问数据库的操作,那么就有可能因为通信的原因或者数据库本身的原因失败,此时我们要通过用户界面来通知管理者发生的错误。简化了的代码示例如下:

/* 错误码定义 */
      class ErrorConstant
      {
          public static final int ERROR_DBACCESS         =  100;
          public static final int ERROR_COMMUNICATION  =  101;
      }
  
      /* 省略了用户界面中的其他的功能 */
      class GUISys
      {
          public void announceError(int errCode) {
  
              switch(errCode) {
  
              case ErrorConstant.ERROR_DBACCESS:
                  /* 通告管理者数据库访问错误的发生*/
              break;
  
              case ErrorConstant.ERROR_COMMUNICATION:
                  /* 通告管理者通信错误的发生*/
              break;
  
              }
          }
      } 
开始,这段代码工作的很好,能够完成我们需要的功能。但是这段代码缺少相应的弹性,很难适应需求的变化。 3、问题分析 熟悉面向对象的读者很快就会发现上面的代码是典型的结构化的方法,结构化的方法是以具体的功能为核心来组织程序的结构,它的封装度仅为1级,即仅有对于特定的功能的封装(函数)。这使得结构化的方法很难适应需求的变化,面向对象的方法正是在这一点上优于结构化的方法。在面向对象领域,是以对象来组成程序结构的,一个对象有自己的职责,通过对象间的交互来完成系统的功能,这使得它的封装度至少为2级,即封装了为完成自己职责的方法和数据。另外面向对象的方法还支持更高层次的封装,比如:通过对于不同的具体对象的共同的概念行为进行描述,我们可以达到3级的封装度- 抽象的类(在Java中就是接口)。封装的层次越高,抽象的层次就越高,使得设计、代码有越高的弹性,越容易适应变化。 考虑对上一节中的代码,如果在系统的开发过程中发现需要对一种新的错误进行处理,比如:用户认证错误,我们该如何做使得我们的系统能够增加对于此项功能的需求呢?一种比较简单、直接的做法就是在增加一条用来处理此项错误的case语句。是的,这种方法的确能够工作,但是这样做是要付出代价的。 首先,随着系统的进一步开发,可能会出现更多的错误类型,那么就会导致对于错误的处理部分代码冗长,不利于维护。其次,也是最根本的一点,修改已经能够工作的代码,很容易引入错误,并且在很多的情况下,错误都是在不经意下引入的,对于这种类型的错误很难定位。有调查表明,我们在开发过程中,用于修正错误的时间并不多,大部分的时间是在调试、发现错误。在面向对象领域,有一个很著名的原则:OCP(Open-Closed Principle),它的核心含意是:一个好的设计应该能够容纳新的功能需求的增加,但是增加的方式不是通过修改又有的模块(类),而是通过增加新的模块(类)来完成的。如果一个设计能够遵循OCP,那么就能够有效的避免上述的问题。 要是一个设计能够符合OCP原则,就要求我们在进行设计时不能简单的以功能为核心。要实现OCP的关键是抽象,抽象表征了一个固定的行为,但是对于这个行为可以有很多个不同的具体实现方法。通过抽象,我们就可以用一个固定的抽象的概念来代替哪些容易变化的数量众多的具体的概念,并且使得原来依赖于哪些容易变化的概念的模块,依赖于这个固定的抽象的概念,这样的结果就是:系统新的需求的增加,仅仅会引起具体的概念的增加,而不会影响依赖于具体概念的抽象体的其他模块。在实现的层面上,抽象体是通过抽象类来描述的,在Java中是接口(interface)。关于OCP的更详细描述,请参见参考文献[2]。 既然知道了问题的本质以及相应的解决方法,下面就来改善我们的代码结构。 4、初步方案 让我们重新审视代码,看看该如何进行抽象。在错误处理中,需要处理不同类型的错误,每个具体的错误具有特定于自己本身的一些信息,但是它们在概念层面上又是一致的,比如:都可以通过特定的方法接口获取自已内部的错误信息,每一个错误都有自己的处理方法。由此可以得到一个初步的方案:可以定义一个抽象的错误基类,在这个基类里面定义一些在概念上适用于所有不同的具体错误的方法,每个具体的错误可以有自己的不同的对于这些方法的实现。代码示例如下:

interface ErrorBase
  {
      public void handle();
              public String getInfo();
  } 

class DBAccessError implements ErrorBase
  {
      public void handle() {
          /* 进行关于数据库访问错误的处理 */
      } 

            public String getInfo() {
              /* 构造返回关于数据库访问错误的信息 */ 
            }
}

class CommunicationError implements ErrorBase
  {
      public void handle() {
          /* 进行关于通信错误的处理 */
      } 

            public String getInfo() {
          /* 构造返回关于通信错误的信息 */ 
            }
}  
这样,我们就可以在错误发生处,构造一个实际的错误对象,并以ErrorBase引用它。然后,交给给错误处理模块,此时错误处理模块就仅仅知道一个类型ErrorBase,而无需知道每一个具体的错误类型,这样就可以使用统一的方式来处理错误了。代码示例如下:

   class GUISys
  {
              public void announceError(ErrorBase error) {
              /* 使用一致的方式处理错误 */ 
            error.handle();
            }
}   

可以看出,对于新的错误类型的增加,仅仅需要增加一个具体的错误类,对于错误处理部分没有任何影响。看上去很完美,也符合OCP原则,但是进一步分析就会发现,这个方案一样存在着问题,我们将在下一个小节进行详细的说明。 5、进一步分析 上一个小节给出了一个方案,对于只有GUISys这一个错误处理者是很完美的,但是情况往往不是这样的。前面也曾经提到过,对于发生的错误,除了要通知系统的使用者外,还要进行其他的处理,比如:试图恢复,记如日志等。可以看出,这些处理方法和把错误通告给使用者是非常不同的,完全没有办法仅仅用一个handle方法来统一所有的不同的处理。但是,如果我们在ErrorBase中增加不同的处理方法声明,在具体的错误类中,根据自身的需要来相应的实现这些方法,好像也是一个不错的方案。代码示例如下:

interface ErrorBase
  {
              public void guiHandle();
              public void logHandle();
  }
  
  class DBAccessError implements ErrorBase
  {
      public void guiHandle() {
          /* 通知用户界面的数据库访问错误处理 */
      }
  
  public void logHandle() {
          /* 通知日志系统的数据库访问错误处理 */
      }
  }
  
  class CommunicationError implements ErrorBase
  {
      public void guiHandle() {
          /* 通知用户界面的通信错误处理 */
      }
  
  public void logHandle() {
          /* 通知日志系统的通信错误处理 */
      }
  }
  
  class GUISys
  {
      public void announceError(ErrorBase error) {
          error.guiHandle();
      }
  }
  
  class LogSys
  {
      public void announceError(ErrorBase error) {
      error.logHandle();
      }
  }  
读者可能已经注意到,这种做法其实也不是十分符合OCP,虽然它把变化局限在ErrorBase这个类层次架构中,但是增加新的处理方法,还是更改了已经存在的ErrorBase类。其实,这种设计方法,还违反了另外一个著名的面向对象的设计原则:SRP(Single Responsibility Principle)。这个原则的核心含意是:一个类应该有且仅有一个职责。关于职责的含意,面向对象大师Robert.C Martin有一个著名的定义:所谓一个类的职责是指引起该类变化的原因,如果一个类具有一个以上的职责,那么就会有多个不同的原因引起该类变化,其实就是耦合了多个互不相关的职责,就会降低这个类的内聚性。错误类的职责就是,保存和自己相关的错误状态,并且提供方法用于获取这些状态。上面的设计中把不同的处理方法也放到错误类中,从而增加了错误类的职责,这样即使和错误类本身没有关系的对于错误处理方式的变化,增加、修改都会导致错误类的修改。这种设计方法一样会在需求变化时,带来没有预料到的问题。那么能否将对错误的处理方法从中剥离出来呢?如果读者比较熟悉设计模式(这里的熟悉是指,设计模式的意图、动机,而不是指怎样去实现一个具体的设计模式),应该会隐隐约约感觉到一个更好的设计方案即将出现。 6、设计模式浮出水面 让我们对问题重新描述一下:我们已经有了一个关于错误的类层次结构,现在我们需要在不改变这个类层次结构的前提下允许我们增加对于这个类层次的新的处理方法。听起来很耳熟吧,不错,这正是过于visitor设计模式的意图的描述。通过对于该模式动机的分析,我们很容易知道,要想使用visitor模式,需要定义两个类层次:一个对应于接收操作的元素的类层次(就是我们的错误类),另一个对应于定义对元素的操作的访问者(就是我们的对于错误的不同处理方法)。这样,我们就转换了问题视角,即把需要不同的错误处理方法的问题转变为需要不同的错误处理类,这样的结果就是我们可以通过增加新的模块(类)来增加新的错误处理方法,而不是通过增加新的错误处理方法(这样做,就势必要修改已经存在的类)。 一旦到了这一部,下面的工作就比较简单了,因为visitor模式已经为我们搭建了一个设计的上下文,此时我们就可以关注visitor模式的实现部分来指导我们下面的具体实现了。下面仅仅给出最终的程序结构的UML图以及代码示例,其中忽略了错误类中的属于错误本身的方法,各个具体的错误处理方法通过这些方法和具体的错误类对象交互,来完成各自的处理功能。 最终的设计的程序结构图 最终的代码示例

interface ErrorBase
  {
      public void handle(ErrorHandler handler);
  }
  
  class DBError implements ErrorBase
  {
      public void handle(ErrorHandler handler) {
          handler.handle(this);
      }
  }
  
  class CommError implements ErrorBase
  {
      public void handle(ErrorHandler handler) {
          handler.handle(this);
      }
  }
  
  interface ErrorHandler 
{
    public void handle(DBrror dbError);
      public void handle(CommError commError);
  }
  
  class GUISys implements ErrorHandler
  {
      public void announceError(ErrorBase error) {
          error.handle(this);
      }
  
      public void handle(DBError dbError) {
          /* 通知用户界面进行有关数据库错误的处理 */ 
    }

    public void handle(CommError commError) {
              /* 通知用户界面进行有关通信错误的处理 */ 
    }
}

class LogSys implements ErrorHandler
  {
      public void announceError(ErrorBase error) {
          error.handle(this);
      }
  
      public void handle(DBError dbError) {
                  /* 通知日志系统进行有关数据库错误的处理 */ 
    }

    public void handle(CommError commError) {
          /* 通知日志系统进行有关通信错误的处理 */ 
    }
} 
7、结论 设计模式并不仅仅是一个有关特定问题的解决方案这个结果,它的意图以及它的动机往往更重要,因为一旦我们理解了一个设计模式的意图、动机,那么在设计过程中,就很容易的发现适用于我们自己的设计模式,从而大大简化设计工作,并且可以得到一个比较理想的设计方案。 另外,在学习设计模式的过程中,应该更加注意设计模式背后的东西,即具体设计模式所共有的的一些优秀的指导原则,这些原则在参考文献[1]的第一章中有详细的论述,基本上有两点: 发现变化,封装变化 优先使用组合(Composition),而不是继承 如果注意从这些方面来学习、理解设计模式,就会得到一些比单个具体设计模式本身更有用的知识,并且即使在没有现成模式可用的情况下,我们也一样可以设计出一个好的系统来。 参考文献 [1] Design Patterns, Gamma, et. al., Addison Wesley [2] Design Principles and Design Patterns,Robert C. Martin,www.objectmentor.com <淘宝热门商品:
 

19.00 元  

【广州商盟】靓一族专营匡威运动鞋/秋冬针织帽子

特价19元 09年新款出口韩国时尚个性净版球球针织帽爆款卷边

 

225.00 元  

上海商盟】易淘视听商城

五钻100%原装松下HTX7耳机经典白色 松下头戴式耳机 东方神起代言


来源:程序员网

小小豆叮

Java Swing中的键盘事件处理

  在java Swing编程过程中,经常需要处理键盘事件,例如处理快捷键等。这里就介绍如何定义键盘事件,以及如何处理这些事件。   在jdk1.2中,分别针对Jcomponent和Text类的对象定制了不同的处理键盘事件的方法:在Jcomponent中,定义了registerKeyboardAction方法,使用这个方法来将需要处理的键盘事件以及处理事件的行为绑定在一起。Text类中具有keymap对象,同Jcomponent中的处理方法类似,这个对象保存着需要处理的键盘事件和对应的行为。   而在jdk1.3中,使用一种新的方法来处理键盘事件,它将jdk1.2的两种方法整合在一起。不需要区分被处理的是Jcomponent还是Text类型的组件。它定义了两个新的类:InputMap和ActionMap。他们均是简单的表或映射。一个InputMap将一个Keystroke对应到一个对象,ActionMap将一个对象对应到一个行为(Action)。通常InputMap中KeyStroke所对应的对象是一个字符串,通过这个字符串可以在ActionMap中查找到相应的行为。   InputMap和ActionMap中均有put方法。InputMap的put方法可以将Keystroke对应到一个对象,而ActionMap的put方法可以将一个对象对应到一个行为。   在每一个Jcomponent组件中,会有三个缺省的InputMap和一个缺省的ActionMap。他们可以通过调用getInputMap(int condition)和getActionMap()得到。三个InputMap分别是当组件本身拥有焦点时的InputMap(WHEN_FOCUSED),当组件的祖先拥有焦点时的InputMap(WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)和组件所在的窗体具有焦点时的InputMap(WHEN_IN_FOCUSED_WINDOW)(括号内表示为了得到这些InputMap,应该在getInputMap中设置的参数)。以下分别说明这三种InputMap:   1, 组件本身拥有焦点时的InputMap:当组件拥有焦点时,键盘按键按下,则java在这个InputMap中查找键盘事件所对应的KeyStroke对象。   2, 组件的祖先拥有焦点时的InputMap:当组件的祖先拥有焦点时,键盘按键按下,则java查找这个InputMap。   3, 组件所在的窗口拥有焦点时的InputMap:当组件所在的窗口具有焦点时,键盘按键按下,则java查找这个InputMap。   当一个键被按下,这个事件被转化成一个KeyStroke对象,java会查找这个Jcomponent的相应InputMap(例如,当组件的祖先具有焦点时,java就查找这个Jcomponent的祖先拥有焦点的InputMap)中是否有这个KeyStroke,如果有,取出它所对应的对象(通常是字符串),利用这个对象在这个Jcomponent的ActionMap中查找,如果找到对应的行为(Action),则java执行这个行为的actionPerformed方法(随后介绍这个方法)。从而达到处理键盘事件的目的。   每一个InputMap可以具有parent属性,这个属性的值是一个InputMap。当在一个InputMap中查找不到键盘事件的KeyStroke时,java会自动在它的parent属性指定的InputMap中查找,依次向上查找,直至找到。使用parent的好处是:当有一些固定的,不希望用户进行改动的键盘映射可以存放在parent属性所指定的InputMap中,从而避免被意外修改;另外可以将多个Jcomponent的缺省InputMap设置具有相同的parent,使得可以共享一些键盘绑定的设置。可以通过InputMap类的setparent()方法设置它的parent属性。ActionMap也具有相同的parent属性,使用方法也相同。   以上是如何将一个键盘事件对应到一个行为,以下就简单介绍行为(Action)。   行为是一个实现了Action接口的类。在Action接口中定义了7个方法。其中最关键的是actionPerformed()方法。这个方法描述了这个行为的具体操作过程。其他几个方法包括setEnabled,isEnabled,putValue,getValue,addPropertyChangeListener,和removePropertyChangeListener方法。他们分别用来设置行为是否可用、判断行为可用的状态、设置和取得行为的一些属性,最后两个方法用来允许其他对象在行动对象的属性发生变化后得到通知。   通常我们使用一个实现了Action接口的大部分方法的抽象类AbstractAction类作为基类,重载actionPerformed方法以实现我们的行为。   我们用一个例子来具体说明如何进行实际的操作。   首先编写一个具体的行为,对指定的键盘事件进行处理:

public class TextAction extends AbstractAction
{
 private String a;
 public TextAction(String a)
 { this.a = a; }
  public void actionPerformed(ActionEvent parm1)
  {
   String b = parm1.getActionCommand(); //得到行为的命令字符串
   System.out.println("command="+b);
   System.out.println("prompt="+this.a);
  }
 } 

  建立四个TextAction对象:

TextAction whenFocusSon = new TextAction("focus son");
TextAction whenFocusFather = new TextAction("focus father");
TextAction window = new TextAction("window");
TextAction ancestor = new TextAction("ancestor"); 

  随后,在一个窗体中加入两个面板,名为sonPanel和parentPanel,使得parentPanel是sonPanel的祖先。并在sonPanel中加入一个名为son的button,在parentPanel中加入名为parent的button。在fatherPanel外加入几个button。

  得到son组件的三个InputMap,并创建一个名为focusFatherIm的InputMap,使得这个InputMap成为focusIm的parent:

//get default inputMap (when focus inputmap) and set a parent InputMap
focusIm = son.getInputMap();
focusFatherIm = new InputMap();
focusIm.setParent(focusFatherIm);

//get WHEN_ANCESTOR_OF_FOCUSED_COMPONENT inputMap 
ancestorIm = son.getInputMap(WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);

//get WHEN_IN_FOCUSED_WINDOW inputMap
windowIm = son.getInputMap(WHEN_IN_FOCUSED_WINDOW);
在这些InputMap中分别加入键盘绑定:
focusIm.put(KeyStroke.getKeyStroke('f'),"actionFocusSon");
focusFatherIm.put(KeyStroke.getKeyStroke('F'),"actionFocusFather");
ancestorIm.put(KeyStroke.getKeyStroke('a'),"actionAncestor");
windowIm.put(KeyStroke.getKeyStroke('w'),"actionWindow");
得到son组件的缺省的ActionMap,并将已经建立的行为与特定的对象(字符串)进行绑定:
am = son.getActionMap();

am.put("actionFocusSon",whenFocusSon);
am.put("actionFocusFather",whenFocusFather);
am.put("actionAncestor",ancestor);
am.put("actionWindow",window); 

运行程序及其相应结果:   1, 单击son按钮,这时如果按下'f','F','a','w',程序均会有相应的输出。这是因为,此时的焦点在son按钮上,而son按钮组件的三个InputMap都是有效的。所以他们对应的事件都会发生。   2, 单击parent按钮,这时按下'w',程序会有相应的输出。而按下'f','F','a',程序没有反应。这是因为parent按钮具有焦点,这个按钮不是son按钮的祖先,而son所在的窗口具有焦点,所以只有组件所在窗口具有焦点的InputMap是有效的。   3, 单击其他的按钮(parentPanel外的按钮),这时按下'w',程序会有相应的输出。而按下'f','F','a',程序没有反应。这是因为这些按钮具有焦点,他们不是son按钮的祖先,而son所在的窗口具有焦点,所以只有组件所在窗口具有焦点的InputMap是有效的。 附:主要程序代码:

import java.awt.*;
import javax.swing.*;
import com.borland.jbcl.layout.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import com.sun.java.swing.plaf.motif.*;

public class EventPanel extends JPanel implements ActionListener
{
 JButton btnYellow = new JButton();
 JButton btnBlue = new JButton();
 JButton btnRed = new JButton();
 JPanel parentPanel = new JPanel();
 JPanel sonPanel = new JPanel();
 XYLayout xYLayout1 = new XYLayout();
 JButton son = new JButton();
 JButton parent = new JButton();
 public EventPanel()
 {
  try{
   jbInit();
  }catch(Exception ex)
  { ex.printStackTrace(); }
  }
 void jbInit() throws Exception
 {
  btnYellow.setText("Yellow");
  btnYellow.setBounds(new Rectangle(35, 23, 97, 29));
  this.setLayout(null);
  btnBlue.setBounds(new Rectangle(154, 21, 97, 29));
  btnBlue.setText("Blue");
  btnRed.setBounds(new Rectangle(272, 24, 97, 29));
  btnRed.setText("Red");
  parentPanel.setBorder(BorderFactory.createRaisedBevelBorder());
  parentPanel.setBounds(new Rectangle(27, 68, 358, 227));
  parentPanel.setLayout(xYLayout1);
  sonPanel.setBorder(BorderFactory.createLoweredBevelBorder());
  son.setText("son");
  parent.setText("parent");
  this.add(btnYellow, null);
  this.add(btnBlue, null);
  this.add(btnRed, null);
  this.add(parentPanel, null);
  parentPanel.add(sonPanel, new XYConstraints(58, 22, 229, 125));
  sonPanel.add(son, null);
  parentPanel.add(parent, new XYConstraints(150, 167, -1, -1));
  btnYellow.addActionListener(this);
  btnRed.addActionListener(this);
  btnBlue.addActionListener(this);

  InputMap focusIm,focusFatherIm,ancestorIm,windowIm;
  ActionMap am;
  //create four TextAction for diff purpose
  TextAction whenFocusSon = new TextAction("focus son");
  TextAction whenFocusFather = new TextAction("focus father");
  TextAction window = new TextAction("window");
  TextAction ancestor = new TextAction("ancestor");
  //get default inputMap (when focus inputmap) and set a parent InputMap
  focusIm = son.getInputMap();
  focusFatherIm = new InputMap();
  focusIm.setParent(focusFatherIm);
  //get WHEN_ANCESTOR_OF_FOCUSED_COMPONENT inputMap
  ancestorIm = son.getInputMap(WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
  //get WHEN_IN_FOCUSED_WINDOW inputMap
  windowIm = son.getInputMap(WHEN_IN_FOCUSED_WINDOW);
  //put the keyStroke to the InputMap
  focusIm.put(KeyStroke.getKeyStroke('f'),"actionFocusSon");
  focusFatherIm.put(KeyStroke.getKeyStroke('F'),"actionFocusFather");
  ancestorIm.put(KeyStroke.getKeyStroke('a'),"actionAncestor");
  windowIm.put(KeyStroke.getKeyStroke('w'),"actionWindow");
  //get the actionMap
  am = son.getActionMap();
  am.put("actionFocusSon",whenFocusSon);
  am.put("actionFocusFather",whenFocusFather);
  am.put("actionAncestor",ancestor);
  am.put("actionWindow",window);
 }
 public void actionPerformed(ActionEvent e)
 {
  //this code is used to change the backgracolor
  Object source=e.getSource();
  Color color=null;//=getBackground();
  if (source==btnYellow) color=Color.yellow;
  else if (source==btnRed) color = Color.red;
  else if (source == btnBlue) color = Color.blue;
  setBackground(color);
  repaint();
 }
}
<淘宝热门商品:
 

88.00 元 

双皇冠 狂卖2000条!Levis 超赞牛仔裤

 

105.00 元 

Levi's 复古铜系列情侣款女装牛仔裤


来源:程序员网

小小豆叮

编程老手与高手的误区

编程老手与高手的误区 自从计算机问世以来,程序设计就成了令人羡慕的职业,程序员在受人宠爱之后容易发展成为毛病特多却常能自我臭美的群体。 如今在Internet上流传的“真正”的程序员据说是这样的: (1) 真正的程序员没有进度表,只有讨好领导的马屁精才有进度表,真正的程序员会让领导提心吊胆。 (2) 真正的程序员不写使用说明书,用户应当自己去猜想程序的功能。 (3) 真正的程序员几乎不写代码的注释,如果注释很难写,它理所当然也很难读。 (4) 真正的程序员不画流程图,原始人和文盲才会干这事。 (5) 真正的程序员不看参考手册,新手和胆小鬼才会看。 (6) 真正的程序员不写文档也不需要文档,只有看不懂程序的笨蛋才用文档。 (7) 真正的程序员认为自己比用户更明白用户需要什么。 (8) 真正的程序员不接受团队开发的理念,除非他自己是头头。 (9) 真正的程序员的程序不会在第一次就正确运行,但是他们愿意守着机器进行若干个30小时的调试改错。 (10) 真正的程序员不会在上午9:00到下午5:00之间工作,如果你看到他在上午9:00工作,这表明他从昨晚一直干到现在。 …… 具备上述特征越多,越显得水平高,资格老。所以别奇怪,程序员的很多缺点竟然可以被当作优点来欣赏。就象在武侠小说中,那些独来独往、不受约束且带点邪气的高手最令人崇拜。我曾经也这样信奉,并且希望自己成为那样的“真正”的程序员,结果没有得到好下场。 我从读大学到博士毕业十年来一直勤奋好学,累计编写了数十万行C++/C代码。有这样的苦劳和疲劳,我应该称得上是编程老手了吧? 我开发的软件都与科研相关(集成电路CAD和3D图形学领域),动辄数万行程序,技术复杂,难度颇高。这些软件频频获奖,有一个软件获得首届中国大学生电脑大赛软件展示一等奖。在1995年开发的一套图形软件库到2000年还有人买。罗列出这些“业绩”,可以说明我算得上是编程高手了吧? 可惜这种个人感觉不等于事实。 读博期间我曾用一年时间开发了一个近10万行C++代码的3D图形软件产品,我内心得意表面谦虚地向一位真正的软件高手请教。他虽然从未涉足过3D图形领域,却在几十分钟内指出该软件多处重大设计错误。让人感觉那套软件是用纸糊的华丽衣服,扯一下掉一块,戳一下破个洞。我目瞪口呆地意识到这套软件毫无实用价值,一年的心血白化了,并且害死了自己的软件公司。 人的顿悟通常发生在最心痛的时刻,在沮丧和心痛之后,我作了深刻反省,“面壁”半年,重新温习软件设计的基础知识。补修“内功”之后,又觉得腰板硬了起来。博士毕业前半年,我曾到微软中国研究院找工作,接受微软公司一位资深软件工程师的面试。他让我写函数strcpy的代码。 太容易了吧? 错! 这么一个小不点的函数,他从三个方面考查: (1)编程风格; (2)出错处理; (3)算法复杂度分析(用于提高性能)。 在大学里从来没有人如此严格地考查过我的程序。我化了半个小时,修改了数次,他还不尽满意,让我回家好好琢磨。我精神抖擞地进“考场”,大汗淋漓地出“考场”。这“高手”当得也太窝囊了。我又好好地反省了一次。 我把反省后的心得体会写成文章放在网上传阅,引起了不少软件开发人员的共鸣。我因此有幸和国产大型IT企业如华为、上海贝尔、中兴等公司的同志们广泛交流。大家认为提高质量与生产率是软件工程要解决的核心问题。高质量程序设计是非常重要的环节,毕竟软件是靠编程来实现的。 我们心目中的老手们和高手们能否编写出高质量的程序来? 不见得都能! 就我的经历与阅历来看,国内大学的计算机教育压根就没有灌输高质量程序设计的观念,教师们和学生们也很少自觉关心软件的质量。勤奋好学的程序员长期在低质量的程序堆中滚爬,吃尽苦头之后才有一些心得体会,长进极慢,我就是一例。 现在国内IT企业拥有学士、硕士、博士文凭的软件开发人员比比皆是,但他们在接受大学教育时就“先天不足”,岂能一到企业就突然实现质的飞跃。试问有多少软件开发人员对正确性、健壮性、可靠性、效率、易用性、可读性(可理解性)、可扩展性、可复用性、兼容性、可移植性等质量属性了如指掌?并且能在实践中运用自如?。“高质量”可不是干活小心点就能实现的! 我们有充分的理由疑虑: (1)编程老手可能会长期用隐含错误的方式编程(习惯成自然),发现毛病后都不愿相信那是真的! (2)编程高手可以在某一领域写出极有水平的代码,但未必能从全局把握软件质量的方方面面。 事实证明如此。我到上海贝尔工作一年来,陆续面试或测试过近百名“新”“老”程序员的编程技能,质量合格率大约是10%。很少有人能够写出完全符合质量要求的if语句,很多程序员对指针、内存管理一知半解,……。 领导们不敢相信这是真的。我做过现场试验:有一次部门新进14名硕士生,在开欢迎会之前对他们进行“C++/C编程技能”摸底考试。我问大家试题难不难?所有的人都回答不难。结果没有一个人及格,有半数人得零分。竞争对手公司的朋友们也做过试验,同样一败涂地。 真的不是我“心狠手辣”或者要求过高,而是很多软件开发人员对自己的要求不够高。 要知道华为、上海贝尔、中兴等公司的员工素质在国内IT企业中是比较前列的,倘若他们的编程质量都如此差的话,我们怎么敢期望中小公司拿出高质量的软件呢?连程序都编不好,还谈什么振兴民族软件产业,岂不胡扯。 我打算定义编程老手和编程高手,请您别见笑。 定义1:能长期稳定地编写出高质量程序的程序员称为编程老手。 定义2:能长期稳定地编写出高难度、高质量程序的程序员称为编程高手。 根据上述定义,马上得到第一推论:我既不是高手也算不上是老手。 在写此书前,我阅读了不少程序设计方面的英文著作,越看越羞惭。因为发现自己连编程基本技能都未能全面掌握,顶多算是二流水平,还好意思谈什么老手和高手。希望和我一样在国内土生土长的程序员朋友们能够做到: (1)知错就改; (2)经常温故而知新; (3)坚持学习,天天向上。 <淘宝热门商品:
 

42.00 元  

同仁堂保健品 协和护肤品 营养品 减肥瘦身品

 

50.00 元  

韩服CASH之家

韩服 HF 跑跑卡丁车 新劳迪 劳迪Introduction 永久 支持韩文名


来源:程序员网

小小豆叮

用java.net包建立双向通讯

  利用java.net包提供的网络能力。以有连接流通讯方式为例, 在服务器端的操作为一般为:    ⑴ 创建一个ServerSocket对象,在指定端口监听客户端发来的请求。    ⑵ 在接收到请求时accept()方法将返回一个Socket对象。    ⑶ 用上述Socket对象创建输入、输出流对象。    ⑷ 通过输入、输出流与客户交互。    ⑸ 交互完毕,关闭输入、输出流与Socket。    ⑹ 服务程序运行结束,关闭ServerSocket。    实现代码代码类如: try{ boolean flag=true; Socket clientSocket=null; ServerSocket serverSocket = new ServerSocket(0); System.out.println("Server listen on: " +serverSocket.getLocalPort()); while(flag){ clientSocket=serverSocket.accept(); DataInputStream is=new DataInputStream( new bufferedInputStream(client Socket.getInputStream())); PrintStream os=new PrintStream( new bufferedOutputStream(clientSocket. getOutputStream())); // 处理Applet请求 os.close(); is.close(); clientSocket.close(); } serverSocket.close(); }catch( IOException e){ System.err.println(" Exception: "+e); }    在客户端的操作为:    ⑴ 创建Socket对象建立与服务器的连接。    ⑵ 用该Socket对象创建输入、输出流。    ⑶ 与服务器交互。    ⑷ 交互完毕,关闭输入、输出流与Socket。    实现代码类如: try { Socket clientSocket =new Socket("serverName",7); OutputStream os=clientSocket.getOutputStream(); DataInputStream is=new DataInputStream( clientSocket.getInputStream()) ; // 其它操作. os.close(); is.close(); clientSocket.close(); }catch(Exception e){ System.err.println("Exception:"+e); }    这种方法只依赖于标准的Java网络支持,不需要用到附加的软件包或工具, 因此显得相当简洁和灵活,易于实现某些特殊的需要。 <淘宝热门商品:
 

268.00 元 

绝对NO1新版mirifem魔力丰丰胸片

 

185.00 元  

累计销售1000多件 G-STAR 雪纺短款夹克


来源:程序员网

小小豆叮

一个socket服务器程序

<淘宝热门商品:
 

 

【上海商盟】高更食品极美滋新奥尔良系列烧烤腌料皇冠旗舰店

 

¥:180.00 

安婴房婴儿用品童装旗舰店/上海总经销可批发、团购

【双皇冠】热卖再次到货!原单DISNEY维尼小熊 专业婴儿定型枕


来源:程序员网

小小豆叮

让 DOM 遍历

DOM Traversal 模块一瞥 Brett McLaughlin(brett@newInstance.com) Enhydra 策略顾问,Lutris Technologies 2001 年 8 月 “文档对象模型(DOM)”提供了有用的模块来以高级方式扩展其核心功能。本文深入研究了 DOM Traversal 模块,演示了如何查明您的语法分析器是否支持该模块以及如何使用它来遍历选中的节点集或整个 DOM 树。读完本文之后,您将彻底理解 DOM Traversal,并会在您的 Java 和 XML 编程工具箱中拥有一个强大的新工具。八个样本代码清单演示了这些技术。 如果您在过去三年中作过很多 XML 处理,那么您几乎一定遇到过“文档对象模型”(简称 DOM)。这种对象模型表示应用程序中的 XML 文档,并提供一种简单的方式来读取 XML 并写入或更改现有文档中的数据(如果您是 DOM 新手,请参阅参考资料来获得更多背景知识。)如果您正在努力成为一名 XML 高手,那您可能已经彻底地学过 DOM,并且知道如何使用它所提供的几乎每一种方法。然而,还有许多 DOM 功能没有被大多数开发人员认识到。 大多数开发人员实际都已接触过 DOM 核心。该核心指的是 DOM 规范,它概括 DOM 的含义、它应该如何操作以及提供哪些方法等等。甚至有经验的开发人员都不太知道或了解许多不太常用的 DOM 模块。这些模块允许开发人员更高效而轻松地使用树、同时处理不同的范围的节点、对 HTML 或 CSS 页面进行操作以及其它任务,所有这些都不是仅使用核心 DOM 规范可以做到的。在以后几个月中,我计划写几篇文章,详细介绍几个模块,包括 HTML 模块 — Range 模块 — 在本文中,将介绍 Traversal 模块。 通过学习如何使用 DOM Traversal,您将看到遍历整个 DOM 树、构建定制对象过滤器来轻易查找所需数据以及以前所未有的轻松方式遍历 DOM 树是多么快捷。我还将向您介绍一个实用程序,该程序允许您检查您选择的语法分析器是否支持特定的 DOM 模块,同时,我还将为您演示许多其它样本代码。那么,请启动您喜爱的源码编辑器,然后让我们开始。 获得信息 首先,确保您有所需工具来遍历一些示例代码。对于本文,您手头要有一个 XML 语法分析器。该语法分析器需要提供 DOM 实现。事实上,那很简单;几乎每一种您可以得到的 XML 语法分析器都支持 SAX(Simple API for XML)和 DOM。您要确保您所用的语法分析器具有 DOM 级别 2 支持,这很简单,只需阅读该语法分析器的发行说明或简单地从供应商处获得最新版本即可。 获得语法分析器之后,您需要确保它支持我们正在讨论的 DOM Traversal 模块。虽然这应该也可以在语法分析器文档中找到关于这方面的说明,但我想为您演示一个简单的编程方法来检查这一点。事实上,“清单 1”中演示的程序可以让您询问任何语法分析器:看它是否有任何模块。我在其中包括了大多数常见 DOM 模块的特定检查,当然包括 DOM Traversal。这个程序使用 DOM 类 org.w3c.dom.DOMImplementation 及其 hasFeature() 方法:通过传入每个模块的名称来检查是否支持这些模块,找出实际支持哪些模块很容易。代码相当简单,我把阅读程序流程的任务留给您。 清单 1. DOMModuleChecker 类 import org.w3c.dom.DOMImplementation; public class DOMModuleChecker { /** Vendor DOMImplementation impl class */ private String vendorImplementationClass = "org.apache.xerces.dom.DOMImplementationImpl"; /** Modules to check */ private String[] moduleNames = {"XML", "Views", "Events", "CSS", "Traversal", "Range", "HTML"}; public DOMModuleChecker() { } public DOMModuleChecker(String vendorImplementationClass) { this.vendorImplementationClass = vendorImplementationClass; } public void check() throws Exception { DOMImplementation impl = (DOMImplementation)Class.forName(vendorImplementationClass) .newInstance(); for (int i=0; ijava KeywordSearcher keywords.xml Processing file: keywords.xml Search phrase found: 'galaxy' Search phrase found: 'Hyperion' Search phrase found: 'dwarves' Search phrase found: 'hobbit' Search phrase found: 'Foundation' Search phrase found: 'Wheel of Time' Search phrase found: 'The Path of Daggers' 显然,随着文档越来越复杂,NodeFilter 实现也会相应地更加复杂。此处的要点在于,DOM Traversal 与这个小过滤器一样有用,它在更复杂的情况下会变得极其强大。例如,可以根据元素/属性名,在文档中查找表示成属性或元素的数据。对于核心 DOM 代码来说,这确实是个棘手的任务,它同样需要进行许多树的遍历,而 NodeIterator 则可以为您处理这些。因此,让您的想象力自由驰骋,并构筑那些过滤器吧! 在森林中查找树 在结束关于 DOM Traversal 的讲座之前,我要简短介绍一下 TreeWalker。由于篇幅所限(我希望这是一篇文章,而不只是一个章节),不想过于深入,但是因为您已经了解了 NodeIterator,这应该很简单。通过清单 8 中的方法创建 TreeWalker: 如果认识到 TreeWalker 方法采用的参数与 createNodeFilter() 方法相同,那并不会引起什么问题。事实上,剩下的唯一问题是“迭代节点与遍历树有什么区别?”答案是:使用 TreeWalker 时,可以维护一个树结构。在使用 NodeIterator 时,返回的节点实际上已从其树中的初始位置分离。迭代节点使操作很快捷,因为一旦返回节点就废弃其树位置。但是,使用 TreeWalker 时,当节点从定制节点过滤器返回时,节点仍保留在它们的树上下文中。这就允许您可以实际通过过滤器来查看整个 XML 文档。 做一个练习,尝试编写一个程序来显示清单 4 中不带处理说明、注释或属性的 XML 文档。在开始前,有一个提示:首先,要使用 TreeWalker 来确保保留树格式。其次,编写一个定制的 NodeFilter 实现,以便只接受元素或文本类型的节点。最后,使用清单 6 中的程序作为模板,并更改几行代码。然后,就像那样,您自己就得到了一个定制的 DOM 树视图。如果您理解了节点部分并且可以编写出这个样本程序,那么您正在成为 DOM Traversal 高手的路上顺利前进。 希望您一直都在看 Traversal 模块所展示的所有可能性。以过滤方式遍历 DOM 树使得寻找元素、属性、文本和其它 DOM 结构变得容易。您还应该能够使用 DOM Traversal 模块编写更有效、结构更好的代码。因此,采用一个现有的搜索 DOM 树的程序,然后转换它,使其使用 traversal 方法;我知道您将对该结果感到满意。同以往一样,请让我知道本文是否对您有所帮助(使用本文所附的论坛),咱们网上见。 关于作者 Brett McLaughlin (brett@newInstance.com) 是 Lutris Technologies 的 Enhydra 策略顾问和分布式系统体系结构方面的专家。他是《Java 和 XML》(O'Reilly) 的作者。他参与了如 Java Servlet、Enterprise JavaBean 技术、XML 和商家对商家应用程序等技术的研究。他与 Jason Hunter 一起发起了 JDOM 项目,该项目为从 Java 应用程序中操纵 XML 提供了一个简单的 API。他还是 Apache Cocoon 项目和 EJBoss EJB 服务器的活跃开发人员以及 Apache Turbine 项目的共同创始人之一。 <淘宝热门商品:
 

¥:29.00 

‖ 淘时尚 ‖入托名字条‖布质可烫可缝‖姓名贴‖

‖四钻‖棉质耐洗‖全彩色免缝姓名条 熨烫入托名字条 迪斯尼

 

 

衣品堂


来源:程序员网

小小豆叮

Jbuild5的中文Help

<淘宝热门商品:
 

228.00 元 

【专柜正品 超值之选】 JACK JONES/杰

 

¥:48.00 

纤体·丽颜New Concept茶坊 纯天然美容瘦身品位花饮

超级瘦腿!(迷迭香柠檬马鞭草)袋泡瘦腿茶 1月量2盒 包快递哦!


来源:程序员网

小小豆叮

MD5算法研究

作者:王可   综述   MD5的全称是Message-Digest Algorithm 5(信息-摘要算法),在90年代初由MIT Laboratory for Computer Science和RSA Data Security Inc的Ronald L. Rivest开发出来,经MD2、MD3和MD4发展而来。它的作用是让大容量信息在用数字签名软件签署私人密匙前被"压缩"成一种保密的格式(就是把一个任意长度的字节串变换成一定长的大整数)。不管是MD2、MD4还是MD5,它们都需要获得一个随机长度的信息并产生一个128位的信息摘要。虽然这些算法的结构或多或少有些相似,但MD2的设计与MD4和MD5完全不同,那是因为MD2是为8位机器做过设计优化的,而MD4和MD5却是面向32位的电脑。这三个算法的描述和C语言源代码在Internet RFCs 1321中有详细的描述(http://www.ietf.org/rfc/rfc1321.txt),这是一份最权威的文档,由Ronald L. Rivest在1992年8月向IEFT提交。   Rivest在1989年开发出MD2算法。在这个算法中,首先对信息进行数据补位,使信息的字节长度是16的倍数。然后,以一个16位的检验和追加到信息末尾。并且根据这个新产生的信息计算出散列值。后来,Rogier和Chauvaud发现如果忽略了检验和将产生MD2冲突。MD2算法的加密后结果是唯一的--既没有重复。   为了加强算法的安全性,Rivest在1990年又开发出MD4算法。MD4算法同样需要填补信息以确保信息的字节长度加上448后能被512整除(信息字节长度mod 512 = 448)。然后,一个以64位二进制表示的信息的最初长度被添加进来。信息被处理成512位Damg?rd/Merkle迭代结构的区块,而且每个区块要通过三个不同步骤的处理。Den Boer和Bosselaers以及其他人很快的发现了攻击MD4版本中第一步和第三步的漏洞。Dobbertin向大家演示了如何利用一部普通的个人电脑在几分钟内找到MD4完整版本中的冲突(这个冲突实际上是一种漏洞,它将导致对不同的内容进行加密却可能得到相同的加密后结果)。毫无疑问,MD4就此被淘汰掉了。   尽管MD4算法在安全上有个这么大的漏洞,但它对在其后才被开发出来的好几种信息安全加密算法的出现却有着不可忽视的引导作用。除了MD5以外,其中比较有名的还有SHA-1、RIPE-MD以及HAVAL等。   一年以后,即1991年,Rivest开发出技术上更为趋近成熟的MD5算法。它在MD4的基础上增加了"安全-带子"(Safety-Belts)的概念。虽然MD5比MD4稍微慢一些,但却更为安全。这个算法很明显的由四个和MD4设计有少许不同的步骤组成。在MD5算法中,信息-摘要的大小和填充的必要条件与MD4完全相同。Den Boer和Bosselaers曾发现MD5算法中的假冲突(Pseudo-Collisions),但除此之外就没有其他被发现的加密后结果了。   Van Oorschot和Wiener曾经考虑过一个在散列中暴力搜寻冲突的函数(Brute-Force Hash Function),而且他们猜测一个被设计专门用来搜索MD5冲突的机器(这台机器在1994年的制造成本大约是一百万美元)可以平均每24天就找到一个冲突。但单从1991年到2001年这10年间,竟没有出现替代MD5算法的MD6或被叫做其他什么名字的新算法这一点,我们就可以看出这个瑕疵并没有太多的影响MD5的安全性。上面所有这些都不足以成为MD5的在实际应用中的问题。并且,由于MD5算法的使用不需要支付任何版权费用的,所以在一般的情况下(非绝密应用领域。但即便是应用在绝密领域内,MD5也不失为一种非常优秀的中间技术),MD5怎么都应该算得上是非常安全的了。   算法的应用   MD5的典型应用是对一段信息(Message)产生信息摘要(Message-Digest),以防止被篡改。比如,在UNIX下有很多软件在下载的时候都有一个文件名相同,文件扩展名为.md5的文件,在这个文件中通常只有一行文本,大致结构如:    MD5 (tanajiya.tar.gz) = 0ca175b9c0f726a831d895e269332461   这就是tanajiya.tar.gz文件的数字签名。MD5将整个文件当作一个大文本信息,通过其不可逆的字符串变换算法,产生了这个唯一的MD5信息摘要。如果在以后传播这个文件的过程中,无论文件的内容发生了任何形式的改变(包括人为修改或者下载过程中线路不稳定引起的传输错误等),只要你对这个文件重新计算MD5时就会发现信息摘要不相同,由此可以确定你得到的只是一个不正确的文件。如果再有一个第三方的认证机构,用MD5还可以防止文件作者的"抵赖",这就是所谓的数字签名应用。   MD5还广泛用于加密和解密技术上。比如在UNIX系统中用户的密码就是以MD5(或其它类似的算法)经加密后存储在文件系统中。当用户登录的时候,系统把用户输入的密码计算成MD5值,然后再去和保存在文件系统中的MD5值进行比较,进而确定输入的密码是否正确。通过这样的步骤,系统在并不知道用户密码的明码的情况下就可以确定用户登录系统的合法性。这不但可以避免用户的密码被具有系统管理员权限的用户知道,而且还在一定程度上增加了密码被破解的难度。   正是因为这个原因,现在被黑客使用最多的一种破译密码的方法就是一种被称为"跑字典"的方法。有两种方法得到字典,一种是日常搜集的用做密码的字符串表,另一种是用排列组合方法生成的,先用MD5程序计算出这些字典项的MD5值,然后再用目标的MD5值在这个字典中检索。我们假设密码的最大长度为8位字节(8 Bytes),同时密码只能是字母和数字,共26+26+10=62个字符,排列组合出的字典的项数则是P(62,1)+P(62,2)….+P(62,8),那也已经是一个很天文的数字了,存储这个字典就需要TB级的磁盘阵列,而且这种方法还有一个前提,就是能获得目标账户的密码MD5值的情况下才可以。这种加密技术被广泛的应用于UNIX系统中,这也是为什么UNIX系统比一般操作系统更为坚固一个重要原因。   算法描述   对MD5算法简要的叙述可以为:MD5以512位分组来处理输入的信息,且每一分组又被划分为16个32位子分组,经过了一系列的处理后,算法的输出由四个32位分组组成,将这四个32位分组级联后将生成一个128位散列值。   在MD5算法中,首先需要对信息进行填充,使其字节长度对512求余的结果等于448。因此,信息的字节长度(Bits Length)将被扩展至N*512+448,即N*64+56个字节(Bytes),N为一个正整数。填充的方法如下,在信息的后面填充一个1和无数个0,直到满足上面的条件时才停止用0对信息的填充。然后,在在这个结果后面附加一个以64位二进制表示的填充前信息长度。经过这两步的处理,现在的信息字节长度=N*512+448+64=(N+1)*512,即长度恰好是512的整数倍。这样做的原因是为满足后面处理中对信息长度的要求。   MD5中有四个32位被称作链接变量(Chaining Variable)的整数参数,他们分别为:A=0x01234567,B=0x89abcdef,C=0xfedcba98,D=0x76543210。   当设置好这四个链接变量后,就开始进入算法的四轮循环运算。循环的次数是信息中512位信息分组的数目。   将上面四个链接变量复制到另外四个变量中:A到a,B到b,C到c,D到d。   主循环有四轮(MD4只有三轮),每轮循环都很相似。第一轮进行16次操作。每次操作对a、b、c和d中的其中三个作一次非线性函数运算,然后将所得结果加上第四个变量,文本的一个子分组和一个常数。再将所得结果向右环移一个不定的数,并加上a、b、c或d中之一。最后用该结果取代a、b、c或d中之一。 以一下是每次操作中用到的四个非线性函数(每轮一个)。    F(X,Y,Z) =(X&Y)|((~X)&Z)    G(X,Y,Z) =(X&Z)|(Y&(~Z))    H(X,Y,Z) =X^Y^Z    I(X,Y,Z)=Y^(X|(~Z))    (&是与,|是或,~是非,^是异或)   这四个函数的说明:如果X、Y和Z的对应位是独立和均匀的,那么结果的每一位也应是独立和均匀的。 F是一个逐位运算的函数。即,如果X,那么Y,否则Z。函数H是逐位奇偶操作符。   假设Mj表示消息的第j个子分组(从0到15),<<    FF(a,b,c,d,Mj,s,ti)表示a=b+((a+(F(b,c,d)+Mj+ti)<<    GG(a,b,c,d,Mj,s,ti)表示a=b+((a+(G(b,c,d)+Mj+ti)<<    HH(a,b,c,d,Mj,s,ti)表示a=b+((a+(H(b,c,d)+Mj+ti)<<    II(a,b,c,d,Mj,s,ti)表示a=b+((a+(I(b,c,d)+Mj+ti)<<   这四轮(64步)是:   第一轮    FF(a,b,c,d,M0,7,0xd76aa478)    FF(d,a,b,c,M1,12,0xe8c7b756)    FF(c,d,a,b,M2,17,0x242070db)    FF(b,c,d,a,M3,22,0xc1bdceee)    FF(a,b,c,d,M4,7,0xf57c0faf)    FF(d,a,b,c,M5,12,0x4787c62a)    FF(c,d,a,b,M6,17,0xa8304613)    FF(b,c,d,a,M7,22,0xfd469501)    FF(a,b,c,d,M8,7,0x698098d8)    FF(d,a,b,c,M9,12,0x8b44f7af)    FF(c,d,a,b,M10,17,0xffff5bb1)    FF(b,c,d,a,M11,22,0x895cd7be)    FF(a,b,c,d,M12,7,0x6b901122)    FF(d,a,b,c,M13,12,0xfd987193)    FF(c,d,a,b,M14,17,0xa679438e)    FF(b,c,d,a,M15,22,0x49b40821)   第二轮    GG(a,b,c,d,M1,5,0xf61e2562)    GG(d,a,b,c,M6,9,0xc040b340)    GG(c,d,a,b,M11,14,0x265e5a51)    GG(b,c,d,a,M0,20,0xe9b6c7aa)    GG(a,b,c,d,M5,5,0xd62f105d)    GG(d,a,b,c,M10,9,0x02441453)    GG(c,d,a,b,M15,14,0xd8a1e681)    GG(b,c,d,a,M4,20,0xe7d3fbc8)    GG(a,b,c,d,M9,5,0x21e1cde6)    GG(d,a,b,c,M14,9,0xc33707d6)    GG(c,d,a,b,M3,14,0xf4d50d87)    GG(b,c,d,a,M8,20,0x455a14ed)    GG(a,b,c,d,M13,5,0xa9e3e905)    GG(d,a,b,c,M2,9,0xfcefa3f8)    GG(c,d,a,b,M7,14,0x676f02d9)    GG(b,c,d,a,M12,20,0x8d2a4c8a)   第三轮    HH(a,b,c,d,M5,4,0xfffa3942)    HH(d,a,b,c,M8,11,0x8771f681)    HH(c,d,a,b,M11,16,0x6d9d6122)    HH(b,c,d,a,M14,23,0xfde5380c)    HH(a,b,c,d,M1,4,0xa4beea44)    HH(d,a,b,c,M4,11,0x4bdecfa9)    HH(c,d,a,b,M7,16,0xf6bb4b60)    HH(b,c,d,a,M10,23,0xbebfbc70)    HH(a,b,c,d,M13,4,0x289b7ec6)    HH(d,a,b,c,M0,11,0xeaa127fa)    HH(c,d,a,b,M3,16,0xd4ef3085)    HH(b,c,d,a,M6,23,0x04881d05)    HH(a,b,c,d,M9,4,0xd9d4d039)    HH(d,a,b,c,M12,11,0xe6db99e5)    HH(c,d,a,b,M15,16,0x1fa27cf8)    HH(b,c,d,a,M2,23,0xc4ac5665)   第四轮    II(a,b,c,d,M0,6,0xf4292244)    II(d,a,b,c,M7,10,0x432aff97)    II(c,d,a,b,M14,15,0xab9423a7)    II(b,c,d,a,M5,21,0xfc93a039)    II(a,b,c,d,M12,6,0x655b59c3)    II(d,a,b,c,M3,10,0x8f0ccc92)    II(c,d,a,b,M10,15,0xffeff47d)    II(b,c,d,a,M1,21,0x85845dd1)    II(a,b,c,d,M8,6,0x6fa87e4f)    II(d,a,b,c,M15,10,0xfe2ce6e0)    II(c,d,a,b,M6,15,0xa3014314)    II(b,c,d,a,M13,21,0x4e0811a1)    II(a,b,c,d,M4,6,0xf7537e82)    II(d,a,b,c,M11,10,0xbd3af235)    II(c,d,a,b,M2,15,0x2ad7d2bb)    II(b,c,d,a,M9,21,0xeb86d391)   常数ti可以如下选择:   在第i步中,ti是4294967296*abs(sin(i))的整数部分,i的单位是弧度。(4294967296等于2的32次方) 所有这些完成之后,将A、B、C、D分别加上a、b、c、d。然后用下一分组数据继续运行算法,最后的输出是A、B、C和D的级联。   当你按照我上面所说的方法实现MD5算法以后,你可以用以下几个信息对你做出来的程序作一个简单的测试,看看程序有没有错误。    MD5 ("") = d41d8cd98f00b204e9800998ecf8427e    MD5 ("a") = 0cc175b9c0f1b6a831c399e269772661    MD5 ("abc") = 900150983cd24fb0d6963f7d28e17f72    MD5 ("message digest") = f96b697d7cb7938d525a2f31aaf161d0    MD5 ("abcdefghijklmnopqrstuvwxyz") = c3fcd3d76192e4007dfb496cca67e13b    MD5 ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") = d174ab98d277d9f5a5611c2c9f419d9f    MD5 ("123456789012345678901234567890123456789012345678901234567890123456789 01234567890") = 57edf4a22be3c955ac49da2e2107b67a   如果你用上面的信息分别对你做的MD5算法实例做测试,最后得出的结论和标准答案完全一样,那我就要在这里象你道一声祝贺了。要知道,我的程序在第一次编译成功的时候是没有得出和上面相同的结果的。   MD5的安全性   MD5相对MD4所作的改进:    1. 增加了第四轮;    2. 每一步均有唯一的加法常数;    3. 为减弱第二轮中函数G的对称性从(X&Y)|(X&Z)|(Y&Z)变为(X&Z)|(Y&(~Z));    4. 第一步加上了上一步的结果,这将引起更快的雪崩效应;    5. 改变了第二轮和第三轮中访问消息子分组的次序,使其更不相似;    6. 近似优化了每一轮中的循环左移位移量以实现更快的雪崩效应。各轮的位移量互不相同。   MD5源程序   在rfc1321种已经有了用C语言实现MD5算法的源程序,如果你需要在Java或者像PHP、C#这样的类C语言上实现的话,只要对那段C代码作一些简单的改动,应该能够很容易的实现。 <淘宝热门商品:
 

¥:9.99 

【黑龙江商盟】棋子儿平价美妆店-烟熏彩妆/假睫毛大全/新娘用品

买十送一全网最低价高级无痕透明根部自然浓密凌乱假睫毛仿真毛发

 

 

天使名妆 08日韩版秋季OL洋装平价针织2件包邮 时尚only韩国代购


来源:程序员网

小小豆叮

关于线程的讲解?(出自Java原著)

Thread Scheduling In Java technology,threads are usually preemptive,but not necessarily Time-sliced(the process of giving each thread an equal amount of CPU time).It is common mistake to believe that "preemptive" is a fancy word for "does time-slicing". For the runtime on a Solaris Operating Environment platform,Java technology does not preempt threads of the same priority.However,the runtime on Microsoft Windows platforms uses time-slicing,so it preempts threads of the same priority and even threads of higher priority.Preemption is not guaranteed;however,most JVM implementations result in behavior that appears to be strictly preemptive.Across JVM implementations,there is no absolute guarantee of preemption or time-slicing.The only guarantees lie in the coder’s use of wait and sleep. The model of a preemptive scheduler is that many threads might be runnable,but only one thread is actually running.This thread continues to run until it ceases to be runnable or another thread of higher priority becomes runnable.In the latter case,the lower priority thread is preempted by the thread of higher priority,which gets a chance to run instead. A thread might cease to runnable (that is,because blocked) for a variety of reasons.The thread’s code can execute a Thread.sleep() call,deliberately asking the thread to pause for a fixed period of time.The thread might have to wait to access a resource and cannot continue until that resource become available. All thread that are runnable are kept in pools according to priority.When a blocked thread becomes runnable,it is placed back into the appropriate runnable pool.Threads from the highest priority nonempty pool are given CPU time. The last sentence is worded loosed because: (1) In most JVM implementations,priorities seem to work in a preemptive manner,although there is no guarantee that priorities have any meaning at all; (2) Microsoft Window’s values affect thread behavior so that it is possible that a Java Priority 4 thread might be running,in spite of the fact that a runnable Java Priority 5 thread is waiting for the CPU. In reality,many JVMs implement pool as queues,but this is not guaranteed hehavior. 线程调度(试翻译,欢迎指正) 在java技术中,线程通常是抢占式的而不需要时间片分配进程(分配给每个线程相等的cpu时间的进程)。一个经常犯的错误是认为“抢占”就是“分配时间片”。 在Solaris平台上的运行环境中,相同优先级的线程不能相互抢占对方的cpu时间。但是,在使用时间片的windows平台运行环境中,可以抢占相同甚至更高优先级的线程的cpu时间。抢占并不是绝对的,可是大多数的JVM的实现结果在行为上表现出了严格的抢占。纵观JVM的实现,并没有绝对的抢占或是时间片,而是依赖于编码者对wait和sleep这两个方法的使用。 抢占式调度模型就是许多线程属于可以运行状态(等待状态),但实际上只有一个线程在运行。该线程一直运行到它终止进入可运行状态(等待状态)或是另一个具有更高优先级的线程变成可运行状态。在后一种情况下,底优先级的线程被高优先级的线程抢占,高优先级的线程获得运行的机会。 线程可以因为各种各样的原因终止并进入可运行状态(因为堵塞)。例如,线程的代码可以在适当时候执行Thread.sleep()方法,故意让线程中止;线程可能为了访问资源而不得不等待直到该资源可用为止。 所有可运行的线程根据优先级保持在不同的池中。一旦被堵塞的线程进入可运行状态,它将会被放回适当的可运行池中。非空最高优先级的池中的线程将获得cpu时间。 最后一个句子是不精确的,因为: (1)在大多数的JVM实现中,虽然不能保证说优先级有任何意义,但优先级看起来象是用抢占方式工作。 (2)微软windows的评价影响线程的行为,以至尽管一个处于可运行状态的优先级为5的java线程正在等待cpu时间,但是一个优先级为4的java线程却可能正在运行。 实际上,许多JVM用队列来实现池,但没有保证行为。 <淘宝热门商品:
 

 

【家倍乐床上笔记本电脑桌专利厂家批发零售店】

 

68.00元  

海洋新奇特购物天堂


来源:程序员网

小小豆叮

比较 Microsoft .NET 和 J2EE 的构成技术

即使你不在微软的平台上写程序,你可能也听过 Microsoft 推出的「.NET」平台,此技术是用来对付非微软阵营的兵器。如果你读过微软的新闻稿,或者你浏览过 MSDN 的内容,还是你出席了微软的专业程序员会议(也就是「.NET」平台现身的地方),你可能仍有两个疑问: 「.NET」平台到底是什么? 「.NET」架构和 J2EE 有哪些差异? 如果你想得更远一点,你还会有第三个问题: 我们能从「.NET」架构中学到一些哪些有助于推展企业软件开发的思维? 目前,「.NET」架构尚嫩,许多细节仍有待微软的「.NET」小组厘清。虽然如此,我们仍然能够从现有的信息中得到上述问题的解答。 「.NET」平台到底是什么? 现今大家对于「.NET」平台的看法有点类似寓言「瞎子摸象」,观点不同,自有不同的想法。有些人说「.NET」是微软的下一代 Visual Studio 开发环境;有些人说它是一个新的程序语言(C#);还有些人说它是以 XML 和 SOAP 为基础的资料交换与传递讯息的机制。其实,上述三者都是「.NET」想扮演的角色,而且还不止于此。 让我们先得到一些较具体的观念。下面列出「.NET」平台内部的组成: C# 是一个「新程序语言」,用来撰写类别和组件。C# 融合了 C/C++ 和 Java 的特色,还多了一些其它的特色,比方说 metadata tag。 一个「通用语言的执行时期系统(common language runtime)」,用来执行 IL 格式的程序代码。任何语言的原始程序只要被编译成 IL 格式之后,就可在「.NET」平台执行。 一组「基础组件」,提供多样的功能(例如:网络),以供执行时期系统使用。 「ASP+」,是新版的 ASP,能让 ASP 被编译成 IL 的格式。 「Win Form」和「Web Form」,是一组新的 UI 组件骨架,供 Visual Studio 使用。 「ADO+」,是新版的 ADO,使用 XML 和 SOAP 来进行资料存取和资料交换。 「.NET」和 J2EE 有哪些差异? 如你所见,「.NET」平台是一堆技术的组合。微软把这些技术当作现有技术(例如:J2EE 和 CORBA)的另一种选择,但实际上比较起来又是如何呢?下面是我们的一些分析比较:

Microsoft.NET

J2EE

主要差异

C# 程序语言

Java 程序语言

C# Java 都源自 C/C++。两者有相当多共同的主要特色(包括:自动内存管理、阶层式名字空间)。C# J avaBeans 学来一些组件观念(propertie/attributeevent),还新增了一些特色(比方说 metadata tag),但是使用不同的语法。

Java 可以在任何有 Java 虚拟机器的平台上执行。C# 目前只能在 Windows 上执行。

C# 使用IL的执行时期系统。透过 just-in-time (JIT) 的编译方式或原生码编译方式来执行。Java 程序是透过 Java 虚拟机器来执行,但是也可以编译成原生码。

.NET」通用组件

Java core API

高阶的「.NET」组件将支持透过 XML SOAP 来存取。(请看下面 ADO+ 的介绍)

Active Server Pages+ (ASP+)

Java ServerPages (JSP)

ASP+ 将可以使用 Visual BasicC#、和其它语言来撰写程序片断,然后被编译成IL的格式(不像以前的 ASP 每次都需要直译)。JSP 使用 Java 的程序代码,编译成 Java bytecode(可以需要时才编译,也可以预先编译好)。

IL 执行时期系统

Java 虚拟机器、CORBA IDLCORBA ORB

.NET」允许不同的程序语言使用 Windows 上的同一套组件。

Java 允许 Java bytecode 在兼容的虚拟机器上都可以执行。

CORBA 允许不同语言和不同平台的对象互相沟通(必须有适合的 ORB)。J2EE 中可以使用CORBA,但两者的整合度不算是很紧密。

Win Form Web Form

Java Swing

类似的 Web 组件在标准的 Java 平台中付之阙如,有些其它厂商在 Java IDE 中提供一些组件。

MS Visual Studio IDE 提供 Win Form 和 Web Form 的 RAD 工具,目前尚未有其它厂商宣称要支持 Win Form 和 Web Form。许多 Java IDE 工具都支持 Swing。

ADO+ SOAP Web 服务

JDBCEJBJMS Java XML 链接库(XML4JJAXP

ADO+ 允许透过 HTTP  进行 XML 资料交换(在远程资料对象和多层的程序之间),也就是SOAP。「.NET」的 Web 服务使用 SOAP 的讯息模型。EJBJDBC 等则是把资料交换的通讯协议交由程序员自行决定,用 HTTPRMI/JRMP IIOP 都可以。

上面是各项技术的比较,下面是两者的整体比较: 特色:「.NET」和 J2EE 都提供许多相似的特色,虽然方法不见得完全一样。   可移植性:「.NET」只能在 Windows 上运作,但是理论上可以支持许多语言。而且,「.Net」支持 SOAP,使得不同平台的组件可以和「.NET」的组件交换讯息。虽然「.NET」中有些技术(比方说 SOAP 和其 discovery 与 lookup 机制)是公开的规格,核心的技术(比方说 IL 执行时期系统、ASP+、Win Form 与 Web Form)都还是由微软所把持,而且微软将会是「.NET」完整开发工具和平台的唯一提供厂商。 J2EE 则可以在任何有 JVM 的平台上执行,只要有兼容的服务(比方说:EJB 容器、JMS 等)即可。J2EE 的一切标准都是公开的,许多厂商都提供兼容的产品和开发工具。但是 J2EE 是一个单一语言的平台,如果要和其它语言平台沟通必须透过 CORBA(目前 J2EE 平台上不见得有支持 CORBA)。 整体来看 上面的各项分析指出「.NET」和 J2EE 的主要差异。微软正在「.NET」上做两件值得注意的事:开放「.NET」给其它程序语言的使用者,并开放「.NET」给非「.NET」组件的使用者(透过 XML 和 SOAP)。 因为「.NET」允许其它语言的组件整合进来,「.NET」赋予 Perl、Eiffel、Cobol 和其它语言的程序员在微软的平台上做事。各种语言的爱好者尤其喜欢这点,因为他们中多数人才不在乎微软和 Sun 以及开放阵营之间的战火。 大家的反应如何? 对微软的程序员来说: 如果你一直守着微软的架构,那么「.NET」的出现是一件好事。ASP+ 比 ASP 好;ADO+ 比 ADO 好;C# 比 C/C++ 好。「.NET」平台差不多要到 2001 才会现身,所以你还有时间准备。毫无疑问地,它将会变成微软预设的开发环境。如果你现在正在使用微软的开发工具,未来移转到「.NET」之后,你也会享受到这种种好处。 然而,「.NET」平台的一些目的太过崇高,不保证能达得到(至少短期内是如此)。比方说,IL 执行系统有一些很明显的难题待克服,想整合进此系统的每个语言必须清楚地定义如何对应到 IL,以及 IL 所需的 metadata。某语言要兼容于 IL 来必须提供编译器(x 语言转 IL,和 IL 转 x 语言)。 这有前例可寻。有许多编译器可将非 Java 语言转成 JVM 可执行的程序,这些语言包括了 JPython、PERCobol、the Tcl/Java project,有趣的是,连 Bertrand Meyer 和一些 Eiffel 语言的家伙也早在几年前就做出 Eiffel-to-JavaVM 的工具。其中只有 JPython 是成功的,其它的工具好象都没人在用,甚至连这些语言本身的族群都不用,虽然这些工具的出现使得他们能使用最爱的语言来写 Java 程序。为什么就是无法引起大家的热诚?我相信这是因为人们怀疑混合两种异质的语言环境会水土不服。如果他们想写在 JVM 上执行的程序,他们宁可花时间去学 Java 语言。我想,同样的情况也会出现在「.NET」平台上,人们宁可花时间去学 C# 来写「.NET」程序,而非使用其它语言。 还有一点要注意的:「.NET」使用的 SOAP 架构的性能值得观察。SOAP 使用 HTTP 来传递 XML。HTTP 不是有效率的通讯协议,而且 XML 还需要额外的文件剖析(parse),这又是计算上的负担。两者的结合会使得交易的速度大大低于其它方案。XML 是一个用途广、健全、又具涵义的讯息机制,而 HTTP 是一个广泛又能避免许多关于防火墙的问题,但是如果效率对你来说很重要,那么你应该多瞧瞧其它的方式,而不要用 SOAP。 对 Java 和 Open Source 族群来说: 「.NET」很容易被视为微软基于市场需求所推出的解决方案。其实,「.NET」是微软开始策略改变的征兆。以往微软在面对其它架构和平台有不错的战果,现在他们面对了来自 Java 和 open source 的压力,开始有了「开放」的迹象,也试着直接去满足程序员的需求,这可是和以往的老大作风大不相同。如果你是 Java 或 open source 的爱好者,请注意:这场战争的本质已经有所改变了。 而且,微软的 IL 执行时期系统有一点值得注意的目标:消弭程序语言和 API 之间的藩篱。Java 消弭了平台间的藩篱,但是如果你想使用 J2EE,你就必须搭配 Java 语言。「.NET」想让你使用你喜爱的语言来开发「.NET」程序,这点是很了不起的(但结果是否会成真仍是个大问号)。从这点来看,J2EE 就比不上「.NET」,但是这点应该不算太重要。 J2EE 如果想迎战「.NET」,有几个议题应该立刻被注意到。首先,J2EE 应该将 XML 好好地整合进来。我不是指制定 XML SAX/DOM 的 API,也不是指拿 XML 当作设定档格式,我说的是 XML 的讯息交换和处理应该被加进 J2EE。当然,目前你可以用 XML 格式当作 JMS 讯息内容,但整个 J2EE 却不会因此受益。XML 是一堆标准的集合,包括业界标准的 API 和 DTD,这应该都是资料交换时可以享受到的好处才是。 但是微软把赌注下在 SOAP 上,而且正积极地将 SOAP 的相关信息传送给程序员。J2EE 也应该如此,我想到的方法之一是在 JMS 上加一层 XML messaging provider,并整合进 Java Naming and Directory Interface(JNDI)和 LDAP、NIS、COS Naming 等技术。这样就等于是结合了 SOAP/BizTalk provider、ebXML provider 等技术。 本文作者:Jim Farley,着有 Java Distributed Computing、Java Enterprise in a Nutshell(合着) 本文译者:蔡学镛 <淘宝热门商品:
 

 

【25元或以上商品任意2件包运】【时尚卓尔】-个性随我秀

 

6.80 元  

北欧橱窗

三冠 三角形强力吸墙角架/置物架/杂物架 承重3公斤 不伤墙面0.14


来源:程序员网

小小豆叮