day12-网络编程
day12-网络编程
今日目标
-
网络编程
-
TCP通信
-
Junit单元测试
-
单例设计模式
-
多例设计模式
-
工厂设计模式
1 网络编程
1.1 软件架构
- C/S结构 :全称为Client/Server结构,是指客户端和服务器结构。常见程序有QQ、迅雷等软件
- B/S结构 :全称为Browser/Server结构,是指浏览器和服务器结构。常见浏览器有谷歌、火狐等
- 两种架构各有优势,但是都离不开网络的支持。网络编程 , 就是在一定的协议下,实现两台计算机的通信的程序
1.2 什么是网络编程
- 在网络通信协议下,不同计算机上运行的程序,可以进行数据传输
1.3 网络编程三要素
- IP地址 : 设备在网络中的地址,是唯一的标识。
- 端口 : 设备在网络中的地址,是唯一的标识。
- 数据在网络中传输的规则,常见的协议有UDP协议和TCP协议。
1.4 IP地址
-
IP:全称”互联网协议地址”,也称IP地址。是分配给上网设备的数字标签。常见的IP分类为:ipv4和ipv6
简单来说 : 就是设备在网络中的唯一标识 , 想要连接哪一台电脑 , 就找到此电脑在网络中的ip地址
-
IP地址常见分类 : ipv4和ipv6
-
常用命令:
- ipconfig:查看本机IP地址
- IP地址:检查网络是否连通
-
特殊IP地址:
- 127.0.0.1:是回送地址也称本地回环地址,可以代表本机的IP地址,一般用来测试使用
-
为了方便我们对IP地址的获取和操作,Java提供了一个类InetAddress 供我们使用
InetAddress:此类表示Internet协议(IP)地址-
static InetAddress getByName(String host) 在给定主机名的情况下确定主机的 IP 地址 String getHostName() 获取此 IP 地址的主机名 String getHostAddress() 返回 IP 地址字符串(以文本表现形式)。
-
/*
* 演示:InetAddress的基本使用
* 封装IP对象:
* static InetAddress getByName(String host) 在给定主机名的情况下确定主机的 IP 地址。
* 获取IP和主机信息:
* String getHostAddress()返回 IP 地址字符串(以文本表现形式)。
* String getHostName()获取此 IP 地址的主机名。
*/
public class InetAddressDemo {
public static void main(String[] args) throws Exception {
// static InetAddress getByName(String host) 在给定主机名的情况下确定主机的 IP 地址。
// suoge表示我的电脑主机名字
InetAddress ia = InetAddress.getByName("suoge");// suoge/192.168.100.107
// InetAddress ia = InetAddress.getByName("192.168.100.107");//
// /192.168.100.107
// System.out.println(ia);
// String getHostAddress()返回 IP 地址字符串(以文本表现形式)。
String ip = ia.getHostAddress();
// String getHostName()获取此 IP 地址的主机名。
String hostName = ia.getHostName();
/*
* InetAddress ia = InetAddress.getByName("suoge"):
* 192.168.100.107=====suoge
* 说明当使用主机名作为函数参数时将计算机名和IP地址同时封装在该对象中,此时调用getHostName获取IP 地址的主机名时,就会获取到指定的计算机名suoge
* ================================================================
* InetAddress ia = InetAddress.getByName("192.168.100.107"):
* 192.168.100.107=====192.168.100.107
* 说明当ip地址作为函数参数时没有将计算机名封装在该对象中,此时调用getHostName获取IP 地址的主机名 时,就会获取到指定的IP
*/
System.out.println(ip + "=====" + hostName);
System.out.println("-------------------------");
}
}
1.5 端口
-
端口:应用程序在设备中唯一的标识。
-
端口号:应用程序的唯一标识方式 , 用两个字节表示的整数,它的取值范围是0~65535。
其中0~1023之间的端口号用于一些知名的网络服务或者应用。
我们自己使用1024以上的端口号就可以了。 -
注意:一个端口号只能被一个应用程序使用。
1.6 通信协议
- 协议:计算机网络中,连接和通信的规则被称为网络通信协议
- UDP协议
- 用户数据报协议(User Datagram Protocol)
- UDP是面向无连接通信协议。
- 速度快,有大小限制一次最多发送64K,数据不安全,易丢失数据。
- TCP协议
- 传输控制协议 (Transmission Control Protocol)
- TCP协议是面向连接的通信协议。
- 速度慢,没有大小限制,数据安全
2 TCP通信
2.1 TCP发送数据
package com.itheima.tcp_demo.demo1;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
/*
客户端 :
发送数据的步骤
1 创建客户端的Socket对象 : Socket(String host, int port) 与指定服务端连接
参数说明:
host 表示服务器端的主机名,也可以是服务器端的IP地址,只不过是String类型的
port 表示服务器端的端口
2 通获Socket对象取网络中的输出流,写数据
OutputStream getOutputStream()
3 释放资源
void close()
*/
public class ClientDemo {
public static void main(String[] args) throws IOException {
// 创建客户端的Socket对象(Socket) 与指定服务端连接
Socket socket = new Socket("127.0.0.1", 10010);
// 通获Socket对象取网络中的输出流,写数据
OutputStream os = socket.getOutputStream();
os.write("hello".getBytes());
// while(true){}
// 释放资源
os.close();
socket.close();
}
}
2.2 TCP接收数据
package com.itheima.tcp_demo.demo1;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
/*
服务端接收数据 :
1 创建服务器端的Socket对象 : ServerSocket类
ServerSocket(int port) : 构造方法需要绑定一个端口号 , port就是端口号
2 监听客户端连接,并接受连接,返回一个Socket对象
Socket accept() : 该方法会一直阻塞直到建立连接
3 获取网络中的输入流,用来读取客户端发送过来的数据
InputStream getInputStream()
4 释放资源 : 服务端一般不会关闭
void close()
*/
public class ServerDemo {
public static void main(String[] args) throws IOException {
// 1 创建服务器端的Socket对象 : ServerSocket类
// ServerSocket(int port) : 构造方法需要绑定一个端口号 , port就是端口号
ServerSocket serverSocket = new ServerSocket(10010);
// 2 监听客户端连接,并接受连接,返回一个Socket对象
// Socket accept() : 该方法会一直阻塞直到建立连接
Socket socket = serverSocket.accept();
//
// 3 获取网络中的输入流,用来读取客户端发送过来的数据
// InputStream getInputStream()
InputStream is = socket.getInputStream();
int by;
System.out.println("read方法执行前");
while ((by = is.read()) != -1) {
System.out.print((char) by);
}
System.out.println("read方法执行后");
}
}
2.3 TCP通信原理分析
2.4 TCP三次握手
2.5 TCP练习1
package com.itheima.tcp_demo.test1;
import java.io.*;
import java.net.Socket;
/*
客户端 :
发送数据的步骤
1 创建客户端的Socket对象 : Socket(String host, int port) 与指定服务端连接
参数说明:
host 表示服务器端的主机名,也可以是服务器端的IP地址,只不过是String类型的
port 表示服务器端的端口
2 通获Socket对象取网络中的输出流,写数据
OutputStream getOutputStream()
3 释放资源
void close()
*/
public class ClientDemo {
public static void main(String[] args) throws IOException {
// 创建客户端的Socket对象(Socket) 与指定服务端连接
Socket socket = new Socket("127.0.0.1", 10010);
// 通获Socket对象取网络中的输出流,写数据
OutputStream os = socket.getOutputStream();
os.write("hello".getBytes());
// 像服务端写入结束标记
socket.shutdownOutput();
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line = br.readLine();
System.out.println(line);
// 释放资源
br.close();
os.close();
socket.close();
}
}
package com.itheima.tcp_demo.test1;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
/*
服务端接收数据 :
1 创建服务器端的Socket对象 : ServerSocket类
ServerSocket(int port) : 构造方法需要绑定一个端口号 , port就是端口号
2 监听客户端连接,并接受连接,返回一个Socket对象
Socket accept() : 该方法会一直阻塞直到建立连接
3 获取网络中的输入流,用来读取客户端发送过来的数据
InputStream getInputStream()
4 释放资源 : 服务端一般不会关闭
void close()
*/
public class ServerDemo {
public static void main(String[] args) throws IOException {
// 1 创建服务器端的Socket对象 : ServerSocket类
// ServerSocket(int port) : 构造方法需要绑定一个端口号 , port就是端口号
ServerSocket serverSocket = new ServerSocket(10010);
// 2 监听客户端连接,并接受连接,返回一个Socket对象
// Socket accept() : 该方法会一直阻塞直到建立连接
Socket socket = serverSocket.accept();
//
// 3 获取网络中的输入流,用来读取客户端发送过来的数据
// InputStream getInputStream()
InputStream is = socket.getInputStream();
int by;
while ((by = is.read()) != -1) {
System.out.print((char) by);
}
BufferedWriter bos = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
bos.write("你谁啊");
bos.close();
is.close();
socket.close();
serverSocket.close();
}
}
2.6 TCP练习2
package com.itheima.tcp_demo.test2;
import java.io.*;
import java.net.Socket;
public class ClientDemo {
public static void main(String[] args) throws IOException {
// 创建客户端Socket对象
Socket socket = new Socket("127.0.0.1", 10086);
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:\\传智播客\\安装包\\好看的图片\\liqin.jpg"));
BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
int by;
while ((by = bis.read()) != -1) {// 从本地中读一个字节
bos.write(by);// 往服务器写一个字节
bos.flush();
}
// 写结束标记
socket.shutdownOutput();
// 把网络中的字节输入流 , 封装成高效的字符输入流
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
// String line;
// while ((line = br.readLine()) != null) {
// System.out.println(line);
// }
String msg = br.readLine();// 读到换行才叫读到一行, 所以必须写服务器必须写newLine
System.out.println(msg);
// 释放资源
bis.close();
socket.close();
}
}
package com.itheima.tcp_demo.test2;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerDemo {
public static void main(String[] args) throws IOException {
// 创建服务端的连接对象
ServerSocket serverSocket = new ServerSocket(10086);
Socket socket = null;
BufferedInputStream bis = null;
BufferedWriter socketBw = null;
while (true) {
// 获取Socket对象
socket = serverSocket.accept();
// 获取网络中的字节输入流 在封装成高效的字节输入流对象
bis = new BufferedInputStream(socket.getInputStream());
// 创建本地的字节输出流 , 封装成高效的字节输出流
BufferedOutputStream bw = new BufferedOutputStream(new FileOutputStream("day13_demo\\图片\\a.jpg"));
int by;
while ((by = bis.read()) != -1) {
bw.write(by);
bw.flush();
}
//关闭本地流
bw.close();
// 获取网络中的字节输出流 , 封装成高效的字符输出流
socketBw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
socketBw.write("谢谢你");
socketBw.newLine();// 必须有换行 , 因为readLine读到换行结束
socketBw.flush();
}
// 释放资源
// socketBw.close();
// bis.close();
// socket.close();
// serverSocket.close();
}
}
2.7 TCP练习3
package com.itheima.tcp_demo.test3;
import java.io.*;
import java.net.Socket;
public class ClientDemo {
public static void main(String[] args) throws IOException {
// 创建客户端Socket对象
Socket socket = new Socket("127.0.0.1", 10086);
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:\\传智播客\\安装包\\好看的图片\\liqin.jpg"));
BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
int by;
while ((by = bis.read()) != -1) {// 从本地中读一个字节
bos.write(by);// 往服务器写一个字节
bos.flush();
}
// 写结束标记
socket.shutdownOutput();
// 把网络中的字节输入流 , 封装成高效的字符输入流
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
// String line;
// while ((line = br.readLine()) != null) {
// System.out.println(line);
// }
String msg = br.readLine();// 读到换行才叫读到一行, 所以必须写服务器必须写newLine
System.out.println(msg);
// 释放资源
bis.close();
socket.close();
}
}
package com.itheima.tcp_demo.test3;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ServerDemo {
public static void main(String[] args) throws IOException {
// 创建服务端的连接对象
ServerSocket serverSocket = new ServerSocket(10086);
ExecutorService executorService = Executors.newFixedThreadPool(10);
while (true) {
// 获取Socket对象
Socket socket = serverSocket.accept();
executorService.submit(new ServerThread(socket));
}
// 释放资源
// socketBw.close();
// bis.close();
// socket.close();
// serverSocket.close();
}
}
package com.itheima.tcp_demo.test3;
import javax.management.relation.RoleUnresolved;
import java.io.*;
import java.net.Socket;
import java.util.UUID;
public class ServerThread implements Runnable {
Socket socket = null;
BufferedOutputStream bw = null;
public ServerThread(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
// 获取网络中的字节输入流 在封装成高效的字节输入流对象
BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
// 创建本地的字节输出流 , 封装成高效的字节输出流
bw = new BufferedOutputStream(new FileOutputStream("day13_demo\\图片\\" + UUID.randomUUID() + ".jpg"));
int by;
while ((by = bis.read()) != -1) {
bw.write(by);
bw.flush();
}
// 获取网络中的字节输出流 , 封装成高效的字符输出流
BufferedWriter socketBw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
socketBw.write("谢谢你");
socketBw.newLine();// 必须有换行 , 因为readLine读到换行结束
socketBw.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
//关闭本地流
try {
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
3 单例设计模式
1.作用
单例设计模式的意思是,一个类只允许创建一个实例,也就是一个对象,对象在堆内存中只能开辟一个空间。
2.实现步骤
- 将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象。
- 在该类内部产生一个唯一的实例化对象,并且将其封装为private static final类型的成员变量。
- 定义一个静态方法返回这个唯一对象。
3. 单例设计模式的类型
根据实例化对象的时机单例设计模式又分为以下两种:
-
饿汉单例设计模式
饿汉单例设计模式就是使用类的时候已经将对象创建完毕,不管以后会不会使用到该实例化对象,先创建了再说。很着急的样子,故被称为“饿汉模式”。
-
懒汉单例设计模式
懒汉单例设计模式就是调用getInstance()方法时实例才被创建,先不急着实例化出对象,等要用的时候才实例化出对象。不着急,故称为“懒汉模式”。
4.代码实现
-
饿汉式
-
比较饥饿,着急创建出来唯一的一个对象
/* 单例设计模式 比较饥饿,着急创建出来唯一的一个对象 */ public class Single { //1、私有化构造函数 private Single(){} //2、创建一个本类的对象 private static final Single s=new Single(); //3、定义一个方法返回本类的对象 public static Single getInstance() { return s; } //测试方法 public void test() { System.out.println("测试方法"); } } class SingleDemo { public static void main(String[] args) { /* 要想获取到Single类的对象,调用getInstance方法 既然不能通过对象来调用,那么只能通过类名来调用 如果要想通过类名来调用方法,那么被调用的方法必须用static来修饰 */ Single s1=Single.getInstance(); s1.test(); //Single s2=Single.getInstance(); } }
-
-
懒汉式
-
比较懒惰,什么时候用对象,就什么时候创建
public class Single { //比较懒惰,什么时候用对象,就什么时候创建 //构造方法私有化 private Single(){ } //创建一个唯一的对象 private static Single s = null; //获取对象的方法 public static synchronized Single getSingle(){ //如果值是null,说明没有创建过这个对象 if(s == null){//t1 t2 s = new Single(); } return s; } }
-
注意:懒汉单例设计模式在多线程环境下可能会实例化出多个对象,不能保证单例的状态,所以加上关键字:synchronized,保证其同步安全。
5. 小结
单例模式可以保证系统中一个类只有一个对象实例。
实现单例模式的步骤:
- 将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象。
- 在该类内部产生一个唯一的实例化对象,并且将其封装为private static final类型的成员变量。
- 定义一个静态方法返回这个唯一对象。
4 多例设计模式
多例模式,是一种常用的软件设计模式。通过多例模式可以保证系统中,应用该模式的类有固定数量的实例。多例类要自我创建并管理自己的实例,还要向外界提供获取本类实例的方法。
-
作用
一个类可以创建多个对象,有多个实例。
-
实现步骤
1.创建一个类, 将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象。
2.在类中定义存放类实例的list集合
3.在类中提供静态代码块,在静态代码块中创建类的实例
4.提供获取类实例的静态方法
-
代码演示
-
比如一个类只允许创建3个对象。
public class Person { //这个类一共创建3个对象 //构造方法私有化 private Person(){} //定义集合用于保存多个对象 private static ArrayList<Person> list = new ArrayList<>(); //静态代码块,只会执行一次,且是在这个类最开始最先执行 static{ //创建3个对象放在集合里 for (int i = 0; i < 3; i++) { list.add(new Person()); } } //定义供外界访问的获取对象的方法 public static Person getPerson(){ //随机一个对象返回给调用者 //创建随机对象 Random r = new Random(); //获取索引 int i = r.nextInt(3); //根据索引从集合中获取对象 Person person = list.get(i); //返回给调用者 return person; } } public class Demo { public static void main(String[] args) { //获取10次,但是获取到的其实就是3个对象 for (int i = 0; i < 10; i++) { Person person = Person.getPerson(); System.out.println(person); } } }
-
5 工厂设计模式
-
介绍
工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。之前我们创建类对象时, 都是使用new 对象的形式创建, 除new 对象方式以外, 工厂模式也可以创建对象.
-
没有使用工厂设计模式创建对象即以前创建对象方式的问题
-
假设定义三个车类 BaoMa BenChi AoDi,然后在测试类创建三个类的对象
-
代码演示:
public class BaoMa { } public class BenChi { } public class AoDi { } public class Test01 { public static void main(String[] args) { //创建三个类的对象 BaoMa baoMa = new BaoMa(); BenChi benChi = new BenChi(); AoDi aoDi = new AoDi(); } }
问题说明:
1.假设当前项目下具有很多个测试类,都要使用这三个类,然后我想统计每个类的对象有多少个,那么 这样统计就会很麻烦
2.测试类和这三个类还耦合在一起了,我们应该降低耦合
-
-
使用工厂设计模式创建对象
- 实现步骤
- 编写一个Car接口
- 编写一个BaoMa类实现Car接口
- 编写一个Benchi类实现Car接口
- 编写一个AoDi类实现Car接口
- 提供一个CarFactory(汽车工厂),定义静态方法用于生产汽车对象
- 定义CarFactoryTest测试汽车工厂
- 实现步骤
-
代码演示
package com.itheima.sh.demo_04; /* 汽车工厂类 */ public class CarFactory { //工厂专门用来创建汽车对象 //name表示调用该方法时指定的车类型 public static Car getCar(String name){ if(name.equals("BaoMa")){ return new BaoMa(); }else if(name.equals("BenChi")){ return new BenChi(); }else if(name.equals("AoDi")){ return new AoDi(); } return null; } } //汽车 public interface Car { } public class BaoMa implements Car{ //宝马 } public class BenChi implements Car{ //奔驰 } public class AoDi implements Car{ //奥迪 } //测试类 public class Test01 { public static void main(String[] args) { //创建三个类的对象 // BaoMa baoMa = new BaoMa(); // BenChi benChi = new BenChi(); // AoDi aoDi = new AoDi(); //--------------------------------- //使用工厂创建对象 Car baoMa = CarFactory.getCar("BaoMa"); System.out.println(baoMa); Car benChi = CarFactory.getCar("BenChi"); System.out.println(benChi); Car AoDi = CarFactory.getCar("AoDi"); System.out.println(AoDi); } }
小结:
-
工厂模式的存在可以改变创建类的对象方式
-
方便管理对象
-
降低类与类之间的耦合,调用工厂类中的方法直接指定字符串即可,创建对象的事情都交给工厂类