Java基础之《netty(21)—WebSocket长连接开发》

news2025/1/17 3:05:40

一、实例要求

1、http协议是无状态的,浏览器和服务器间的请求响应一次,下一次会重新创建连接
2、要求:实现基于webSocket的长连接的全双工的交互
3、改变http协议多次请求的约束,实现长连接了,服务器可以发送消息给浏览器
4、客户端浏览器和服务器端会相互感知,比如服务器关闭了,浏览器会感知,同样浏览器关闭了,服务器会感知

二、服务端

1、WebSocketServer.java

package netty.websocket;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.stream.ChunkedWriteHandler;

public class WebSocketServer {

	public static void main(String[] args) {
		//创建两个线程组
		EventLoopGroup bossGroup = new NioEventLoopGroup(1);
		EventLoopGroup workerGroup = new NioEventLoopGroup(); //默认cpu核数*2
		
		try {
			ServerBootstrap server = new ServerBootstrap();
			server.group(bossGroup, workerGroup)
				.channel(NioServerSocketChannel.class)
				//在boosGroup增加一个日志处理器
				.handler(new LoggingHandler(LogLevel.INFO))
				.childHandler(new ChannelInitializer<SocketChannel>() {

					@Override
					protected void initChannel(SocketChannel ch) throws Exception {
						ChannelPipeline pipeline = ch.pipeline();
						
						//因为基于http协议,所以我们使用http的编码和解码器
						pipeline.addLast(new HttpServerCodec());
						
						//是以块方式写的,添加ChunkedWriteHandler处理器
						//chunkedWriteHandler是处理大数据传输的,不会占用大内存或导致内存溢出问题,它会维护管理大文件传输过程中时复杂的状态
						pipeline.addLast(new ChunkedWriteHandler());
						
						/**
						 * 说明:
						 * 1. http数据在传输过程中是分段的,HttpObjectAggregator就是可以将多个段聚合起来
						 * 2. 这就是为什么,当浏览器发送大量数据时,就会发送多次http请求
						 */
						pipeline.addLast(new HttpObjectAggregator(10240));
						
						/**
						 * 说明:
						 * 1. 对应WebSocket,它的数据是以帧(frame)形式传递
						 * 2. 可以看到WebSocketFrame下面有六个子类
						 * 3. 浏览器请求时:ws://localhost:7000/hello 表示请求的uri
						 * 4. WebSocketServerProtocolHandler核心功能是将http协议升级为ws协议,保持长连接
						 */
						pipeline.addLast(new WebSocketServerProtocolHandler("/hello"));
						
						//自定义的handler,处理业务逻辑
						pipeline.addLast(new MyTextWebSocketFrameHandler());
						
						
					}
				});
			
			//启动服务器
			ChannelFuture cf = server.bind(7000).sync();
			cf.channel().closeFuture().sync();
			
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			bossGroup.shutdownGracefully();
			workerGroup.shutdownGracefully();
		}
	}
}

2、MyTextWebSocketFrameHandler.java

package netty.websocket;

import java.time.LocalDateTime;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;

/**
 * 这里TextWebSocketFrame类型,表示一个文本帧(frame)
 * @author user
 *
 */
public class MyTextWebSocketFrameHandler extends SimpleChannelInboundHandler<TextWebSocketFrame>{

	@Override
	protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
		
		System.out.println("服务器端收到消息:" + msg.text());
		
		//回复浏览器
		ctx.channel().writeAndFlush(new TextWebSocketFrame("服务器时间:" + LocalDateTime.now() + " " + msg.text()));
		
		
	}

	//当web客户端连接后,触发方法
	@Override
	public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
		//id表示唯一的值,LongText是唯一的,ShortText不是唯一的
		System.out.println("handlerAdded被调用" + ctx.channel().id().asLongText());
		System.out.println("handlerAdded被调用" + ctx.channel().id().asShortText());
	}
	
	@Override
	public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
		System.out.println("handlerRemoved被调用" + ctx.channel().id().asLongText());
	}
	
	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		System.out.println("异常发生 " + cause.getMessage());
		ctx.close(); //关闭通道连接
	}
}

三、客户端

1、hello.html

<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<title>hello</title>
<style>
	
</style>
</head>

<body>
	<form onsubmit="return false">
		<textarea name="message" style="height:300px; width: 300px"></textarea>
		
		<input type="button" value="发送消息" onclick="send(this.form.message.value)" />
		
		<textarea id="responseText" style="height:300px; width: 300px"></textarea>
		
		<input type="button" value="清空内容" onclick="document.getElementById('responseText').value=''">
	</form>
</body>
</html>

<script>
	//也可使用SockJS库

	var socket;
	//判断当前浏览器是否支持WebSocket
	if(window.WebSocket) {
		//go on
		socket = new WebSocket("ws://localhost:7000/hello");
		
		//相当于channelRead0,event收到服务器端回送的消息
		socket.onmessage = function(event) {
			var rt = document.getElementById('responseText');
			rt.value = rt.value + "\n" + event.data;
			
		}
		
		//相当于连接开启(感知到连接开启)
		socket.onopen = function(event) {
			var rt = document.getElementById('responseText');
			rt.value = "连接开启了...";
			
		}
		
		//连接关闭(感知到连接关闭)
		socket.onclose = function(event) {
			var rt = document.getElementById('responseText');
			rt.value = rt.value + "\n" + "连接关闭了...";
			
		}
	} else {
		alert("当前浏览器不支持WebSocket编程");
	}
	
	//发送消息到服务器
	function send(message) {
		if(!window.socket){ //判断socket是否创建好
			return;
			
		}
		
		if(socket.readyState == WebSocket.OPEN) {
			//通过socket发送消息
			socket.send(message);
		} else {
			alert("连接未开启...");
		}
	}
</script>

四、测试

1、启动服务端
2、打开测试页面
3、发送数据
4、关闭测试页面

14:40:27.359 [main] DEBUG io.netty.util.internal.logging.InternalLoggerFactory - Using SLF4J as the default logging framework
14:40:27.366 [main] DEBUG io.netty.channel.MultithreadEventLoopGroup - -Dio.netty.eventLoopThreads: 16
14:40:27.392 [main] DEBUG io.netty.util.internal.InternalThreadLocalMap - -Dio.netty.threadLocalMap.stringBuilder.initialSize: 1024
14:40:27.392 [main] DEBUG io.netty.util.internal.InternalThreadLocalMap - -Dio.netty.threadLocalMap.stringBuilder.maxSize: 4096
14:40:27.405 [main] DEBUG io.netty.channel.nio.NioEventLoop - -Dio.netty.noKeySetOptimization: false
14:40:27.405 [main] DEBUG io.netty.channel.nio.NioEventLoop - -Dio.netty.selectorAutoRebuildThreshold: 512
14:40:27.428 [main] DEBUG io.netty.util.internal.PlatformDependent - Platform: Windows
14:40:27.431 [main] DEBUG io.netty.util.internal.PlatformDependent0 - -Dio.netty.noUnsafe: false
14:40:27.432 [main] DEBUG io.netty.util.internal.PlatformDependent0 - Java version: 8
14:40:27.434 [main] DEBUG io.netty.util.internal.PlatformDependent0 - sun.misc.Unsafe.theUnsafe: available
14:40:27.435 [main] DEBUG io.netty.util.internal.PlatformDependent0 - sun.misc.Unsafe.copyMemory: available
14:40:27.436 [main] DEBUG io.netty.util.internal.PlatformDependent0 - java.nio.Buffer.address: available
14:40:27.436 [main] DEBUG io.netty.util.internal.PlatformDependent0 - direct buffer constructor: available
14:40:27.438 [main] DEBUG io.netty.util.internal.PlatformDependent0 - java.nio.Bits.unaligned: available, true
14:40:27.438 [main] DEBUG io.netty.util.internal.PlatformDependent0 - jdk.internal.misc.Unsafe.allocateUninitializedArray(int): unavailable prior to Java9
14:40:27.438 [main] DEBUG io.netty.util.internal.PlatformDependent0 - java.nio.DirectByteBuffer.<init>(long, int): available
14:40:27.438 [main] DEBUG io.netty.util.internal.PlatformDependent - sun.misc.Unsafe: available
14:40:27.439 [main] DEBUG io.netty.util.internal.PlatformDependent - -Dio.netty.tmpdir: C:\Users\sjcui\AppData\Local\Temp (java.io.tmpdir)
14:40:27.439 [main] DEBUG io.netty.util.internal.PlatformDependent - -Dio.netty.bitMode: 64 (sun.arch.data.model)
14:40:27.442 [main] DEBUG io.netty.util.internal.PlatformDependent - -Dio.netty.maxDirectMemory: 3767533568 bytes
14:40:27.442 [main] DEBUG io.netty.util.internal.PlatformDependent - -Dio.netty.uninitializedArrayAllocationThreshold: -1
14:40:27.444 [main] DEBUG io.netty.util.internal.CleanerJava6 - java.nio.ByteBuffer.cleaner(): available
14:40:27.444 [main] DEBUG io.netty.util.internal.PlatformDependent - -Dio.netty.noPreferDirect: false
14:40:27.463 [main] DEBUG io.netty.util.internal.PlatformDependent - org.jctools-core.MpscChunkedArrayQueue: available
14:40:27.922 [main] DEBUG io.netty.channel.DefaultChannelId - -Dio.netty.processId: 14316 (auto-detected)
14:40:27.924 [main] DEBUG io.netty.util.NetUtil - -Djava.net.preferIPv4Stack: false
14:40:27.924 [main] DEBUG io.netty.util.NetUtil - -Djava.net.preferIPv6Addresses: false
14:40:28.292 [main] DEBUG io.netty.util.NetUtil - Loopback interface: lo (Software Loopback Interface 1, 127.0.0.1)
14:40:28.293 [main] DEBUG io.netty.util.NetUtil - Failed to get SOMAXCONN from sysctl and file \proc\sys\net\core\somaxconn. Default: 200
14:40:28.695 [main] DEBUG io.netty.channel.DefaultChannelId - -Dio.netty.machineId: 00:50:56:ff:fe:c0:00:01 (auto-detected)
14:40:28.709 [main] DEBUG io.netty.util.ResourceLeakDetector - -Dio.netty.leakDetection.level: simple
14:40:28.709 [main] DEBUG io.netty.util.ResourceLeakDetector - -Dio.netty.leakDetection.targetRecords: 4
14:40:28.742 [main] DEBUG io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.numHeapArenas: 16
14:40:28.742 [main] DEBUG io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.numDirectArenas: 16
14:40:28.742 [main] DEBUG io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.pageSize: 8192
14:40:28.742 [main] DEBUG io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.maxOrder: 11
14:40:28.743 [main] DEBUG io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.chunkSize: 16777216
14:40:28.743 [main] DEBUG io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.tinyCacheSize: 512
14:40:28.743 [main] DEBUG io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.smallCacheSize: 256
14:40:28.743 [main] DEBUG io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.normalCacheSize: 64
14:40:28.743 [main] DEBUG io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.maxCachedBufferCapacity: 32768
14:40:28.743 [main] DEBUG io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.cacheTrimInterval: 8192
14:40:28.744 [main] DEBUG io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.cacheTrimIntervalMillis: 0
14:40:28.744 [main] DEBUG io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.useCacheForAllThreads: true
14:40:28.744 [main] DEBUG io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.maxCachedByteBuffersPerChunk: 1023
14:40:28.755 [main] DEBUG io.netty.buffer.ByteBufUtil - -Dio.netty.allocator.type: pooled
14:40:28.756 [main] DEBUG io.netty.buffer.ByteBufUtil - -Dio.netty.threadLocalDirectBufferSize: 0
14:40:28.756 [main] DEBUG io.netty.buffer.ByteBufUtil - -Dio.netty.maxThreadLocalCharBufferSize: 16384
14:40:28.788 [nioEventLoopGroup-2-1] INFO io.netty.handler.logging.LoggingHandler - [id: 0x4552488b] REGISTERED
14:40:28.790 [nioEventLoopGroup-2-1] INFO io.netty.handler.logging.LoggingHandler - [id: 0x4552488b] BIND: 0.0.0.0/0.0.0.0:7000
14:40:28.795 [nioEventLoopGroup-2-1] INFO io.netty.handler.logging.LoggingHandler - [id: 0x4552488b, L:/0:0:0:0:0:0:0:0:7000] ACTIVE
14:40:33.520 [nioEventLoopGroup-2-1] INFO io.netty.handler.logging.LoggingHandler - [id: 0x4552488b, L:/0:0:0:0:0:0:0:0:7000] READ: [id: 0x625a8961, L:/0:0:0:0:0:0:0:1:7000 - R:/0:0:0:0:0:0:0:1:53593]
14:40:33.522 [nioEventLoopGroup-2-1] INFO io.netty.handler.logging.LoggingHandler - [id: 0x4552488b, L:/0:0:0:0:0:0:0:0:7000] READ COMPLETE
14:40:33.554 [nioEventLoopGroup-3-1] DEBUG io.netty.buffer.AbstractByteBuf - -Dio.netty.buffer.checkAccessible: true
14:40:33.554 [nioEventLoopGroup-3-1] DEBUG io.netty.buffer.AbstractByteBuf - -Dio.netty.buffer.checkBounds: true
14:40:33.558 [nioEventLoopGroup-3-1] DEBUG io.netty.util.ResourceLeakDetectorFactory - Loaded default ResourceLeakDetector: io.netty.util.ResourceLeakDetector@20f9f5d4
handlerAdded被调用005056fffec00001-000037ec-00000001-371268f177830cec-625a8961
handlerAdded被调用625a8961
14:40:33.626 [nioEventLoopGroup-3-1] DEBUG io.netty.util.Recycler - -Dio.netty.recycler.maxCapacityPerThread: 4096
14:40:33.626 [nioEventLoopGroup-3-1] DEBUG io.netty.util.Recycler - -Dio.netty.recycler.maxSharedCapacityFactor: 2
14:40:33.626 [nioEventLoopGroup-3-1] DEBUG io.netty.util.Recycler - -Dio.netty.recycler.linkCapacity: 16
14:40:33.626 [nioEventLoopGroup-3-1] DEBUG io.netty.util.Recycler - -Dio.netty.recycler.ratio: 8
14:40:33.666 [nioEventLoopGroup-3-1] DEBUG io.netty.handler.codec.http.websocketx.WebSocketServerHandshaker - [id: 0x625a8961, L:/0:0:0:0:0:0:0:1:7000 - R:/0:0:0:0:0:0:0:1:53593] WebSocket version V13 server handshake
14:40:33.669 [nioEventLoopGroup-3-1] DEBUG io.netty.handler.codec.http.websocketx.WebSocketServerHandshaker - WebSocket version 13 server handshake key: r7/aFvcPnHHmSbIV9YikkQ==, response: ZFKKX75rdh34EPuWz+qzZqXX3PQ=
服务器端收到消息:你好吗
服务器端收到消息:冬天冷,吃火锅
handlerRemoved被调用005056fffec00001-000037ec-00000001-371268f177830cec-625a8961

五、要点

1、HttpObjectAggregator
说明:
1) http数据在传输过程中是分段的,HttpObjectAggregator就是可以将多个段聚合起来
2) 这就是为什么,当浏览器发送大量数据时,就会发送多次http请求

2、WebSocketServerProtocolHandler
说明:
1) 对应WebSocket,它的数据是以帧(frame)形式传递
2) 可以看到WebSocketFrame下面有六个子类
3) 浏览器请求时:ws://localhost:7000/hello 表示请求的uri
4) WebSocketServerProtocolHandler核心功能是将http协议升级为ws协议,保持长连接
5) 是通过一个状态码101来切换的

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/145033.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Go第 10 章 :面向对象编程(上)

Go第 10 章 &#xff1a;面向对象编程(上) 10.1 结构体 10.1.1 看一个问题 10.1.2 使用现有技术解决 单独的定义变量解决 代码演示&#xff1a; 使用数组解决 代码演示&#xff1a; 10.1.3 现有技术解决的缺点分析 使用变量或者数组来解决养猫的问题&#xff0c;不利于数…

AI与艺术——图像生成网络经典算法

生成模型是一种训练模型进行无监督学习的模型&#xff0c;即&#xff0c;给模型一组数据&#xff0c;希望从数据中学习到信息后的模型能够生成一组和训练集尽可能相近的数据。图像生成&#xff08;Image generation&#xff0c;IG&#xff09;则是指从现有数据集生成新的图像的…

代码块和执行顺序

1、什么是代码块 使用{}定义的一段代码我们称之为代码块 一般分为&#xff1a; 1. 普通代码块 2. 实例代码块 3. 静态代码块 2、普通代码块 定义在方法中的代码块&#xff0c;可以用于限定局部变量的生命周期。 public class Main{public static void main(String[] args) {{…

【分享】百度更喜欢自媒体站,9种方法让你轻松玩转微博营销

微博营销是指通过微博平台为商家、个人等创造价值而执行的一种营销方式。 以下九种方式&#xff0c;你一定要学习一下&#xff0c;视频最后&#xff0c;有干货。 1、注重价值的传递 企业博客经营者首先要改变观念——企业微博的“索取”与“给予”之分&#xff0c;企业微博是…

2023你冲不冲,冲冲冲冲~~

前言&#xff1a;\textcolor{Green}{前言&#xff1a;}前言&#xff1a; &#x1f49e;2022已经成为历史&#xff0c;趁着还没有遗忘来看看我这一年发生了什么事。 在这一年中有很多的收获也有一些遗憾&#xff0c;但是这不会让我停下来&#xff0c;未来的2023又会发生什么&…

ArcGIS 中的 7 种主要地理处理工具

地理处理工具可以提高人们的工作效率&#xff0c;并且这些工具可以批量应用于编辑&#xff0c;例如应用于所有功能或所有选定功能。本文将和大家一起探讨 7 个 ArcGIS 中的地理处理工具。 地理处理的用途 模型中的地理处理工具可解决许多空间完整性的问题&#xff0c;应用地理…

分布式缓存系统 Ignite、Hazelcast、Ehcache

一、Apache IgniteApache Ignite是一个以内存为中心的分布式数据库、缓存和处理平台&#xff0c;支持事务、分析以及流式负载&#xff0c;可以在PB级数据上享有内存级的性能。如果禁用原生持久化&#xff0c;Ignite就是一个分布式缓存&#xff0c;它实现了JCache规范&#xff0…

深入解析Linux虚拟化KVM-Qemu分析之中断虚拟化

说明&#xff1a; KVM版本&#xff1a;5.9.1QEMU版本&#xff1a;5.0.0工具&#xff1a;Source Insight 3.5&#xff0c; Visio 1. 概述 本文会将ARM GICv2中断虚拟化的总体框架和流程讲清楚&#xff0c;这个曾经困扰我好几天的问题在被捋清的那一刻&#xff0c;让我有点每有…

JsonUtility读写json简单应用

使用Unity提供的JsonUtility 简单封装了一个将数据以json格式存储到本地,方便数据读写的案例;一共三个脚本:MyJsonRW : 提供SaveJsonData和LoadWithJson,实现数据与json的转换和读写;TestData : 测试数据类,列举了部分常用的简单数据类型(不过有坑,后面会说);MyJsonExample : 例…

新诤信张强—兔年展望|2023-2025:看风的必不撒种、望云的必不收割

新诤信知识产权服务股份有限公司 首席技术官 张强 “看风的必不撒种、望云的必不收割”出自古老的犹太智慧书《传道书》&#xff0c;意思是&#xff0c;如果要等刮风才撒种&#xff0c;快要下雨才收割&#xff0c;一切就都来不及了。元宇宙的发展布局&#xff0c;也是这个道理…

基础算法[四]之图的那些事儿

文章目录前言图的表示邻接矩阵邻接表结构存储遍历路径搜索多源最短路问题问题描述Floyd实现模板单源最短路径问题Dijkstra算法朴素版本堆优化邻接表python实现Bellman-Ford 算法实现SPFA 算法实现python 版本判断负环小结最小生成树Prim算法Kruskra算法实现python版本二分图二分…

网络资源归档标准WARC介绍

WARC---Web ARChive 01 ● WARC格式概述 ● WARC&#xff08;Web ARChive&#xff09;格式是网络资源存档中使用的常见文件格式&#xff0c;全称为Web Archive File Format&#xff0c;由互联网保存联盟&#xff08;International Internet Preservation Consortium&#xff0…

2023年1月6日星期五-PPP/BPP相关学习

独立均匀分布matlab的rand指令可以帮助我们生成[0,1]的均匀分布的数据,这样,如果我们想要[a,b]的分布数据,只需要a+(b-a)*rand就可以了。 [a,b] 均值,标准差 均值

Git 常用基本命令

文章目录基本命令仓库相关命令提交相关命令分支相关命令tag 相关命令撤销相关命令IDEA Git 相关操作仓库相关操作提交相关操作提交代码步骤分支相关操作tag 相关操作撤销相关操作参考资料本文主要介绍 Git 常用的基本命令。基本命令 仓库相关命令 创建新仓库 git init克隆项…

C#,图像二值化(15)——全局阈值的一维最大熵(1D maxent)算法及源程序

1、最大熵&#xff08;maxent&#xff09; 最大熵&#xff08;maxent&#xff09;方法植根于信息理论&#xff0c;并已成功应用于许多领域&#xff0c;包括物理学和自然语言处理。它创建了一个模型&#xff0c;该模型最好地解释了可用数据&#xff0c;但有一个约束&#xff0c…

甘特图中的依赖关系是什么?

依赖关系是甘特图应用中常见的概念。甘特图依赖关系也称为任务依赖关系&#xff0c;它是指项目任务之间的关系&#xff0c;需要按特定顺序执行一个或多个任务才能完成某项任务&#xff0c;依赖于完成前一任务的任务是后继任务&#xff0c;而其依赖的任务是前导任务。依赖关系一…

字典特征提取、文本特征提取、jieba分词处理、tf-idf文本特征提取概念及代码实现

一、特征提取 特征提取&#xff1a;将任意数据&#xff08;如文本或图像&#xff09;转换为可用于机器学习的数字特征&#xff0c;特征值化是为了计算机更好的去理解数据 特征提取api&#xff1a;sklearn.feature_extraction 特征提取分类 字典特征提取(特征离散化)文本特征…

小蓝本 第一本《因式分解技巧》 第九章 待定系数法 笔记 (第九天)

小蓝本 第一本《因式分解技巧》 第九章 待定系数法 笔记 &#xff08;第九天&#xff09;前言待定系数法二次因式一次因式分解缺陷二次因式分解方法注意既约的情况拓展习题9题目题解前言 已经进行了9天&#xff0c;第八章有一点烧脑&#xff0c;但感觉还是很不错的&#xff0c…

Python类继承:深入了解

引言 前段时间刚好学习了&#xff0c;C中类方面的内容&#xff0c;发现和Python中有许多不同之处&#xff0c;尤其是在类的继承这一块&#xff0c;今天有空&#xff0c;正好写篇文章&#xff0c;记录一下有兴趣的东西&#xff0c;也算是对Python的一个复习。 1 C中的构造析构函…

设计模式学习(二):Observer观察者模式

一、什么是Observer模式在Observer模式中&#xff0c;当观察对象的状态发生变化时&#xff0c;会通知给观察者。Observer模式适用于根据对象状态进行相应处理的场景。简单一句话概况就是&#xff1a;观察者会发送观察对象状态变化的通知。二、Observer模式示例代码下面示例程序…