Java学习笔记-Day39 Java UDP通讯、URL类、单例模式



一、UDP通讯


Java语言中使用数据报(Datagram)进行基于UDP的网络编程。数据报(Datagram)如同邮件系统,不能保证可靠的传输。

在 java.net 包中提供了 DatagramSocket 和 DatagramPacket 两个类,用来支持数据报通信。DatagramSocket 用于在程序之间建立传送数据报的通信连接,而DatagramPacket 则用来表示一个数据报。

用数据报方式进行网络编程时,无论客户端还是服务器端都要建立一个 DatagramSocket类的对象,用来接收或发送数据报,然后使用 DatagramPacket类的对象作为传输数据的载体。

1、DatagramSocket类


DatagramSocket 类表示用于发送和接收数据报包的数据报套接字。数据报套接字是分组传送服务的发送或接收点,用于在程序之间建立传送数据报的通信连接。

  • 构造方法

(1)public DatagramSocket(int port) throws SocketException:构造数据报套接字并将其绑定到本地主机上的指定端口。

	DatagramSocket ds = new DatagramSocket(5555);

(2)public DatagramSocket() throws SocketException:构造数据报套接字并将其绑定到本地主机上的任何可用端口。

	DatagramSocket ds = new DatagramSocket();

  • 部分方法

(1)public void send(DatagramPacket p) throws IOException:从此数据报套接字发送数据报包。

(2)public void receive(DatagramPacket p) throws IOException:从此数据报套接字接收数据报包。 当此方法返回时, DatagramPacket的缓冲区将填充接收到的数据。 数据包数据包还包含发送者的IP地址和发件人机器上的端口号。该方法阻塞,直到接收到数据报。 数据报包对象的length字段包含接收到的消息的长度。 如果消息长于数据包的长度,消息将被截断。

2、DatagramPacket类


DatagramPacket类表示数据报包,数据报包用于实现无连接分组传送服务。

  • 构造方法

(1)public DatagramPacket(byte[] buf, int length):构造一个DatagramPacket 用于接收长度的数据包length 。length参数必须小于或等于buf.length 。

参数列表
① buf - 用于保存传入数据报的缓冲区。
② length - 要读取的字节数。

	byte[] buf = new byte[1024];
	DatagramPacket dp = new DatagramPacket(buf, buf.length);

(2)public DatagramPacket(byte[] buf, int length, InetAddress address, int port):构造用于发送长度的分组的数据报包length指定主机上到指定的端口号。 length参数必须小于或等于buf.length 。

参数列表
① buf - 分组数据。
② length - 包长度。
③ address - 目的地址。
④ port - 目的端口号。

	byte[] buf = "你好,世界!".getBytes();
	InetAddress address = InetAddress.getByName("127.0.0.1");
	DatagramPacket dp = new DatagramPacket(buf, buf.length, address, 5555);

3、InetAddress类


InetAddress类表示Internet协议(IP)地址。

  • 部分方法

public static InetAddress getByName(String host) throws UnknownHostException:确定主机名称的IP地址。 主机名称可以是机器名称,例如“ java.sun.com ”或其IP地址的文本表示。 如果提供了文字IP地址,则只会检查地址格式的有效性。

	InetAddress address = InetAddress.getByName("127.0.0.1");

4、代码案例

  • TestDataGramSend.java(发送数据)

	import java.io.IOException;
	import java.net.DatagramPacket;
	import java.net.DatagramSocket;
	import java.net.InetAddress;
	import java.net.SocketException;
	
	/**
	 * 发送数据
	 * @author Administrator
	 *
	 */
	public class TestDataGramSend {
    
    
		public static void main(String[] args) {
    
    
			DatagramSocket ds = null;
			try {
    
    
				//构造数据报套接字,并将其绑定到本地主机上的任何可用端口。
				ds = new DatagramSocket();
				byte[] buf = "你好,世界!".getBytes();
				//确定主机名称的IP地址。 
				InetAddress address = InetAddress.getByName("127.0.0.1");
				//构造用于发送长度length的分组的数据报包指定主机上到指定的端口号。
				DatagramPacket dp = new DatagramPacket(buf, buf.length, address, 5555);
				//从此套接字发送数据报包。
				ds.send(dp);
	
			} catch (SocketException e) {
    
    
				e.printStackTrace();
			} catch (IOException e) {
    
    
				e.printStackTrace();
			} finally {
    
    
				if(ds != null) {
    
    
					ds.close();
				}
			}
		}
	}

  • TestDataGramReceive.java(接收数据)

	import java.io.IOException;
	import java.net.DatagramPacket;
	import java.net.DatagramSocket;
	import java.net.SocketException;
	
	/**
	 * 接收数据
	 * 
	 * @author Administrator
	 *
	 */
	public class TestDataGramReceive {
    
    
		public static void main(String[] args) {
    
    
			DatagramSocket ds = null;
			try {
    
    
				// 构造数据报套接字,并将其绑定到本地主机上的指定端口
				DatagramSocket ds = new DatagramSocket(5555);
				byte[] buf = new byte[1024];
				// 构造一个DatagramPacket,用于接收长度为length的数据包 。
				// buf:用于保存传入数据报的缓冲区。
				// length:要读取的字节数。
				DatagramPacket dp = new DatagramPacket(buf, buf.length);
				// 从此套接字接收数据报包。 当此方法返回时, DatagramPacket的缓冲区将填充接收到的数据。
				ds.receive(dp);
				System.out.println(new String(buf));
			} catch (SocketException e) {
    
    
				e.printStackTrace();
			} catch (IOException e) {
    
    
				e.printStackTrace();
			} finally {
    
    
				if (ds != null) {
    
    
					ds.close();
				}
			}
		}
	}

二、URL类

1、URL类


URL类表示统一资源定位符,是指向万维网上的资源的指针。

  • 构造方法

(1)public URL(String spec) throws MalformedURLException:表示从String形成一个URL对象。

参数列表
spec - 要解析为URL的 String 。

	URL url = new URL("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1606386463469&di=a5480c8a250aad1afbf90b042539fea7&imgtype=0&src=http%3A%2F%2Fpic1.win4000.com%2Fwallpaper%2F2017-11-03%2F59fc3992949f7.jpg");

  • 部分方法

public URLConnection openConnection() throws IOException:返回一个URLConnection实例,表示与URL引用的远程对象的URL 。

	URLConnection uc = url.openConnection();

2、URLConnection类


URLConnection 类的实例可以用于从URL引用的资源中读取和写入。URLConnection类不能通过new运算符直接创建对象,而是通过URL类的openConnection方法返回一个URLConnection类的对象。

  • 部分方法

(1)public InputStream getInputStream() throws IOException:返回从此打开的连接读取的输入流。

	InputStream is = uc.getInputStream();

(2)public OutputStream getOutputStream() throws IOException:返回写入此连接的输出流。

	OutputStream is = uc.getOutputStream ();

3、通过url下载文件的代码案例


	import java.io.FileNotFoundException;
	import java.io.FileOutputStream;
	import java.io.IOException;
	import java.io.InputStream;
	import java.io.OutputStream;
	import java.net.MalformedURLException;
	import java.net.URL;
	import java.net.URLConnection;
	
	/**
	 * 通过url下载文件
	 * 
	 * @author Administrator
	 *
	 */
	public class DownLoad {
    
    
		public static void main(String[] args) {
    
    
			InputStream is = null;
			FileOutputStream fos = null;
			try {
    
    
				URL url = new URL("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1606386463469&di=a5480c8a250aad1afbf90b042539fea7&imgtype=0&src=http%3A%2F%2Fpic1.win4000.com%2Fwallpaper%2F2017-11-03%2F59fc3992949f7.jpg");
				URLConnection uc = url.openConnection();
	
				is = uc.getInputStream();
	
				fos = new FileOutputStream("E:\\dowloadPhoto.jpg");
	
				byte[] b = new byte[1024];
	
				int len = 0;
	
				while ((len = is.read(b)) != -1) {
    
    
					fos.write(b, 0, len);
				}
				System.out.println("文件下载完成");
			} catch (MalformedURLException e) {
    
    
				e.printStackTrace();
			} catch (FileNotFoundException e) {
    
    
				e.printStackTrace();
			} catch (IOException e) {
    
    
				e.printStackTrace();
			} finally {
    
    
				try {
    
    
					if (fos != null) {
    
    
						fos.close();
					}
					if (is != null) {
    
    
						is.close();
					}
	
				} catch (IOException e) {
    
    
					e.printStackTrace();
				}
			}
	
		}
	}

三、单例模式

1、设计模式简介


设计模式是一套代码设计经验的总结。项目中合理的运用设计模式可以巧妙的解决很多问题。

1.1、设计模式的分类


设计模式可以分为三大类:

(1)创建型模式

工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。

(2)结构型模式

适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。

(3)行为型模式

策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

1.2、设计模式的原则


设计模式的六大原则:

(1)开闭原则(Open Close Principle)

开闭原则的意思是:对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。简言之,是为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类。

(2)里氏代换原则(Liskov Substitution Principle)

里氏代换原则是面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。LSP 是继承复用的基石,只有当派生类可以替换掉基类,且软件单位的功能不受到影响时,基类才能真正被复用,而派生类也能够在基类的基础上增加新的行为。里氏代换原则是对开闭原则的补充。实现开闭原则的关键步骤就是抽象化,而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。

(3)依赖倒转原则(Dependence Inversion Principle)

这个原则是开闭原则的基础,具体内容:针对接口编程,依赖于抽象而不依赖于具体。

(4)接口隔离原则(Interface Segregation Principle)

这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。它还有另外一个意思是:降低类之间的耦合度。由此可见,其实设计模式就是从大型软件架构出发、便于升级和维护的软件设计思想,它强调降低依赖,降低耦合。

(5)迪米特法则,又称最少知道原则(Demeter Principle)

最少知道原则是指:一个实体应当尽量少地与其他实体之间发生相互作用,使得系统功能模块相对独立。

(6)合成复用原则(Composite Reuse Principle)

合成复用原则是指:尽量使用合成/聚合的方式,而不是使用继承。

2、单例模式


单例模式是Java中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。单例模式确保在一个应用程序中某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例单例实例。

单例模式只应在有真正的单一实例的需求时才可使用:单例类只能有一个实例、单例类必须自己创建自己的唯一实例、单例类必须给所有其他对象提供这一实例。

Java中实现单例模式可以通过两种形式实现:① 饿汉模式、② 懒汉模式

实现要点:
① 构造方法私有化
② 提供公共静态的方法得到一个类的对象
③ 饿汉模式、懒汉模式

2.1、饿汉模式


饿汉模式在类加载时就完成了初始化,所以类加载较慢,但获取对象的速度快。由于基于类加载机制避免了多线程的同步问题(静态初始化将保证在任何线程能够访问到域之前初始化它)。

	/**
	 * 单例模式:饿汉模式
	 * @author Administrator
	 *
	 */
	public class HungrySingleton {
    
    
		private static HungrySingleton instance = new HungrySingleton();
		
		private HungrySingleton() {
    
    
			super();
		}
		
		public static HungrySingleton getInstance() {
    
    
			return instance;
		}
	}
	

2.2、懒汉模式

2.2.1、实现单例模式


(1)懒汉方式实现单例模式能够提高类加载性能。

	/**
	 * 单例模式:懒汉模式(线程不安全)
	 * @author Administrator
	 *
	 */
	public class LazySingleton {
    
    
		private static LazySingleton instance = null;
		
		private LazySingleton() {
    
    
			super();
		}
		
		public static LazySingleton getInstance() {
    
    
			if(instance == null) {
    
    
				instance = new LazySingleton();
			}
			return instance;
		}
	}


(2)与饿汉模式借助JVM的类加载内部同步机制实现了线程的安全不同,懒汉方式在类加载时不进行对象的初始化,所以需要注意单例实例的线程安全性,因为多线程同时调用 getInstance方法 时可能会产生多个示例,甚至会破坏实例,违背单例的设计原则。可以为返回单例实例的方法设置同步用来保证线程安全性。但是,由于整个方法被同步,因此效率相对较低。

	/**
	 * 单例模式:懒汉模式(方法同步保证线程安全)
	 * @author Administrator
	 *
	 */
	public class LazySingleton {
    
    
		private static LazySingleton instance = null;
		
		private LazySingleton() {
    
    
			super();
		}
		
		public synchronized static LazySingleton getInstance() {
    
    
			if(instance == null) {
    
    
				instance = new LazySingleton();
			}
			return instance;
		}
	}

2.2.2、通过静态内部类实现懒汉模式


通过静态内部类的方式实现单例模式是线程安全的,同时静态内部类不会在LazySingleton类加载时就加载,而是在调用getInstance()方法时才进行加载。

	/**
	 * 单例模式:懒汉模式(通过静态内部类)
	 * @author Administrator
	 *
	 */
	public class LazySingleton {
    
    
		private static class SingletonHolder{
    
    
			private static final LazySingleton instance = new LazySingleton();
		}
		
		private LazySingleton() {
    
    
			super();
		}
		
		public static final LazySingleton getInstance() {
    
    
			return SingletonHolder.instance;
		}
	}

2.2.3、通过枚举实现懒汉模式


JDK1.5之后引入了枚举,由于枚举的特性,可以利用其来实现单例,它不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。

  • LazySingleton.java(实现懒汉模式的枚举类)
	public enum LazySingleton {
    
    
	    INSTANCE;
	    public void method() {
    
    
	        System.out.println("method");
	    }
	}

  • TestLazySingleton .java(测试类)
	public class TestLazySingleton {
    
    
	    public static void main(String[] args) {
    
    
	    	LazySingleton.INSTANCE.method();
	    }
	}

猜你喜欢

转载自blog.csdn.net/qq_42141141/article/details/110200820