1. 程式人生 > >網絡編程TCP/UDP,反射

網絡編程TCP/UDP,反射

args 增強 pla 創建對象 兩個 提供服務 ddr 輸出流 lose

網絡編程

端口:
物理端口:實體的端口
邏輯端口:用於標識進程的邏輯地址,不同進程的標識;有效端口:0~65535,其中0~1024系統使用或保留端口。

java 中ip對象:InetAddress.
import java.net.*;
class  IPDemo{
public static void main(String[] args) throws UnknownHostException{
//通過名稱(ip字符串or主機名)來獲取一個ip對象。
InetAddress ip = InetAddress.getByName("www.baidu.com");//java.net.UnknownHostException
System.out.println("addr:"+ip.getHostAddress());
System.out.println("name:"+ip.getHostName());
}
}
Socket:套接字,通信的端點。

  就是為網絡服務提供的一種機制,通信的兩端都有Socket,網絡通信其實就是Socket間的通信,數據在兩個Socket間通過IO傳輸。

UDP傳輸:

  • 1,只要是網絡傳輸,必須有socket 。
  • 2,數據一定要封裝到數據包中,數據包中包括目的地址、端口、數據等信息。

直接操作udp不可能,對於java語言應該將udp封裝成對象,易於我們的使用,這個對象就是DatagramSocket. 封裝了udp傳輸協議的socket對象。

因為數據包中包含的信息較多,為了操作這些信息方便,也一樣會將其封裝成對象。這個數據包對象就是:DatagramPacket.

通過這個對象中的方法,就可以獲取到數據包中的各種信息。

DatagramSocket具備發送和接受功能,在進行udp傳輸時,需要明確一個是發送端,一個是接收端。

udp的發送端:
1,建立udp的socket服務,創建對象時如果沒有明確端口,系統會自動分配一個未被使用的端口。
2,明確要發送的具體數據。
3,將數據封裝成了數據包。
4,用socket服務的send方法將數據包發送出去。
5,關閉資源。
- - -

import java.net.*;
class  UdpSend{
public static void main(String[] args)throws Exception {
//1,建立udp的socket服務。
DatagramSocket ds = new DatagramSocket(8888);//指定發送端口,不指定系統會隨機分配。
//2,明確要發送的具體數據。
String text = "udp傳輸演示 哥們來了";
byte[] buf = text.getBytes();
//3,將數據封裝成了數據包。
DatagramPacket dp = new DatagramPacket(buf,
buf.length,InetAddress.getByName("10.1.31.127"),10000);
//4,用socket服務的send方法將數據包發送出去。
ds.send(dp);
//5,關閉資源。
ds.close();
}
}

udp的接收端:
1,創建udp的socket服務,必須要明確一個端口,作用在於,只有發送到這個端口的數據才是這個接收端可以處理的數據。
2,定義數據包,用於存儲接收到數據。
3,通過socket服務的接收方法將收到的數據存儲到數據包中。
4,通過數據包的方法獲取數據包中的具體數據內容,比如ip、端口、數據等等。
5,關閉資源。

class UdpRece {
public static void main(String[] args) throws Exception{
//1,創建udp的socket服務。
DatagramSocket ds = new DatagramSocket(10000);
//2,定義數據包,用於存儲接收到數據。先定義字節數組,數據包會把數據存儲到字節數組中。
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket(buf,buf.length);
//3,通過socket服務的接收方法將收到的數據存儲到數據包中。
ds.receive(dp);//該方法是阻塞式方法。
//4,通過數據包的方法獲取數據包中的具體數據內容,比如ip,端口,數據等等。
String ip = dp.getAddress().getHostAddress();
int port = dp.getPort();
String text = new String(dp.getData(),0,dp.getLength());//將字節數組中的有效部分轉成字符串。
System.out.println(ip+":"+port+"--"+text);
//5,關閉資源。
ds.close();
}
}
TCP傳輸

tcp的兩個端點:一個是客戶端,一個是服務端。

  • 客戶端:對應的對象,Socket
  • 服務端:對應的對象,ServerSocket

TCP客戶端:

  1. 建立tcp的socket服務,最好明確具體的地址和端口。這個對象在創建時,就已經可以對指定ip和端口進行連接(三次握手)。
  2. 如果連接成功,就意味著通道建立了,socket流就已經產生了。只要獲取到socket流中的讀取流和寫入流即可,只要通過getInputStream和getOutputStream就可以獲取兩個流對象。
  3. 關閉資源。
import java.net.*;
import java.io.*;
//需求:客戶端給服務器端發送一個數據。
class  TcpClient{
public static void main(String[] args) throws Exception{
Socket s = new Socket("10.1.31.69",10002);
OutputStream out = s.getOutputStream();//獲取了socket流中的輸出流對象。
out.write("tcp演示,哥們又來了!".getBytes());
s.close();
}
}

TCP服務端:

  1. 創建服務端socket服務,並監聽一個端口。
  2. 服務端為了給客戶端提供服務,獲取客戶端的內容,可以通過accept方法獲取連接過來的客戶端對象。
  3. 可以通過獲取到的socket對象中的socket流和具體的客戶端進行通訊。
  4. 如果通訊結束,關閉資源。註意:要先關客戶端,再關服務端。
class  TcpServer{
public static void main(String[] args) throws Exception{
ServerSocket ss = new ServerSocket(10002);//建立服務端的socket服務
Socket s = ss.accept();//獲取客戶端對象
String ip = s.getInetAddress().getHostAddress();
System.out.println(ip+".....connected");
//可以通過獲取到的socket對象中的socket流和具體的客戶端進行通訊。
InputStream in = s.getInputStream();//讀取客戶端的數據,使用客戶端對象的socket讀取流
byte[] buf = new byte[1024];
int len = in.read(buf);
String text = new String(buf,0,len);
System.out.println(text);
//如果通訊結束,關閉資源。註意:要先關客戶端,在關服務端。
s.close();
ss.close();
}
}
反射技術

   其實就是動態加載一個指定的類,並獲取該類中的所有的內容。而且將字節碼文件封裝成對象,並將字節碼文件中的內容都封裝成對象,這樣便於操作這些成員。簡單說:反射技術可以對一個類進行解剖。

反射的好處:大大的增強了程序的擴展性。

反射的基本步驟:
1、獲得Class對象,就是獲取到指定的名稱的字節碼文件對象。
2、實例化對象,獲得類的屬性、方法或構造函數。
3、訪問屬性、調用方法、調用構造函數創建對象。

獲取這個Class對象,有三種方式:
1:通過每個對象都具備的方法getClass來獲取。弊端:必須要創建該類對象,才可以調用getClass方法。
2:每一個數據類型(基本數據類型和引用數據類型)都有一個靜態的屬性class。弊端:必須要先明確該類。
前兩種方式不利於程序的擴展,因為都需要在程序使用具體的類來完成。
3:使用的Class類中的方法,靜態的forName方法。
指定什麽類名,就獲取什麽類字節碼文件對象,這種方式的擴展性最強,只要將類名的字符串傳入即可。
// 1. 根據給定的類名來獲得 用於類加載
String classname = "cn.itcast.reflect.Person";// 來自配置文件
Class clazz = Class.forName(classname);// 此對象代表Person.class
// 2. 如果拿到了對象,不知道是什麽類型 用於獲得對象的類型
Object obj = new Person();
Class clazz1 = obj.getClass();// 獲得對象具體的類型
// 3. 如果是明確地獲得某個類的Class對象 主要用於傳參
Class clazz2 = Person.class;

反射的用法:
1)、需要獲得java類的各個組成部分,首先需要獲得類的Class對象,獲得Class對象的三種方式:
Class.forName(classname)用於做類加載
obj.getClass()用於獲得對象的類型
類名.class 用於獲得指定的類型,傳參用

2)、反射類的成員方法:
Class clazz = Person.class;
Method method = clazz.getMethod(methodName, new Class[]{paramClazz1, paramClazz2});
method.invoke();

3)、反射類的構造函數:
Constructor con = clazz.getConstructor(new Class[]{paramClazz1, paramClazz2,...})
con.newInstance(params...)

4)、反射類的屬性:
Field field = clazz.getField(fieldName);
field.setAccessible(true);
field.setObject(value);

獲取了字節碼文件對象後,最終都需要創建指定類的對象:
創建對象的兩種方式(其實就是對象在進行實例化時的初始化方式):
1,調用空參數的構造函數:使用了Class類中的newInstance()方法。
2,調用帶參數的構造函數:先要獲取指定參數列表的構造函數對象,然後通過該構造函數的對象的newInstance(實際參數) 進行對象的初始化。

綜上所述,第二種方式,必須要先明確具體的構造函數的參數類型,不便於擴展。所以一般情況下,被反射的類,內部通常都會提供一個公有的空參數的構造函數。
- - -
// 如何生成獲取到字節碼文件對象的實例對象。

Class clazz = Class.forName("cn.itcast.bean.Person");//類加載
// 直接獲得指定的類型
clazz = Person.class;
// 根據對象獲得類型
Object obj = new Person("zhangsan", 19);
clazz = obj.getClass();
 
Object obj = clazz.newInstance();//該實例化對象的方法調用就是指定類中的空參數構造函數,給創建對象進行初始化。當指定類中沒有空參數構造函數時,該如何創建該類對象呢?請看method_2();
public static void method_2() throws Exception {
Class clazz = Class.forName("cn.itcast.bean.Person");
//既然類中沒有空參數的構造函數,那麽只有獲取指定參數的構造函數,用該函數來進行實例化。
//獲取一個帶參數的構造器。
Constructor constructor = clazz.getConstructor(String.class,int.class);
//想要對對象進行初始化,使用構造器的方法newInstance();
Object obj = constructor.newInstance("zhagnsan",30);
//獲取所有構造器。
Constructor[] constructors = clazz.getConstructors();//只包含公共的
constructors = clazz.getDeclaredConstructors();//包含私有的
for(Constructor con : constructors) {
System.out.println(con);
}
}

-反射指定類中的方法:
//獲取類中所有的方法。

public static void method_1() throws Exception {
Class clazz = Class.forName("cn.itcast.bean.Person");
Method[] methods = clazz.getMethods();//獲取的是該類中的公有方法和父類中的公有方法。
methods = clazz.getDeclaredMethods();//獲取本類中的方法,包含私有方法。
for(Method method : methods) {
System.out.println(method);
}
}
//獲取指定方法;
public static void method_2() throws Exception {
Class clazz = Class.forName("cn.itcast.bean.Person");
//獲取指定名稱的方法。
Method method = clazz.getMethod("show", int.class,String.class);
//想要運行指定方法,當然是方法對象最清楚,為了讓方法運行,調用方法對象的invoke方法即可,但是方法運行必須要明確所屬的對象和具體的實際參數。
Object obj = clazz.newInstance();
method.invoke(obj, 39,"hehehe");//執行一個方法
}
//想要運行私有方法。
public static void method_3() throws Exception {
Class clazz = Class.forName("cn.itcast.bean.Person");
//想要獲取私有方法。必須用getDeclearMethod();
Method method = clazz.getDeclaredMethod("method", null);
// 私有方法不能直接訪問,因為權限不夠。非要訪問,可以通過暴力的方式。
method.setAccessible(true);//一般很少用,因為私有就是隱藏起來,所以盡量不要訪問。
}
//反射靜態方法。
public static void method_4() throws Exception {
Class clazz = Class.forName("cn.itcast.bean.Person");
Method method = clazz.getMethod("function",null);
method.invoke(null,null);
}

正則表達式
其實是用來操作字符串的一些規則。

  • 好處:正則的出現,對字符串的復雜操作變得更為簡單。
  • 特點:將對字符串操作的代碼用一些符號來表示。只要使用了指定符號,就可以調用底層的代碼對字符串進行操作。符號的出現,簡化了代碼的書寫。
  • 弊端:符號的出現雖然簡化了書寫,但是卻降低了閱讀性。
    其實更多是用正則解決字符串操作的問題。

組:用小括號標示,每定義一個小括號,就是一個組,而且有自動編號,從1開始。
只要使用組,對應的數字就是使用該組的內容。別忘了,數組要加\。
(aaa(wwww(ccc))(eee))技巧,從左括號開始數即可。有幾個左括號就是幾組。

常見操作:
1,匹配:其實用的就是String類中的matches方法。
String reg = "[1-9][0-9]{4,14}";
boolean b = qq.matches(reg);//將正則和字符串關聯對字符串進行匹配。
2,切割:其實用的就是String類中的split方法。
3,替換:其實用的就是String類中的replaceAll();
4,獲取:
1),先要將正則表達式編譯成正則對象。使用的是Pattern中靜態方法 compile(regex);
2),通過Pattern對象獲取Matcher對象。
Pattern用於描述正則表達式,可以對正則表達式進行解析。
而將規則操作字符串,需要從新封裝到匹配器對象Matcher中。
然後使用Matcher對象的方法來操作字符串。
如何獲取匹配器對象呢?
通過Pattern對象中的matcher方法。該方法可以正則規則和字符串想關聯。並返回匹配器對象。
3),使用Matcher對象中的方法即可對字符串進行各種正則操作。

網絡編程TCP/UDP,反射