java秒杀项目总结

article/2025/8/14 5:08:27

java秒杀项目总结

本项目专攻秒杀模块,共分为七个章节

第一章 项目框架搭建

1.Spring Boot环境搭建
2.集成Thymeleaf , Result结果封装

  • 前期前后端并未分离,使用Thymeleaf来获取后台传来的数据
  • Result结果封装可以让代码更规范,成功的时候只传数据,失败的时候传递状态码

3.集成Mybatis+ Druid
4.集成Jedis+ Redis安装+通用缓存Key封装

  • 这里使用的是自己封装的jedis
  • 通用缓存key封装,定义一个接口,过期时间和缓存前缀。抽象类继承接口实现通用缓存名字和过期时间,再有各种key继承抽象类,实现通用缓存

在这里插入图片描述

第二章实现登录功能

1.数据库设计

2.明文密码两次MD5处理
两次加密:

  • 1、当你输入提交到表单使用md5对输入的密码加密

  • 2、当你将表单中的密码插入到数据库时,再对表单的密码加密

  • 为什么两次md5?
    客户端:我们使用密码+固定Salt来形成最终密码
    服务端:将用户输入

3 JSR303参数检验+全局异常处理器
为什么要做JSR303参数检验?
前端的校验只是有效性的校验(手机号输错,密码错误),服务端的校验是防止恶意的用户。
JSR303检验账号是否符合规范标准,@IsMobile自己写的注解

/*
获取表单提交的数据
* 看表单中参数是否能正确传递
* */
public class LoginVo {@NotNull@IsMobileprivate String mobile;@NotNull@Length(min = 32)private String password;。。。}

@Valid注解写在输入参数前面,输入参数对应的类,里面的各项成员变量上面还能加注解约束

@RequestMapping("/do_login")@ResponseBodypublic Result<String> doLogin(HttpServletResponse response,@Valid LoginVo loginVo){log.info(loginVo.toString());//登录String token=miaoshaUserService.login(response,loginVo);return Result.success(token);}

当参数校验返回false即校验失败时,那么就会出现一个BindException异常,为了显示友好就写一个全局异常处理器去拦截这个异常。当然其他的异常也能够被拦截。

怎么实现友好显示的?
当用户登录时,如果后台登录方法查不到用户或者密码不匹配那么就会抛一个全局异常,抛出的这个异常会被我们定义的全局异常处理器拦截,拦截到之后会return一个错误信息,前台ajax就会回调显示这个错误信息,用户能更友好的看到错误信息。

4.分布式Session
背景:分布式集群,多台服务器。客户端第一次请求落在第一台服务器上,第二次请求落在第二台服务器上。那么第二次Session就会丢失。

解决方案: 1.容器原生的Session同步,就是将一台计算机上的Session同步到其他计算机上,这样性能开销大。

2.分布式Session,实际情况中用的比较多。Session并没有存到容器中来而是存到了缓存中,这就是分布式Session。

分布式Session具体实现: 用户登录成功,会生成一个token。token用于生成键,用户信息作为值,将这对键值对存到redis中,然后实例一个Cookie(“token”,token),将这个Cookie写进去写到response中,那么下次这个用户再次发请求就会带着这个Cookie。配置参数解析器就能根据Cookie携带的值到redis中查到用户信息,然后注入到方法的请求参数中。

public String login(HttpServletResponse response, LoginVo loginVo) {。。。。//生成cookieString token= UUIDUtil.uuid();//cookie写到response,session写到redisaddCookie(response,token,user);return token;}
private void addCookie(HttpServletResponse response, String token, MiaoshaUser user) {redisService.set(MiaoshaUserKey.token,token,user);Cookie cookie=new Cookie(COOKI_NAME_TOKEN,token);cookie.setMaxAge(MiaoshaUserKey.token.expireSeconds());cookie.setPath("/");response.addCookie(cookie);}

第三章实现秒杀功能

1.数据库设计
数据库并没有遵循三范式,有冗余,但是冗余是必须的。
2.商品列表页
3.商品详情页
4.订单详情页

秒杀功能:
三步:
判断库存、判断是否已经秒杀到了、减库存下订单(事务)。
卖超:
(1)减库存SQL,加上库存是否小于零的条件。
(2)订单表结构增加唯一索引(用户id和秒杀商品id),防止一个用户下多次单。
(3)减库存这个操作的返回值为1的时候才继续后面的下订单,否则会出现生成的订单数量远远多于卖出商品的数量。

另外还实现了倒计时功能,判断当前是否可以秒杀(就是比较时间的大小):

@RequestMapping("/to_detail/{goodsId}")
public String detail(Model model,MiaoshaUser user,@PathVariable("goodsId")long goodsId) {model.addAttribute("user", user);GoodsVo goods = goodsService.getGoodsVoByGoodsId(goodsId);model.addAttribute("goods", goods);long startAt = goods.getStartDate().getTime();long endAt = goods.getEndDate().getTime();long now = System.currentTimeMillis();int miaoshaStatus = 0;int remainSeconds = 0;if(now < startAt ) {//秒杀还没开始,倒计时miaoshaStatus = 0;remainSeconds = (int)((startAt - now )/1000);}else  if(now > endAt){//秒杀已经结束miaoshaStatus = 2;remainSeconds = -1;}else {//秒杀进行中miaoshaStatus = 1;remainSeconds = 0;}model.addAttribute("miaoshaStatus", miaoshaStatus);model.addAttribute("remainSeconds", remainSeconds);return "goods_detail";
}
function countDown(){var remainSeconds = $("#remainSeconds").val();var timeout;if(remainSeconds > 0){//秒杀还没开始,倒计时$("#buyButton").attr("disabled", true);timeout = setTimeout(function(){$("#countDown").text(remainSeconds - 1);$("#remainSeconds").val(remainSeconds - 1);countDown();},1000);}else if(remainSeconds == 0){//秒杀进行中$("#buyButton").attr("disabled", false);if(timeout){clearTimeout(timeout);}$("#miaoshaTip").html("秒杀进行中");}else{//秒杀已经结束$("#buyButton").attr("disabled", true);$("#miaoshaTip").html("秒杀已经结束");}
}

第四章JMeter压测

1, JMeter入门
2,自定义变量模拟多用户
生成500个用户的token和密码保存到一个文件当中,压测时加载文件模拟多用户

第五章页面优化技术

大并发的瓶颈就是数据库。应对并发最有效的就是缓存

1.页面缓存+ URL缓存+对象缓存

页面缓存适用场景:适合于变化不大的场景,比如商品列表。实际项目中商品列表可能会分页,不可能每页都缓存,只是缓存前两页。

页面缓存:第一次请求过来就将渲染好的页面存到redis中,下次请求就直接从redis中取页面。

页面缓存并不是将所有页面都缓存,而是将变化不大的,页面缓存和URL缓存都设置过期时间(60s),而对象缓存根据token获取用户,且对象缓存永久有效,

 @RequestMapping(value = "/to_list",produces = "text/html")@ResponseBodypublic String list(HttpServletRequest request, HttpServletResponse response,Model model, MiaoshaUser user) {model.addAttribute("user", user);//1、先取缓存String html = redisService.get(GoodsKey.getGoodsList, "", String.class);if(!StringUtils.isEmpty(html)){//缓存不为空return html;}List<GoodsVo> goodsList = goodsService.listGoodsVo();model.addAttribute("goodsList",goodsList);//return "goods_list";//缓存为空IWebContext ctx=new WebContext(request,response,request.getServletContext(),request.getLocale(),model.asMap());//手动渲染html=thymeleafViewResolver.getTemplateEngine().process("goods_list", ctx);if(!StringUtils.isEmpty(html)){redisService.set(GoodsKey.getGoodsList,"",html); //存入缓存}return html;}

对象缓存:实现分布式Session就是,将用户对象缓存到redis中。

2.页面静态化,前后端分离

页面静态化:就是浏览器将HTML页面存在客户端,通过ajax获取数据拿到客户端在渲染页面。(这样就不用下载页面了,只需要下载动态数据就好了。

//商品列表页跳转到商品详情页面,goods_detail.htm放到静态文件夹里面
<td><a th:href="'/goods_detail.htm?goodsId='+${goods.id}">详情</a></td>//静态页面goods_detail.htm,里面的js
$(function(){//countDown();getDetail();
});function getDetail(){//这个方法获取请求传过来的参数var goodsId = g_getQueryString("goodsId");$.ajax({url:"/goods/detail/"+goodsId,type:"GET",success:function(data){if(data.code == 0){//渲染页面的方法render(data.data);}else{layer.msg(data.msg);}},error:function(){layer.msg("客户端请求有误");}});
}

3.静态资源优化

注意:js文件在浏览器本地会有缓存,如果改动了js文件,下次请求加载的还是本地缓存的js文件,导致前端代码跑不通。解决方法引入js文件的链接后面加一个版本参数。代码跑不通就debug,查看数据流是不是对的,这样能尽快锁定哪里出了问题。

第六章接口优化

总目标:减少数据库的访问量。

如何对他做优化?

减少对数据库的访问, redis和mq

把订单同步下单改为异步

好处:库存不足后,后面的请求对数据库基本没有压力

异步下单,既不是返回成功,也不是返回失败,而是返回排队中

1 Redis预减库存减少数据库访问

容器初始化的时候将秒杀商品的库存和内存标记加载到Redis中,前面来的请求将redis缓存的库存减完后,后面的请求过来直接返回秒杀结束。

2.内存标记减少Redis访问

比如说前面10个请求已经将redis中缓存的库存减到0了,那么后面的请求会继续将redis中的库存减为负数,显然后面的请求将redis中的库存减成负数是多余的,而且还增加了redis的访问量。那么这里就做一个内存标记,缓存中库存大于零的时候内存标记为false,当缓存中的库存减为0时内存标记就为true。当为false时请求能往下走,反之直接返回秒杀结束。

3. RabbitMQ队列缓冲,异步下单,增强用户体验

服务端异步的请求出队,将订单写到缓存,用户去查找,看成功还是失败

创建秒杀信息类

MiaoshaMessage(用户信息和秒杀商品id)

将信息类发送出去

Direct交换机,将信息类对象转为字符串,进队

接收者:将string还原为对象

从信息类里面拿用户信息和商品id后

入队成功的时候去轮询

怎么做轮询?判断一个用户有没有秒杀到商品

获取秒杀结果,调用方法(如果秒杀订单不为空,成功,等于空,两种情况,失败和排队中,无法辨别,这是根据标记来判断是不是因为库存不足导致的失败)

生成库存不足标记的方法,往redis里面设置一个值,新建miaoshakey,永久生效

如果redis里面存在这个key,就说明卖完了

4. RabbitMQ安装与Spring Boot集成

package com.imooc.miaosha.controller;import com.imooc.miaosha.access.AccessLimit;
import com.imooc.miaosha.domain.MiaoshaMessage;
import com.imooc.miaosha.rabbitmq.MQSender;
import com.imooc.miaosha.redis.*;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;import com.imooc.miaosha.domain.MiaoshaOrder;
import com.imooc.miaosha.domain.MiaoshaUser;
import com.imooc.miaosha.domain.OrderInfo;
import com.imooc.miaosha.result.CodeMsg;
import com.imooc.miaosha.result.Result;
import com.imooc.miaosha.service.GoodsService;
import com.imooc.miaosha.service.MiaoshaService;
import com.imooc.miaosha.service.MiaoshaUserService;
import com.imooc.miaosha.service.OrderService;
import com.imooc.miaosha.vo.GoodsVo;import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;@Controller
@RequestMapping("/miaosha")
public class MiaoshaController implements InitializingBean {@AutowiredMiaoshaUserService userService;@AutowiredRedisService redisService;@AutowiredGoodsService goodsService;@AutowiredOrderService orderService;@AutowiredMiaoshaService miaoshaService;@AutowiredMQSender sender;private HashMap<Long,Boolean> localOverMap=new HashMap<Long,Boolean>();//系统初始化时,将库存加载进缓存,并将秒杀商品的状态标记为false@Overridepublic void afterPropertiesSet() throws Exception {List<GoodsVo> goodsList = goodsService.listGoodsVo();//判断一下商品列表是否为空if(goodsList==null){return;}for (GoodsVo goods : goodsList) {Integer stockCount = goods.getStockCount();redisService.set(GoodsKey.getMiaoshaGoodsStock,""+goods.getId(),stockCount);localOverMap.put(goods.getId(),false);}}/*** QPS:* 1000 * 10* */@RequestMapping(value="/{path}/do_miaosha", method=RequestMethod.POST)@ResponseBodypublic Result<Integer> miaosha(Model model, MiaoshaUser user,@RequestParam("goodsId")long goodsId,@PathVariable("path")String path) {model.addAttribute("user", user);if(user == null) {return Result.error(CodeMsg.SESSION_ERROR);}//校验秒杀pathboolean check=miaoshaService.checkPath(user,goodsId,path);if(!check){return Result.error(CodeMsg.REQUEST_ERROR);}//先判断一下该秒杀商品的状态(内存标记,减少redis访问)Boolean b = localOverMap.get(goodsId);if(b){ //说明缓存中的商品已经减为0return  Result.error(CodeMsg.MIAO_SHA_OVER);}//收到请求,减少缓存中的库存long stock = redisService.decr(GoodsKey.getMiaoshaGoodsStock, "" + goodsId);if(stock<0){localOverMap.put(goodsId,true);return Result.error(CodeMsg.MIAO_SHA_OVER);}//判断是否已经秒杀到了MiaoshaOrder order = orderService.getMiaoshaOrderByUserIdGoodsId(user.getId(), goodsId);if(order != null) {return Result.error(CodeMsg.REPEATE_MIAOSHA);}//入队MiaoshaMessage miaoshaMessage=new MiaoshaMessage();miaoshaMessage.setUser(user);miaoshaMessage.setGoodsId(goodsId);sender.sendMiaoshaMessage(miaoshaMessage);return Result.success(0);//0代表排队中/*//判断库存GoodsVo goods = goodsService.getGoodsVoById(goodsId);//10个商品,req1 req2int stock = goods.getStockCount();if(stock <= 0) {return Result.error(CodeMsg.MIAO_SHA_OVER);}//判断是否已经秒杀到了MiaoshaOrder order = orderService.getMiaoshaOrderByUserIdGoodsId(user.getId(), goodsId);if(order != null) {return Result.error(CodeMsg.REPEATE_MIAOSHA);}//减库存 下订单 写入秒杀订单OrderInfo orderInfo = miaoshaService.miaosha(user, goods);return Result.success(orderInfo);*/}/** 返回orderId :成功* -1:秒杀失败* 0:排队中* */@RequestMapping(value="/result", method=RequestMethod.GET)@ResponseBodypublic Result<Long> miaoshaResult(Model model,MiaoshaUser user,@RequestParam("goodsId")long goodsId) {model.addAttribute("user", user);if (user == null) {return Result.error(CodeMsg.SESSION_ERROR);}//获取秒杀结果long result=miaoshaService.getMiaoshaResult(user.getId(), goodsId);return Result.success(result);}}
}

缺陷:库存在缓存中的key是永不过期的,当你该库存的时候,需要将缓存中的key先删除

/*** orderId:成功* -1:秒杀失败* 0: 排队中* */public long getMiaoshaResult(Long userId, long goodsId) {MiaoshaOrder order = orderService.getMiaoshaOrderByUserIdGoodsId(userId, goodsId);if(order==null){//如果订单为空,有两种状态,排队和库存不足导致的失败,根据标记状态来判断boolean isOver = getGoodsOver(goodsId);if(isOver){return -1;}else{return 0;}}else {return order.getOrderId();}}

在做减库存操作时,如果减库存失败,在缓存中添加一个key。因此在去查询秒杀结果时,如果订单为空(有两种状态,排队和库存不足导致的失败)再根据标记状态(缓存中有没有对应的key)来判断是那种个情况,如果有说明是库存不足,如果没有说明是正在排队中。

5.访问Nginx水平扩展
系统的负载均衡nginx,如果前面没有加缓存,单群加服务器没有作用,全都落在db上,db并发是有限的,再加服务器也是没用的,我们基于带有有良好的扩展性。

第七章安全优化

在这里插入图片描述

防止恶意用户刷我们的接口,秒杀开始之前不知道访问那个地址,比较安全

验证码作用: 1、防止机器人或工具刷
2、没有验证码,大家只是点击鼠标请求集中,数据库压力大 (有的话消耗时间,将瞬间的并发量分散到10s开)

接口限流防刷: 系统本身容量有限,防止用户恶意刷接口,在某个时间端内限制用户访问的次数。

1.秒杀接口地址隐藏

思路:秒杀开始之前,先去请求接口获取秒杀地址

1.接口改造,带上PathVariable参数
2.添加生成地址的接口
3.秒杀收到请求,先验证PathVariable

//获取动态秒杀路径
function getMiaoshaPath(){var goodsId = $("#goodsId").val();g_showLoading();$.ajax({url:"/miaosha/path",type:"GET",data:{goodsId:goodsId,verifyCode:$("#verifyCode").val()},success:function(data){if(data.code == 0){var path = data.data;doMiaosha(path);}else{layer.msg(data.msg);}},error:function(){layer.msg("客户端请求有误");}});
}@AccessLimit(seconds = 10,maxCount = 5,needLogin = true)@RequestMapping(value="/path", method=RequestMethod.GET)@ResponseBodypublic Result<String> getmiaoshaPath(HttpServletRequest request,MiaoshaUser user,@RequestParam("goodsId")long goodsId,@RequestParam(value = "verifyCode",defaultValue = "0")int verifyCode) {if (user == null) {return Result.error(CodeMsg.SESSION_ERROR);}//判断验证码boolean check=miaoshaService.checkverifyCode(user,goodsId,verifyCode);if(!check){return Result.error(CodeMsg.REQUEST_ILLEGAL);}//生成秒杀pathString path= miaoshaService.setmiaoshaPath(user,goodsId);return Result.success(path);}

前端拿到path后在调用秒杀接口
秒杀要接受path,校验
怎么验证? get缓存redis里面的key和传过来的path是否相等

public String setmiaoshaPath(MiaoshaUser user, long goodsId) {if(user==null|| goodsId<=0){return null;}String path= MD5Util.md5(UUIDUtil.uuid()+"123456");//将秒杀path存到redis,有效期60sredisService.set(MiaoshaKey.getMiaoshaPath,""+user.getId()+"_"+goodsId,path);return path;}public boolean checkPath(MiaoshaUser user, long goodsId,String path) {if(path==null || user==null){return false;}//从redis里面根据key取pathString str = redisService.get(MiaoshaKey.getMiaoshaPath, "" + user.getId() + "_" + goodsId, String.class);return path.equals(str);}

2.数学公式验证码

public boolean checkverifyCode(MiaoshaUser user, long goodsId, int verifyCode) {if(user == null || goodsId <=0) {return false;}//从缓存中取验证码和输入的比较Integer OldCode = redisService.get(MiaoshaKey.getMiaoshaVerifyCode, user.getId() + "," + goodsId, Integer.class);if(OldCode==null || OldCode-verifyCode!=0){return false;}//验证之后,将缓存中的验证码删除redisService.delete(MiaoshaKey.getMiaoshaVerifyCode,user.getId() + "," + goodsId);return true;}

3.接口防刷

需求:设置10秒钟内,最多请求5次,超过这个次数就算为非法请求,提示访问太频繁。
设计:使用拦截器,将这个功能与业务代码分离,能让其他方法形成复用。

获取注解上的时间,设置为缓存key的过期时间。去缓存中获取已访问次数,如果缓存为空的话,说明第一次访问,设置缓存并将次数设为1。之后在不超过最大访问次数的基础上,每次访问缓存中的数加1.

 @Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {if(handler instanceof HandlerMethod){//先获取用户MiaoshaUser user=getUser(request,response);//将获取的用户存起来,方便后面的调用传递UserContext.setUser(user);HandlerMethod hm=(HandlerMethod)handler;//获取方法上的注解AccessLimit accessLimit = hm.getMethodAnnotation(AccessLimit.class);if (accessLimit==null){return true; //没有注解}//有注解获取注解的参数int seconds=accessLimit.seconds();int maxCount=accessLimit.maxCount();boolean needLogin=accessLimit.needLogin();//获取keyString key=request.getRequestURI();//如果需要登陆if(needLogin){if(user==null){//提示错误信息render(response,CodeMsg.SESSION_ERROR);return false;}//key需要加上用户idkey+="_"+user.getId();}else {//如果不需要登陆什么都不做}//查询访问次数AccessKey ak= AccessKey.withExpire(seconds);Integer count = redisService.get(ak, key, Integer.class);if(count==null){//说明是第一次访问redisService.set(ak,key,1);}else if(count<maxCount){redisService.incr(ak,key);}else {//大于次数render(response,CodeMsg.ACCESS_ERROR);return false;}}return true;
}

自定义的注解

@Retention(RUNTIME)
@Target(METHOD)
public @interface AccessLimit {int seconds();int maxCount();boolean needLogin() default true;}

http://chatgpt.dhexx.cn/article/QxOXvnVI.shtml

相关文章

Java飞机大战项目

飞机大战游戏是一款十分有趣的射击类小游戏,流畅的画面,高难度的挑战。游戏中,玩家驾驶英雄机,在空中进行战斗。点击并移动自己的英雄机,发射炮弹,打掉敌飞机以及蜜蜂,来获得分数和奖励,打掉一架敌飞机赢得5分,打掉一只蜜蜂赢得1条命或是获得20次双倍火力,如果撞上敌…

Java游戏项目分享

很多小伙伴学习Java&#xff0c;在学习完基础之后都会去找一些项目练手&#xff0c;用来提升自己的技术&#xff0c;在这里&#xff0c;学姐找了一些Java初级练手项目&#xff0c;供小伙伴们用来练手。下面简单介绍一下本次学姐带来的六大Java游戏项目。 Java六大初级练手项目&…

Java项目文件目录结构介绍

Java中有三个比较容易弄混的“文件夹”。 folder 文件夹 &#xff0c;普通的文件夹&#xff0c;IDE不会对他检查source folder 源文件夹 用于存放Java源代码的package java 中的包&#xff0c;必须存放在source folder 下。 src main javawebappresources 文件视图下的东西。…

如何将 Java 项目转换成 Maven 项目

亲测可用&#xff0c;若有疑问请私信 本文内容 Java 项目Maven 项目Java 项目转换成 Maven 项目 本文主要介绍如何将 Java 项目转换成 Maven 项目。首先要明确的是&#xff0c;用 Maven 管理 Java 项目的确方便。它带给你直观的方便是&#xff1a;你不用在网上找 Jar 包&…

Java项目获取路径方法

Java项目中根据相对路径和绝对路径获取文件的方法&#xff08;重要&#xff09; 首先&#xff0c;项目中文件分布情况如下&#xff0c;innerFile.txt位于test.test包下&#xff0c;innerInnerFile.txt位于test.test.inner包下&#xff0c;outterFile.txt位于包的根目录下&…

Java-银行项目

该项目用了三个类Customer,CustomerList,CustomerView。 Customer为实体对象&#xff0c;用来封装用户信息。相当于model。 CustomerList为Customer对象的管理模块&#xff0c;内部用数组管理Customer对象&#xff0c;并提供相应的添加&#xff0c;修改&#xff0c;删除和遍历…

MATLAB箱线图

MATLAB笔记之 作图 箱线图 1、峰值偏度检验 2、相关性作图 MATLAB分析数据相关性实例 3、箱线图看离散程度 如何使用matlab软件进行峰度-偏度检验https://jingyan.baidu.com/article/380abd0ab6f1c75d90192cee.html Matlab图解峰度kurtosis与偏度skewness 原1 Matlab图解峰度…

MATLAB运算符

1. MATLAB运算符 1.1 算术运算符 算术运算符运算法则算术运算符运算法则ABA与B相加(A、B为数值或矩阵)A-BA与B相减(A、B为数值或矩阵)A*BA与B相乘(A、B为数值或矩阵)A.*BA与B相应元素相乘(A、B为相同维度的矩阵)A/BA与B相除(A、B为数值或矩阵)A./BA与B相应元素相除(A、B为相同…

matlab subs函数

在matlab命令行查看subs函数的帮助 subs函数一共有三种使用方法&#xff1a; subs(s,old,new) subs(s,new) subs(s) 点击 subs 的参考页可以看到下面的详细说明: 第一种使用方法 说明&#xff1a;subs函数返回 函数s 的一个拷贝&#xff0c;s函数中的所有old变量都会替换为…

Matlab fprintf

fopen 文件访问类型 仅供学习记录 clc clear %% 01换行输出结果 fprintf([...\n...Matlab可以实现输出换行显示 \n...把你想要输出的内容换行写在[]中 \n...用换行符实现换行 \n...效果就是这样的 \n...]) %% …

数学建模与MATLAB计算之006.MATLAB中的条件语句

006.MATLAB中的条件语句 文章目录 006.MATLAB中的条件语句一、单分支if条件语句二、双分支if条件语句三、多分支if条件语句四、switch语句五、try语句 选择结构是根据给定的条件成立或不成立&#xff0c;分别执行不同的语句。 MATLAB 用于实现选择结构的语句有** if语句、swit…

matlab源码说明

目录 1.MATLAB概述 2.MATLAB程序使用几个常规注意实现 2.1.运行过程可能出现Out of Memory的问题解决办法 2.2.保存大于2G的数据 2.3.程序运行方法 1.MATLAB概述 Matlab经过不断的发展和完善,如今已成为覆盖多个学科,是具有超强数值计算能力和仿真分析能力的软件。…

Matlab逻辑运算符/与/或/非/异或/all/any

Logical()是非0则1&#xff0c;如果数字是非零&#xff0c;输出为1&#xff0c;数字为零&#xff0c;输出为0 与或非 与&#xff1a;&&#xff0c;有0则0&#xff0c;全1则1 或&#xff1a;|&#xff0c;有1则1&#xff0c;全0则0 非&#xff1a;~&#xff0c;0变1&#…

转行学计算机测试,转行软件测试后悔了

原标题&#xff1a;转行软件测试后悔了 很多时候&#xff0c;都在想当初做的转行软件测试行业的决定是对的吗&#xff1f;现在后悔还来得及吗&#xff1f; 其实我刚参加织雀教育的培训班一个月&#xff0c;各方面都学起来感觉有难度&#xff0c;毕竟是零基础转行的&#xff0c;…

【小结】南京大学软件工程专硕2021二战小结

今年的考研也告一段落了&#xff0c;从导师那里听到了一些风声&#xff0c;应该是可以上岸了&#xff0c;上班划水&#xff0c;没啥事&#xff0c;总结一下两年的混子生活吧。拟录取出来就公开。&#xff08;三百多分也舔着个脸写经验贴&#xff09; 逻辑很差、文笔稀碎、错别…

嵌入式软件工程师_程序人生一名嵌入式软件工程师的成长总结

哈喽,我是老吴。今天分享一篇18年毕业生的总结文。文章于首发于[嵌入式大杂烩] (2019-10-16)。文章里干货多多,各位慢慢品读吧。 一、我的工作经历 1.1 第一家公司 1.2 第二家公司 1.3 第三家公司二、小、中、大公司的特点 2.1 小公司 2.2 中等公司 2.3 大…

2023年双非二战跨考上岸南京大学软件工程842专硕考研初试403分经验贴

&#x1f4a1; 考研过程的中心思想&#xff1a;脚踏实地、少走弯路、不要乱搞 目录 1.前期准备 2.个人情况 3.择校方面 4.备考里程表 5.数学 6.英语 7.政治 8.专业课 9.复试 10.后记 & 致谢 由于有不少朋友都私信我&#xff0c;想要做的笔记&#xff0c;所以我建…

气象学和计算机专业,没毕业就被签走的铁饭碗专业 气象学

有一个专业的就业率很好&#xff0c;到那时人们却不这样认为&#xff0c;是哪个专业呢&#xff1f; 气象学 每每一说到这个专业的就业好&#xff0c;都有很多人来反对。他怎么能好呢&#xff1f;我也没看见身边有人做气象这个行业的啊、 其实气象学的就业率是非常的高的&#x…

软件工程课程的第一次作业

软件工程课程第一次作业 1、建立一个public代码仓库1.1获取秘钥1.2 创建代码仓库1.2.1仓库地址 2、评估当前的自己2.1个人简介2.2当前值 3、展望未来3.1 回答关于构建之法的问题 4、学习路线 这个作业属于哪个课程广工软件工程课程学习社区作业要求作业链接作业目标创建博客、学…

从物理到软件工程,中山大学转专业2017纪实

我的转专业历程 其实早就有想写几篇文章纪念在中大的这一年,也确实写过一篇,但是还是太懒没能坚持下去,在知乎上获得的成就感也不是很高。正好借这个机会写出来当作能够在未来唤起一点点回忆的东西吧。 还是把原来最后面的联系方式摆在上面比较好 qq:715817451 还依稀记得…