黑马程序员-JAVA.net-网络工具类粗解

——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——-


Java自1.0就已集成网络操作的工具,提供了从基于流的Java.net包以实现网络访问和操作,而Java1.4又假如了nio包,利用异步和面向缓冲的新方式实现了更高效的网络操作工具类,并以此重写了部分io包中的内容。

Java.net

java.net 包可以大致分为两个部分:

  • 低级 API,用于处理以下抽象:
    • 地址,也就是网络标识符,如 IP 地址。
    • 套接字,也就是基本双向数据通信机制。
    • 接口,用于描述网络接口。
  • 高级 API,用于处理以下抽象:
    • URI,表示统一资源标识符。
    • URL,表示统一资源定位符。
    • 连接,表示到 URL 所指向资源的连接。

地址

InetAddress 类是表示 IP(Internet 协议)地址的抽象。它拥有两个子类:
- 用于 IPv4 地址的 Inet4Address。
- 用于 IPv6 地址的 Inet6Address。
IP 地址是 IP 使用的4个byte(IPv4)或者16个byte(IPv6);对于Hostname,将通过主机名解析服务(通常是DNS)将他解析为IP地址。

ipv6

并非所有系统和网络环境都支持 IPv6 协议,在 IPv6 不可用或被显式禁用的情况下,在此情况下,大多数方法在使用 Inet6Address 调用时都将抛出异常。

简单的创建InetAddress 的例子

try {
            InetAddress ip = null;

            //localhost
            ip = Inet4Address.getLocalHost();
            sp(ip);//xxxx-xxxx/192.168.1.100 for example

            //loopback
            ip= Inet4Address.getLoopbackAddress();
            sp(ip);//localhost/127.0.0.1 

            //Indicate a ip & hostname without NS
            ip=Inet4Address.getByAddress("Its.A.Fake.Domian", 
                    new byte[]{(byte) 0xfe,(byte) 0xfe,(byte) 0xfe,(byte) 0xfe});
            sp(ip);//Its.A.Fake.Domian/254.254.254.254

            //Indicate a ip with 4bytes for ipv4
            ip=Inet4Address.getByAddress(
                    new byte[]{(byte) 0xfe,(byte) 0xfe,(byte) 0xfe,(byte) 0xfe});
            sp(ip);///254.254.254.254

            //Indicate a ip for hostname using NS
            ip=Inet4Address.getByName("www.oracle.com");
            sp(ip);//www.oracle.com/184.50.90.127

        } catch (UnknownHostException e) {
            e.printStackTrace();
        }

套接字

套接字是实际操作传输层的Java工具对象,基于UDP和TCP的传输都可以通过构建套接字来完成。

端口

端口是TCP和UDP协议中对同一IP地址不同应用对象的区分手段,TCP与UDP的端口是互不影响的。

UDP与DatagramSocket

UDP:User Datagram Protocol是一种面向无连接,基于数据包的传输层协议,一个UDP数据包最大为65535bytes。但是UDP不含任何传输保证,数据的有效性必须由应用自行处理,但相对于TCP,它的连接开销小,相对速度快,很多协议也是基于UDP的,比如很重要的DNS(UDP53)协议,DHCP(UDP67/68)协议;另外很多协议同时支持TCP以及UDP以实现更好的数据交换。

DatagramSocket是Java中处理UDP点对点传输的网络端点对象。通过传输DatagramPacket网络包实现UDP协议的传输。将DatagramPacket目标指定为广播地址可以实现域内网络多播。

DatagramSocket的构造方法允许指定端口,当绑定端口失败时会抛出SocketException;若不指定,将由JVM随机安排一个可用的端口。
由于端口属于系统资源,必须保证端口在使用完后通过close()方法释放。

MulticastSocket

UDP的组播,多播组通过 D 类 IP 地址和标准 UDP 端口号指定,通过joinGroup(group)加入组,将消息发送到多播组时,该主机和端口的所有预定接收者都将接收到消息,多个 MulticastSocket 可以同时预定多播组和端口,并且都会接收到组数据报。

UDP的简单例子

一个简单的控制台通过本地环回的UDP信息收发

        final int port = 36314;
        try {
            UdpReciver serv = new UdpReciver(port);
            StringUdpSender cli=new StringUdpSender(port);
            new Thread(serv).start();
            new Thread(cli).start();
            ts(10000);
            serv.stop();
        } catch (IOException e) {
            e.printStackTrace();
        }
class StringUdpSender implements Runnable {
    private DatagramSocket soc;
    private final String exitFlag;
    private final int port;
    public StringUdpSender(int port,String exitFlag) throws SocketException {
        super();
        this.exitFlag = exitFlag;
        soc=new DatagramSocket();
        this.port=port;
    }
     public StringUdpSender(int port) throws SocketException {
         this(port,"exit");
    }
    @Override
    public void run() {
        String msg=null;
        BufferedReader br= new BufferedReader(new  InputStreamReader(System.in));
        DatagramPacket dp=null;
        while(true){
            try {
                msg=br.readLine();
                if(!exitFlag.equals(msg)){
                    dp=new DatagramPacket(msg.getBytes(), 
                            msg.getBytes().length, InetAddress.getByName("localhost"), port);
                    soc.send(dp);
                }else{
                    break;
                }
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
        if (soc != null && !soc.isClosed()) {
            soc.close();
        }
    }

}

class UdpReciver implements Runnable {
    private DatagramSocket soc;
    private volatile boolean runFlag = false;

    public UdpReciver(int port) throws SocketException {
        super();
        this.soc = new DatagramSocket(port);
    }

    public void stop() {
        this.setRunFlag(false);
    }

    public boolean isRunning() {
        return runFlag;
    }

    private synchronized void setRunFlag(boolean runFlag) {
        this.runFlag = runFlag;
    }

    @Override
    public void run() {
        this.setRunFlag(true);
        DatagramPacket dp = new DatagramPacket(new byte[1024], 1024);
        while (runFlag) {
            try {
                this.soc.receive(dp);
            } catch (IOException e) {
                e.printStackTrace();
            }
            sp(dp.getAddress());
            sp(new String(dp.getData(),0,dp.getLength()));
        }
        if (soc != null && !soc.isClosed()) {
            soc.close();
            sp("Server stop");
        }
    }

    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        if (soc != null && !soc.isClosed()) {
            soc.close();
        }
    }
}

TCP

TCP(Transfer Control Protocol)是面向连接的,相对可靠的数据传输协议,TCP的双方必须经过3次握手来达成一个连接,有了连接之后才能开始传输数据,TCP为了保证不发生丢包,就给每个包一个序号,同时序号也保证了传送到接收端实体的包的按序接收。TCP用一个校验和函数来检验数据是否有错误;在发送和接收时都要计算校验和,一旦有问题将请求重发,包丢失也将请求重发,而重复的包将丢弃。这样TCP就达成了一个可靠的数据传送。
TCP的连接时有状态的,双方通过协议信号更新彼此的状态,常见的服务端LISTEN表示监听中,任一方ESTABLISHED 表示连接已建立等。

Socket

Socket是Java中TCP端点的抽象,套接字的实际工作由 SocketImpl 类的实例执行。
客户端可以通过构造一个包含服务器IP和端口的Socket尝试打开TCP连接。
打开连接之后,可以通过getInputStream和getOutputStream与对端通信。

ServerSocket

ServerSocket是Java中TCP服务端的抽象,绑定一个端口即可开始服务。服务器套接字的实际工作由 SocketImpl 类的实例执行。
ServerSocket通过accept() 阻塞方法获得客户端的连接,然后可以操作这个连接来处理交互。

TCP服务器和客户端的简单例子

这个例子实现了简单的TCP服务器和客户端,服务器对客户端送来的String简单的+上”response”返回客户端,服务端实现了简单的异步消息和连接统计。


public class TcpDemo {

    public static void main(String[] args) {
        final int serverPort = 23333;
        InetAddress serverAddr = null;
        try {
            serverAddr = InetAddress.getLocalHost();
        } catch (UnknownHostException e1) {
            e1.printStackTrace();
        }

        //start Server
        try {
            new Thread(new TcpServer(serverPort)).start();
        } catch (IOException e1) {
            e1.printStackTrace();
        }

        //start Client
        Socket s = null;
        //5 thread client
//      for (int i = 0; i < 5; i++) {
//          try {
//              s = new Socket(serverAddr, serverPort);
//          } catch (IOException e) {
//              e.printStackTrace();
//          }
//          new Thread(new TcpClient(s)).start();
//      }

        //about 400 thread client
        while(true){
            ts(10);

            try {
                s = new Socket(serverAddr, serverPort);
            } catch (IOException e) {
                e.printStackTrace();
            }
            new Thread(new TcpClient(s)).start();
        }
    }

}

class TcpClient implements Runnable {
    //end connection key word
    public static final String ENDCONN="##$$$$##";
    Socket socket;

    public TcpClient(Socket socket) {
        super();
        this.socket = socket;
    }

    @Override
    public void run() {
//      sp(socket+" Client");
        //client request

        //emulate chat
        Random r=new Random();
        int times=r.nextInt(20)+5;
        PrintWriter pw=null;
        BufferedReader bfr=null;
        try {
            pw = new PrintWriter(socket.getOutputStream());
            bfr=new BufferedReader(new InputStreamReader(socket.getInputStream()));
        } catch (IOException e1) {
            e1.printStackTrace();
        }
        for (int i = 0; i < times; i++) {
            String msg = "hello from"+socket.getLocalPort()+"##"+i;
            pw.println(msg);
            pw.flush();
            try {
                msg=bfr.readLine();
            } catch (IOException e) {
                e.printStackTrace();
            }
//          sp(msg);
            ts(300);
        }
        pw.println(TcpClient.ENDCONN);
        pw.flush();
//      if(socket!=null && !socket.isClosed()){
//          try {
//              socket.close();
//          } catch (IOException e) {
//              e.printStackTrace();
//          }
//      }
    }

}

class TcpServer implements Runnable {
    private ServerSocket s;
    private ExecutorService exes;
    private final BlockingQueue connects;
    private volatile ConcurrentHashMap> results;

    public TcpServer(int port) throws IOException {
        super();
        this.s = new ServerSocket(port);
        this.exes = Executors.newCachedThreadPool();
        this.connects = new SynchronousQueue();
        this.results = new ConcurrentHashMap>();
    }

    @Override
    public void run() {
        // Dispatcher thread
        new Thread(new Runnable() {

            @Override
            public void run() {
                while (true) {
                    try {
                        Socket s = connects.take();
                        String stamp = s.toString() + Instant.now().toString();
                        Future result = exes.submit(new RequestHandler(
                                s));
                        results.put(stamp, result);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
        // Result collector thread
        new Thread(new Runnable() {

            @Override
            public void run() {
                while (true) {
                    ts(1000);
                    List remove=new ArrayList();
                    results.forEach((String stamp,Future result) -> {
                        if(result.isDone()){
                            try {
//                              sp(result.get());
                                remove.add(stamp);
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }else if(result.isCancelled()){
//                          sp(stamp+" is Cancelled");
                            remove.add(stamp);
                        }
                    });
                    for (String string : remove) {
                        results.remove(string);
                    }
                    sp("Missions : "+results.size());
                }
            }
        }).start();
        //Serv to Client
        while (true) {
            try {
                final Socket socket = s.accept();
                try {
                    this.connects.put(socket);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }

}

class RequestHandler implements Callable<String> {
    private Socket socket;

    public RequestHandler(Socket conn) {
        super();
        this.socket = conn;
    }

    @Override
    public String call() throws Exception {
        //server Business

        PrintWriter pw=null;
        BufferedReader bfr=null;
        String msg=null;
        try {
            pw = new PrintWriter(socket.getOutputStream());
            bfr=new BufferedReader(new InputStreamReader(socket.getInputStream()));
        } catch (IOException e1) {
            e1.printStackTrace();
        }
        while(true){
            msg=bfr.readLine();
            if(TcpClient.ENDCONN.equals(msg)){
//              sp(this+" recieved ENDCONN");
                break;
            }
//          sp(msg);
            pw.println(msg+" : responsed");
            pw.flush();
        }
        socket.close();
        return "done :"+socket;
    }

}

网络接口

NetworkInterface 实际指的是网络设备,包括硬件和软件模拟的,比如本地网络环回(lo)就是一个操作系统虚拟的网络设备。
NetworkInterface类提供Java访问本地NetworkInterface的工具,通过它,可以查询比如MAC地址,接口是否物理/虚拟,当前传输MTU值等等有价值的硬件信息,它还有一个很便捷的方法NetworkInterface getByInetAddress(InetAddress addr):可以查询绑定了指定IP地址的网络接口。

高级 API

URI

Uniform Resource Identifier 统一资源标识符,是表示一个网络中资源的标识字符串,这种标识指定了某种协议以便使用者通过合适的方式使用。
URI由URL(地址)和URN(名称)组成,常见的URI有不透明URI和分层URI
比如mailto:[email protected] urn:isbn:096139210x 就是不透明URI,其特点是方案定义部分不以’/’开始,其结构可以表示为:[scheme:]scheme-specific-part[#fragment]
而更常见的是分层URI,http://java.sun.com/j2se/1.3/ docs/guide/collections/designfaq.html#28 ../../../demo/jfc/SwingSet2/src/SwingSet2.java 等都是分层URI,分层 URI 还要按照下面的语法进行进一步的解析:[scheme:][//authority][path][?query][#fragment]

Java.net中的URI类提供了构造和解析URI的方便工具,可以通过字符串解析URI的各个部分,也可以通过已有URI构建新的URI。最后,可以把从URI提取URL来使用,这也是最常用的。

URL

Uniform Resource Location 统一资源定位符 ,它是指向互联网“资源”的指针。URL是URI的定位部分,他们之间可以用toURI() 和toURL() 方法相互转换 。
URL 可选择指定一个“端口”,它是用于建立到远程主机 TCP 连接的端口号。如果未指定该端口号,则使用协议默认的端口。例如,http 协议的默认端口为 80。还可以指定一个备用端口,用.au:端口号

Java.net中的URL类除了像URI一样可以解析和构造(解析和构造也可以使用URLDecoder和URLEncoder)以外,特别的,它提供了一个极其方便的方法 openConnection()返回一个URLConnection对象,这个方法通过反射查询当前jre支持的协议,如果支持就打开一个协议连接,然后就可以直接操作了。

URLConnection

这里是一个打开HttpURLConnection的例子:

            URL url=new URL("http://www.java.com");
            URLConnection conn=url.openConnection();
            BufferedReader bfr=new BufferedReader(new InputStreamReader(conn.getInputStream()));
            String str;
            while((str=bfr.readLine())!=null){
                sp(str);
            }

协议处理程序

Java中不仅可以直接使用系统的协议处理程序打开连接,也可以自定义协议处理程序,只需要继承URLStreamHandler抽象类即可。

Java.nio 中的网络相关

@Since Java 1.4
Java1.4中加入了新的IO体系(JSR 51),包括面向缓冲区的各种Buffer对象,双向连接的Channel,Selector模式的多路复用。
在Java1.7中,nio又得到了补充(JSR 203),包括更多的文件系统操作API(包括可插拔的自定义的文件系统), 还提供了对socket和文件的异步(Async) I/O操作。

用Async方法实现异步网络服务

下面是一个基于异步非阻塞模式的服务器简单例子,双方仅仅是简单的进行几十次字符串传递即关闭连接。


import java.io.*;
import java.net.*;
import java.nio.*;
import java.nio.channels.*;
import java.time.Instant;
import java.util.*;
import java.util.concurrent.*;

import static cc.sisel.util.Quic.*;


/**
 * @author lz
 *
 */
public class AsyncTcpDemo {

    public static void main(String[] args) throws IOException,
            InterruptedException, ExecutionException {

        timeMark();

        InetSocketAddress servAddr = new InetSocketAddress("localhost", 22333);

        // start server
        new Thread(new Runnable() {

            @Override
            public void run() {
                new AsyncServer(servAddr.getPort(), 100).start();
                // blocking server main thread to keep async alive
                while (true) {
                    try {
                        Thread.sleep(Integer.MAX_VALUE);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
        // wait server init
        ts(300);

        Random r = new Random();
        // 5000 clients
        for (int i = 0; i < 5000; i++) {
            ts(10);
            new Thread(new AsyncClient(servAddr, r.nextInt(50) + 10)).start();
        }
        sp("ALL DONE");
    }

}

class AsyncServer {
    private AsynchronousServerSocketChannel servsc;
    private final int port, backlog;

    private boolean started;

    public AsyncServer(int port, int backlog) {
        super();
        this.port = port;
        this.backlog = backlog;
        init();
    }

    public void start() {
        // start by accept a connection channel
        servsc.accept(null, new AcceptCompletionHandler());
        this.started = true;
    }

    private void init() {
        // build async group
        AsynchronousChannelGroup asyncGroup = null;
        try {
            // async thread pool
            asyncGroup = AsynchronousChannelGroup.withCachedThreadPool(
                    Executors.newCachedThreadPool(), 10);
        } catch (IOException e) {
            e.printStackTrace();
        }
        // open serversocket
        try {
            this.servsc = AsynchronousServerSocketChannel.open(asyncGroup);
        } catch (IOException e) {
            e.printStackTrace();
        }
        // config addr resuse & tcp recieve buffer
        try {
            this.servsc.setOption(StandardSocketOptions.SO_REUSEADDR, true);
            this.servsc.setOption(StandardSocketOptions.SO_RCVBUF, 16 * 1024);
        } catch (IOException e) {
            e.printStackTrace();
        }
        // bind to port ;ip= 0.0.0.0 for everyone
        try {
            this.servsc.bind(new InetSocketAddress("0.0.0.0", this.port),
                    this.backlog);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void pendingAccept() {
        // after accepted channel reaccept cyclicly
        if (this.started && this.servsc.isOpen()) {
            servsc.accept(null, new AcceptCompletionHandler());
        } else {
            throw new IllegalStateException("Controller has been closed");
        }
    }

    // accept complete event handler
    class AcceptCompletionHandler implements
            CompletionHandler {
        final ByteBuffer buffer = ByteBuffer.allocate(1024);

        @Override
        public void completed(AsynchronousSocketChannel channel,
                Object attachment) {
            new ASession(channel, AsyncServer.this).start();
        }

        @Override
        public void failed(Throwable exc, Object attachment) {
            System.out.println("failed: " + exc);
            pendingAccept();
        }
    }

}

/**
 * server session handle it's read & write
 * 
 * @author lz
 *
 */
class ASession {
    private AsynchronousSocketChannel channel;
    private AsyncServer server;
    private final ByteBuffer buffer = ByteBuffer.allocate(1024);
    private WriteCompletionHandler writeh;
    private ReadCompletionHandler readh;

    public ASession(AsynchronousSocketChannel channel, AsyncServer server) {
        this.channel = channel;
        this.writeh = new WriteCompletionHandler();
        this.readh = new ReadCompletionHandler();
        this.server = server;
    }

    public void start() {
        channel.read(buffer, null, readh);
    }

    class WriteCompletionHandler implements CompletionHandler {

        @Override
        public void completed(Integer result, Object attachment) {
            if (channel.isOpen()) {
                buffer.clear();
                channel.read(buffer, 20, TimeUnit.SECONDS, null, readh);
            }
        }

        @Override
        public void failed(Throwable exc, Object attachment) {
            sp("########################## WRITE FAIL IN SERVER");
            exc.printStackTrace();
        }

    }

    class ReadCompletionHandler implements CompletionHandler {
        @Override
        public void completed(Integer result, Object attachment) {
            buffer.flip();
            String msg = readBuffer(buffer);
            // sp("server recieved:" + msg);
            buffer.clear();
            // if ENDCODE then close session and pending server next
            if (AsyncClient.ENDCODE.equals(msg)) {
                if (channel.isOpen()) {
                    try {
                        channel.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                server.pendingAccept();
            } else {
                // write response
                if (channel != null && channel.isOpen()) {
                    channel.write(
                            ByteBuffer.wrap((msg + " responsed.").getBytes()),
                            10, TimeUnit.SECONDS, null, writeh);
                }
            }
        }

        @Override
        public void failed(Throwable exc, Object attachment) {
            sp("########################## READ FAIL IN SERVER");
            exc.printStackTrace();
        }
    }
}

/**
 * client emualtor send simple message & recieve
 * 
 * @author lz
 *
 */
class AsyncClient implements Runnable {
    public static final String ENDCODE = "&#$$#&$$";
    private final InetSocketAddress serverAddr;
    private int chatCount;
    private AsynchronousSocketChannel client = null;
    private final ByteBuffer buffer = ByteBuffer.allocate(1024);
    // event handles
    private ConnectCompletionHandler connecth;
    private WriteCompletionHandler writeh;
    private ReadCompletionHandler readh;
    private NullCompletionHandler endh;

    public AsyncClient(InetSocketAddress serverAddr, int chatCount) {
        super();
        this.serverAddr = serverAddr;
        this.chatCount = chatCount < 1 ? 1 : chatCount;
        this.connecth = new ConnectCompletionHandler();
        this.writeh = new WriteCompletionHandler();
        this.readh = new ReadCompletionHandler();
        this.endh = new NullCompletionHandler();
    }

    @Override
    public void run() {
        try {
            client = AsynchronousSocketChannel.open();
        } catch (IOException e) {
            e.printStackTrace();
        }
        // launch conn
        if (client != null && client.isOpen()) {
            client.connect(serverAddr, null, connecth);
        }
    }

    class ConnectCompletionHandler implements CompletionHandler {

        @Override
        public void completed(Void result, Object attachment) {
            buffer.clear();
            if (chatCount > 0) {
                ts(300);
                if (client.isOpen()) {
                    client.write(ByteBuffer.wrap(("message:" + chatCount)
                            .getBytes()), 10, TimeUnit.SECONDS, null, writeh);
                    chatCount--;
                }
            } else {
                if (client.isOpen()) {
                    client.write(ByteBuffer.wrap(ENDCODE.getBytes()), 10,
                            TimeUnit.SECONDS, null, endh);
                }
            }
        }

        @Override
        public void failed(Throwable exc, Object attachment) {
            sp("########################## CONNECT FAIL IN CLIENT");
            ts(300);
            client.connect(serverAddr, null, connecth);
            exc.printStackTrace();
        }

    }

    class WriteCompletionHandler implements CompletionHandler {

        @Override
        public void completed(Integer result, Object attachment) {
            if (client.isOpen()) {
                buffer.clear();
                client.read(buffer, 20, TimeUnit.SECONDS, null, readh);
            }
        }

        @Override
        public void failed(Throwable exc, Object attachment) {
            sp("########################## WRITE FAIL IN CLIENT");
            exc.printStackTrace();
        }

    }

    class ReadCompletionHandler implements CompletionHandler {

        @Override
        public void completed(Integer result, Object attachment) {
            if (client.isOpen()) {
                buffer.flip();
                String msg = readBuffer(buffer);
                // sp("client recieved :" + msg);
                buffer.clear();
                if (chatCount > 0) {
                    // ts(300);
                    client.write(ByteBuffer.wrap(("message:" + chatCount)
                            .getBytes()), 10, TimeUnit.SECONDS, null, writeh);
                    chatCount--;
                } else {
                    client.write(ByteBuffer.wrap(ENDCODE.getBytes()), 10,
                            TimeUnit.SECONDS, null, endh);
                }
            }
        }

        @Override
        public void failed(Throwable exc, Object attachment) {
            sp("########################## READ FAIL IN CLIENT");
            exc.printStackTrace();
        }

    }
}

class NullCompletionHandler implements CompletionHandler {

    @Override
    public void completed(Integer result, Object attachment) {
        //just do nothing
    }

    @Override
    public void failed(Throwable exc, Object attachment) {
        //just do nothing
    }

}

附:
- sp和ts方法,偷懒用:


    /**
     * 简单的在控制台输出 对于非数组类型,输出.toString() 对于数组类型,输出:类型[内容] 不递归
     * 
     * @param o
     *            被打印的对象
     */
    public static void sp(Object o) {
        if (o == null) {
            System.out.println("null");
            return;
        }
        if (o.getClass().isArray()) {
            StringBuilder sb = new StringBuilder();
            int len = Array.getLength(o);
            sb.append(o.getClass().getComponentType().getSimpleName());
            if (len == 0) {
                sb.append("[]");
                System.out.println(sb.toString());
                return;
            }
            sb.append('[');
            for (int i = 0; i < len; i++) {
                if (i != 0) {
                    sb.append(':');
                }
                sb.append(Array.get(o, i));
            }
            sb.append(']');
            System.out.println(sb.toString());
        } else {
            System.out.println(o);
        }
    }

    /**
     * 简单的换行 调用sp("");
     */
    public static void sp() {
        sp("");
    }

    /**
     * !!仅用于test 或者 demo 简单的包装了Thread.sleep(millis); 并catch
     * (InterruptedException e)不作任何处理
     * 
     * @param millis 毫秒数
     *            @see java.lang.Thread#sleep(long millis)
     */
    public static void ts(long millis) {
        if(millis<0){
            return;
        }
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

你可能感兴趣的:(学习,黑马,网络)