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();
}
}