软件结构
软件结构分类:
C/S结构 :全称为Client/Server结构,是指客户端和服务器结构。常见程序有QQ、迅雷等软件
B/S结构 :全称为Browser/Server结构,是指浏览器和服务器结构。常见浏览器有谷歌、火狐等
两种架构各有优势,但是都离不开网络的支持。网络编程 , 就是在一定的协议下,实现两台计算机的通信的程序
网络编程的三要素
网络编程概述
网络编程在网络通信协议下,不同计算机上运行的程序,可以进行数据传输
思考:
A电脑中的QQ,发送消息给B电脑中的QQ。那么需要有哪些条件才能发送?
在这个小故事中,找到成功取钱的要素有哪些?
银行的地址 —- 确定了银行的位置
柜台号 —- 通过柜台跟客户交流并处理业务
填写取款单的规则 —- 办理业务的规则
IP地址
设备在网络中的地址,是唯一的标识。
端口
应用程序在设备中唯一的标识。
协议
数据在网络中传输的规则,常见的协议有UDP协议和TCP协议。
IP地址
IP:全称”互联网协议地址”,也称IP地址。是分配给上网设备的数字标签。常见的IP分类为:ipv4和ipv6简单来说 : 就是设备在网络中的唯一标识 , 想要连接哪一台电脑 , 就找到此电脑在网络中的ip地址
IP地址常见分类 : ipv4和ipv6
IPv4:
IPv6:
由于互联网的不断发展,IP地址的需求量愈来愈大,而IPv4的模式下IP的总数是有限的。
采用128位地址长度,16位一组,一共分成8组。
常用命令:
ipconfig:查看本机IP地址
ping IP地址:检查网络是否连通
特殊IP地址:
127.0.0.1:是回送地址也称本地回环地址,可以代表本机的IP地址,一般用来测试使用
Java中IP操作
InetAddress 的使用
为了方便我们对IP地址的获取和操作,Java提供了一个类InetAddress 供我们使用
InetAddress:此类表示Internet协议(IP)地址
方法名 | 说明 |
---|---|
static InetAddress getByName(String host) | 在给定主机名的情况下获取InetAddress类的对象 |
String getHostName() | 获取此 IP 地址的主机名 |
String getHostAddress() | 返回 IP 地址字符串(以文本表现形式)。 |
public class InetAddressDemo {
public static void main(String[] args) throws UnknownHostException {
//获取本机的IP地址对象
InetAddress localHost = InetAddress.getLocalHost();
String hostName = localHost.getHostName();
String hostAddress = localHost.getHostAddress();
System.out.println("hostName = " + hostName);
System.out.println("hostAddress = " + hostAddress);
//指定主机名 , 域名 , IP 获取 IP 地址对象
InetAddress baidu = InetAddress.getByName("www.baidu.com");
String baiduhostAddress = baidu.getHostAddress();
String baiduhostName = baidu.getHostName();
System.out.println("baiduhostAddress = " + baiduhostAddress);
System.out.println("baiduhostName = " + baiduhostName);
}
}
端口
端口:
应用程序在设备中唯一的标识。
端口号:
应用程序的唯一标识方式 , 用两个字节表示的整数值,它的取值范围是0~65535。
其中0~1023之间的端口号用于一些知名的网络服务或者应用。
我们自己使用1024以上的端口号就可以了。
注意:一个端口号只能被一个应用程序使用。
协议
协议:计算机网络中,连接和通信的规则被称为网络通信协议
UDP协议
用户数据报协议(User Datagram Protocol)
UDP是面向无连接通信协议。
速度快,有大小限制一次最多发送64K,数据不安全,易丢失数据。
TCP协议
传输控制协议 (Transmission Control Protocol)
CP协议是面向连接的通信协议。
速度慢,没有大小限制,数据安全。
小结
网络编程:就是可以让两台计算机进行数据交互。
网络编程三要素:
IP:设备在网络中唯一的标识。
端口号:应用程序在设备中唯一的标识。
协议:数据在传输过程中遵守的规则。
TCP通信程序
TCP发送数据步骤
创建客户端的Socket对象 : Socket类
Socket(String host, int port) : 与指定服务端连接
参数说明:
host 表示服务器端的主机名,也可以是服务器端的IP地址,只不过是String类型的
port 表示服务器端的端口
通获Socket对象取网络中的输出流,写数据
OutputStream getOutputStream()释放资源void close()
public class ClientDemo {
public static void main(String[] args) throws IOException {
//服务端现在测试自己的电脑
//1 创建Socket对象
Socket socket = new Socket("127.0.0.1" , 8888);
//2 获取输出流,使用输出流写数据
OutputStream netOut = socket.getOutputStream();
netOut.write("Hello".getBytes());
//3.释放资源
socket.close();
}
}
TCP接收数据
创建服务器端的Socket对象 : ServerSocket类
ServerSocket(int port) : 构造方法需要绑定一个端口号 , port就是端口号
监听客户端连接,并接受连接,返回一个Socket对象
Socket accept() : 该方法会一直阻塞直到建立连接
获取网络中的输入流,用来读取客户端发送过来的数据
InputStream getInputStream()
释放资源 : 服务端一般不会关闭
void close()
public class ServerDemo {
public static void main(String[] args) throws IOException {
// 1 创建服务器端的Socket对象 : ServerSocket类
ServerSocket serverSocket = new ServerSocket(8888);
// 2 监听客户端连接,并接受连接,返回一个Socket对象
//等待客户端的链接
System.out.println("服务端已启动 等待客户端的链接~~~");
Socket socket = serverSocket.accept();
System.out.println("有客户端已连接~~~");
//3 获取网络中的输入流,用来读取客户端发送过来的数据
InputStream netIn = socket.getInputStream();
byte[] buf = new byte[1024];
int len = netIn.read(buf);
//把有效字节数组转换成字符串
System.out.println(new String(buf , 0 ,len));
//4 释放资源 : 服务端一般不会关闭
socket.close(); // 当服务器完成一个客户端结束需要关闭
serverSocket.close();//一般不关
}
}
先运行服务端 再运行客户端 运行效果:
TCP通信原理分析:
三次握手:
TCP通信程序练习 1
客户端:发送数据,接收服务器反馈数据
服务器:接收数据,给出反馈
建立客户端
public class ClientDemo {
public static void main(String[] args) throws IOException {
//1.创建客户端的Socket对象
Socket socket = new Socket("127.0.0.1", 8989);
//2.通获Socket对象取网络中的输出流
OutputStream netOut = socket.getOutputStream();
netOut.write("你瞅啥?".getBytes());//发送给服务端
socket.shutdownOutput();//关闭输出流,告诉对方数据传输完毕
//3.通获Socket对象取网络中的输入流
InputStream netIn = socket.getInputStream();
byte[] buf = new byte[1024];
int len = netIn.read(buf);
System.out.println("客户端收到服务端数据" + new String(buf , 0 ,len));
// 4 释放资源
socket.close();
}
}
服务器接收数据
public class ServerDemo {
public static void main(String[] args) throws IOException {
// 1 创建服务器端的Socket对象 : ServerSocket类
ServerSocket serverSocket = new ServerSocket(8989);
while (true) {
// 2 监听客户端连接,并接受连接,返回一个Socket对象
System.out.println("等待客户端连接。。。。");
Socket socket = serverSocket.accept();
//等待客户端的链接
//3 获取网络中的输入流,用来读取客户端发送过来的数据
InputStream netIn = socket.getInputStream();
byte[] buf = new byte[1024];
int len = netIn.read(buf);
//把有效字节数组转换成字符串
System.out.println("服务端收到客户端数据" + new String(buf, 0, len));
//4 获取网络输出流,写数据给客户端
OutputStream netOut = socket.getOutputStream();
netOut.write("瞅你咋地".getBytes());//发送给客户端
//5 释放资源 : 服务端一般不会关闭
socket.close(); // 当服务器完成一个客户端结束需要关闭
}
//serverSocket.close();//一般不关
}
}
先运行服务端 再运行客户端 运行效果:
TCP通信程序练习 2
客户端:将本地文件上传到服务器。接收服务器的反馈。
服务器:接收客户端上传的文件,上传完毕之后给出反馈。
建立客户端
public class ClientDemo {
public static void main(String[] args) throws IOException {
//1.创建Sacket对象,指定服务器的地址和端口
Socket socket = new Socket("127.0.0.1", 8899);
//2.获取一个输出流来发送文件数据
OutputStream netOut = socket.getOutputStream();
//3.创建一个本地文件输入流,用来读取本地文件
FileInputStream localIn = new FileInputStream("D:\\img1\\月球.png");
//4.边读边写
int len;
byte[] buf = new byte[8 * 1024];
while ((len = localIn.read(buf)) != -1) {//读取本地文件
//发送给服务器
netOut.write(buf , 0 ,len);
}
//5.告诉服务器端,数据写出完毕
socket.shutdownOutput();
//6.获取一个网络的输入流,用来读取服务端相应的数据
InputStream netIn = socket.getInputStream();
len = netIn.read(buf);
System.out.println("收到服务器的信息" + new String(buf , 0 , len));
//7.释放资源
localIn.close();
socket.close();
}
}
服务器接收数据
public class ServerDemo {
public static void main(String[] args) throws IOException {
//1.创建服务器对象,指定端口
ServerSocket serverSocket = new ServerSocket(8899);
while (true) { //实现服务端持续服务
//2.等待接收客户端的链接accept
System.out.println("等待客户端的链接。。。");
Socket socket = serverSocket.accept();
System.out.println("客户端连接成功!!");
//3.获取网络的输入流,获取客户端发送的字节信息
InputStream netIn = socket.getInputStream();
//4.创建一个本地输出流,用来保存文件
FileOutputStream localOut =
//UUID. randomUUID()方法生成随机的文件名 就不会把第一次文件覆盖
//new FileOutputStream("day12_demo/aaa/" + UUID.randomUUID() + ".png");
new FileOutputStream("day12_demo/aaa/月球.png" + System.currentTimeMillis());
//将客户端的文件数据保存到本地
int len;
byte[] buf = new byte[8 * 1024];
while ((len = netIn.read(buf)) != -1) {//从网络中读取客户端数据
//读取的网络数据保存到文件
localOut.write(buf, 0, len);
}
//5.获取一个网络输出流,告知客户端上传成功
OutputStream netOut = socket.getOutputStream();
netOut.write("恭喜上传成功!".getBytes());
//6.释放资源
localOut.close();
socket.close();
}
}
}
先运行服务端 再运行客户端 运行效果:
开启多线程处理
public class ServerDemo {
public static void main(String[] args) throws IOException {
//1.创建服务器对象,指定端口
ServerSocket serverSocket = new ServerSocket(8899);
while (true) { //实现服务端持续服务
//2.等待接收客户端的链接accept
System.out.println("等待客户端的链接。。。");
Socket socket = serverSocket.accept();
System.out.println("客户端连接成功!!");
//构建任务
FileUploadTask fileUploadTask = new FileUploadTask(socket);
//让线程取执行 创建80个线程池
ExecutorService threadPool = Executors.newFixedThreadPool(80);
threadPool.submit(fileUploadTask);
}
}
}
class FileUploadTask implements Runnable{
private final Socket socket;
public FileUploadTask(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
InputStream netIn = null;
FileOutputStream localOut = null;
try {
//3.获取网络的输入流,获取客户端发送的字节信息
netIn = socket.getInputStream();
//4.创建一个本地输出流,用来保存文件
localOut =
//new FileOutputStream("day12_demo/aaa/" + UUID.randomUUID() + ".png"); UUID
new FileOutputStream("day12_demo/aaa/月球" + System.currentTimeMillis() +".png");
//将客户端的文件数据保存到本地
int len;
byte[] buf = new byte[8 * 1024];
while ((len = netIn.read(buf)) != -1) {//从网络中读取客户端数据
//读取的网络数据保存到文件
localOut.write(buf, 0, len);
}
//5.获取一个网络输出流,告知客户端上传成功
OutputStream netOut = socket.getOutputStream();
netOut.write("恭喜上传成功!".getBytes());
}catch (Exception e){
System.out.println("上传出错了!!");
} finally {
//6.释放资源
try {
if (localOut != null)
localOut.close();
}catch (IOException e){
e.printStackTrace();
}
try{
if (socket != null) {
socket.close();
}
}catch (IOException e){
e.printStackTrace();
}
}
}
}
单例设计模式
- 将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象。
- 在该类内部产生一个唯一的实例化对象
- ,并且将其封装为private static 类型的成员变量。
- 定义一个静态方法返回这个唯一对象。
单例设计模式的类型
- 饿汉单例设计模式就是使用类的时候已经将对象创建完毕不管以后会不会使用到该实例化对象,先创建了再说。很着急的样子,故被称为“饿汉模式”。
代码如下:
public class Singleton {
// 1.将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象。
private Singleton() {}
// 2.在该类内部产生一个唯一的实例化对象,并且将其封装为private static 类型的成员变量。
private static final Singleton instance = new Singleton();
// 3.定义一个静态方法返回这个唯一对象。
public static Singleton getInstance() {
return instance;
}
}
懒汉单例设计模式
懒汉单例设计模式就是调用getInstance()方法时对象才被创建也就是说先不急着实例化出对象,等要用的时候才实例化出对象。不着急,故称为“懒汉模式”。代码如下:
实现代码
public class Demo {
public static void main(String[] args) {
//不能创建对象 因为构造方法私有化了
//King k1 = new King("秦始皇");
//饿汉测试
King k1 = King.getInstance();
for (int i = 0; i < 10; i++) {
King k = King.getInstance();
System.out.println("k = " + k);
}
//懒汉测试
for (int i = 0; i < 10; i++) {
King2 k2 = King2.getInstance();
System.out.println("k2 = " + k2);
}
}
}
//饿汉设计
public class King {
private String name;
//1.将构造方法私有化
private King(String name) {
this.name = name;
}
//2. 在该类内部产生一个唯一的实例化对象,并且将其封装为private static 类型的成员变量。
private static final King instance = new King("秦始皇");
//3. 定义一个静态方法返回这个唯一对象。
public static King getInstance(){
return instance;
}
}
//懒汉设计
public class King2 {
private String name;
public King2(String name) {
this.name = name;
}
private static King2 instance;
public synchronized static King2 getInstance(){
if (instance == null) {
instance = new King2("刘邦");
}
return instance;
}
}
多例设计模式
多例设计模式的作用
- 多例模式,是一种常用的设计模式之一。通过多例模式可以保证项目中,应用该模式的类有固定数量的实例。
多例类要自我创建并管理自己的实例,还要向外界提供获取本类实例的方法。
使用场景:线程池
线程池 = Executors.newFixedThreadPool(3);
实现步骤
1.创建一个类, 将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象。<br /> 2.在类中定义该类被创建对象的总数量<br /> 3.在类中定义存放类实例的list集合<br /> 4.在类中提供静态代码块,在静态代码块中创建类的实例<br /> 5.提供获取类实例的静态方法
实现代码
- 某一个学科有固定3位老师,年级中上该课程的老师就是这三位老师其中一位 要求使用多例模式 ,每次获取的都是这三位老师其中一位
```java
public class Demo {
public static void main(String[] args) {
} }for (int i = 0; i < 10; i++) {
Teacher teacher = Teacher.getTeacher();
System.out.println(teacher);
}
public class Teacher {
private String name;
//1.创建一个类, 将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象。
public Teacher(String name) {
this.name = name;
}
// 2.在类中定义该类被创建对象的总数量
private static final int COUNT = 3;
//3.在类中定义存放类实例的list集合
private static final List
@Override
public String toString() {
return "Teacher{" +
"name='" + name + '\'' +
'}';
}
}
<a name="LB4ms"></a>
#### 小结
- 多例模式作用 : 可以保证项目中一个类有固定个数的实例, 在实现需求的基础上, 能够提高实例的复用性.实现多例模式的步骤 :
- 创建一个类, 将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象。
- 在类中定义该类被创建的总数量
- 在类中定义存放类实例的list集合
- 在类中提供静态代码块,在静态代码块中创建类的实例
- 提供获取类实例的静态方法
<a name="b58kn"></a>
## 工厂设计模式
<a name="nJhps"></a>
#### 概述
- 工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。之前我们创建类对象时, 都是使用 new 对象的形式创建, 除new 对象方式以外, 工厂模式也可以创建对象.
<a name="DvKUV"></a>
#### 作用
- 解决类与类之间的耦合问题
<a name="yUOM9"></a>
#### 案例实践
- 需求:定义汽车工厂类,生产各种品牌的车
- 实现步骤
- 编写一个Car接口, 提供run方法
- 编写一个Falali类实现Car接口,重写run方法
- 编写一个Benchi类实现Car接口,重写run方法
- 提供一个CarFactory(汽车工厂),用于生产汽车对象
- 定义CarFactoryTest测试汽车工厂
- **实现代码**
```java
public class CarTest {
public static void main(String[] args) {
Car car = CarFactory.getInstance(CarBrand.FALALI);
car.run();
}
}
// 定义车的品牌枚举
enum CarBrand {
// 枚举项
FALALI, BENCHI, DAZHONG, BAOMA;
}
// 生产汽车对象的工厂
class CarFactory {
// 私有构造
private CarFactory() {
}
public static Car getInstance(CarBrand brand) {
switch (brand) {
case FALALI:
return new Falali(1);
case BENCHI:
return new Benchi();
default:
return null;
}
}
}
//// 生产汽车对象的工厂
//class CarFactory {
// // 私有构造
// private CarFactory() {
// }
//
// public static Car getInstance(String brand) {
// switch (brand) {
// case "Falali":
// return new Falali(1);
// case "Benchi":
// return new Benchi();
// default:
// return null;
// }
// }
//}
// 编写一个Car接口, 提供run方法
interface Car {
public abstract void run();
}
class Falali implements Car {
public Falali(int num) {
}
@Override
public void run() {
System.out.println("法拉利破百需要3秒!");
}
}
class Benchi implements Car {
@Override
public void run() {
System.out.println("奔驰破百需要5秒!");
}
}