浅谈反射机制

1. 何为反射?

反射(Reflection)机制指的是程序在运行的时候能够获取自身的信息。具体来说,反射允许程序在运行时获取关于自己代码的各种信息。如果知道一个类的名称或者它的一个实例对象, 就能把这个类的所有方法和变量的信息(方法名,变量名,方法,修饰符,类型,方法参数等等所有信息)找出来。这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。简单来说,就是面向对象看待一个类,即以面向对象的思想来抽象一个类。

反射是一种强大的功能,广泛应用于框架设计、依赖注入、单元测试和动态代理等领域。然而,由于反射会绕过编译时的类型安全检查,所以使用不当可能会导致更高的运行时错误几率和性能开销。因此,在使用反射时需要格外谨慎,以避免潜在的风险。

2. 反射的意义所在?

  1. 通过反射机制可以让程序创建和控制任何类的对象,无需提前硬编码目标类。
  2. 使用反射机制能够在运行时构造一个类的对象、判断一个类所具有的成员变量和方法、调用一个对象的方法。
  3. 反射机制是构建框架技术的基础所在,使用反射可以避免将代码写死在框架中。

不过反射也存在明显的缺点:使用反射性能较低,需要解析字节码,且如果将内存中的对象进行解析,相对不安全,会破坏封装性。

3. 通过反射实例化对象

在Java中,可以通过反射获取类的构造器、方法、字段等信息:

class Test {public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {// 下面这段代码是以反射机制的方式创建对象。// 通过反射机制,获取Class,通过Class来实例化对象Class c = Class.forName("com.reflectBean.User");// newInstance() 这个方法会调用User这个类的无参数构造方法,完成对象的创建。// 重点是:newInstance()调用的是无参构造,必须保证无参构造是存在的!Object obj = c.newInstance();Method method = c.getMethod("myMethod");method.invoke(obj);}
}

4. 获取Class类的三种方式

在Java编程中,获取 Class 对象的三种常用方式如下:

1. 通过类名的静态属性 class: 这是获取 Class 对象最简单的方法,适用于已知编译时类型的情况。

Class<MyClass> clazz = MyClass.class;

2. 通过对象的实例方法 getClass: 这是在运行时获取一个对象的 Class 对象的方法,适用于已知一个实例化对象的情况。

MyClass obj = new MyClass();
Class<? extends MyClass> clazz = obj.getClass();

3. 通过类名的字符串 forName: 这是动态加载类的一种方式,特别有用在类名只有在运行时才能确定的情况下。

try {Class<?> clazz = Class.forName("com.reflectBean.User");
} catch (ClassNotFoundException e) {e.printStackTrace();
}

5. 常用的Class类的方法

Class 类在 Java 中定义了大量的方法,可以用来反射性地获取类的各种信息或进行操作。不过需要注意的是,在使用反射进行私有成员访问时,需要设置相应的访问权限,如 setAccessible(true)。以下是一些常用的方法:

1. 类信息获取方法

  • getName():返回完整类名带包名。

  • getSimpleName():获取类的简单名称。

  • getCanonicalName():获取类的规范名称。

  • getPackage():获取类所在的包。

  • getModifiers():获取类的修饰符(如 public, abstract, final 等)。

2. 构造函数相关方法

  • getConstructor(Class<?>... parameterTypes):获取指定参数类型的公共构造函数。

  • getConstructors():获取所有公共构造函数。

  • getDeclaredConstructor(Class<?>... parameterTypes):获取指定参数类型的任意(包括私有)构造函数。

  • getDeclaredConstructors():获取所有任意(包括私有)构造函数。

3. 方法相关方法

  • getMethod(String name, Class<?>... parameterTypes):获取指定名称和参数类型的公共方法。
  • getMethods():获取所有公共方法,包括从父类继承的方法。
  • getDeclaredMethod(String name, Class<?>... parameterTypes):获取指定名称和参数类型的任意方法。
  • getDeclaredMethods():获取所有任意方法(包括私有)。

4. 字段(成员变量)相关方法

  • getField(String name):获取指定名称的公共字段。
  • getFields():获取所有公共字段。
  • getDeclaredField(String name):获取指定名称的任意字段。
  • getDeclaredFields():获取所有任意字段(包括私有)。

5. 类层次结构相关方法

  • getSuperclass():获取父类。
  • getInterfaces():获取实现的接口。
  • isAssignableFrom(Class<?> cls):判断当前类或接口是否可以从指定类型的对象赋值(即能否进行类型转换)。

6. 实例创建方法

  • newInstance():创建类的实例,需要无参构造函数。

7. 其他方法

  • getAnnotations():获取类上的注解。
  • isAnnotation():判断类是否是注解类型。
  • isInterface():判断是否为接口。
  • isArray():判断是否为数组。
  • isPrimitive():判断是否为基本数据类型。
  • isInstance(Object obj):判断给定对象是否为该 Class 的实例。

6. 获取Field的四种方式

在Java中,可以通过Class对象获取某个类的字段(Field),Field类代表类的成员变量。获取 Field的几种常用方式如下:

1. 通过字段名获取公共字段 (public 属性):

try {Class<?> clazz = Class.forName("com.reflectBean.User");Field field = clazz.getField("userName");// 对 field 进行操作// ...} catch (NoSuchFieldException | ClassNotFoundException e) {e.printStackTrace();
}

2. 获取所有公共字段 (public 属性):

try {Class<?> clazz = Class.forName("com.reflectBean.User");Field[] fields = clazz.getFields();for (Field field : fields) {// 对每个 field 进行操作// ...    }
} catch (ClassNotFoundException e) {e.printStackTrace();
}

3. 通过字段名获取任何字段(包括私有、保护和包访问属性):

try {Class<?> clazz = Class.forName("com.reflectBean.User");Field field = clazz.getDeclaredField("password");// 允许访问私有字段field.setAccessible(true);  // 对 field 进行操作,比如设置可访问性field.setAccessible(true);
} catch (NoSuchFieldException | ClassNotFoundException e) {e.printStackTrace();
}

4. 获取所有字段(包括私有、保护和包访问属性):

try {Class<?> clazz = Class.forName("com.reflectBean.User");Field[] fields = clazz.getDeclaredFields();for (Field field : fields) {// 对每个 field 进行操作,比如设置可访问性field.setAccessible(true);}
} catch (ClassNotFoundException e) {e.printStackTrace();
}

7. 常用的Field类的方法

在Java中,Field类提供了许多方法,用于获取和操作类的字段(成员变量)。以下是一些常用的方法:

1. 获取字段信息的方法

  • getName():返回字段的名称。
  • getType():返回表示字段类型的 Class 对象。
  • getGenericType():返回表示字段的 Type 对象,包括泛型类型信息。
  • getModifiers():返回字段的修饰符整数编码(使用 Modifier 类中的方法可以解码)。

2. 访问和修改字段值的方法

  • get(Object obj):从指定对象中获取该字段的值。
  • getByte(Object obj):从指定对象中获取该字段的值(byte类型)。
  • set(Object obj, Object value):将指定对象的该字段设置为新的值。

3. 访问控制

  • setAccessible(boolean flag):设置字段的可访问性。即使字段是私有的,通过设置可访问性也可以访问它。

8. 获取Method的四种方式

在Java中,可以通过反射机制获取类的方法(Method)对象。以下是几种常用的方法获取方式:

1. 通过方法名和参数类型获取公共方法

try {// 获取类的Class对象Class<?> clazz = Class.forName("com.reflectBean.User");// 通过方法名和参数类型获取公共方法Method method = clazz.getMethod("publicMethod", String.class);// 调用方法method.invoke(null, "参数");} catch (Exception e) {e.printStackTrace();}

2. 获取所有公共方法

try {// 获取类的Class对象Class<?> clazz = Class.forName("com.reflectBean.User");// 获取所有公共方法Method[] methods = clazz.getMethods();// 遍历所有方法for (Method method : methods) {// 获取方法名String methodName = method.getName();// 判断方法名是否为getNameif (methodName.equals("getName")) {// 执行getName方法Object result = method.invoke(user);System.out.println(result);}}} catch (Exception e) {e.printStackTrace();}

3. 通过方法名和参数类型获取任意方法(包括私有、保护和默认访问权限的方法)

try {// 获取类的Class对象Class<?> clazz = Class.forName("com.reflectBean.User");// 通过方法名和参数类型获取任意方法Method method = clazz.getDeclaredMethod("privateMethod", int.class);// 调用方法method.invoke(new com.reflectBean.User(), 100);// 允许访问私有方法method.setAccessible(true);} catch (Exception e) {e.printStackTrace();}

4. 获取所有的任意方法(包括私有、保护和默认访问权限的方法)

try {// 获取类的Class对象Class<?> clazz = Class.forName("com.example.MyClass");// 获取所有任意方法Method[] methods = clazz.getDeclaredMethods();for (Method method : methods) {// 对每个 method 进行操作// ...}} catch (Exception e) {e.printStackTrace();}

9. 常用的Method的方法

Method类在Java反射中提供了许多方法,这些方法可以用于获取方法的各类信息、调用方法以及操作注解等。以下是一些常用的Method类方法:

1. 获取方法信息的方法

  • String getName():获取方法的名称。
  • Class<?> getDeclaringClass():获取声明此方法的类或接口的Class对象。
  • Class<?> getReturnType():获取方法的返回类型。
  • Type getGenericReturnType():获取方法的泛型返回类型。
  • Class<?>[] getParameterTypes():获取方法的参数类型数组。
  • Type[] getGenericParameterTypes():获取方法的泛型参数类型数组。
  • int getParameterCount():获取方法的参数数量。
  • Class<?>[] getExceptionTypes():获取方法声明的异常类型数组。
  • Type[] getGenericExceptionTypes():获取方法声明的泛型异常类型数组。
  • int getModifiers():获取方法的修饰符,用于确定方法是public、private、protected、static等。
  • Annotation[][] getParameterAnnotations():获取方法的参数上的注解。
  • String toGenericString():返回描述方法的字符串,包括泛型信息。

2. 调用方法

  • Object invoke(Object obj, Object... args):调用此Method对象表示的底层方法。

3. 注解相关的方法

  • <T extends Annotation> T getAnnotation(Class<T> annotationClass):获取指定类型的注解。
  • Annotation[] getAnnotations():获取此方法上存在的所有注解。
  • Annotation[] getDeclaredAnnotations():获取直接存在于此方法上的所有注解。
  • <T extends Annotation> T getDeclaredAnnotation(Class<T> annotationClass):获取直接存在于此方法上的指定类型的注解。
  • <T extends Annotation> T[] getDeclaredAnnotationsByType(Class<T> annotationClass):获取直接存在于此方法上的指定类型的所有注解。
  • <T extends Annotation> T getAnnotationsByType(Class<T> annotationClass):获取此方法上存在的指定类型的所有注解。
  • boolean isAnnotationPresent(Class<? extends Annotation> annotationClass):判断此方法上是否存在指定类型的注解。

4. 设置和获取访问控制

  • void setAccessible(boolean flag):设置方法的可访问性。如果是 true,则强制使能此方法,即使它是私有的。
  • boolean isAccessible():判断此方法是否可以通过反射执行。

5. 其他常用方法

  • boolean isSynthetic():判断此方法是否是编译器生成的合成方法。
  • boolean isVarArgs():判断此方法是否接受可变数量的参数。

10. 实践出真知

好了,扯了那么久反射的概念和基础使用,接下来,结合反射的内容,我们来简单演示通过反射去实现一些基本业务。

10.1 如何通过反射获取和设置对象私有字段的值?

先捋清楚思路,获取和设置对象私有字段的值,大致可以分为四个步骤实现:

1. 获取对象的 Class 对象。
2. 通过反射获取私有字段 Field 对象。
3. 设置字段的可访问性为 true,以绕过 Java 语言访问检查。
4. 通过反射获取和设置字段的值。

那么接下来演示下代码的实现流程:

假设有一个包含私有字段的类:AccountBalance.class

public class AccountBalance {// 设置初始值为100private BigDecimal balance = new BigDecimal(100);// 默认构造函数public AccountBalance() {}// 打印字段值的方法public void printField() {System.out.println("当前的账户余额为: " + balance);}
}

接下来,我们来演示下怎么通过反射获取账户余额,然后变更私有字段:

public class Test {public static void main(String[] args) {try {// 创建 AccountBalance 对象实例AccountBalance account = new AccountBalance();// 获取 AccountBalance 的 Class 对象Class<?> clazz = account.getClass();// 获取私有字段Field privateField = clazz.getDeclaredField("balance");// 设置字段的可访问性为 trueprivateField.setAccessible(true);// 获取私有字段的值BigDecimal initBlance = (BigDecimal) privateField.get(account);System.out.println("最初的账号余额为: " + initBlance);// 设置私有字段的值privateField.set(account, new BigDecimal(50));// 验证字段值已经更新initBlance = (BigDecimal) privateField.get(account);System.out.println("本次扣除的账户余额为: " + initBlance);// 使用类的方法验证字段被正确修改account.printField();} catch (NoSuchFieldException | IllegalAccessException e) {e.printStackTrace();}}
}

控制台输出:

10.2 如何用反射机制创建对象呢?

依旧是先整理下步骤,使用反射机制创建对象,大致可分为3个步骤:

1. 获取类的Class对象。
2. 通过Class对象获取构造方法(构造器)。注意,这里虽然可以通过Class对象的newInstance方法简单地创建一个类的实例,但是这种方式只能调用无参构造函数,故该实例不给予演示。
3. 使用构造方法创建类的实例。

还是假设我们有一个包含无参和有参数构造函数的类AccountBalance.class

public class AccountBalance {// 设置初始值为100private BigDecimal balance = new BigDecimal(100);// 默认构造函数public AccountBalance() {System.out.println("调用无参构造函数");}// 有参构造函数public AccountBalance(BigDecimal initialBalance) {this.balance = initialBalance;System.out.println("调用带参数的构造函数:初始化余额为 " + initialBalance);}// 打印字段值的方法public void printField() {System.out.println("当前的账户余额为: " + balance);}
}

使用Constructor.newInstance()创建对象并访问其方法

public void Test {public static void main(String[] args) {try {// 获取 AccountBalance 的 Class 对象Class<?> clazz = Class.forName("com.example.AccountBalance");// 获取有参构造函数Constructor<?> constructor = clazz.getConstructor(BigDecimal.class);// 使用构造函数创建实例并传递参数Object obj = constructor.newInstance(new BigDecimal(200));// 验证对象创建成功,通过调用方法Method printMethod = clazz.getMethod("printField");printMethod.invoke(obj);} catch (Exception e) {e.printStackTrace();}}}

10.3 如何反编译一个类的构造方法?

反编译一个类的构造方法,大致可以分为四个步骤实现:

1. 获取需要被反编译的类的Class对象

2. 获取类所有声明的构造方法

3. 循环遍历所有的构造方法

4. 获取每个构造方法的修饰符、参数类型并格式化输出

我们仍然以AccountBalance类为例进行说明。

public class AccountBalance {// 设置初始值为100private BigDecimal balance = new BigDecimal(100);// 默认构造函数public AccountBalance() {System.out.println("调用无参构造函数");}// 有参构造函数public AccountBalance(BigDecimal initialBalance) {this.balance = initialBalance;System.out.println("调用带参数的构造函数:初始化余额为 " + initialBalance);}// 打印字段值的方法public void printField() {System.out.println("当前的账户余额为: " + balance);}
}

进行反编译构造方法流程:

public void Test {public static void main(String[] args) {try {// 获取 AccountBalance 的 Class 对象Class<?> clazz = Class.forName("com.example.AccountBalance");// 获取所有声明的构造方法(包括私有构造方法)Constructor<?>[] constructors = clazz.getDeclaredConstructors();for (Constructor<?> constructor : constructors) {// 获取构造方法的修饰符String modifiers = Modifier.toString(constructor.getModifiers());// 获取构造方法的参数类型Class<?>[] parameterTypes = constructor.getParameterTypes();StringBuilder params = new StringBuilder();for (int i = 0; i < parameterTypes.length; i++) {params.append(parameterTypes[i].getSimpleName());if (i < parameterTypes.length - 1) {params.append(", ");}}// 打印构造方法的详细信息System.out.println(modifiers + " " + clazz.getSimpleName() + "(" + params + ") { }");}} catch (ClassNotFoundException e) {e.printStackTrace();}}}

10.4  给定一个类,如何获取这个类的基类以及实现的接口呢?

这种问题的思路,需要考虑两点:
1. 怎么获取该类的父类:这点可以使用Class提供的getSuperClass()方法来获取。

2. 怎么获取类的接口:这点可以使用Class提供的getInterfaces()方法获取实现的接口。

假设定义了以下类和接口:ChildClass.class 和 Parent.class、Car.java。

public class ChildClass extends ParentClass implements Car {}
public class ParentClass {}
public interface Car {
}

获取一个类的父类和实现的接口的具体示例:

public static void main(String[] args) throws ClassNotFoundException {try {// 获取 ChildClass 的 Class 对象Class<?> clazz = Class.forName("com.example.ChildClass");// 获取父类Class<?> superClass = clazz.getSuperclass();System.out.println("父类: " + superClass.getName());// 获取实现的接口Class<?>[] interfaces = clazz.getInterfaces();System.out.println("实现的接口:");for (Class<?> iface : interfaces) {System.out.println(iface.getName());}} catch (ClassNotFoundException e) {e.printStackTrace();}
}

11. 总结

反射机制是Java语言中一种强大的功能,它让我们能够在运行时进行一些本应只能在编译时完成的操作。这个功能的特点就是使得程序可以动态地检查和操作类、方法、字段等,从而带来了极大的灵活性。简单来说,反射机制使得开发人员能够编写更加灵活和动态的代码。举个例子,我们可以根据配置文件来动态创建对象、实现依赖注入、在运行时调用方法而不需要提前知道方法的名称。

然而,反射机制虽然给我们带来了好处,但也有一些弊端需要注意。首先,反射通常会比直接调用要慢,因为它会绕过一些编译时的优化。其次,由于反射机制允许操作私有成员,可能会破坏封装性和安全性。最后,使用反射的代码可读性降低,同时也更难以维护和调试。因此,在使用反射时需要谨慎考虑其影响。

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

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

相关文章

PyQt5显示QImage并将QImage转换为PIL图像保存到缓存

PyQt5显示QImage并将QImage转换为PIL图像保存到缓存 1、效果图 2、流程 1、获取摄像头资源,打开摄像头 2、截取图像 3、opencv读的通道是BGR,要转成RGB 4、往显示视频的Label里显示QImage 5、将QImage转换为PIL图像,并保存到缓存 6、获取图像中人脸信息3、代码 # -*- codin…

python-22-零基础自学python-数据分析基础 打开文件 读取文件信息

学习内容&#xff1a;《python编程&#xff1a;从入门到实践》第二版 知识点&#xff1a; 读取文件 、逐行读取文件信息等 练习内容&#xff1a; 练习10-1:Python学习笔记 在文本编辑器中新建一个文件&#xff0c;写几句话来总结一下你至此学到的Python知识&#xff0c;其中…

Docker-11☆ Docker Compose部署RuoYi-Cloud

一、环境准备 1.安装Docker 附:Docker-02-01☆ Docker在线下载安装与配置(linux) 2.安装Docker Compose 附:Docker-10☆ Docker Compose 二、源码下载 若依官网:RuoYi 若依官方网站 鼠标放到"源码地址"上,点击"RuoYi-Cloud 微服务版"。 跳转至G…

vue对axios进行请求响应封装

一、原因 像是在一些业务逻辑上&#xff0c;比如需要在请求之前展示loading效果&#xff0c;或者在登录的时候判断身份信息&#xff08;token&#xff09;等信息有没有过期&#xff0c;再者根据服务器响应回来的code码进行相应的提示信息。等等在请求之前&#xff0c;之后做的一…

创建react的脚手架

Create React App 中文文档 (bootcss.com) 网址&#xff1a;creat-react-app.bootcss.com 主流的脚手架&#xff1a;creat-react-app 创建脚手架的方法&#xff1a; 方法一&#xff08;JS默认&#xff09;&#xff1a; 1. npx create-react-app my-app 2. cd my-app 3. …

【深度学习练习】心脏病预测

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 一、什么是RNN RNN与传统神经网络最大的区别在于&#xff0c;每次都会将前一次的输出结果&#xff0c;带到下一隐藏层中一起训练。如下图所示&#xff1a; …

comsol随机材料参数赋值

comsol随机材料参数赋值 在comsol中定义外部matlab函数 在comsol中定义外部matlab函数 首选项&#xff0c;安全性&#xff0c;允许 材料中&#xff0c;将杨氏模量更改为变量函数 计算 应力有波动&#xff0c;可见赋值成功 也可以看到赋值的材料参数&#xff1a;

CnosDB:深入理解时序数据修复函数

CnosDB是一个专注于时序数据处理的数据库。CnosDB针对时序数据的特点设计并实现了三个强大的数据修复函数&#xff1a; timestamp_repair – 对时间戳列进行有效修复&#xff0c;支持插入、删除、不变等操作。value_repair – 对值列进行智能修复&#xff0c;根据时间戳间隔和…

Unity | Shader基础知识(第十七集:学习Stencil并做出透视效果)

目录 一、前言 二、了解unity预制的材质 三、什么是Stencil 四、UGUI如何使用Stencil&#xff08;无代码&#xff09; 1.Canvas中Image使用Stencil制作透视效果 2.学习Stencil 3.分析透视效果的需求 五、模型如何使用Stencil 1.shader准备 2.渲染顺序 3.Stencil代码语…

CFS三层内网渗透——外网打点(一)

目录 外网打点 先爆破一下看看有没有啥可进攻路径 尝试那个可疑的路径发现是thinkphp这个框架&#xff0c;同时也知道了版本&#xff0c;那就nday打吧 写入php ​编辑写入php成功&#xff0c;简简单单nday拿下​编辑 蚁剑rce尝试链接 打点成功 外网打点 先爆破一下看看有…

前端使用Threejs加载机械臂并控制机械臂跳舞

1. 前言 在我的第一篇博客中,大致讲解了如何使用threejs导入机械臂模型,以及如何让机械臂模型动起来的案例,可以看一下之前的博客前端使用Threejs控制机械臂模型运动 本篇博客主要讲解的是在原来的基础上添加GSAP动画库的应用,可以通过动画,来让机械臂进行指定轨迹位姿的运动…

【鸿蒙学习笔记】页面布局

官方文档&#xff1a;布局概述 常见页面结构图 布局元素的组成 线性布局&#xff08;Row、Column&#xff09; 了解思路即可&#xff0c;更多样例去看官方文档 Entry Component struct PracExample {build() {Column() {Column({ space: 20 }) {Text(space: 20).fontSize(15)…

vue3实现echarts——小demo

版本&#xff1a; 效果&#xff1a; 代码&#xff1a; <template><div class"middle-box"><div class"box-title">检验排名TOP10</div><div class"box-echart" id"chart1" :loading"loading1"&…

conda中创建环境并安装tensorflow1版本

conda中创建环境并安装tensorflow1版本 一、背景二、命令三、验证一下 一、背景 最近需要使用tensorflow1版本的&#xff0c;发个记录&#xff01; 二、命令 conda create -n tf python3.6 #创建tensorflow虚拟环境 activate tf #激活环境&#xff0c;每次使用的时候都…

【c语言】玩转文件操作

&#x1f31f;&#x1f31f;作者主页&#xff1a;ephemerals__ &#x1f31f;&#x1f31f;所属专栏&#xff1a;C语言 目录 引言 一、文件的打开和关闭 1.流 2.标准流 3.文本文件和二进制文件 4.控制文件打开与关闭的函数 二、文件的顺序读写 三、文件的随机读写 1…

Mac本地部署大模型-单机运行

前些天在一台linux服务器&#xff08;8核&#xff0c;32G内存&#xff0c;无显卡&#xff09;使用ollama运行阿里通义千问Qwen1.5和Qwen2.0低参数版本大模型&#xff0c;Qwen2-1.5B可以运行&#xff0c;但是推理速度有些慢。 一直还没有尝试在macbook上运行测试大模型&#xf…

PostgreSQL主从同步

目录 一、主从复制原理 二、配置主数据库 2.1 创建同步账号 2.2 配置同步账号访问控制 2.3 设置同步参数 3.4 重启主数据库 三、配置从数据库 3.1 停止从库 3.2 清空从库数据文件 3.3 拉取主库数据文件 3.4 配置从库同步参数 3.5 启动从库 四、测试主从 4.1在主库…

前端JS特效第24集:jquery css3实现瀑布流照片墙特效

jquery css3实现瀑布流照片墙特效&#xff0c;先来看看效果&#xff1a; 部分核心的代码如下(全部代码在文章末尾)&#xff1a; <!DOCTYPE html> <html lang"en"> <head> <meta charset"UTF-8" /> <title>jquerycss3实现瀑…

Studying-代码随想录训练营day31| 56.合并区间、738.单调递增的数字、968.监控二叉树、贪心算法总结

第31天&#xff0c;贪心最后一节(ง •_•)ง&#x1f4aa;&#xff0c;编程语言&#xff1a;C 目录 56.合并区间 738.单调递增的数字 968.监控二叉树 贪心算法总结 56.合并区间 文档讲解&#xff1a;代码随想录合并区间 视频讲解&#xff1a;手撕合并区间 题目&#xf…

firewalld(6)自定义services、ipset

简介 在前面的文章中我们已经介绍了zone、rich rule 、--direct等功能和基本配置。在前面文章中&#xff0c;我们提到过firewalld内置了很多服务&#xff0c;可以通过firewall-cmd --get-services来查看服务&#xff0c;也可以通过配置文件查看这些服务/var/lib/firewalld/ser…