Java初级笔记No.4(更新中)

2018-07-27  本文已影响17人  wenmingxing
2018.7.27

1、Java的主要内置数据结构包括:枚举(Enumeration),位集合(BitSet),向量(Vector), 栈(Stack), 字典(Dictionary), 哈希表(Hashtable), 属性(Properties)

枚举(Enumeration)
枚举接口定义了一种从数据结构中取回连续元素的方式。例如,枚举定义了一个叫nextElement的方法,该方法用来得到一个包含多个元素的数据结构的下一个元素,在这一点上很像是C++中的迭代器。

而事实上,这种传统接口确实已经被迭代器取代了,在现在代码中已经很少被使用了。

import java.util.Vector;
import java.util.Enumeration;

public class EnumerationTester {
    public static void main(String[] args) {
        Enumeration<String> days;
        Vector<String> dayNames = new Vector<String>();
        dayNames.add("Sunday");
        dayNames.add("Monday");
        dayNames.add("Tuesday");
        dayNames.add("Wednesday");
        dayNames.add("Thursday");
        dayNames.add("Friday");
        dayNames.add("Saturday");
        
        days = dayNames.elements();
        while (days.hasMoreElements()) {
            System.out.println(days.nextElement());
        }
    }
}

位集合(BitSet)
位集合实现了一组可以单独设置和清除的位或标志。
该类在处理一组Boolean值的时候非常有用,只需要给每个值赋予一位,然后对位进行相应的设置或清除,就可以对布尔值进行操作了。

import java.util.BitSet;

public class BitSetDemo {
    public static void main(String[] args) {
        BitSet bits1 = new BitSet(16);
        BitSet bits2 = new BitSet(16);
        
        // set some bits
        for (int i = 0; i < 16; ++i) {
            if (i % 2 == 0) bits1.set(i);
            if (i % 5 != 0) bits2.set(i);
        }
        
        System.out.println("Initial pattern in bits1 : ");
        System.out.println(bits1);
        
        System.out.println("Initial pattern in bits2 : ");
        System.out.println(bits2);
        
        // AND bits
        bits2.and(bits1);
        System.out.println("bits2 AND bits1 : ");
        System.out.println(bits2);  
        
        // OR bits
        bits2.or(bits1);
        System.out.println("bits2 OR bits1 : ");
        System.out.println(bits2);
        
        // XOR bits
        bits2.xor(bits1);
        System.out.println("bits2 XOR bits1 : ");
        System.out.println(bits2);
    }
}

向量(Vector)
向量类和传统数组非常类似,但是Vector的大小能根据需要动态的变化。
与C++STL中的vector中不同,Java的Vector中可以存储不同类型的元素:

import java.util.*;

public class VectorDemo {
    public static void main(String[] args) {
        Vector v = new Vector(3,2);
        System.out.println("Initial size : " + v.size());
        System.out.println("Initial capacity : " + v.capacity());
        
        v.addElement(new Integer(1));
        v.add(2);
        v.add(3);
        v.add(4);
        
        System.out.println("Capacity after four additions:" + v.capacity());
        
        v.addElement(new Double(5.45));
                v.addElement(new String("haha"));
        
        Enumeration vEnum = v.elements();
        System.out.println("Elements in vector:");
        while (vEnum.hasMoreElements()) {
            System.out.println(vEnum.nextElement() + " ");
        }
        System.out.println();
    }
}

栈(Stack)

package test3;

import java.util.*;

public class StackDemo {
    static void showpush(Stack<Integer> st, int a) {
        st.push(new Integer(a));
        System.out.println("push(" + a + ")");
        System.out.println("stack: " + st);
    }
    
    static void showpop(Stack<Integer> st) {
        System.out.print("pop -> ");
        Integer a = (Integer) st.pop();
        System.out.println(a);
        System.out.println("stack : " + a);
    }
    
    public static void main(String[] args) {
        Stack<Integer> st = new Stack<Integer>();
        
        System.out.println("stack : " + st);
        showpush(st, 42);
        showpush(st, 66);
        showpush(st, 99);
        
        showpop(st);
        showpop(st);
        showpop(st);
        
        try {
            showpop(st);
        } catch (EmptyStackException e) {
            System.out.println("empty stack");
        }
    }
}

字典(Dictionary)
字典类定义了键映射到值的数据结构。
其实Dictionary已经过时了,在实际开发中应该使用Map接口来实现k-v的存储功能。

package test3;

import java.util.*;

public class MapDemo {
    public static void main(String[] args) {
        Map m1 = new HashMap();
        m1.put("Zara", "8");
        m1.put("Mahnaz", "31");
        m1.put("Ayan", "12");
        m1.put("Daisy", "14");
        
        System.out.println();
        System.out.println("Map Elements");
        
        System.out.println(m1);
        
        System.out.println(m1.get("Zara"));
    }
}

哈希表(Hashtable)
Hashtable类提供了一种在用户定义键结构的基础上来组织数据的手段。
例如,在地址列表的哈希表中,你可以根据邮政编码作为键来存储和排序数据,而不是通过人名。

其实Hashtable就是Dictionary的具体实现,与HashMap类很相似,但是其支持同步。

package test3;

import java.util.*;

public class HashTableDemo {
    public static void main(String[] args) {
        //Create a hash
        Hashtable balance = new Hashtable();
        Enumeration names;
        String str;
        double bal;
        
        balance.put("Zara", new Double(3434.34));
        balance.put("Mahnaz", new Double(123.22));
        balance.put("Ayan", new Double(1378.00));
        balance.put("Daisy", new Double(99.22));
        balance.put("Qadir", new Double(-19.08));
        
        // Show all balances in hash table
        names = balance.keys();
        while (names.hasMoreElements()) {
            str = (String) names.nextElement();
            System.out.println(str + ": " + balance.get(str));
        }
        
        System.out.println();
        
        //Deposit 1000 into Zara's account
        bal = ((Double)balance.get("Zara")).doubleValue();
        balance.put("Zara", new Double(bal+1000));
        System.out.println("Zara's new balance:" + balance.get("Zara"));
    }
}

属性(Properties)
Properties继承于Hashtable。表示一个持久的属性集。属性列表中每个键及其对应值都是一个字符串。

import java.util.*;
 
public class PropDemo {
 
   public static void main(String args[]) {
      Properties capitals = new Properties();
      Set states;
      String str;
      
      capitals.put("Illinois", "Springfield");
      capitals.put("Missouri", "Jefferson City");
      capitals.put("Washington", "Olympia");
      capitals.put("California", "Sacramento");
      capitals.put("Indiana", "Indianapolis");
 
      // Show all states and capitals in hashtable.
      states = capitals.keySet(); // get set-view of keys
      Iterator itr = states.iterator();
      while(itr.hasNext()) {
         str = (String) itr.next();
         System.out.println("The capital of " +
            str + " is " + capitals.getProperty(str) + ".");
      }
      System.out.println();
 
      // look for state not in list -- specify default
      str = capitals.getProperty("Florida", "Not Found");
      System.out.println("The capital of Florida is "
          + str + ".");
   }
}  

2、Java集合框架

集合框架被设计成要满足以下几个目标:
· 该框架必须是搞性能的,基本集合(动态数组,链表,树,哈希表)的实现也必须是高效的;
· 该框架允许不同类型的集合,以类似的方式工作,具有高度的互操作性;
· 对一个集合的扩展和适应必须是简单的。

集合框架是一个用来代表和操纵集合的同一架构。所有的集合框架都包含如下内容:
· 接口:代表集合的首先数据类型。例如Collection、List、Set、Map等。之所以定义多个接口,是为了以不同方式操作集合对象;
· 实现(类):是集合接口的具体实现。从本质上讲,它们是可重复使用的数据结构,如ArrayList、LinkedList、HashSet、HashMap;
· 算法: 是实现集合接口的对象里的方法执行的一些有用的算法, 如搜索和排序,这些算法被称为多态,那是因为相同的方法可以在相似的接口上有不同的实现。

3、使用迭代器
迭代器,能够通过血环来得到或删除集合的元素。如ListIterator继承了Iterator,以允许双向遍历列表和修改元素。

遍历ArrayList

package test3;

import java.util.*;
public class Test {
    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();
        list.add("Hello");
        list.add("World");
        list.add("HAHAHAHA");
        
        // 第一种遍历方法,使用foreach遍历List
        for (String str : list) {
            System.out.println(str);
        }
        
        //第二种遍历方式,将链表变为数组相关的内容
        String[] strArray = new String[list.size()];
        list.toArray(strArray);
        for (int i = 0; i < strArray.length; ++i) {
            System.out.println(strArray[i]);
        }
        
        //第三种遍历方式,使用Iterator
        Iterator<String> ite = list.iterator();
        while (ite.hasNext()) {
            System.out.println(ite.next());
        }
        
    }
}

遍历Map

package test3;

import java.util.*;
public class Test {
    public static void main(String[] args) {
        Map<String, String> map = new HashMap<String, String>();
        map.put("1", "value1");
        map.put("2", "value2");
        map.put("3", "value3");
        
        //第一种遍历方法,普遍使用,二次取值
        System.out.println("通过Map.keySet遍历key和value:");
        for (String key : map.keySet()) {
            System.out.println("key = " + key + " and value = " + map.get(key));
        }
        
        //第二种遍历方式
        System.out.println("通过Map.entrySet使用iterator遍历key和value:");
        Iterator<Map.Entry<String, String>> it = map.entrySet().iterator();
        while (it.hasNext()) {
            Map.Entry<String, String> entry = it.next();
            System.out.println("key=" + entry.getKey() + " and value=" + entry.getValue());
        }
        
        //第三种遍历方式,推荐,尤其是在容量大时 
        System.out.println("通过Map.entrySet遍历key和value:");
        for (Map.Entry<String, String> entry : map.entrySet()) {
            System.out.println("key=" + entry.getKey() + " and value=" + entry.getKey());
        }
        
        //第四种遍历方式
        System.out.println("通过Map.values()遍历所有的value,但是不能得到key:");
        for (String v : map.values()) {
            System.out.println("value = " + v);
        }
    }
}

4、了解Java泛型
泛型的概念与C++中的泛型没有区别,这是一种程序设计思想。
泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。
可以写一个泛型方法,该方法在调用时可以接收不同类型的参数。根据传递给泛型方法的参数类型,编译器适当地处理每一个方法调用。

下面是定义泛型方法的规则:
· 所有泛型方法声明都有一个类型参数声明部分(由尖括号分隔),该类型参数声明部分在方法返回类型之前;
· 每一个类型参数声明部分包含一个或多个类型参数,参数见用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符;
· 类型参数能被用来声明返回值类型,并且能作为泛型方法得到的实际参数类型的占位符;
· 泛型方法体的声明和其他方法一样。注意类型参数只能代表引用类型,不能是原始类型(如int,double,char等)。

实例
下面的例子演示了如何使用泛型方法打印不同字符串的元素:

public class GenericMethodTest {
    //泛型方法 printArray
    public static <E> void printArray(E[] inputArray) {
        //
        for (E element : inputArray) {
            System.out.printf("%s", element);
        }
        System.out.println();
    }
    
    public static void main(String[] args) {
        Integer[] intArray = {1,2,3,4,5};
        Double[] doubleArray = {1.1,2.2,3.3,4.4,5.5};
        Character[] charArray = {'H', 'E', 'L', 'L', 'O'};
        
        System.out.println("Integer : ");
        printArray(intArray);
        
        System.out.println("Double : ");
        printArray(doubleArray);
        
        System.out.println("Character : ");
        printArray(charArray);
        
    }
}

有界的参数实例
有时候,我们会想限制那些被允许传递到一个类型参数的类型种类范围,例如,一个操作数字的方法可能只希望接受Number或者Number子类的实例。者就是有界类型参数的目的。
首先需要列出参数类型的名称,后面跟extends或者implements

package test2;

public class MaximumTest {
    /*返回三个可被Comparable的最大值*/
    public static <T extends Comparable<T>> T maximum(T x, T y, T z) {
        T max = x;
        if (y.compareTo(max) > 0)
            max = y;
        if (z.compareTo(max) > 0)
            max = z;
        
        return max;
    }
    
    public static void main(String[] args) {
        System.out.printf("%d, %d, %d 中最大的数为:%d\n\n", 3,4,5,maximum(3,4,5));
        
        System.out.printf("%.1f, %.1f, %.1f 中最大的数为:%.1f\n\n", 6.6,8.8,7.7, maximum(6.6,8.8,7.7));
        
        System.out.printf("%s, %s, %s 中最大的数为: %s\n\n", "apple", "pear", "orange", maximum("apple", "pear", "orange"));
    }
}

5、泛型类

泛型类的声明和非泛型类的声明类似,除了在类名后面添加了类型参数声明部分。

和泛型方法一样,泛型类的类型参数声明部分也包含一个或多个类型参数,参数见用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。因为他们接受一个或多个参数,这些类被称为参数化的类或参数化的类型。

package test2;

public class Box<T> {
    private T t;
    public void add(T t) {
        this.t = t;
    }
    
    public T get() {
        return t;
    }
    
    public static void main(String[] args) {
        Box<Integer> integerBox = new Box<Integer>();
        Box<String> stringBox = new Box<String>();
        
        integerBox.add(new Integer(10));
        stringBox.add(new String("LiMing"));
        
        System.out.println("Integer:" + integerBox.get());
        System.out.println("String:" + stringBox.get());
    }
}

6、类型通配符

类型通配符一般是使用?代替具体类型参数。例如List<?>在逻辑上是List<String>, List<Integer>, 等所有List<>的父类:

package test2;

import java.util.*;

public class GenericTest {
    public static void main(String[] args) {
        List<String> name = new ArrayList<String>();
        List<Integer> age = new ArrayList<Integer>();
        List<Number> number = new ArrayList<Number>();
        
        name.add("icon");
        age.add(18);
        number.add(314);
        
        getData(name);
        getData(age);
        getData(number);
    }
    
    public static void getData(List<?> data) {
        System.out.println(data.get(0));
    }
}

因为getData()这个方法的参数是List<?>类型的,所以name,age,number都可以作为这个方法的实参,这就是通配符的作用。

同理,也可以限定类型通配符上限,例如令其只能接受Number及其下层子类类型:

public static void getData(List<? extends Number> data) {
    System.out.println(data.get(0));
}  

这样的话,对于String类型的name就会报错。

7、Java序列化

Java中提过了一种对象序列化的机制,该机制中,一个对象可以被表示为一个字节序列,该字节序列包括了该对象的数据、有关对象的类型的信息和存储在对象中的数据的类型。

将序列化对象写入文件之后,可以从文件中读取出来,并且对它进行反序列化,也就是说,对象的类型信息、对象的数据,还有对象中的数据类型可以用来在内存中新建对象。

这个过程都是JVM实现的,也就是说,在一个平台上序列化的对象可以在另一个平台上完成反序列化。

类ObjectInputStream和ObjectOutputStream是高层次的数据流,它们包含反序列化和序列化对象的方法。

该方法序列化一个对象,并将它发送到输出流。

public final void writeObject(Object x) throws IOException

readObject()方法可以反序列化一个对象,它的返回值是Object,因此需要将它转化成何时的数据类型:

public final Object readObject() throws IOException ClassNotFoundException

实例
一个类的对象要向序列化成功,必须满足两个条件:
· 该类必须实现java.io.Serializable接口;
· 该类的所有属性必须是可序列化的。

public class Employee implements java.io.Serializable {
    public String name;
    public String address;
    public transient int SSN;   //不包含在序列化表示中
    public int number;
    public void mailCheck() {
        System.out.println("Mailing a check to " + name + " " + address);
    }
}

ObjectOutputStream类用来序列化一个对象,如下的SerializeDemo例子实例化了上面的Employee对象,并将该对象序列化到了一个文件中。

改程序执行后,会创建一个名为employee.ser文件,当然也可以为.txt文件。

import java.io.*;

public class SerializeDemo {
    public static void main(String[] args) {
        Employee e = new Employee();
        e.name = "LiMing";
        e.address = "BeiJing";
        e.SSN = 11122333;
        e.number = 101;
        
        try {
            FileOutputStream fileOut = new FileOutputStream("/tmp/employee.ser");
            ObjectOutputStream out = new ObjectOutputStream(fileOut);
            out.writeObject(e);     //序列化
            out.close();
            fileOut.close();
            System.out.println("Serialized data is saved in /tmp/employee.ser");
        } catch(IOException i) {
            i.printStackTrace();
        }
    }
}  

下面的DeserializeDemo程序实现了反序列化:

public class DeserializeDemo
{
   public static void main(String [] args)
   {
      Employee e = null;
      try
      {
         FileInputStream fileIn = new FileInputStream("/tmp/employee.ser");
         ObjectInputStream in = new ObjectInputStream(fileIn);
         e = (Employee) in.readObject();
         in.close();
         fileIn.close();
      }catch(IOException i)
      {
         i.printStackTrace();
         return;
      }catch(ClassNotFoundException c)
      {
         System.out.println("Employee class not found");
         c.printStackTrace();
         return;
      }
      System.out.println("Deserialized Employee...");
      System.out.println("Name: " + e.name);
      System.out.println("Address: " + e.address);
      System.out.println("SSN: " + e.SSN);
      System.out.println("Number: " + e.number);
    }
}  

运行结果为:

由于属性SSN被设置为了transient,表示为短暂的,不会被发送到输出流,也就不会参与序列化,所以反序列化后的SSN属性为0。

8、Java中的Socket编程
在java中java.net.Socket代表一个套接字,并且java.net.ServerSocket类为服务器程序提供了一种来监听客户端,并建立连接的机制。

java中Socket编程的基本步骤如下:
· 服务器实例化一个ServeSocket对象,表示通过服务器上的端口通信。
· 服务器调用ServerSocket类的accept方法,该方法将一直等待,知道客户端连接到服务器上给定的端口;
· 服务器正在等待,一个客户端实例化一个Socket对象,指定服务器名称和端口号来请求连接;
· Socket类的构造函数视图将客户端连接到指定的服务器和端口。如果通信被简历,则客户端创建一个Socket对象能够与服务器进行通信;
· 在服务器端,accept()方法返回服务器上一个新的socket引用,该socket连接到客户端的socket;

连接建立后,通过使用I/O流在进行通信,每一个socket都有一个输出流和一个输入流,客户端的输出流连接服务器端的输入流,而客户端的输入流连接服务器端的输出流。

/*client*/

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

public class GreetingClient {
    public static void main(String[] args) {
        String serverName = args[0];
        int port = Integer.parseInt(args[1]);
        try {
            System.out.println("连接到主机:" + serverName + ", 端口号:" + port);

            Socket client = new Socket(serverName, port);
            System.out.println("远程主机地址:" + client.getRemoteSocketAddress());
            OutputStream outToServer = client.getOutputStream();
            DataOutputStream out = new DataOutputStream(outToServer);

            out.writeUTF("Hello from " + client.getLocalSocketAddress());
            InputStream inFromServer = client.getInputStream();
            DataInputStream in = new DataInputStream(inFromServer);
            System.out.println("服务器响应:" + in.readUTF());
            client.close();
        } catch(IOException e) {
            e.printStackTrace();
        }
    }
}  
/*server*/

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

public class GreetingServer extends Thread {
    private ServerSocket serverSocket;

    public GreetingServer(int port) throws IOException {
        serverSocket = new ServerSocket(port);
        serverSocket.setSoTimeout(10000);
    }

    public void run() {
        while (true) {
            try {
                System.out.println("等待远程连接,端口号为:" + serverSocket.getLocalPort() + "...");
                Socket server = serverSocket.accept();
                System.out.println("远程主机地址:" + server.getRemoteSocketAddress());

                DataInputStream in = new DataInputStream(server.getInputStream());
                System.out.println(in.readUTF());
                DataOutputStream out = new DataOutputStream(server.getOutputStream());

                out.writeUTF("谢谢连接我:" + server.getLocalSocketAddress() + "Goodbye!");
                server.close();

            } catch(SocketTimeoutException s) {
                System.out.println("Socket timed out!");
                break;
            } catch(IOException e) {
                e.printStackTrace();
                break;
            }
        }
    }

    public static void main(String[] args) {
        int port = Integer.parseInt(args[0]);
        try {
            Thread t = new GreetingServer(port);
            t.run();
        } catch(IOException e) {
            e.printStackTrace();
        }
    }
}  
上一篇下一篇

猜你喜欢

热点阅读