反射机制大揭秘-进阶Java技巧,直击核心!


反射在Java中扮演着重要的角色,掌握了反射,就等于掌握了框架设计的钥匙。本文将为您逐步讲解反射的基本概念、获取Class对象的三种方式、使用反射实例化对象并操作属性和方法,还有解析包的相关内容。跟随我一起探索反射的奥秘,提升编码技能!


一、反射基本概念


Java 反射是一个强大的特性,它允许程序在运行时查询、访问和修改类、接口、字段和方法的信息,以及创建和操作对象。通过反射,我们可以在运行时动态地创建对象,调用方法,修改字段值,这些在传统的面向对象编程中是难以实现的。
1、运行时类型识别 RTTI(Run-Time Type Identification)

RTTI 即运行时类型识别,是许多编程语言中用于在程序执行期间确定对象类型的一种机制。在Java这种强类型语言中,RTTI提供了一种方式来获取对象的实际类型,这在处理多态性、动态类型转换和反射时尤为重要。


Java中RTTI的主要组成部分:

(1)、instanceof运算符

instanceof关键字用于在运行时检查对象是否是特定类的实例,或者是否实现了特定的接口。它返回一个布尔值,如果对象是指定类型的实例,则返回true,否则返回false

Object obj = new MyClass();
boolean isMyClassInstance = obj instanceof MyClass; // true

(2)、Class类

java.lang.Class类是反射机制的核心类,它代表类的元数据。每个Java类在加载时都会创建一个Class对象。Class对象包含了类的名称、字段、方法、构造函数等信息。

Class<?> clazz = obj.getClass(); // 获取obj的Class对象
String className = clazz.getName(); // 获取类名

(3)、反射API

Java的反射API允许程序在运行时查询和操作类的结构。通过反射,你可以获取类的信息,创建对象实例,调用方法,访问字段等。

Class<?> clazz = Class.forName("MyClass");
Constructor<?> constructor = clazz.getConstructor();
Object obj = constructor.newInstance();

(4)、类型转换

在Java中,类型转换分为自动类型转换(向上转型)和强制类型转换(向下转型)。向上转型不需要显式操作,因为子类可以自动转换为父类类型。向下转型需要显式操作,并且通常需要instanceof检查以确保转换的安全性。

// 向上转型
MyClass myClass = new SubClass();// 向下转型,需要先检查类型
if (myClass instanceof SubClass) {SubClass subClass = (SubClass) myClass;
}

(5)、动态方法分派

在Java中,方法调用是基于对象的实际类型进行分派的,这称为动态绑定或晚期绑定。这意味着即使方法调用在编译时是未知的,JVM在运行时也能确定调用哪个方法。

class Base {void show() { System.out.println("Base"); }
}
class Derived extends Base {void show() { System.out.println("Derived"); }
}Base base = new Derived();
base.show(); // 输出 "Derived",因为base实际上是Derived类型

RTTI的优点:

  • 灵活性:RTTI提供了在运行时处理不同类型的灵活性,使得代码更加通用。
  • 多态性:RTTI支持多态性,允许将子类对象视为父类类型,而JVM在运行时确定正确的方法实现。
  • 动态行为:通过反射,RTTI允许程序在运行时动态地改变其行为。

RTTI的缺点:

  • 性能开销:使用RTTI,特别是反射,可能会引入额外的性能开销。
  • 安全问题:RTTI可能会破坏封装性,允许访问私有成员,这可能导致安全问题。
  • 复杂性:过度依赖RTTI可能会使代码难以理解和维护。

RTTI是Java中一个强大的特性,它使得程序能够在运行时识别对象的实际类型,并执行相应的操作。正确使用RTTI可以提高程序的灵活性和动态性,但开发者需要权衡其性能和安全性的影响。


2、Class类

Java中的每个类都隐式地继承自java.lang.Object类,而java.lang.ClassObject的一个子类。

Class类是反射的核心,它代表了一个类或接口的静态类型信息。

每个Java类型(类、接口、数组等)都有一个对应的Class对象。

数组同样也被映射为class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象。

基本类型boolean,byte,char,short,int,long,float,double和关键字void同样表现为 class 对象。

每个java类运行时都在JVM里表现为一个class对象,可通过类名.class、类型.getClass()、Class.forName(“类名”)等方法获取class对象)。

public final class Class<T> implements java.io.Serializable,GenericDeclaration,Type,AnnotatedElement {private static final int ANNOTATION= 0x00002000;private static final int ENUM      = 0x00004000;private static final int SYNTHETIC = 0x00001000;private static native void registerNatives();static {registerNatives();}/** Private constructor. Only the Java Virtual Machine creates Class objects.   //私有构造器,只有JVM才能调用创建Class对象* This constructor is not used and prevents the default constructor being* generated.*/private Class(ClassLoader loader) {// Initialize final field for classLoader.  The initialization value of non-null// prevents future JIT optimizations from assuming this final field is null.classLoader = loader;}

由上可知:

  • Class类也是类的一种,与class关键字是不一样的。

  • 手动编写的类被编译后会产生一个Class对象,其表示的是创建的类的类型信息,而且这个Class对象保存在同名.class的文件中(字节码文件)

  • 每个通过关键字class标识的类,在内存中有且只有一个与之对应的Class对象来描述其类型信息,无论创建多少个实例对象,其依据的都是用一个Class对象。

  • Class类只存私有构造函数,因此对应Class对象只能有JVM创建和加载。

  • Class类的对象作用是运行时提供或获得某个对象的类型信息,这点对于反射技术很重要。

  • 再来看看 Class类的方法

方法名说明
forName()(1)获取Class对象的一个引用,但引用的类还没有加载(该类的第一个对象没有生成)就加载了这个类。
(2) 为了产生Class引用,forName()立即就进行了初始化。
Object-getClass()获取Class对象的一个引用,返回表示该对象的实际类型的Class引用。
getName()取全限定的类名(包括包名),即类的完整名字。
getSimpleName()获取类名(不包括包名)
getCanonicalName()获取全限定的类名(包括包名)
isInterface()判断Class对象是否是表示一个接口
getInterfaces()返回Class对象数组,表示Class对象所引用的类所实现的所有接口。
getSupercalss()返回Class对象,表示Class对象所引用的类所继承的直接基类。应用该方法可在运行时发现一个对象完整的继承结构。
newInstance()返回一个Oject对象,是实现“虚拟构造器”的一种途径。使用该方法创建的类,必须带有无参的构造器。
getFields()获得某个类的所有的公共(public)的字段,包括继承自父类的所有公共字段。 类似的还有getMethods和getConstructors。
getDeclaredFields获得某个类的自己声明的字段,即包括public、private和proteced,默认但是不包括父类声明的任何字段。类似的还有getDeclaredMethods和getDeclaredConstructors。

3、类加载

类加载机制和类字节码技术,感兴趣的朋友请前往查阅。

  • 探索Java的DNA-JVM字节码深度解析
  • Java 类加载机制解密一探到底

其中,这里我们需要回顾的是,类加载机制流程:

包括5个阶段:加载、验证、准备、解析和初始化。其中加载、验证、准备、初始化这4个阶段的顺序是确定的,只有解析阶段在特定情况下可以在初始化之后再开始。

在这里插入图片描述


二、反射组件及使用方法


在Java中,Class类是反射机制的一部分,它代表了一个类或接口的静态类型信息。使用Class类,你可以获取类的信息,包括构造函数、方法、字段等。

以下示例代码,展示如何获取和使用Class类对象。


1、获取Class对象

要获取一个Class对象,你可以使用以下方法之一:

  • 使用.class语法。
  • 使用Class.forName()静态方法。

(1)、使用.class语法
public class MyClass {public void myMethod() {System.out.println("Hello, World!");}
}public class Main {public static void main(String[] args) {Class<MyClass> myClassClass = MyClass.class; // 获取MyClass的Class对象System.out.println(myClassClass.getName()); // 打印类名}
}

(2)、使用Class.forName()
public class Main {public static void main(String[] args) throws ClassNotFoundException {Class<?> myClassClass = Class.forName("com.example.MyClass"); // 获取MyClass的Class对象System.out.println(myClassClass.getName()); // 打印类名}
}

(3)、对象实例获取
Object obj = new Object();
Class clazz = obj.getClass();

2、获取类的构造函数
public class Main {public static void main(String[] args) throws Exception {Class<?> myClassClass = Class.forName("com.example.MyClass");Constructor<?> constructor = myClassClass.getConstructor(); // 获取无参构造函数System.out.println(constructor);}
}

3、创建类的实例
public class Main {public static void main(String[] args) throws Exception {Class<?> myClassClass = Class.forName("com.example.MyClass");Constructor<?> constructor = myClassClass.getConstructor();Object myClassInstance = constructor.newInstance(); // 创建MyClass的实例myClassInstance.getClass().getMethod("myMethod").invoke(myClassInstance); // 调用方法}
}

4、获取类的方法
public class Main {public static void main(String[] args) throws Exception {Class<?> myClassClass = Class.forName("com.example.MyClass");Method method = myClassClass.getMethod("myMethod"); // 获取myMethod方法System.out.println(method);}
}

5、调用方法
public class Main {public static void main(String[] args) throws Exception {Class<?> myClassClass = Class.forName("com.example.MyClass");Constructor<?> constructor = myClassClass.getConstructor();Object myClassInstance = constructor.newInstance();Method method = myClassClass.getMethod("myMethod");method.invoke(myClassInstance); // 调用myMethod方法}
}

6、获取类的字段

反射可以获取某个类的所有属性信息,包括私有属性。

public class Main {public static void main(String[] args) throws Exception {Class<?> myClassClass = Class.forName("com.example.MyClass");Field field = myClassClass.getField("myField"); // 获取public字段System.out.println(field);}
}

7、访问字段的值
public class Main {public static void main(String[] args) throws Exception {Class<?> myClassClass = Class.forName("com.example.MyClass");Constructor<?> constructor = myClassClass.getConstructor();Object myClassInstance = constructor.newInstance();Field field = myClassClass.getField("myField");field.setAccessible(true); // 如果字段是private,需要设置为可访问Object fieldValue = field.get(myClassInstance); // 获取字段值System.out.println(fieldValue);}
}

请注意,以上示例代码中的com.example.MyClass需要替换为实际的类路径。此外,如果类、方法或字段是私有的,你可能需要调用setAccessible(true)来允许反射访问它们。使用反射时要小心,因为它可能会破坏封装性,并带来性能开销。


8、反射获取包信息

Package类提供了一些与包相关的实用方法和信息。比如获取包名、包版本等。示例:

Package pkg = Class.class.getPackage();
System.out.println("包名: " + pkg.getName());
System.out.println("包说明: " + pkg.getSpecificationTitle());
System.out.println("包版本: " + pkg.getSpecificationVersion());

三、反射的优缺点

  • 性能开销:反射操作通常比直接代码调用要慢,因为它涉及到类型解析和动态调用。
  • 安全问题:反射可以破坏封装性,允许代码访问私有成员,这可能导致安全问题。
  • 难以优化:由于反射操作的动态性,JVM难以对其进行优化。

四、反射的使用场景

1、框架开发

许多Java框架(如Spring、Hibernate)使用反射来实现依赖注入、ORM映射等。


(1)、依赖注入(DI)

场景:依赖注入是一种设计模式,用于实现控制反转(IoC),允许框架在运行时自动装配对象的依赖关系。

案例:Spring框架使用反射来实现依赖注入。Spring容器在启动时会扫描指定的包,查找带有特定注解(如@Component@Service等)的类,并为这些类创建实例和管理它们的生命周期。

示例代码

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;public class Main {public static void main(String[] args) {ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);MyService myService = context.getBean(MyService.class);myService.doWork();}
}// AppConfig.java
@Configuration
public class AppConfig {@Beanpublic MyService myService() {return new MyServiceImpl();}
}// MyService.java
public interface MyService {void doWork();
}// MyServiceImpl.java
@Service
public class MyServiceImpl implements MyService {@Overridepublic void doWork() {System.out.println("Doing work...");}
}

(2)、对象关系映射(ORM)

场景:ORM框架允许开发者使用面向对象的方式来操作数据库,而不是使用SQL语句。

案例:Hibernate是一个流行的ORM框架,它使用反射来将Java对象映射到数据库表中。

示例代码

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;public class Main {public static void main(String[] args) {Configuration configuration = new Configuration().configure();SessionFactory sessionFactory = configuration.buildSessionFactory();Session session = sessionFactory.openSession();try {session.beginTransaction();Employee employee = new Employee(1, "John Doe", "Developer");session.save(employee);session.getTransaction().commit();} finally {session.close();}}
}// Employee.java
@Entity
@Table(name = "employees")
public class Employee {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private int id;private String name;private String jobTitle;// Constructors, getters and setters
}

(3)、动态代理

场景:动态代理允许在运行时创建一个实现了一组接口的新类,而不需要事先编写具体的类代码。

案例:Java的java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口可以用来创建动态代理。

示例代码

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;public class Main {public static void main(String[] args) {MyService myService = (MyService) Proxy.newProxyInstance(MyService.class.getClassLoader(),new Class<?>[]{MyService.class},new MyServiceHandler(new MyServiceImpl()));myService.doWork();}
}interface MyService {void doWork();
}class MyServiceImpl implements MyService {public void doWork() {System.out.println("Doing work...");}
}class MyServiceHandler implements InvocationHandler {private MyService myService;public MyServiceHandler(MyService myService) {this.myService = myService;}public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("Before method: " + method.getName());method.invoke(myService, args);System.out.println("After method: " + method.getName());return null;}

2、插件系统

在Java中,反射可以用来实现一个插件系统,这允许主应用程序在运行时加载和使用插件,而无需在编译时知道插件的具体实现。这种机制非常有用,因为它提供了极大的灵活性和可扩展性。

假设我们有一个文本编辑器应用程序,我们希望允许用户通过插件来扩展编辑器的功能,比如添加语法高亮、拼写检查等。


步骤 1: 定义插件接口

首先,我们定义一个插件接口,所有插件都必须实现这个接口。

public interface TextEditorPlugin {void apply(String text);
}

步骤 2: 创建具体插件

然后,我们创建具体的插件实现。

public class SpellCheckPlugin implements TextEditorPlugin {@Overridepublic void apply(String text) {// 实现拼写检查逻辑System.out.println("Spell check applied: " + text);}
}public class SyntaxHighlightPlugin implements TextEditorPlugin {@Overridepublic void apply(String text) {// 实现语法高亮逻辑System.out.println("Syntax highlighted: " + text);}
}

步骤 3: 加载和使用插件

最后,我们使用反射来加载和使用插件。

import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;public class PluginLoader {public static void loadAndApplyPlugins(String pluginPath, String text) {// 创建类加载器URL[] urls = new URL[] { new File(pluginPath).toURI().toURL() };URLClassLoader classLoader = new URLClassLoader(urls);// 获取插件JAR中的所有类List<Class<?>> pluginClasses = new ArrayList<>();try {String[] entries = ((String) new File(pluginPath).listFiles()[0]).split(" ");for (String entry : entries) {Class<?> clazz = Class.forName(entry, true, classLoader);if (TextEditorPlugin.class.isAssignableFrom(clazz)) {pluginClasses.add(clazz);}}} catch (Exception e) {e.printStackTrace();}// 实例化插件并应用for (Class<?> pluginClass : pluginClasses) {try {TextEditorPlugin plugin = (TextEditorPlugin) pluginClass.newInstance();plugin.apply(text);} catch (InstantiationException | IllegalAccessException e) {e.printStackTrace();}}// 关闭类加载器try {classLoader.close();} catch (IOException e) {e.printStackTrace();}}public static void main(String[] args) {String text = "This is a sample text for the editor.";loadAndApplyPlugins("path/to/plugin/folder", text);}
}

在这个例子中,PluginLoader类负责加载插件。它首先创建一个URLClassLoader来加载插件JAR文件。然后,它获取JAR文件中的所有类,并检查这些类是否实现了TextEditorPlugin接口。如果是,它将这些类实例化并调用apply方法。


请注意,这个例子假设插件JAR文件中的类名存储在一个文本文件中,并且这个文本文件位于插件文件夹中。这只是一个简化的示例,实际应用中可能需要更复杂的机制来发现和加载插件。


通过这种方式,我们可以实现一个灵活的插件系统,主应用程序可以在运行时加载和使用插件,而无需在编译时知道插件的具体实现。这为应用程序的扩展和定制提供了极大的便利。


3、运行时配置

反射在运行时配置中非常有用,它允许应用程序根据配置文件在运行时动态加载类和创建对象。这种机制使得应用程序能够灵活地适应不同的环境和需求,而无需重新编译或部署。

运行时配置的使用场景
  • 环境适应性:应用程序可以根据不同的环境(开发、测试、生产)加载不同的配置。
  • 模块化:应用程序可以由多个模块组成,每个模块都可以在运行时动态加载。
  • 可扩展性:应用程序可以设计为可扩展的,允许在运行时添加新功能。
  • 插件支持:如前所述,插件系统可以利用运行时配置来加载和集成插件。

假设我们有一个简单的应用程序,它需要根据配置文件来决定使用哪个服务类来处理请求。我们将使用一个配置文件来指定服务类的全限定名,并使用反射来动态加载和实例化这个类。


步骤 1: 创建服务接口

首先,我们定义一个服务接口,所有的服务类都将实现这个接口。

public interface Service {void execute();
}

步骤 2: 创建具体的服务实现

然后,我们创建具体的服务实现。

public class ServiceImplA implements Service {@Overridepublic void execute() {System.out.println("Executing Service A");}
}public class ServiceImplB implements Service {@Overridepublic void execute() {System.out.println("Executing Service B");}
}

步骤 3: 创建配置文件

接下来,我们创建一个配置文件(例如config.txt),它包含服务类的全限定名。

com.example.ServiceImplA

步骤 4: 使用反射加载和使用服务

最后,我们使用反射来根据配置文件加载和使用服务。

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;public class RuntimeConfigDemo {public static void main(String[] args) {// 从配置文件中读取服务类的名称String className;try {className = new String(Files.readAllBytes(new File("config.txt").toPath()));} catch (IOException e) {e.printStackTrace();return;}// 使用反射加载并实例化服务类Service service = loadService(className);if (service != null) {service.execute();} else {System.out.println("Service class could not be loaded or instantiated.");}}private static Service loadService(String className) {try {Class<?> serviceClass = Class.forName(className);return (Service) serviceClass.getDeclaredConstructor().newInstance();} catch (Exception e) {e.printStackTrace();return null;}}
}

在这个例子中,RuntimeConfigDemo类负责根据配置文件加载服务。它首先从config.txt文件中读取服务类的全限定名,然后使用Class.forName来加载类,接着通过getDeclaredConstructor().newInstance()来创建类的实例。最后,它调用服务的execute方法。

通过这种方式,应用程序可以根据配置文件在运行时动态地加载和使用不同的服务实现,而无需事先知道具体的服务类。这为应用程序提供了极大的灵活性和可配置性。


以上就是本文的主要内容,希望您在阅读过程中有所收获。敬请期待下一期,我将分享更多反射的实战技巧和使用场景,让您的Java之旅更上一层楼!


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

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

相关文章

使用 Ubuntu + Docker + Vaultwarden + Tailscale 自建密码管理器

使用 Ubuntu Docker Vaultwarden Tailscale 自建密码管理器 先决条件 一台运行 Ubuntu 系统的服务器。可以是云提供商的 VPS、家庭网络中的树莓派、或者 Windows 电脑上的虚拟机等等 一个 Tailscale 账户。如果还没有 Tailscale 账户&#xff0c;可以通过此链接迅速创建一个…

SelfKG论文翻译

SelfKG: Self-Supervised Entity Alignment in Knowledge Graphs SelfKG&#xff1a;知识图中的自监督实体对齐 ABSTRACT 实体对齐旨在识别不同知识图谱&#xff08;KG&#xff09;中的等效实体&#xff0c;是构建网络规模知识图谱的基本问题。在其发展过程中&#xff0c;标…

zynq之UART

之前尝试UART0&#xff08;MIO50、51&#xff09;&#xff0c;串口调试助手收到发送的内容。 现在板子上EMIO端有多个串口&#xff0c;所以看看这个怎么弄。 串口是484的转接板&#xff08;接232的串口就会输出乱码&#xff09; https://blog.51cto.com/u_15262460/2882973 …

【九十三】【算法分析与设计】719. 找出第 K 小的数对距离,N 台电脑的最长时间,二分答案法

719. 找出第 K 小的数对距离 - 力扣&#xff08;LeetCode&#xff09; 数对 (a,b) 由整数 a 和 b 组成&#xff0c;其数对距离定义为 a 和 b 的绝对差值。 给你一个整数数组 nums 和一个整数 k &#xff0c;数对由 nums[i] 和 nums[j] 组成且满足 0 < i < j < nums.le…

java调用远程接口下载文件

在postman中这样下载文件 有时下载文件太大postman会闪退&#xff0c;可以通过代码下载&#xff0c;使用hutool的http包

3步操作助您轻松实现苹果手机照片一键传输至电脑

对于很多使用苹果手机的用户来说&#xff0c;随着手机中照片和视频数量的不断积累&#xff0c;如何将这些珍贵的回忆从手机转移到电脑&#xff0c;以便更好地保存、整理和分享&#xff0c;成为了一个值得关注的问题。那么&#xff0c;苹果手机怎么把照片导入电脑呢&#xff1f;…

鸿蒙课程培训 | 讯方技术与鸿蒙生态服务公司签约,成为鸿蒙钻石服务商

3月15日&#xff0c;深圳市讯方技术股份有限公司与鸿蒙生态服务公司签署合作协议&#xff0c;讯方技术成为鸿蒙钻石服务商&#xff0c;正式进军鸿蒙原生应用培训开发领域。讯方技术总裁刘国锋、副总经理刘铭皓、深圳区域总经理张松柏、深圳区域交付总监张梁出席签约仪式。 作…

乡村振兴的乡村产业创新发展:培育乡村新兴产业,打造乡村产业新名片,促进乡村经济多元化发展

目录 一、引言 二、乡村产业创新发展的必要性 &#xff08;一&#xff09;适应新时代发展要求 &#xff08;二&#xff09;满足消费升级需求 &#xff08;三&#xff09;促进农民增收致富 三、培育乡村新兴产业策略 &#xff08;一&#xff09;加强科技创新引领 &#…

Android下HWC以及drm_hwcomposer普法((上)

Android下HWC以及drm_hwcomposer普法((上) 引言 按摩得全套&#xff0c;错了&#xff0c;做事情得全套&#xff0c;普法分析也是如此。drm_hwcomposer如果对Android图形栈有一定研究的童鞋们应该知道它是Android提供的一个的图形后端合成处理HAL模块的实现。但是在分析这个之前…

Java复习-集合篇

集合 集合分为俩大类 单列集合 每个元素数据只包含一个值 双列集合 每个元素包含俩个键值对 Conllection单列集合 单列集合常用的主要是下列几种 List集合 List系列集合的特点&#xff1a;添加元素是有序、可重复、有索引 这里我们来试一下ArrayList ArrayList<String&g…

Spring OAuth2:开发者的安全盾牌!(上)

何利用Spring OAuth2构建坚不可摧的安全体系&#xff1f;如何使用 OAuth2 从跨域挑战到性能优化&#xff0c;每一个环节都为你的应用保驾护航&#xff1f; 文章目录 Spring OAuth2 详解1. 引言简述OAuth2协议的重要性Spring Framework对OAuth2的支持概述 2. 背景介绍2.1 OAuth2…

智能仓储物流系统(WMS)系列-管理查询调整

好的应用系统应是细分简单&#xff0c;界面简洁易操作&#xff0c;程序代码简洁易懂的。

史上最全排序算法整理(2)

本篇文章我们将接着上篇继续介绍常见的排序算法&#xff0c;有需要的小伙伴可以移步史上最全排序算法整理&#xff08;1&#xff09;查看相关内容哦 1.冒泡排序 1.1基本思想 在待排序的一组数中&#xff0c;将相邻的两个数进行比较&#xff0c;若前面的数比后面的数大就交换两…

gitlab 创建 ssh 和 token

文章目录 一、创建ssh key二、将密钥内容复制到gitlab三、创建token 一、创建ssh key 打开控制台cmd&#xff0c;执行命令 ssh-keygen -t rsa -C xxxxx xxxxx是你自己的邮箱 C:\Users\xx\.ssh 目录下会创建一个名为id_rsa.pub的文件&#xff0c;用记事本打开&#xff0c;并…

基于深度学习的中文情感分析系统python flask

基于python的毕业设计 基于深度学习的中文情感分析系统(flask)(源码说明文档演示) 毕业设计课程设计期末大作业、课程设计、高分必看&#xff0c;下载下来&#xff0c;简单部署&#xff0c;就可以使用。 包含&#xff1a;项目源码、数据库脚本、软件工具等&#xff0c;该项目…

【Spring Cloud】微服务工程中的服务注册与发现配置中心-Consul

Catalog Spring Cloud Consul一、需求二、是什么三、优点四、缺点五、怎么用六、细节 Spring Cloud Consul 一、需求 多个微服务之间通过RestTemplate中的api相互调用&#xff0c;一般要写死微服务的IP地址和端口号&#xff0c;相当于硬编码&#xff0c;非常不灵活&#xff0…

MyBatis出现:SQLSyntaxErrorException: Unknown column ‘XXX‘ in ‘field list‘

<update id"updateStudent">update tb_students set stu_name${stuName},stu_gender${stuGender},stu_age${stuAge},stu_tel${stuTel}where stu_num ${stuNum}</update> 本质上来说&#xff0c;是Mybatis使用上的错误&#xff0c;不熟悉&#xff0c;理…

SQL函数--union all 使用方法及案例

1. 使用方法 在 SQL 中&#xff0c;UNION ALL 操作用于结合两个或更多 SELECT 语句的结果集&#xff0c;包括所有匹配的行&#xff0c;甚至包括重复的行。这与 UNION 不同&#xff0c;因为 UNION 会自动删除重复的行。 满足条件&#xff1a; 1、两个select查询的列的数量必须相…

Ai速递5.29

全球AI新闻速递 1.摩尔线程与无问芯穹合作&#xff0c;实现国产 GPU 端到端 AI 大模型实训。 2.宝马工厂&#xff1a;机器狗上岗&#xff0c;可“嗅探”故障隐患。 3.ChatGPT&#xff1a;macOS 开始公测。 4.Stability AI&#xff1a;推出Stable Assistant&#xff0c;可用S…

企业网络的“瑞士军刀”:探索“一端多能”设备的多面性

在数字化时代&#xff0c;企业网络需求的复杂性和多样性不断增长&#xff0c;传统的单一功能网络设备已难以满足这些需求。企业需要一种集多种功能于一身的“一端多能”网络设备&#xff0c;以应对各种网络环境和业务需求&#xff0c;就像是一把多功能、灵活、可靠的瑞士军刀&a…