java反射(reflection)的基本理解和使用

news2024/11/14 3:05:18

目录

一、什么是反射

二、反射的主要用途?

三、什么情况下使用反射

四、反射有什么优点?

1、增加程序的灵活性

2、避免将固有的逻辑程序写死到代码里

3、提高代码的复用率

4、支持动态代理和动态配置

5、支持自动化测试和代码生成

6、自由度高,可以无视访问权限限制

五、反射的缺点

六、反射实现

1、三种获取Class对象的方式

2、通过JSON字符串进行反射

3、Class相关操作

4、constructor反射调用构造方法

5、getConstructors()和getDeclaredConstructors()的区别

6、反射成员变量Field

7、反射调用方法

8、反射调用静态方法

9、模仿mybatis执行sql反射


一、什么是反射

        反射(Reflection)是一种 Java 程序运行期间的动态技术,可以在运行时(runtime )检查、修改其自身结构或行为。通过反射,程序可以访问、检测和修改它自己的类、对象、方法、属性等成员。

二、反射的主要用途?

        动态加载类:程序可以在运行时动态地加载类库中的类;

        动态创建对象:反射可以基于类的信息,程序可以在运行时,动态创建对象实例;

        调用方法:反射可以根据方法名称,程序可以在运行时,动态地调用对象的方法(即使方法在编写程序时还没有定义)

        访问成员变量:反射可以根据成员变量名称,程序可以在运行时,访问和修改成员变量(反射可以访问私有成员变量)

        运行时类型信息:反射允许程序在运行时,查询对象的类型信息,这对于编写通用的代码和库非常有用;

  • spring 框架使用反射来自动装配组件,实现依赖注入;
  • MyBatis 框架使用反射来创建 resultType 对象,封装数据查询结果;

三、什么情况下使用反射

        Java中的反射机制是指在程序运行时动态获取类的信息以及使用该信息来创建、操作和销毁对象的能力。下面是一些Java中使用反射的情况:

  • 动态加载类:可以使用类加载器动态加载要使用的类,而不是在编译期间声明对该类的依赖关系。
  • 获取类的信息:通过反射可以获取一个类的属性、方法、构造函数等信息,甚至可以获取注解和泛型信息。
  • 通过名称调用方法或访问属性:使用反射可以根据方法/属性名称动态地调用/访问对应的方法/属性,这使得编写通用代码更加容易。
  • 动态代理:使用反射可以实现动态代理,即在运行时动态地创建一个实现某个接口的代理类,从而实现一些特殊的功能,如事务处理等。

四、反射有什么优点?

1、增加程序的灵活性

        反射允许程序在运行时动态地访问和操作类的属性和方法,这意味着开发者可以根据需要,在程序执行过程中改变程序的行为,而无需在编译时硬编码这些行为。这种灵活性使得程序能够适应不同的运行环境或需求变化。

2、避免将固有的逻辑程序写死到代码里

        通过反射,开发者可以将一些决策逻辑或行为延迟到运行时进行,而不是在编译时就确定下来。这有助于减少代码中的硬编码,提高代码的可维护性和可扩展性。

3、提高代码的复用率

反射使得开发者可以编写一些通用的函数或方法,这些函数或方法能够动态地处理不同类型的对象或方法。这有助于提高代码的复用率,减少重复代码的编写。

4、支持动态代理和动态配置

        通过反射,开发者可以在运行时动态地创建代理对象,从而实现对目标对象的代理。这种机制在 AOP(面向切面编程)等领域中非常有用。此外,反射还支持在运行时读取和修改程序的配置参数,从而实现动态配置。

5、支持自动化测试和代码生成

        反射技术可以应用于白动化测试中,通过动态地创建测试用例来验证程序的行为。同时,反射还可以用于在运行时生成程序代码,这在某些需要动态构建程序结构的场景中非常有用。

6、自由度高,可以无视访问权限限制

        在某些情况下,反射可以绕过 ava 等语言的访问控制机制,直接访问私有成员。虽然这可能会带来一些安全风险,但在某些特定的应用场景中,这种能力是非常有用的;

五、反射的缺点

        性能开销:反射操作通常比直接代码调用更慢,因为涉及到额外的检查和解步骤

        安全问题:如果允许程序修改私有成员或调用私有方法,可能会破坏封装性和安全性。

        代码可读性和可维护性:过度使用反射可能会使代码难以理解和维护,因为它隐藏了类型之间的依赖关系。

六、反射实现

        首先是定义了一个实体类Document

public class Document {

	private String name;
	private int favCount;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getFavCount() {
		return favCount;
	}

	public void setFavCount(int favCount) {
		this.favCount = favCount;
	}

	public Document() {
		super();
	}

	public Document(String name, int favCount) {
		super();
		this.name = name;
		this.favCount = favCount;
	}

	@Override
	public String toString() {
		return "Document [name=" + name + ", favCount=" + favCount + "]";
	}

	private Document(String name) {
		super();
		this.name = name;
	}

	public Document(int favCount) {
		super();
		this.favCount = favCount;
	}
	
	public static void dosth() {
		System.out.println("这是一个dosth静态方法");
	}
	
}
1、三种获取Class对象的方式
public static void main(String[] args) throws ClassNotFoundException {
		// 方式1:通过类名
		Class stringClass1 = String.class;
		
		// 方式2:通过Class类的forName()方法
		Class stringClass2 = Class.forName("java.lang.String");
		
		// 方式3:通过对象调用getClass()方法
		Class stringClass3 = "".getClass();
		
		System.out.println(stringClass1.hashCode());
		System.out.println(stringClass2.hashCode());
		System.out.println(stringClass3.hashCode());
			
	}

        Class对象是 Java 反射机制的一部分,表示运行时的类或接口信息。一个类在 JVM 中只有一个 Class 对象,因此无论通过哪种方式获取该类的 Class 对象,结果都是相同的。

        首先第一种通过类名后加.class来获取class对象。第二种通过Class类的forName方法,根据类的全限定名获取Class对象。第三种是通过一个对象调用getClass方法获取该对象所属类的 Class 对象。

2、通过JSON字符串进行反射
	public static void main(String[] args) {
		String json = "{\"name\":\"长安荔枝\",\"favCount\":234}";
		
		// 方法定义
		// public static T parseObject(String json,Class jsonClass)
		
		Document doc = JSON.parseObject(json,Document.class);
		System.out.println(doc.getName());
		System.out.println(doc.getFavCount());
	}

        这里导入了fastjson的jar包,获取到了JSON类来调用对应的方法,把json字符串传递给了parseObject方法,它是一个静态的方法,它将 JSON 字符串解析为指定类型的 Java 对象。第一个参数是传入的json字符串,后面是要解析的对象类型。最后转换成功后打印对象的两个属性。

        执行结果:

 

3、Class相关操作
	public static void main(String[] args) throws ClassNotFoundException {
		Class clz = Class.forName("java.util.HashMap");

		// 获取类名
		System.out.println("完全限定名:" + clz.getName());
		System.out.println("简单的类名:" + clz.getSimpleName());
		System.out.println();

		// 获取包名
		System.out.println("package包名:" + clz.getPackage().getName());
		System.out.println();

		// 获取成员变量
		Field[] fieldArray = clz.getDeclaredFields();
		System.out.println("成员变量字段");
		for (Field field : fieldArray) {
			System.out.println(field);
		}
		System.out.println();

		// 获取成员方法
		Method[] methodArray = clz.getDeclaredMethods();
		System.out.println("成员方法");
		for (Method method : methodArray) {
			System.out.println(method);
		}
	}

        调用类名后通过调用.getName()返回类的完全限定名,比如java.util.HashMap。

        通过.getSimpleName()返回类的简单名(不包含包名的类名),比如HashMap。

        通过.getPackage().getName()返回类所在包的名称,比如java.util。

        通过.getDeclaredMethods()方法返回类中所有的成员方法,包括私有、保护、默认(包)和公共方法,最后打印所有对象信息。

        执行结果:

4、constructor反射调用构造方法
	public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {
		Class clz = Class.forName("com.apesource.demo01.Document");
		
		// 方式1:直接通过Class对象,调用newInstance()方法
		Object objx = clz.newInstance(); // 相当于在执行无参构造方法
		
		// 方式2:通过构造器(构造方法)
		// 无参构造方法
		Constructor constructor1 = clz.getDeclaredConstructor(); // 获取无参构造方法
		System.out.println(constructor1);
		Object obj1 = constructor1.newInstance();
		
		// 有参构造方法
		Constructor constructor2 = clz.getDeclaredConstructor(String.class); // 获取有参构造方法
		System.out.println(constructor2);
		Object obj2 = constructor2.newInstance("两京十五日");
		
		Constructor constructor3 = clz.getDeclaredConstructor(int.class); // 获取有参构造方法
		System.out.println(constructor2);
		Object obj3 = constructor3.newInstance(11);
		
		Constructor constructor4 = clz.getDeclaredConstructor(String.class,int.class); // 获取有参构造方法
		System.out.println(constructor2);
		Object obj4 = constructor4.newInstance("两京十五日",11);
		
		System.out.println(objx);
		System.out.println(obj1);
		System.out.println(obj2);
		System.out.println(obj3);
		System.out.println(obj4);
	}

        首先获取Class对象,通过调用newInstance()方法直接创建类的实例。通过调用这个方法会默认调用无参构造方法。如果没有无参或者默认无参就会抛出异常。

        然后通过调用getDeclaredConstructor()获取无参构造方法,并创建 Constructor 对象,如果里面传入了参数就会返回对应的有参无参 Constructor 对象。然后再通过.newInstance()方法调用对应的构造方法,有参数则传入参数,无参数就无需传参,然后根据构造方法创造对应的实体类对象(这里是指Document对象)。

        执行结果:

5、getConstructors()和getDeclaredConstructors()的区别
	public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, ClassNotFoundException {
		Class clz = Class.forName("com.apesource.demo01.Document");
		
		// 获取一组构造器
		Constructor[] constructorArray1 = clz.getConstructors(); // public
		Constructor[] constructorArray2 = clz.getDeclaredConstructors(); // public、private
		
		// 获取指定构造器
		Constructor constructor = clz.getDeclaredConstructor(String.class);
		System.out.println(constructor);
		
		// 调用私有构造器,必须设置它的访问权限
		constructor.setAccessible(true);
		
		// 调用构造器,创建对象
		Object obj = constructor.newInstance("长安三万里");
		System.out.println(obj);
	}

        这里调用不同的方法获取Constructor对象数组,通过getConstructors()方法返回类的所有public构造器数组,意味着只能获取那些在外部可见的构造器,比如public等。

        通过getDeclaredConstructors()方法返回类的所有构造器数组,包括 public、protected、default(包级私有)、和 private 构造器,无论它们的访问修饰符如何。

        虽然getConstructors()获取私有的构造方法,但是任然不对其进行调用操作,所以这里可以用setAccessible(true)方法来绕过 Java 访问控制检查,使其可以被调用。

6、反射成员变量Field
	public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException, SecurityException {
		// 硬编码的方式
		Document doc1 = new Document();
//		doc1.name = "";
//		doc1.favCount = 64;

		// 反射的方式
		Class clz = Class.forName("com.apesource.demo01.Document"); // 获取类型信息
		Object obj = clz.newInstance(); // 创建对象

		// 获取指定名称的成员变量
		Field nameField = clz.getDeclaredField("name");
		Field favCountField = clz.getDeclaredField("favCount");

		// 访问私有的成员变量,必须设置权限
		nameField.setAccessible(true);
		favCountField.setAccessible(true);

		// 使用成员变量,将指定数据存入对象中
		nameField.set(obj, "长安十二时辰");
		favCountField.setInt(obj, 128);

		System.out.println(obj);

	}

        这里通过getDeclaredField("属性名")获取类中名为 name 的字段(成员变量),即使这个字段是私有的。

        由于 name 和 favCount 是私有字段,直接访问会被 Java 的访问控制机制阻止。通过 setAccessible(true)可以绕过访问控制,使得可以访问和修改这些私有字段。

        然后通过set方法,这个方法可以传与对应的字段相同类型的参数value 的类型必须能够赋值给 name 字段,使用 nameField 字段对象将 "长安十二时辰" 的值设置到 obj 对象的 name 字段中setInt方法是可以设置int属性,使用 favCountField 字段对象将 128 的值设置到 obj 对象的 favCount 字段中。最后打印对象。

7、反射调用方法
	public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {
		// 硬编码的方式
//		Document doc1 = new Document();
//		doc1.setName("海底两万里");
//		doc1.setFavCount(10025);
		
		// 反射的方式
		Class clz = Class.forName("com.apesource.demo01.Document");
		Object doc1 = clz.newInstance(); // 创建对象
		
		// 获取指定名称和参数类型的方法
		Method setNameMethod = clz.getMethod("setName", String.class);
		Method setFavCountMethod = clz.getMethod("setFavCount",int.class);
		
		// 执行方法
		// 相当于doc1.setName("海底两万里");
		setNameMethod.invoke(doc1, "海底两万里");
		
		// doc1.setFavCount(10025);
		setFavCountMethod.invoke(doc1, 10025);
		
		System.out.println(doc1);
	}

        这里通过getMethod方法,获取 Document 类中名为 setName 且参数类型为 String 的 public 方法的 Method 对象。后面的方法过程相同。

        然后再通过invoke方法,相当于调用 doc1.setName("海底两万里"),将 name 字段设置为 "海底两万里"。后面的调用过程相同。

        最后输出结果。

8、反射调用静态方法
	public static void main(String[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, ClassNotFoundException {
		// 硬编码的方式
		// 调用静态方法
		// Document.dosth();
		String ret1 = String.format("HashMap的默认初始容量是%d,加载因子默认是%.2f", 16, 0.75f);
		System.out.println(ret1);

		// 反射的方式
		Class clz1 = Class.forName("com.apesource.demo01.Document");
		Method dosthMethod = clz1.getMethod("dosth");
		dosthMethod.invoke(null);

		Class clz2 = String.class;
		Method formatMehtod = clz2.getMethod("format", String.class, Object[].class);
		String ret2 = formatMehtod.invoke(null, "HashMap的默认初始容量是%d,加载因子默认是%.2f", new Object[] { 16, 0.75f }).toString();

		System.out.println(ret2);
	}

        还是一样的通过getMethod方法来获取dosth静态方法的Method对象,因为dosth是静态方法,所以第一个参数可以传 null这相当于 Document.dosth()。然后调用的format方法时,传入的是有参,第一个参数写为null,后面的参数正常来写。

        最后输出结果。

9、模仿mybatis执行sql反射

        首先我们定义一个实体类Subject

public class Subject {
	private int id;
	private String title;
	private String url;
	private boolean playable;
	private boolean isNew;
	private double rate;
	private String cover;
	private String type;
	
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getTitle() {
		return title;
	}
	public void setTitle(String title) {
		this.title = title;
	}
	public String getUrl() {
		return url;
	}
	public void setUrl(String url) {
		this.url = url;
	}
	public boolean isPlayable() {
		return playable;
	}
	public void setPlayable(boolean playable) {
		this.playable = playable;
	}
	public boolean isNew() {
		return isNew;
	}
	public void setNew(boolean isNew) {
		this.isNew = isNew;
	}
	public double getRate() {
		return rate;
	}
	public void setRate(double rate) {
		this.rate = rate;
	}
	public String getCover() {
		return cover;
	}
	public void setCover(String cover) {
		this.cover = cover;
	}
	public String getType() {
		return type;
	}
	public void setType(String type) {
		this.type = type;
	}
	@Override
	public String toString() {
		return "Subject [id=" + id + ", title=" + title + ", url=" + url + ", playable=" + playable + ", isNew=" + isNew
				+ ", rate=" + rate + ", cover=" + cover + ", type=" + type + "]";
	}
	
	
}

       

        然后我们在sql.properties配置文件中写入sql查询语句:

sql = select id,title,url,playable,is_new As isNew,rate,cover,type from subject

       

        我们要创建一个sql执行器

// SQL执行器
public class SQLHandler {
	private static final String JDBC_URL = "jdbc:mysql://localhost:3306/mybatis_demo?serverTimezone=GMT";
	private static final String DB_USER = "root";
	private static final String DB_PASS = "123456";

        前三个定义的静态成员常量是JDBC_URL、DB_USER 和 DB_PASS 是连接数据库所需的参数。分别是指定了 MySQL 数据库的连接 URL、数据库的用户名和密码

       

        然后是通过上面三个静态常量创建好的连接对象来创建创建 PreparedStatement 对象以执行 SQL 查询。ResultSet 对象用于存储查询结果。

	// 执行
	public static <T> List<T> execute(String sql, Class<T> resultType) throws NoSuchFieldException, SecurityException {
		try {
			// 创建Connection链接对象
			Connection con = DriverManager.getConnection(JDBC_URL, DB_USER, DB_PASS);

			// 创建PerparedStatement执行对象
			PreparedStatement pst = con.prepareStatement(sql);

			// 执行SQL语句,获取结果集
			ResultSet rs = pst.executeQuery();

        

        通过.getMetaData()方法获取所有刚才提取到的结果集的字段名称。然后创建一个用于保存查询出来字段结果的List集合。

        // 获取结果集的字段名称
        ResultSetMetaData resultSetMetaData = rs.getMetaData();

        // 创建一个用于保存查询结果的List集合
        List<T> queryList = new ArrayList<T>();

        建立一个循环,每次都回去到结果集的每一行,通过反射创建 resultType 类的一个新实例。

        while (rs.next()) {
            // 每行数据,对应一个数据对象(反射方式创建)
            Object resultData = resultType.newInstance();

        因为一行数据都包含了所有字段,要完成所有字段的注入,才能完成对象的注入,完成实例化创建加载。所以这里进行遍历刚才提取到的所有字段,通过getColumnLabel(i)提取到对应的字段名,然后根据字段名调用getDeclaredField()获取 resultType 类中对应字段的 Field 对象。最后在设置是允许访问私有字段。

            // 按照字段名称,获取字段别名
            for (int i = 1; i <= resultSetMetaData.getColumnCount(); i++) {
                // 获取当前字段名
                String columnLabel = resultSetMetaData.getColumnLabel(i);

                // 根据字段名称,获取成绩变量对象
                Field field = resultType.getDeclaredField(columnLabel);
                field.setAccessible(true);

        然后是根据当前字段的类型来进行判断注入,通过.getColumnType(i)获取当前的字段类型,再根据.getColumnType(i)返回类型枚举来进行判断,如果是Types.INTEGER的话就是匹配获取数据类型为int数据设置到 resultData 对象中。Types.DOUBLE就是匹配获取数据类型为double数据设置到 resultData 对象中。如果其他类型的话就直接设置字符串值。如果字段是布尔类型,则需要额外处理,判断是否为true或者false,如果是则按照布尔类型来注入,如果是String类型,则正常注入。

            // 判断当前字段的类型
            // 根据字段名称,获取当前行中的指定数据
            switch (resultSetMetaData.getColumnType(i)) {
                case Types.INTEGER:
                    field.setInt(resultData, rs.getInt(columnLabel));
                    break;
                case Types.DOUBLE:
                    field.setDouble(resultData, rs.getDouble(columnLabel));
                    break;
                default:
                    String value = rs.getString(columnLabel);
                    if ("true".equals(value) || "false".equals(value)) {
                        field.setBoolean(resultData, Boolean.valueOf(value));
                    } else {
                        field.set(resultData, value);
                    }
            }

        最后将填充好的 resultData 对象添加到 queryList 中, 捕获 InstantiationException、IllegalAccessException 和 SQLException 异常,打印错误堆栈并返回空列表。

        接着就新建一个Test测试类进行测试:

public class Test {
	public static void main(String[] args) throws IOException, NoSuchFieldException, SecurityException {
		// 创建Properties对象
		Properties props = new Properties();
		
		// 读取执行的配置文件
		InputStream in = Test.class.getResourceAsStream("sql.properties");
		props.load(in);
	
		String sql = props.getProperty("sql");
		List<Subject> lsub = SQLHandler.execute(sql, Subject.class);
		System.out.println("查询结果数量:" + lsub.size());
		for(Subject Subject : lsub) {
			System.out.println(Subject);
		}
		
//		List<City> lcity = SQLHandler.execute(sql, City.class);
//		System.out.println("查询结果数量:" + lcity.size());
//		for(City city : lcity) {
//			System.out.println(city);
//		}
	}
}

        首先是创建一个Properties对象,Properties是一个用于存储键值对的类,通常用于读取配置文件中的数据。

        然后通过getResourceAsStream()方法从sql.properties文件中读取数据,再将配置文件内容加载到Properties对象中。在上面我已经将properties文件展示出来了。

        接着通过getProperty()方法到sql.properties文件读取键为sql的SQL查询语句。然后通过之前定义的 SQLHandler 类执行 SQL 查询,并将结果映射到 Subject 类的对象中,结果以 List<Subject> 的形式返回。

        最后输出查询结果的数量和每个 Subject 对象的内容。    

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

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

相关文章

ABAP EXCEL宏函数应用

【应用场景】 1. excel导出项目及对应的分期,楼栋的各个产品类型对应的各个面积指标数据, 分项目/分期/楼栋三个SHEET页签。当用户在楼栋层级编辑完产品类型对应的面积指标时,可以 通过宏函数自下往上先汇总到相同产品类型+面积指标的分期层级,再自动汇总到项目层级面积…

万龙觉醒免费辅助脚本:VMOS云手机助力物资获取与养成!

在《万龙觉醒》中&#xff0c;资源获取和英雄养成是游戏的重要组成部分&#xff0c;而使用VMOS云手机可以为玩家带来更高效的游戏体验。通过使用VMOS云手机&#xff0c;玩家可以轻松实现24小时不间断的游戏辅助&#xff0c;无需反复安装或更新游戏&#xff0c;因为VMOS云手机自…

upload-labs通关教程

一.第一关&#xff08;前端绕过&#xff09; 首先准备一个php小马 <?php phpinfo();?>这是调用phpinfo这个函数&#xff0c;查看php的配置信息 首先上传1.php发现 使用BP抓不到包&#xff0c;猜测被前端验证了&#xff0c;查看网页源代码 可以看到下面有一段js代码过…

国内超声波清洗机哪个品牌好?非常好用的超声波清洗机

在当今社会快速生活的节奏下&#xff0c;个人健康与生活品质成为了焦点话题。面对传统清洁方法的局限性和可能对物品造成的伤害&#xff0c;人们愈发倾向于探索更高效、安全的清洁方案。超声波清洗技术恰逢其时地步入大众视野&#xff0c;凭借其高效清洁力与环保特性&#xff0…

鸿蒙开发(API 12 Beta6版)【P2P模式】 网络WLAN服务开发

概述 无线局域网&#xff08;Wireless Local Area Networks&#xff0c;WLAN&#xff09;&#xff0c;是通过无线电、红外光信号或者其他技术发送和接收数据的局域网&#xff0c;用户可以通过WLAN实现结点之间无物理连接的网络通讯。常用于用户携带可移动终端的办公、公众环境…

打造无死角安防网:EasyCVR平台如何助力智慧警务实现视频+AI的全面覆盖

一、背景概述 随着科技的飞速发展&#xff0c;智慧城市建设已成为提升社会治理能力、增强公共安全水平的重要途径。在警务领域&#xff0c;智慧警务作为智慧城市的重要组成部分&#xff0c;正通过融合视频监控技术与人工智能&#xff08;AI&#xff09;解决方案&#xff0c;实…

数据库中的主码、候选码、主属性、非主属性

参考链接 候选码、主码、全码、外码、主属性、主键、主关键字、非主属性清晰总结 - 知乎 (zhihu.com) 1.码&#xff1a; 能够标识一条记录的属性或者属性集 2.候选码 能够标识一条记录的最小属性集 任一候选键的任何真子集都不能唯一标识一个记录&#xff08;比如在成绩表…

诸葛io:消费金融行业智能运营与数智化经营模型构建

规范化的快速发展已成为消费金融行业的主旋律&#xff0c;消金公司亟待制定数智化转型战略&#xff0c;建设自主营销与精细化运营能力&#xff0c;以找到存量时代的全新增长密码。 容联云旗下诸葛智能公司作为数智化经营领域的杰出代表&#xff0c;凭借数据技术先进性、金融业…

将esp32-s3-eye做为USB网络摄像头(UVC设备)

官方网址&#xff1a;usb_webcam 支持UVC同步、批量传输模型只支持MJPEG传输格式支持板上LCD动画esp32-s3-eye&#xff08;IDF v5.0或更高版本&#xff09; 硬件要求 官方默认的USB WebCam config就是乐鑫带摄像头OV2604的esp32-s3-eye&#xff0c;其他的开发板可以参考官方网…

仿微信QQ聊天系统源码设计开发

一、引言 随着移动互联网的快速发展&#xff0c;即时通讯软件已成为人们日常生活中不可或缺的一部分。仿微信QQ聊天系统作为一种模仿主流聊天软件功能的应用&#xff0c;旨在为用户提供高效、便捷的沟通方式。本文将从系统架构、功能模块等方面详细介绍仿微信QQ聊天系统的设计…

Visual studio自动添加头部注释

记事本打开VS版本安装目录下的Class.cs文件 增加如下内容&#xff1a;

Linux 访问控制列表(Access Control List)

在Linux中&#xff0c;目录或文件的权限是针对的所有者(owner)&#xff0c;所属组(group)&#xff0c;其他人(others)这3种类别来设置的。这种根据类别控制权限的方法无法精确控制每个用户的行为。为了解决这个问题&#xff0c;Linux引入了访问控制列表&#xff08;Access Cont…

网络安全知识:什么是访问控制列表 (ACL)?

访问控制列表 (ACL) 是网络安全和管理的基础。它们在确定谁或什么可以访问网络内的特定资源方面发挥着重要作用。 本文深入探讨了 ACL 的复杂性&#xff0c;探索了其类型、组件、应用程序和最佳实践。我们还将比较不同操作系统的 ACL&#xff0c;并讨论它们在网络架构中的战略…

基于C#的UDP协议消息传输

1. 服务端 internal class Program{static void Main(string[] args){//1.创建SocketSocket socketServer new Socket(AddressFamily.InterNetwork,SocketType.Dgram,ProtocolType.Udp);//2.绑定IP、端口号EndPoint endPoint new IPEndPoint(IPAddress.Parse("127.0.0.1&…

Grid布局常用属性梳理

布局属性 aligin-items 作用&#xff1a;CSS align-items 属性设置了所有直接子元素的 align-self 值作为一个组。在 Flexbox 中&#xff0c;它控制子元素在交叉轴上的对齐。在 Grid 布局中&#xff0c;它控制了子元素在其网格区域内的块向轴上的对齐。 应用对象&#xff1a;…

VIC leak detection MS40-MS50检漏仪手侧

VIC leak detection MS40-MS50检漏仪手侧

web基础之XSS

一、搭建XSS平台 安装 1、我这里安装在本地的Phpstudy上&#xff0c;安装过程就是一路下一步&#xff08;可以改安装路径&#xff09;&#xff0c;附上下载链接&#xff1a; # 官网&#xff1a;https://www.xp.cn/download.html# 蓝莲花 - github下载 https://github.com/fi…

报名啦|PolarDB数据库创新设计赛(天池杯)等你来战

2024年全国大学生计算机系统能力大赛PolarDB数据库创新设计赛&#xff08;天池杯&#xff09;现已正式启动报名&#xff0c;大赛面向全国普通高等学校全日制在读本专科学生&#xff0c;欢迎同学们踊跃参加&#xff01; 大赛介绍 2024年全国大学生计算机系统能力大赛PolarDB数…

C语言:刷题日志(3)

一.猴子选大王 一群猴子要选新猴王。新猴王的选择方法是&#xff1a;让N只候选猴子围成一圈&#xff0c;从某位置起顺序编号为1~N号。从第1号开始报数&#xff0c;每轮从1报到3&#xff0c;凡报到3的猴子即退出圈子&#xff0c;接着又从紧邻的下一只猴子开始同样的报数。如此不…