【JavaEE进阶】图书管理系统开发日记——捌

news2024/11/18 19:58:27

文章目录

  • 🍃前言
  • 🎍统一数据返回格式
    • 🚩快速入门
    • 🚩存在问题
      • 🎈问题原因
      • 🎈代码修改
    • 🚩统一格式返回的优点
  • 🍀统一异常处理
  • 🌲前端代码的修改
    • 🚩登录页面
    • 🚩图书列表
    • 🚩删除图书
    • 🚩批量删除图书
    • 🚩添加图书
    • 🚩获取图书详情
    • 🚩修改图书
  • ⭕总结

🍃前言

今天我们将对图书管理系统进行收尾工作,今天的开发任务有两个

  1. 实现统一数据返回格式
  2. 实现统一异常的处理

🎍统一数据返回格式

在【JavaEE进阶】图书管理系统开发日记——柒实现拦截器时,博主对数据的返回格式其实已经进行了封装
在这里插入图片描述
在这里插入图片描述
但是如果每一个接口都这样写,岂不太麻烦了一点儿。

其实spring boot为我们提供了统一数据格式返回的功能

🚩快速入门

统⼀的数据返回格式使⽤ @ControllerAdvice 和ResponseBodyAdvice 的⽅式实现@ControllerAdvice 表⽰控制器通知类

添加类 ResponseAdvice ,实现 ResponseBodyAdvice 接⼝,并在类上添加@ControllerAdvice 注解

@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {
    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return true;
    }
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType,
                                  MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest
                                          request, ServerHttpResponse response) {
        return Result.success(body);
    }
}
  • supports方法:判断是否要执行beforeBodyWrite方法.true为执行,false不执行.通过该⽅法可以选择哪些类或哪些方法的response要进行处理,其他的不进行处理
从returnType获取类名和⽅法名
//获取执⾏的类
Class<?> declaringClass = returnType.getMethod().getDeclaringClass();
//获取执⾏的⽅法
Method method = returnType.getMethod();
  • beforeBodyWrite方法:对response方法进行具体操作处理

接下来我们进行测试一下,我们先来看一下没有添加统一功能的时候的登录返回
在这里插入图片描述
添加统一功能返回后

在这里插入图片描述
但是此时如果运用到我们的项目中,是会出现问题的

🚩存在问题

这里我就不演示错误的情况了

这里直接给出结论,返回结果为String或i为Result类型时会出现错误

🎈问题原因

那么是什么原因造成的呢?

SpringMVC默认会注册⼀些⾃带的 HttpMessageConverter (从先后顺序排列分别为ByteArrayHttpMessageConverter ,
StringHttpMessageConverter , SourceHttpMessageConverter ,SourceHttpMessageConverter,AllEncompassingFormHttpMessageConverter )

public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
        implements BeanFactoryAware, InitializingBean {
    //...
    public RequestMappingHandlerAdapter() {
        this.messageConverters = new ArrayList<>(4);
        this.messageConverters.add(new ByteArrayHttpMessageConverter());
        this.messageConverters.add(new StringHttpMessageConverter());
        if (!shouldIgnoreXml) {
            try {
                this.messageConverters.add(new SourceHttpMessageConverter<>());
            }
            catch (Error err) {
// Ignore when no TransformerFactory implementation is available
            }
        }
        this.messageConverters.add(new
                AllEncompassingFormHttpMessageConverter());
    }
//...
}

其中AllEncompassingFormHttpMessageConverter会根据项⽬依赖情况添加对应的HttpMessageConverter

public AllEncompassingFormHttpMessageConverter() {
    if (!shouldIgnoreXml) {
        try {
            addPartConverter(new SourceHttpMessageConverter<>());
        }
        catch (Error err) {
// Ignore when no TransformerFactory implementation is available
        }
        if (jaxb2Present && !jackson2XmlPresent) {
            addPartConverter(new Jaxb2RootElementHttpMessageConverter());
        }
    }
    if (kotlinSerializationJsonPresent) {
        addPartConverter(new KotlinSerializationJsonHttpMessageConverter());
    }
    if (jackson2Present) {
        addPartConverter(new MappingJackson2HttpMessageConverter());
    }
    else if (gsonPresent) {
        addPartConverter(new GsonHttpMessageConverter());
    }
    else if (jsonbPresent) {
        addPartConverter(new JsonbHttpMessageConverter());
    }
    if (jackson2XmlPresent && !shouldIgnoreXml) {
        addPartConverter(new MappingJackson2XmlHttpMessageConverter());
    }
    if (jackson2SmilePresent) {
        addPartConverter(new MappingJackson2SmileHttpMessageConverter());
    }
}

在依赖中引⼊jackson包后,容器会把MappingJackson2HttpMessageConverter 自动注册到
messageConverters 链的末尾.

Spring会根据返回的数据类型,从 messageConverters 链选择合适的HttpMessageConverter .

当返回的数据是非字符串时,使用的MappingJackson2HttpMessageConverter 写⼊返回对象.

当返回的数据是字符串时,StringHttpMessageConverter 会先被遍历到,这时会认为StringHttpMessageConverter 可以使用

public abstract class AbstractMessageConverterMethodProcessor extends
        AbstractMessageConverterMethodArgumentResolver
        implements HandlerMethodReturnValueHandler {
    //...代码省略
    protected <T> void writeWithMessageConverters(@Nullable T value,
                                                  MethodParameter returnType,
                                                  ServletServerHttpRequest inputMessage, ServletServerHttpResponse
                                                          outputMessage)
            throws IOException, HttpMediaTypeNotAcceptableException,
            HttpMessageNotWritableException {
//...代码省略
        if (selectedMediaType != null) {
            selectedMediaType = selectedMediaType.removeQualityValue();
            for (HttpMessageConverter<?> converter : this.messageConverters) {
                GenericHttpMessageConverter genericConverter = (converter
                        instanceof GenericHttpMessageConverter ?
                        (GenericHttpMessageConverter<?>) converter : null);
                if (genericConverter != null ?
                        ((GenericHttpMessageConverter)
                                converter).canWrite(targetType, valueType, selectedMediaType) :
                        converter.canWrite(valueType, selectedMediaType)) {
//getAdvice().beforeBodyWrite 执⾏之后, body转换成了Result类型的
                    结果
                            body = getAdvice().beforeBodyWrite(body, returnType,
                            selectedMediaType,
                            (Class<? extends HttpMessageConverter<?>>)
                                    converter.getClass(),
                            inputMessage, outputMessage);
                    if (body != null) {
                        Object theBody = body;
                        LogFormatUtils.traceDebug(logger, traceOn ->
                                "Writing [" + LogFormatUtils.formatValue(theBody,
                                        !traceOn) + "]");
                        addContentDispositionHeader(inputMessage, outputMessage);
                        if (genericConverter != null) {
                            genericConverter.write(body, targetType,
                                    selectedMediaType, outputMessage);
                        }
                        else {
//此时cover为StringHttpMessageConverter
                            ((HttpMessageConverter) converter).write(body,
                                    selectedMediaType, outputMessage);
                        }
                    }
                    else {
                        if (logger.isDebugEnabled()) {
                            logger.debug("Nothing to write: null body");
                        }
                    }
                    return;
                }
            }
        }
//...代码省略
    }
//...代码省略
}

在 ((HttpMessageConverter) converter).write(body, selectedMediaType,outputMessage) 的处理中,调用父类的write方法

由于 StringHttpMessageConverter 重写了addDefaultHeaders方法,所以会执行⼦类的⽅法

在这里插入图片描述

然⽽⼦类 StringHttpMessageConverter 的addDefaultHeaders⽅法定义接收参数为String,此时t为Result类型,所以出现类型不匹配"Result cannot be cast to java.lang.String"的异常

🎈代码修改

如果⼀些⽅法返回的结果已经是Result类型了,那就直接返回Result类型的结果即可

如果是String类型,进行处理一下即可

@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {
    @Autowired
    private ObjectMapper objectMapper;

    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return true;
    }

    @SneakyThrows
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        //在返回之前, 需要做的事情
        //body 是返回的结果
        if (body instanceof Result){
            return body;
        }
        if (body instanceof String){
            return objectMapper.writeValueAsString(Result.success(body));
        }
        return Result.success(body);
    }
}

🚩统一格式返回的优点

  1. ⽅便前端程序员更好的接收和解析后端数据接⼝返回的数据
  2. 降低前端程序员和后端程序员的沟通成本,按照某个格式实现就可以了,因为所有接⼝都是这样返回的.
  3. 有利于项⽬统⼀数据的维护和修改.
  4. 有利于后端技术部⻔的统⼀规范的标准制定,不会出现稀奇古怪的返回内容

🍀统一异常处理

统⼀异常处理使⽤的是 @ControllerAdvice+@ExceptionHandler 来实现的,

  • @ControllerAdvice 表⽰控制器通知类
  • @ExceptionHandler 是异常处理器,

两个结合表示当出现异常的时候执行某个通知,也就是执行某个方法事件

简单使用如下:

@ControllerAdvice
@ResponseBody
public class ErrorAdvice {
	@ExceptionHandler
	public Object handler(Exception e) {
		return Result.fail(e.getMessage());
	}
}

以上代码表⽰,如果代码出现Exception异常(包括Exception的⼦类),就返回⼀个Result的对象,

Result对象的设置参考博主对返回消息的包装Result.fail(e.getMessage())

public static Result fail(String msg) {
	Result result = new Result();
	result.setStatus(ResultStatus.FAIL);
	result.setErrorMessage(msg);
	result.setData("");
	return result;
}

现在我们将它应用于项目中,我们需要针对不同的异常进行返回不同的结果
在这里插入图片描述

@ResponseBody
@ControllerAdvice
public class ErrorAdvice {
    @ExceptionHandler
    public Object handler(Exception e) {
        return Result.fail(e.getMessage());
    }
    @ExceptionHandler
    public Object handler(NullPointerException e) {
        return Result.fail("发⽣NullPointerException:"+e.getMessage());
    }
    @ExceptionHandler
    public Object handler(ArithmeticException e) {
        return Result.fail("发⽣ArithmeticException:"+e.getMessage());
    }
}

至此我们的图书管理系统后端开发已经完毕了,接下来由于我们统一了数据返回格式,所以我们需要对前端代码进行修改

🌲前端代码的修改

🚩登录页面

登录界⾯没有拦截,只是返回结果发⽣了变化,所以只需要根据返回结果修改对应代码即可

登录结果代码修改

function login() {
    $.ajax({
        type: "post",
        url: "/user/login",
        data: {
            name: $("#userName").val(),
            password: $("#password").val()
        },
        success: function (result) {
            if (result.status=="SUCCESS" && result.data==true) {
                location.href = "book_list.html";
            } else {
                alert("账号或密码不正确!");
            }
        }
    });
}

🚩图书列表

针对图书列表⻚有两处变化

  1. 拦截器进行了强制登录校验,如果校验失败,则http状态码返回401,此时会⾛ajax的error逻辑处理
  2. 接⼝返回结果发生了变化

图书列表代码修改:

function getBookList() {
    $.ajax({
        type: "get",
        url: "/book/getListByPage"+location.search,
        success: function (result) {
            console.log(result);
            if (result == null || result.data == null) {
                location.href = "login.html";
                return;
            }
            if (result != null) {
                var finalHtml = "";
                var data = result.data;
                for (var book of data.records) {
                    finalHtml += '<tr>';
                    finalHtml += '<td><input type="checkbox" name="selectBook" value="'+book.id+'" id="selectBook" class="book-select"></td>'
                    finalHtml += '<td>' + book.id + '</td>';
                    finalHtml += '<td>' + book.bookName + '</td>';
                    finalHtml += '<td>' + book.author + '</td>';
                    finalHtml += '<td>' + book.count + '</td>';
                    finalHtml += '<td>' + book.price + '</td>';
                    finalHtml += '<td>' + book.publish + '</td>';
                    finalHtml += '<td>' + book.statusCN + '</td>';
                    finalHtml += '<td><div class="op">';
                    finalHtml += '<a href="book_update.html?bookId=' + book.id + '">修改</a>'
                    finalHtml += '<a href="javascript:void(0)" onclick="deleteBook(' + book.id + ')">删除</a>'
                    finalHtml += '</div></td>';
                    finalHtml += "</tr>";
                }
                $("tbody").html(finalHtml);

                //翻页信息
                $("#pageContainer").jqPaginator({
                    totalCounts: data.total, //总记录数
                    pageSize: 10,    //每页的个数
                    visiblePages: 5, //可视页数
                    currentPage: data.pageRequest.currentPage,  //当前页码
                    first: '<li class="page-item"><a class="page-link">首页</a></li>',
                    prev: '<li class="page-item"><a class="page-link" href="javascript:void(0);">上一页<\/a><\/li>',
                    next: '<li class="page-item"><a class="page-link" href="javascript:void(0);">下一页<\/a><\/li>',
                    last: '<li class="page-item"><a class="page-link" href="javascript:void(0);">最后一页<\/a><\/li>',
                    page: '<li class="page-item"><a class="page-link" href="javascript:void(0);">{{page}}<\/a><\/li>',
                    //页面初始化和页码点击时都会执行
                    onPageChange: function (page, type) {
                        console.log("第" + page + "⻚, 类型:" + type);
                        if (type != 'init') {
                            location.href = "book_list.html?currentPage=" + page;
                        }
                    }
                });
            }
        },
        error(error) {
            if(error.status == 401) {
                location.href = "login.html";
            }
        }
    });
}

🚩删除图书

function deleteBook(id) {
		//...代码省略
    success: function (result) {
        if(result.status=="SUCCESS" || result.data==""){
			//重新刷新⻚⾯
            location.href = "book_list.html"
        }else{
            alert(result.data);
        }
    },
    error: function (error) {
        if (error != null && error.status == 401) {
			//⽤⼾未登录
            location.href = "login.html";
        }
    }
	//...代码省略
}

🚩批量删除图书

function batchDelete() {
    var isDelete = confirm("确认批量删除?");
    if (isDelete) {
        //获取复选框的id
        var ids = [];
        $("input:checkbox[name='selectBook']:checked").each(function () {
            ids.push($(this).val());
        });
        console.log(ids);
        //批量删除
        $.ajax({
            type: "post",
            url: "/book/batchDeleteBook?ids="+ids,
            success: function (result) {
                if (result.status=="SUCCESS" || result.data==true) {
                    alert("删除成功");
                    //重新刷新⻚⾯
                    location.href = "book_list.html"
                }
            },
            error: function (error) {
                if (error != null && error.status == 401) {
                    //⽤⼾未登录
                    location.href = "login.html";
                }
            }
        });
    }
}

🚩添加图书

function add() {
    $.ajax({
        type: "post",
        url: "/book/addBook",
        data: $("#addBook").serialize(),
        success: function (result) {
            console.log(result);
            console.log(result.data);
            if (result.status == "SUCCESS" && result.data == "") {
                location.href = "book_list.html"
            } else {
                console.log(result);
                alert("添加失败:" + result.data);
            }
        },
        error: function (error) {
            if (error != null && error.status == 401) {
                //⽤⼾未登录
                alert("⽤⼾未登录");
                location.href = "login.html";
            }
        }
    });
}

🚩获取图书详情

$.ajax({
    type:"get",
    url: "/book/queryBookById"+location.search,
    success:function(book){
        if (result.status == "SUCCESS" && result.data != null) {
            var book = result.data;
            if (book != null) {
                $("#bookId").val(book.id);
                $("#bookName").val(book.bookName);
                $("#bookAuthor").val(book.author);
                $("#bookStock").val(book.count);
                $("#bookPrice").val(book.price);
                $("#bookPublisher").val(book.publish);
                $("#bookStatus").val(book.status);
            }
        }
    },
    error: function (error) {
        if (error != null && error.status == 401) {
            //⽤⼾未登录
            alert("⽤⼾未登录");
            location.href = "login.html";
        }
    }
});

🚩修改图书

function update() {
    $.ajax({
        type: "post",
        url: "/book/updateBook",
        data: $("#updateBook").serialize(),
        success: function (result) {
            if (result.status == "SUCCESS" || result.data == "") {
                location.href = "book_list.html"
            } else {
                console.log(result);
                alert("修改失败:" + result.data);
            }
        },
        error: function (error) {
            if (error != null && error.status == 401) {
                //⽤⼾未登录
                location.href = "login.html";
            }
        }
    });
}

⭕总结

关于《【JavaEE进阶】 图书管理系统开发日记——捌》就讲解到这儿,图书管理系统到此也就开发完毕了,感谢大家的支持,欢迎各位留言交流以及批评指正,如果文章对您有帮助或者觉得作者写的还不错可以点一下关注,点赞,收藏支持一下!

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

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

相关文章

openai.CLIP多模态模型简介

介绍 OpenAI CLIP&#xff08;Contrastive Language–Image Pretraining&#xff09;是一种由OpenAI开发的多模态学习模型。它能够同时理解图像和文本&#xff0c;并在两者之间建立联系&#xff0c;实现了图像和文本之间的跨模态理解。 如何工作 CLIP模型的工作原理是将来自…

三、软考-系统架构设计师笔记-计算机系统基础知识

计算机系统概述 计算机系统是指用于数据管理的计算机硬件、软件及网络组成的系统。 它是按人的要求接收和存储信息&#xff0c;自动进行数据处理和计算&#xff0c;并输出结果信息的机器系统。 冯诺依曼体系计算机结构&#xff1a; 1、计算机硬件组成 冯诺依曼计算机结构将…

C#理论 —— WPF 应用程序Console 控制台应用

文章目录 1. WPF 应用程序1.1 工程创建1.2 控件1.2.1 控件的公共属性1.2.1 TextBox 文本框1.2.1 Button 按钮 *. Console 控制台应用1.1 工程创建 1. WPF 应用程序 1.1 工程创建 Visual Studio 中新建项目 - 选择WPF 应用程序&#xff1b; 1.2 控件 1.2.1 控件的公共属性 …

2024常用的 Python 自动化测试框架有哪些?

Unittest是Python中最常用的测试框架之一&#xff0c;它提供了丰富和强大的测试工具和方法&#xff0c;可以帮助开发者更好地保证代码质量和稳定性&#xff0c;本文就来介绍下Unittest单元测试框架。 1. 介绍 unittest是Python的单元测试框架&#xff0c;它提供了一套丰富的测…

【MySQL】基本查询(表的增删改查)-- 详解

CRUD&#xff1a;Create&#xff08;创建&#xff09;&#xff0c;Retrieve&#xff08;读取&#xff09;&#xff0c;Update&#xff08;更新&#xff09;&#xff0c;Delete&#xff08;删除&#xff09;。 一、Create insert [into] table_name [(column [, column] ...)] v…

硬件工程师入门基础知识(三)钽电容应用(二)

钽电容应用&#xff08;二&#xff09; 1.钽电容使用容量选择2.非固体电解质钽电容器使用时应注意的问题2.1 容量和损耗2.2 直流漏电流2.3 使用电压2.4 反向电压2.5 纹波电流2.6 失效率的影响因素2.7 补充说明&#xff1a; 1.钽电容使用容量选择 许多情况下&#xff0c;高能混…

自定义Chrome的浏览器开发者工具DevTools界面的字体和样式

Chrome浏览器开发者工具默认的字体太小&#xff0c;想要修改但没有相关设置。 外观——字体可以自定义字体&#xff0c;但大小不可以调整。 github上有人给出了方法 整理为中文教程&#xff1a; 1.打开浏览器开发者工具&#xff0c;点开设置——实验&#xff0c;勾上红框设…

实现unity场景切换

本文实现两个按键实现场景1和场景2之间的切换 ①首先在unity 3D中创建两个场景&#xff0c;分别为Scene1和Scene2 ②在Scene1中创建一个Button&#xff0c;修改txt内容为“To Scene2”&#xff0c;并在Buttons下创建一个空物体&#xff0c;用于挂载脚本。 脚本Trans Scene.…

自然语言:信息抽取技术在CRM系统中的应用与成效

一、引言 在当今快速变化的商业环境中&#xff0c;客户关系管理&#xff08;CRM&#xff09;已成为企业成功的关键因素。CRM系统的核心在于有效地管理客户信息&#xff0c;跟踪与客户的每一次互动&#xff0c;以及深入分析这些数据以提升客户满意度和忠诚度。在我最近参与的一个…

综合实战(volume and Compose)

"让我&#xff0c;重获新生~" MySQL 灾难恢复 熟练掌握挂载卷的使用&#xff0c;将Mysql的业务数据存储在 外部。 实战思想: 使用 MySQL 5.7 的镜像创建容器并创建一个普通数据卷 "mysql-data"用来保存容器中产生的数据。我们需要容器连接到Mysql服务&a…

智慧公厕:打造智慧城市环境卫生新标杆

随着科技的不断发展和城市化进程的加速推进&#xff0c;智慧城市建设已经成为各地政府和企业关注的焦点。而作为智慧城市环境卫生管理的基础设施&#xff0c;智慧公厕的建设和发展也备受重视&#xff0c;被誉为智慧城市的新标杆。本文以智慧公厕源头厂家广州中期科技有限公司&a…

OpenAI要为GPT-4解决数学问题了:奖励模型指错,解题水平达到新高度

原文&#xff1a;OpenAI要为GPT-4解决数学问题了&#xff1a;奖励模型指错&#xff0c;解题水平达到新高度 - 知乎 对于具有挑战性的 step-by-step 数学推理问题&#xff0c;是在每一步给予奖励还是在最后给予单个奖励更有效呢&#xff1f;OpenAI 的最新研究给出了他们的答案。…

LASSO算法

LASSO (Least Absolute Shrinkage and Selection Operator) 是一种回归分析的方法&#xff0c;它能够同时进行变量选择和正则化&#xff0c;以增强预测准确性和模型的解释性。LASSO通过在损失函数中加入一个L1惩罚项来实现这一点。该惩罚项对系数的绝对值进行约束。 基本概念 …

前后端延迟怎么解决

当今互联网应用的发展越来越迅猛&#xff0c;用户对于网站或应用的性能要求也越来越高。其中一个重要方面就是前后端延迟的解决&#xff0c;也就是减少前端与后端之间的通信时间延迟&#xff0c;提高用户体验。本文将详细介绍如何解决前后端延迟的问题。 网络延迟 数据在网络…

springboot项目中使用mybatis作为数据查询框架,如何实现查询sql的日志打印输出?

在Spring Boot项目中使用MyBatis作为数据查询框架时&#xff0c;可以通过配置日志记录器来实现SQL查询的日志打印输出。MyBatis支持多种日志框架&#xff0c;如SLF4J、Log4j2等。这里介绍几种常见的配置方法&#xff1a; 1. 使用application.properties或application.yml配置 …

如何开通微信小程序商城

微信小程序店铺是一种新型的线上商城&#xff0c;可以帮助商家快速搭建自己的线上销售平台&#xff0c;吸引更多的用户进行购买。作为小程序服务商&#xff0c;我们可以帮助商家开通微信小程序店铺&#xff0c;提升他们的线上销售业绩。 1. 进入采云小程序。进入采云小程序首页…

skiplist(高阶数据结构)

目录 一、概念 二、实现 三、对比 一、概念 skiplist是由William Pugh发明的&#xff0c;最早出现于他在1990年发表的论文《Skip Lists: A Probabilistic Alternative to Balanced Trees》 skiplist本质上是一种查找结构&#xff0c;用于解决算法中的查找问题&#xff0c;…

ntp时钟服务安装- 局域网节点时间同步

场景&#xff1a; 一般部署大数据相关应用服务&#xff0c;各个节点之间需要时间同步&#xff1b;内网情况下&#xff0c;很可能各节点之前时间可能不一致&#xff0c;或者过一段时间后 又不一致了 ntp 时钟服务器&#xff1a; 可用于内网各个节点之前得时间同步&#xff0c;安…

C#理论 —— 基础语法、数据类型、变量、常量、运算符、三大结构

文章目录 1. 基础语法1.1 标识符命名规则1.2 C# 关键字1.3 C#注释 2. 数据类型2.1 值类型&#xff08;Value types&#xff09;2.2 引用类型&#xff08;Reference types&#xff09;2.2.1 对象&#xff08;Object&#xff09;类型3.2.2 动态&#xff08;Dynamic&#xff09;类…

ubuntu常见配置

ubuntu各个版本的安装过程大差小不差&#xff0c;可以参考&#xff0c;ubuntu20.04 其它版本换一下镜像版本即可 安装之后需要配置基本的环境&#xff0c;我的话大概就以下内容&#xff0c;后续可能有所删改 sudo apt-get update sudo apt-get install gcc sudo apt-get inst…