当前位置: 首页 > news >正文

java高级之单元测试、反射

1、Junit测试工具

@Test定义测试方法
1.被@BeforeClass标记的方法,执行在所有方法之前
2.被@AfterCalss标记的方法,执行在所有方法之后
3.被@Before标记的方法,执行在每一个@Test方法之前
4.被@After标记的方法,执行在每一个@Test方法之后

public class StringUtilTest{@Beforepublic void test1(){System.out.println("--> test1 Before 执行了");}@BeforeClasspublic static void test11(){System.out.println("--> test11 BeforeClass 执行了");}@Afterpublic void test2(){System.out.println("--> test2 After 执行了");}@AfterCalsspublic static void test22(){System.out.println("--> test22 AfterCalss 执行了");}
}

2、反射

定义:反射技术,指的是加载类的字节码到内存,并以编程的方法解刨出类中的各个成分(成员变量、方法、构造器等)

2.1、获得class对象(字节码对象)

由于Java的设计原则是万物皆对象,获取到的类其实也是以对象的形式体现的,叫字节码对象,用Class类来表示。获取到字节码对象之后,再通过字节码对象就可以获取到类的组成成分了,这些组成成分其实也是对象,其中每一个成员变量用Field类的对象来表示每一个成员方法用Method类的对象来表示每一个构造器用Constructor类的对象来表示

获取Class对象的三种方式

  • Class c1=类名.class
  • 调用Class提供方法:public static Class forName(String package);
  • Object提供的方法: public Class getClass();Class c3=对象getClass();

2.2 获取类的构造器

Class提供了从类中获取构造器的方法
方法
Constructor<?>[] getConstructors() 获取全部构造器(只能获取public修饰的)
Constructor<?>[] getDeclaredConstructors() 获取全部构造器(只要存在就能拿到)
Constructor<T> getConstructor(Class<?>... parameterTypes) 获取某个构造器(只能获取public修饰的)
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 获取某个构造器(只要存在就能拿到)get:获取
Declared: 有这个单词表示可以获取任意一个,没有这个单词表示只能获取一个public修饰的
Constructor: 构造方法的意思
后缀s: 表示可以获取多个,没有后缀s只能获取一个

2.3获取构造器的作用

获取类构造器的作用: 依然是初始化对象返回
Constructor提供的方法
T newInstance(Object... initargs)(可以是有的任意参数数量)调用此构造器对象表示的构遣器,并传入参数,完成对象的初始化并返回public void  setAccessible(boolean flag) 设置为true,表示禁止检查访问控制(暴力反射)

2.4 反射获取成员变量&使用

Class提供了从类中获取成员变量的方法
方法
public Field[] getFields() 获取类的全部成员变量(只能获取public修饰的)
public Field[] getDeclaredFields() 获取类的全部成员变量(只要存在就能拿到)
public Field getField(String name) 获取类的某个成员变量(只能获取public修饰的)
public Field getDeclaredField(String name) 获取类的某个成员变量(只要存在就能拿到)
获取到成员变量的作用: 依然是赋值、取值。
方法
void set(Object obj,object value): 赋值(需要传入对象,不然不知道为哪个对象赋值)
object get(Object obj) 取值
public void  setAccessible(boolean flag)  设置为true,表示禁止检查访问控制 (暴力反射)

2.5成员方法的获取和取值

获取类的成员方法
Class提供了从类中获取成员方法的API。
方法
Method[] getMethods() 获取类的全部成员方法(只能获取public修饰的)
Method[] getDeclaredMethods() 获取类的全部成员方法(只要存在就能拿到)
Method getMethod(String name, Class<?>... parameterTypes) 获取类的某个成员方法(只能获取public修饰的)
Method getDeclaredMethod(String name, Class<?>.. parameterTypes) 获取类的某个成员方法(只要存在就能拿到)成员方法的作用: 依然是执行
Method提供的方法
public Object invoke(Object obj,Object... args) 触发某个对象的该方法执行。
public void setAccessible(boolean flag) 设置为true,表示禁止检查访问控制(暴力反射)

public class Test3Method{
public static void main(String[] args){
//1、反射第一步:先获取到Class对象
Class c = Cat.class;

    //2、获取类中的全部成员方法Method[] methods = c.getDecalaredMethods();//3、遍历这个数组中的每一个方法对象for(Method method : methods){System.out.println(method.getName()+"-->"+method.getParameterCount()+"-->"+method.getReturnType());}System.out.println("-----------------------");//4、获取private修饰的run方法,得到Method对象Method run = c.getDecalaredMethod("run");//执行run方法,在执行前需要取消权限检查Cat cat = new Cat();run.setAccessible(true);Object rs1 = run.invoke(cat);System.out.println(rs1)//5、获取private 修饰的eat(String name)方法,得到Method对象Method eat = c.getDeclaredMethod("eat",String.class);eat.setAccessible(true);Object rs2 = eat.invoke(cat,"鱼儿");System.out.println(rs2)
}

}

获取运行返回结果
在这里插入图片描述

2.6 反射的作用

反射使用来写框架的,也就是不管输入的什么类型的对象,我们都可以获取对应的class文件去做一些统一的处理。下面让我们写一个框架,能够将任意一个对象的属性名和属性值写到文件中去。不管这个对象有多少个属性,也不管这个对象的属性名是否相同。

1.先写好两个类,一个Student类和Teacher2.写一个ObjectFrame类代表框本架在ObjectFrame类中定义一个saveObject(Object obj)方法,用于将任意对象存到文件中去参数:Object obj: 就表示要存入文件中的对象3.编写方法内部的代码,往文件中存储对象的属性名和属性值1)参数obj对象中有哪些属性,属性名是什么实现值是什么,中有对象自己最清楚。2)接着就通过反射获取类的成员变量信息了(变量名、变量值)3)把变量名和变量值写到文件中去

写一个ObjectFrame表示自己设计的框架,代码如下图所示

public class ObjectFrame{public static void saveObject(Object obj) throws Exception{PrintStream ps = new PrintStream(new FileOutputStream("模块名\\src\\data.txt",true));//1)参数obj对象中有哪些属性,属性名是什么实现值是什么,中有对象自己最清楚。//2)接着就通过反射获取类的成员变量信息了(变量名、变量值)Class c = obj.getClass(); //获取字节码ps.println("---------"+class.getSimpleName()+"---------");Field[] fields = c.getDeclaredFields(); //获取所有成员变量//3)把变量名和变量值写到文件中去for(Field field : fields){String name = field.getName();Object value = field.get(obj)+"";ps.println(name);}ps.close();}
}

使用自己设计的框架,往文件中写入Student对象的信息和Teacher对象的信息。

先准备好Student类和Teacher类

public class Student{private String name;private int age;private char sex;private double height;private String hobby;
}
public class Teacher{private String name;private double salary;
}

创建一个测试类,在测试中类创建一个Student对象,创建一个Teacher对象,用ObjectFrame的方法把这两个对象所有的属性名和属性值写到文件中去。

public class Test5Frame{@Testpublic void save() throws Exception{Student s1 = new Student("黑马吴彦祖",45, '男', 185.3, "篮球,冰球,阅读");Teacher s2 = new Teacher("播妞",999.9);ObjectFrame.save(s1);ObjectFrame.save(s2);}
}

最终结果是,将不同类的信息保存至文件中

3注解的使用

注解是和反射一起使用的,为了实现框架而服务。我们可以理解为JUnit这个注解一样,告诉系统加了这个注解就会执行,同样也和spring中定义bean的意思差不多,可以通过注解知道系统中有哪些bean。

  • Java注解是代码中的特殊标记,比如@Override、@Test等,作用是:让其他程序根据注解信息决定怎么执行该程序

3.1自定义注解的格式

public @interface MyTest{String aaa();boolean bbb() default true;	//default true 表示默认值为true,使用时可以不赋值。String[] ccc();
}

在这里阿插入图片描述
注意:注解的属性名如何是value的话,并且只有value没有默认值,使用注解时value名称可以省略。比如现在重新定义一个MyTest2注解

public @interface MyTest2{String value(); //特殊属性}
@MyTest2("孙悟空") //等价于 @MyTest2(value="孙悟空")

3.2注解本质是什么呢

1.MyTest1注解本质上是接口,每一个注解接口都继承子Annotation接口
2.MyTest1注解中的属性本质上是抽象方法
3.@MyTest1实际上是作为MyTest接口的实现类对象
4.@MyTest1(aaa=“孙悟空”,bbb=false,ccc={“Python”,“前端”,“Java”})里面的属性值,可以通过调用aaa()、bbb()、ccc()方法获取到

3.3元注解

元注解是修饰注解的注解

@Target是用来声明注解只能用在那些位置,比如:类上、方法上、成员变量上等
@Retetion是用来声明注解保留周期,比如:源代码时期、字节码时期、运行时期

在这里插入图片描述
例如test的@target就是type,@retention就是Runtime

3.4 解析注解

1.如果注解在类上,先获取类的字节码对象,再获取类上的注解
2.如果注解在方法上,先获取方法对象,再获取方法上的注解
3.如果注解在成员变量上,先获取成员变量对象,再获取变量上的注解
总之:注解在谁身上,就先获取谁,再用谁获取谁身上的注解
public class AnnotationTest3{@Testpublic void parseClass(){//1.先获取Class对象Class c = Demo.class;//2.解析Demo类上的注解if(c.isAnnotationPresent(MyTest4.class)){//获取类上的MyTest4注解MyTest4 myTest4 = (MyTest4)c.getDeclaredAnnotation(MyTest4.class);//获取MyTests4注解的属性值System.out.println(myTest4.value());System.out.println(myTest4.aaa());System.out.println(myTest4.bbb());}}@Testpublic void parseMethods(){//1.先获取Class对象Class c = Demo.class;//2.解析Demo类中test1方法上的注解MyTest4注解Method m = c.getDeclaredMethod("test1");if(m.isAnnotationPresent(MyTest4.class)){//获取方法上的MyTest4注解MyTest4 myTest4 = (MyTest4)m.getDeclaredAnnotation(MyTest4.class);//获取MyTests4注解的属性值System.out.println(myTest4.value());System.out.println(myTest4.aaa());System.out.println(myTest4.bbb());}}
}

上图为获取类和方法上的注解。

3.4 注解的应用场景-模拟Junit写一个测试框架

也就是将有注解的方法或者类进行特殊处理

public class AnnotationTest4{@MyTestpublic void test1(){System.out.println("=====test1====");}@MyTestpublic void test2(){System.out.println("=====test2====");}public void test3(){System.out.println("=====test2====");}public static void main(String[] args){AnnotationTest4 a = new AnnotationTest4();//1.先获取Class对象Class c = AnnotationTest4.class;//2.解析AnnotationTest4类中所有的方法对象Method[] methods = c.getDeclaredMethods();for(Method m: methods){//3.判断方法上是否有MyTest注解,有就执行该方法if(m.isAnnotationPresent(MyTest.class)){m.invoke(a);}}}
}

4.动态代理

关键点:接口(申明代理的方法,相当于把要做的抽象出来,可以在proxy产生代理对象的时候作为参数2表示代理的样子是什么样子)、工具类生成动态代理对象、Proxy类中的newInstamce产生代理对象、重写invoke方法可以实现回调函数

ProxyUtil工具类,为BigStar对象生成代理对象public class ProxyUtil {public static Star createProxy(BigStar bigStar){/* newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)参数1:用于指定一个类加载器参数2:指定生成的代理长什么样子,也就是有哪些方法参数3:用来指定生成的代理对象要干什么事情*/// Star starProxy = ProxyUtil.createProxy(s);// starProxy.sing("好日子") starProxy.dance()Star starProxy = (Star) Proxy.newProxyInstance(ProxyUtil.class.getClassLoader(),new Class[]{Star.class}, new InvocationHandler() {@Override // 回调方法public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 代理对象要做的事情,会在这里写代码if(method.getName().equals("sing")){System.out.println("准备话筒,收钱20万");}else if(method.getName().equals("dance")){System.out.println("准备场地,收钱1000万");}return method.invoke(bigStar, args);}});return starProxy;}
}

new proxyInstance(loader,class<?> [] interfaces,invocationhalder):

  • 1、定义类加载器
  • 2、定义代理对象中应该有的方法,可以有多个接口
  • 3、这个是定义代理对象应该做什么

在这里插入图片描述

public class ProxyUtil {public static UserService createProxy(UserService userService){UserService userServiceProxy= (UserService) Proxy.newProxyInstance(ProxyUtil.class.getClassLoader(),new Class[]{UserService.class}, new InvocationHandler() {@Overridepublic Object invoke(                                                                             Object proxy, Method method, Object[] args) throws Throwable {                             if(method.getName().equals("login") ||                                             method.getName().equals("deleteUsers")||method.getName().equals("selectUsers")){//方法运行前记录毫秒值         long startTime = System.currentTimeMillis();//执行方法Object rs = method.invoke(userService, args);//执行方法后记录毫秒值long endTime = System.currentTimeMillis();System.out.println(method.getName() + "方法执行耗时:" + (endTime - startTime)/ 1000.0 + "s");return rs;}else {Object rs = method.invoke(userService, args);return rs;                                                                }}                                                                 });//返回代理对象return userServiceProxy;}
}

AOP的底层也是动态代理实现,别的地方使用到的是mybatis中的延迟加载就是使用CGLIB动态代理,来实现需要用到这个函数时,采取执行搜索保存操作。

相关文章:

java高级之单元测试、反射

1、Junit测试工具 Test定义测试方法 1.被BeforeClass标记的方法,执行在所有方法之前 2.被AfterCalss标记的方法&#xff0c;执行在所有方法之后 3.被Before标记的方法&#xff0c;执行在每一个Test方法之前 4.被After标记的方法&#xff0c;执行在每一个Test方法之后 public …...

MSQL系列(十三) Mysql实战-left/right/inner join 使用详解及索引优化

Mysql实战-left/right/inner join 使用详解及索引优化 前面我们讲解了BTree的索引结构&#xff0c;也详细讲解下Join的底层驱动表 选择原理&#xff0c;今天我们来了解一下为什么会出现内连接外连接&#xff0c;两种连接方式&#xff0c;另外实战一下内连接和几种最常用的join…...

前端面试题之HTML篇

1、src 和 href 的区别 具有src的标签有&#xff1a;script、img、iframe 具有href的标签有&#xff1a;link、a 区别 src 是source的缩写。表示源的意思&#xff0c;指向资源的地址并下载应用到文档中。会阻塞文档的渲染&#xff0c;也就是为什么js脚本放在底部而不是头部的…...

Django ORM:数据库操作的Python化艺术

Django的对象关系映射器&#xff08;ORM&#xff09;是其核心功能之一&#xff0c;允许开发者使用Python代码来定义、操作和查询数据库。这篇文章将带你深入了解Django ORM的强大之处&#xff0c;从基本概念到高级查询技巧&#xff0c;提供丰富的示例帮助你掌握使用Django ORM进…...

react受控组件与非受控组件

React中的组件可以分为受控组件和非受控组件&#xff1a; 受控组件&#xff1a;受控组件是指组件的值受到React组件状态的控制。通常在组件中&#xff0c;我们会通过state来存储组件的值&#xff0c;然后再将state的值传递给组件的props&#xff0c;从而实现组件的双向数据绑定…...

小米产品面试题:淘宝为何需要确认收货?京东为何不需要?

亲爱的小米粉丝们&#xff0c;大家好&#xff01;我是小米&#xff0c;一个热爱技术、热衷于分享的小编。今天&#xff0c;我要和大家聊聊一个有趣的话题&#xff1a;为什么淘宝购物需要确认收货&#xff0c;而京东不需要&#xff1f;这可是一个让很多人纳闷的问题&#xff0c;…...

(1)上位机底部栏 UI如何设置

上位机如果像设置个多页面切换&#xff1a; 位置&#xff1a; 代码如下&#xff1a; "tabBar": {"color": "black","selectedColor": "#d43c33","borderStyle":"black","backgroundColor": …...

中国多主数据库:压强投入,期待破茧

拿破仑曾说&#xff1a;“战争的艺术就是在某一点上集中最大优势兵力”&#xff0c;强调了力量集中的重要性。 如今&#xff0c;国际形势风云变幻&#xff0c;西方世界对中国的围剿不再仅仅体现在军事和地缘政治上&#xff0c;而更多表现在经济与科技上。在科技领域&#xff0…...

JavaScript在ES6及后续新增的常用新特性

JavaScript经历了不同标本的迭代&#xff0c;在不断完善中会添加不同的新特性来解决前一个阶段的瑕疵&#xff0c;让我们开发更加便捷与写法更加简洁&#xff01; 1、箭头函数&#xff1a; 箭头函数相比传统的函数语法&#xff0c;具有更简洁的语法、没有自己的this值、不会绑…...

试试流量回放,不用人工写自动化测试case了

大家好&#xff0c;我是洋子&#xff0c;接触过接口自动化测试的同学都知道&#xff0c;我们一般要基于某种自动化测试框架&#xff0c;编写自动化case&#xff0c;编写自动化case的依据来源于接口文档&#xff0c;对照接口文档里面的请求参数进行人工添加接口自动化case 其实…...

密钥管理系统功能及作用简介 安当加密

密钥管理系统的功能主要包括密钥生成、密钥注入、密钥备份、密钥恢复、密钥更新、密钥导出和服务&#xff0c;以及密钥的销毁等。 密钥生成&#xff1a;通过输入一到多组的密钥种子&#xff0c;按照可再现或不可再现的模式生成所需要的密钥。一般采用不可再现模式作为密钥生成…...

vue中watch属性的用法

在Vue中&#xff0c;watch属性用于监听一个数据的变化&#xff0c;并且在数据变化时执行一些操作。它可以观察一个具体的数据对象&#xff0c;从而在该数据对象发生变化时触发对应的回调函数。 使用watch属性的步骤如下&#xff1a; 在Vue实例中添加一个watch对象 new Vue({…...

Redis-使用java代码操作Redis

&#x1f3c5;我是默&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; ​ &#x1f31f;在这里&#xff0c;我要推荐给大家我的专栏《Linux》。&#x1f3af;&#x1f3af; &#x1f680;无论你是编程小白&#xff0c;还是有一定基础的程序员&#xff0c;这…...

0基础学习PyFlink——事件时间和运行时间的窗口

大纲 定制策略运行策略Reduce完整代码滑动窗口案例参考资料 在 《0基础学习PyFlink——时间滚动窗口(Tumbling Time Windows)》一文中&#xff0c;我们使用的是运行时间(Tumbling ProcessingTimeWindows)作为窗口的参考时间&#xff1a; reducedkeyed.window(TumblingProcess…...

Git Rebase 优化项目历史

在软件开发过程中&#xff0c;版本控制是必不可少的一环。Git作为当前最流行的版本控制系统&#xff0c;为开发者提供了强大的工具来管理和维护代码历史。git rebase是其中一个高级特性&#xff0c;它可以用来重新整理提交历史&#xff0c;使之更加清晰和线性。本文将详细介绍g…...

两种MySQL OCP认证应该如何选?

很多同学都找姚远老师说要参加MySQL OCP认证培训&#xff0c;但绝大部分同学并不知道MySQL OCP认证有两种&#xff0c;以MySQL 8.0为例。 一种是管理方向&#xff0c;叫&#xff1a;Oracle Certified Professional, MySQL 8.0 Database Administrator&#xff08;我考试的比较…...

Java用log4j写日志

日志可以方便追踪和调试问题&#xff0c;以前用log4net写日志&#xff0c;换Java了改用log4j写日志&#xff0c;用法和log4net差不多。 到apache包下载下载log4j的包&#xff0c;解压后把下图两个jar包引入工程 先到网站根下加一个log4j2.xml的配置文件来配置日志的格式和参…...

PCTA认证考试-01_TiDB数据库架构概述

TiDB 数据库架构概述 一、学习目标 理解 TiDB 数据库整体结构。了解 TiDB Server&#xff0c;TiKV&#xff0c;TiFlash 和 PD 的主要功能。 二、TiDB 体系架构 1. TiDB Server 2. TiKV OLTP 3. Placement Driver 4. TiFlash OLAP OLTPOLAPHTAP...

路由过滤路由引入

目录 一、实验拓扑 二、实验需求 三、实验步骤 1、配置IP地址 2、配置RIP和OSPF 3、配置路由引入 4、使用路由过滤&#xff0c;使 R4 无法学到 R1 的业务网段路由&#xff0c;要求使用 prefix-list 进行匹配 5、OSPF 区域中不能出现 RIP 协议报文 一、实验拓扑 二、实…...

视频剪辑技巧:批量合并视频,高效省时,添加背景音乐提升品质

随着社交媒体的兴起&#xff0c;视频制作越来越受到人们的关注。掌握一些视频剪辑技巧&#xff0c;可以让我们轻松地制作出令人惊艳的视频。本文将介绍一种高效、省时的视频剪辑技巧&#xff0c;帮助您批量合并视频、添加背景音乐&#xff0c;并提升视频品质。现在一起来看看云…...

linux之kylin系统nginx的安装

一、nginx的作用 1.可做高性能的web服务器 直接处理静态资源&#xff08;HTML/CSS/图片等&#xff09;&#xff0c;响应速度远超传统服务器类似apache支持高并发连接 2.反向代理服务器 隐藏后端服务器IP地址&#xff0c;提高安全性 3.负载均衡服务器 支持多种策略分发流量…...

springboot 百货中心供应链管理系统小程序

一、前言 随着我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;百货中心供应链管理系统被用户普遍使用&#xff0c;为方…...

树莓派超全系列教程文档--(62)使用rpicam-app通过网络流式传输视频

使用rpicam-app通过网络流式传输视频 使用 rpicam-app 通过网络流式传输视频UDPTCPRTSPlibavGStreamerRTPlibcamerasrc GStreamer 元素 文章来源&#xff1a; http://raspberry.dns8844.cn/documentation 原文网址 使用 rpicam-app 通过网络流式传输视频 本节介绍来自 rpica…...

反向工程与模型迁移:打造未来商品详情API的可持续创新体系

在电商行业蓬勃发展的当下&#xff0c;商品详情API作为连接电商平台与开发者、商家及用户的关键纽带&#xff0c;其重要性日益凸显。传统商品详情API主要聚焦于商品基本信息&#xff08;如名称、价格、库存等&#xff09;的获取与展示&#xff0c;已难以满足市场对个性化、智能…...

日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする

日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする 1、前言(1)情况说明(2)工程师的信仰2、知识点(1) にする1,接续:名词+にする2,接续:疑问词+にする3,(A)は(B)にする。(2)復習:(1)复习句子(2)ために & ように(3)そう(4)にする3、…...

uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖

在前面的练习中&#xff0c;每个页面需要使用ref&#xff0c;onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入&#xff0c;需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...

ETLCloud可能遇到的问题有哪些?常见坑位解析

数据集成平台ETLCloud&#xff0c;主要用于支持数据的抽取&#xff08;Extract&#xff09;、转换&#xff08;Transform&#xff09;和加载&#xff08;Load&#xff09;过程。提供了一个简洁直观的界面&#xff0c;以便用户可以在不同的数据源之间轻松地进行数据迁移和转换。…...

在WSL2的Ubuntu镜像中安装Docker

Docker官网链接: https://docs.docker.com/engine/install/ubuntu/ 1、运行以下命令卸载所有冲突的软件包&#xff1a; for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done2、设置Docker…...

ip子接口配置及删除

配置永久生效的子接口&#xff0c;2个IP 都可以登录你这一台服务器。重启不失效。 永久的 [应用] vi /etc/sysconfig/network-scripts/ifcfg-eth0修改文件内内容 TYPE"Ethernet" BOOTPROTO"none" NAME"eth0" DEVICE"eth0" ONBOOT&q…...

ABAP设计模式之---“简单设计原则(Simple Design)”

“Simple Design”&#xff08;简单设计&#xff09;是软件开发中的一个重要理念&#xff0c;倡导以最简单的方式实现软件功能&#xff0c;以确保代码清晰易懂、易维护&#xff0c;并在项目需求变化时能够快速适应。 其核心目标是避免复杂和过度设计&#xff0c;遵循“让事情保…...