自行建网站 所需费用郑州软件开发外包

pingmian/2025/10/10 1:25:55/文章来源:
自行建网站 所需费用,郑州软件开发外包,项目符号在哪里设置,新能源汽车价格补贴分享来源#xff1a;微信号#xff1a;java一日一条 要想了解Java动态代理#xff0c;首先要了解什么叫做代理#xff0c;熟悉设计模式的朋友一定知道在Gof总结的23种设计模式中#xff0c;有一种叫做代理(Proxy)的对象结构型模式#xff0c;动态代理中的代理#xff0c…分享来源微信号java一日一条         要想了解Java动态代理首先要了解什么叫做代理熟悉设计模式的朋友一定知道在Gof总结的23种设计模式中有一种叫做代理(Proxy)的对象结构型模式动态代理中的代理指的就是这种设计模式。 在我看来所谓的代理模式和23种设计模式中的“装饰模式”是一个东西。23种设计模式中将它们作为两种模式网上也有些文章讲这两种模式的异同从细节来看确实可以人为地区分这两种模式但是抽象到一定高度后我认为这两种模式是完全一样的。因此学会了代理模式也就同时掌握了装饰模式。 代理模式 代理模式简单来说就是对一个对象进行包装包装后生成的对象具有和原对象一样的方法列表但是每个方法都可以是被包装过的。 静态代理 让我们先来看一段代码 package common;   public class Test {    static interface Subject{        void sayHi();        void sayHello();     }      static class SubjectImpl implements Subject{          Override         public void sayHi() {            System.out.println(hi);        }          Override        public void sayHello() {            System.out.println(hello);        }     }      static class SubjectImplProxy implements Subject{        private Subject target;          public SubjectImplProxy(Subject target) {            this.targettarget;        }          Override        public void sayHi() {            System.out.print(say:);            target.sayHi();        }          Override        public void sayHello() {            System.out.print(say:);            target.sayHello();        }     }      public static void main(String[] args) {        Subject subjectnew SubjectImpl();        Subject subjectProxynew SubjectImplProxy(subject);        subjectProxy.sayHi();        subjectProxy.sayHello();     } } 这段代码中首先定义了一个Subject接口接口中有两个方法。 然后定义了SubjectImpl类实现Subject接口并实现其中的两个方法到这里肯定是没问题的。 现在再定义一个SubjuectImplProxy类也实现Subject接口。这个SubjectImplProxy类的作用是包装SubjectImpl类的实例它的内部定义一个变量target来保存一个SubjectImpl的实例。SubjectImplProxy也实现了接口规定的两个方法并且在它的实现版本中都调用了SubjectImpl的实现但是又添加了自己的处理逻辑。 相信这段代码不难理解它通过对SubjectImpl进行包装达到了给输出内容添加前缀的功能。这种代理方式叫做静态代理。 动态代理 从上面的演示中我们不难看出静态代理的缺点我们对SubjectImpl的两个方法是进行的相同的包装但是却要在SubjectImplProxy里把相同的包装逻辑写两次而且以后如果Subject接口再添加新的方法SubjectImplProxy也必须要添加新的实现尽管SubjectImplProxy对所有方法的包装可能都是一样的。 下面我把上面例子的静态代理改成动态代理我们来看一下区别 package common;   import java.lang.invoke.MethodHandle; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy;   public class Test {    static interface Subject{        void sayHi();        void sayHello();     }      static class SubjectImpl implements Subject{          Override        public void sayHi() {            System.out.println(hi);        }          Override        public void sayHello() {            System.out.println(hello);        }     }      static class ProxyInvocationHandler implements InvocationHandler{        private Subject target;        public ProxyInvocationHandler(Subject target) {            this.targettarget;        }          Override        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {            System.out.print(say:);            return method.invoke(target, args);        }       }      public static void main(String[] args) {        Subject subjectnew SubjectImpl();        Subject subjectProxy(Subject) Proxy.newProxyInstance(subject.getClass().getClassLoader(), subject.getClass().getInterfaces(), new ProxyInvocationHandler(subject));        subjectProxy.sayHi();        subjectProxy.sayHello();       } } 只看main方法的话只有第二行和之前的静态代理不同同样是生成一个subjectProxy代理对象只是生成的代码不同了。静态代理是直接new 一个SubjectImplProxy的实例而动态代理则调用了java.lang.reflect.Proxy.newProxyInstance()方法我们来看一下这个方法的源码    public static Object newProxyInstance(ClassLoader loader,                                          Class?[] interfaces,                                          InvocationHandler h)        throws IllegalArgumentException     {        if (h null) {            throw new NullPointerException();        }          /*         * Look up or generate the designated proxy class.         */        Class? cl getProxyClass(loader, interfaces);  //获取代理类的Class          /*         * Invoke its constructor with the designated invocation handler.         */        try {             Constructor cons cl.getConstructor(constructorParams);  //constructorParams是写死的 { InvocationHandler.class }上边返回的代理类Class一定是extends Proxy的而Proxy有一个参数为InvocationHandler的构造函数              return cons.newInstance(new Object[] { h });  //这里通过构造函数将我们自己定义的InvocationHandler的子类传到代理类的实例里当我们调用代理类的任何方法时实际上都会调用我们定义的InvocationHandler子类重写的invoke()函数        } catch (NoSuchMethodException e) {            throw new InternalError(e.toString());        } catch (IllegalAccessException e) {            throw new InternalError(e.toString());        } catch (InstantiationException e) {            throw new InternalError(e.toString());        } catch (InvocationTargetException e) {            throw new InternalError(e.toString());        }     } 上面的 Class? cl getProxyClass(loader, interfaces);  调用的getProxyClass方法 public static Class? getProxyClass(ClassLoader loader,                                         Class?... interfaces)        throws IllegalArgumentException     {        if (interfaces.length 65535) {  //因为在class文件中一个类保存的接口数量是用2个字节来表示的因此java中一个类最多可以实现65535个接口            throw new IllegalArgumentException(interface limit exceeded);        }          Class? proxyClass null;          /* collect interface names to use as key for proxy class cache */        String[] interfaceNames new String[interfaces.length];          // for detecting duplicates        SetClass? interfaceSet new HashSet();        //验证interfaces里的接口是否能被类加载器加载是否是接口是否有重复的           for (int i 0; i interfaces.length; i) {            /*             * Verify that the class loader resolves the name of this             * interface to the same Class object.             */            String interfaceName interfaces[i].getName();            Class? interfaceClass null;            try {                 interfaceClass Class.forName(interfaceName, false, loader);            } catch (ClassNotFoundException e) {            }            if (interfaceClass ! interfaces[i]) {                 throw new IllegalArgumentException(                     interfaces[i] is not visible from class loader);            }              /*             * Verify that the Class object actually represents an             * interface.             */            if (!interfaceClass.isInterface()) {                 throw new IllegalArgumentException(                     interfaceClass.getName() is not an interface);            }              /*             * Verify that this interface is not a duplicate.             */            if (interfaceSet.contains(interfaceClass)) {                 throw new IllegalArgumentException(                     repeated interface: interfaceClass.getName());            }            interfaceSet.add(interfaceClass);              interfaceNames[i] interfaceName;        }          /*         * Using string representations of the proxy interfaces as         * keys in the proxy class cache (instead of their Class         * objects) is sufficient because we require the proxy         * interfaces to be resolvable by name through the supplied         * class loader, and it has the advantage that using a string         * representation of a class makes for an implicit weak         * reference to the class.         */        ListString key Arrays.asList(interfaceNames);          //使用interfaces列表作为key缓存在cache里也就是实现了相同interfaces的代理类只会创建加载一次          /*         * Find or create the proxy class cache for the class loader.         */        MapListString, Object cache;        synchronized (loaderToCache) {            cache loaderToCache.get(loader);            if (cache null) {                 cache new HashMap();                 loaderToCache.put(loader, cache);             }            /*             * This mapping will remain valid for the duration of this             * method, without further synchronization, because the mapping             * will only be removed if the class loader becomes unreachable.             */        }          /*         * Look up the list of interfaces in the proxy class cache using         * the key.  This lookup will result in one of three possible         * kinds of values:         *     null, if there is currently no proxy class for the list of         *         interfaces in the class loader,         *     the pendingGenerationMarker object, if a proxy class for the         *         list of interfaces is currently being generated,         *     or a weak reference to a Class object, if a proxy class for         *         the list of interfaces has already been generated.         */      //看看缓存里有没有如果有就直接取出来然后return否则判断根据pendingGenerationMarker判断是否有其它线程正在生成当前的代理类如果有则cache.wait()等待如果没有则创建。        synchronized (cache) {            /*             * Note that we need not worry about reaping the cache for             * entries with cleared weak references because if a proxy class             * has been garbage collected, its class loader will have been             * garbage collected as well, so the entire cache will be reaped             * from the loaderToCache map.             */            do {                 Object value cache.get(key);                 if (value instanceof Reference) {                     proxyClass (Class?) ((Reference) value).get();                 }                 if (proxyClass ! null) {                     // proxy class already generated: return it                     return proxyClass;                 } else if (value pendingGenerationMarker) {                     // proxy class being generated: wait for it                     try {                         cache.wait();                     } catch (InterruptedException e) {                         /*                          * The class generation that we are waiting for should                          * take a small, bounded time, so we can safely ignore                          * thread interrupts here.                          */                     }                     continue;                 } else {                     /*                      * No proxy class for this list of interfaces has been                      * generated or is being generated, so we will go and                      * generate it now.  Mark it as pending generation.                      */                     cache.put(key, pendingGenerationMarker);                     break;                 }            } while (true);        }      //确认要生成的代理类所属的包如果interfaces里所有接口都是public的代理类所属包就是默认包如果有interface不是public那么所有不是public的interface必须在一个包里否则报错。        try {            String proxyPkg null;     // package to define proxy class in              /*             * Record the package of a non-public proxy interface so that the             * proxy class will be defined in the same package.  Verify that             * all non-public proxy interfaces are in the same package.             */            for (int i 0; i interfaces.length; i) {                 int flags interfaces[i].getModifiers();                 if (!Modifier.isPublic(flags)) {                     String name interfaces[i].getName();                     int n name.lastIndexOf(.);                     String pkg ((n -1) ? : name.substring(0, n 1));                     if (proxyPkg null) {                         proxyPkg pkg;                     } else if (!pkg.equals(proxyPkg)) {                         throw new IllegalArgumentException(                             non-public interfaces from different packages);                     }                 }            }              if (proxyPkg null) {     // if no non-public proxy interfaces,                 proxyPkg ;          // use the unnamed package            }              {                 /*                  * Choose a name for the proxy class to generate.                  */                 long num;                 synchronized (nextUniqueNumberLock) {                     num nextUniqueNumber;                 }                 String proxyName proxyPkg proxyClassNamePrefix num;  //生成代理类的名字proxyPkg是上面确定下来的代理类所在的包名proxyClassNamePrefix是写死的字符串“$Proxy”num是一个全局唯一的long型数字从0开始累积每次生成新的代理类就1从这里也能看出生成的动态代理类的数量不能超过Long.maxValue                 /*                  * Verify that the class loader hasnt already                  * defined a class with the chosen name.                  */                   /*                  * Generate the specified proxy class.                 */                 byte[] proxyClassFile ProxyGenerator.generateProxyClass(                     proxyName, interfaces);  //生成一个以proxyName为类名的实现了Interfaces里所有接口的类的字节码                 try {                     proxyClass defineClass0(loader, proxyName,                         proxyClassFile, 0, proxyClassFile.length);  //加载生成的类                 } catch (ClassFormatError e) {                     /*                      * A ClassFormatError here means that (barring bugs in the                      * proxy class generation code) there was some other                      * invalid aspect of the arguments supplied to the proxy                      * class creation (such as virtual machine limitations                      * exceeded).                      */                     throw new IllegalArgumentException(e.toString());                 }            }            // add to set of all generated proxy classes, for isProxyClass            proxyClasses.put(proxyClass, null);          } finally {            /*             * We must clean up the pending generation state of the proxy             * class cache entry somehow.  If a proxy class was successfully             * generated, store it in the cache (with a weak reference);             * otherwise, remove the reserved entry. In all cases, notify             * all waiters on reserved entries in this cache.             */        //创建成功则将cache中该key的pendingGenerationMarker替换为实际的代理类的弱引用否则也要清除pendingGenerationMarker标记不管是否成功都要执行cache.notifyAll()让其它要创建相同代理类并且执行了cache.wait()的线程恢复执行。            synchronized (cache) {                 if (proxyClass ! null) {                     cache.put(key, new WeakReferenceClass?(proxyClass));                 } else {                     cache.remove(key);                 }                 cache.notifyAll();            }        }        return proxyClass; //最后返回代理类Class     }   到这里我们已经把动态代理的java源代码都解析完了现在思路就很清晰了 Proxy.newProxyInstance(ClassLoader loader,Class?[] interfaces,InvocationHandler h) 方法简单来说执行了以下操作 1.生成一个实现了参数interfaces里所有接口且继承了Proxy的代理类的字节码然后用参数里的classLoader加载这个代理类。   2.使用代理类父类的构造函数 Proxy(InvocationHandler h)来创造一个代理类的实例将我们自定义的InvocationHandler的子类传入。   3.返回这个代理类实例因为我们构造的代理类实现了interfaces也就是我们程序中传入的subject.getClass().getInterfaces()里的所有接口因此返回的代理类可以强转成Subject类型来调用接口中定义的方法。   现在我们知道了用Proxy.newProxyInstance()返回的subjectProxy可以成功强转成Subject类型来调用接口中定义的方法了那么在调用方法后代理类实例怎么进行处理的呢这就需要看一下代理类的源码了。但是代理类是程序动态生成字节码加载的怎么看源码呢没关系可以在main方法中加入System.getProperties().put(“sun.misc.ProxyGenerator.saveGeneratedFiles”,”true”)这样就会把生成的代理类Class文件保存在本地磁盘上然后再反编译可以得到代理类的源码 package common;   import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.UndeclaredThrowableException;   public final class $Proxy0 extends Proxy  implements Test.Subject {  private static Method m4;  private static Method m1;  private static Method m3;  private static Method m0;  private static Method m2;    static   {      try {          m4 Class.forName(Test$Subject).getMethod(sayHello, new Class[0]);          m1 Class.forName(java.lang.Object).getMethod(equals, new Class[] { Class.forName(java.lang.Object) });          m3 Class.forName(Test$Subject).getMethod(sayHi, new Class[0]);          m0 Class.forName(java.lang.Object).getMethod(hashCode, new Class[0]);          m2 Class.forName(java.lang.Object).getMethod(toString, new Class[0]);     } catch (Exception e) {         throw new RuntimeException(e);     }   }    public $Proxy0(InvocationHandler paramInvocationHandler)   {    super(paramInvocationHandler);   }    public final void sayHello()   {    try     {      this.h.invoke(this, m4, null);      return;     }     catch (RuntimeException localRuntimeException)     {      throw localRuntimeException;     }    catch (Throwable localThrowable)     {        throw new UndeclaredThrowableException(localThrowable);     }   }    public final boolean equals(Object paramObject)   {    try     {      return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();     }    catch (RuntimeException localRuntimeException)     {      throw localRuntimeException;     }    catch (Throwable localThrowable)     {        throw new UndeclaredThrowableException(localThrowable);     }   }    public final void sayHi()   {    try     {      this.h.invoke(this, m3, null);      return;     }    catch (RuntimeException localRuntimeException)     {      throw localRuntimeException;     }    catch (Throwable localThrowable)     {        throw new UndeclaredThrowableException(localThrowable);     }   }    public final int hashCode()   {    try     {      return ((Integer)this.h.invoke(this, m0, null)).intValue();     }    catch (RuntimeException localRuntimeException)     {      throw localRuntimeException;     }    catch (Throwable localThrowable)     {        throw new UndeclaredThrowableException(localThrowable);     }   }    public final String toString()   {    try     {      return (String)this.h.invoke(this, m2, null);     }    catch (RuntimeException localRuntimeException)     {      throw localRuntimeException;     }    catch (Throwable localThrowable)     {        throw new UndeclaredThrowableException(localThrowable);     }   } }   我们可以看到代理类内部实现比较简单在调用每个代理类每个方法的时候都用反射去调h的invoke方法(也就是我们自定义的InvocationHandler的子类中重写的invoke方法)用参数传递了代理类实例、接口方法、调用参数列表这样我们在重写的invoke方法中就可以实现对所有方法的统一包装了。 总结 动态代理相对于静态代理在使用上的优点主要是能够对一个对象的所有方法进行统一包装而且后期被代理的类添加方法的时候动态代理类不需要改动。   缺点是要求被代理的类必须实现了接口因为动态代理类在实现的时候继承了Proxy类java不支持多继承因此动态代理类只能根据接口来定义方法。 最后动态代理之所以叫做动态代理是因为java在实现动态代理的时候动态代理类是在运行时动态生成和加载的相对的静态代理类和其他普通类一下在类加载阶段就加载了。转载于:https://www.cnblogs.com/tiger-fu/p/7831811.html

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

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

相关文章

珠海品牌网站建河南网站建设推广

" A flow of control within a process that consists of a PC, a register set and a stack space" 本章将介绍为单个运行进程提供的新抽象 —— 线程 (thread) 线程是 调度的一个基本单位(basic unit of CPU scheduling)一个单独的线程至…

重庆网站建站建设平台为什么要做手机网站

1. 前言入门篇我们已经成功运行hello.benchmarks.yml并输出测试结果,本篇我们就hello.benchmarks.yml、以及运行的shell脚本详细解读下其中的含义2. 剖析hello.benchmarks.yml2.1. job在hello.benchmarks.yml中我们定义了一个新的job: server,并指定了仓…

网站策划要遵循的原则淡水网站建设定制

SpringBoot经典之作 进入Spring Boot世界 准备开发环境搭建开发工具 基础 Spring Boot基础分层开发Web应用程序响应式编程 进阶 Spring Boot进阶用ORM操作SQL数据库接口架构风格——RESTful集成安全框架,实现安全认证和授权集成Redis,实现高并发集成R…

网站建设流程书籍做旅游攻略网站好

最近来个需求,要求给小程序的 modal 增加个关闭按钮,上网一查发现原来 2018 年就有人给出解决方案了,于是总结下微信小程序自定义组件的思路:一句话,用 wxml css实现和原生组件类似的样式和效果,之后用 JS…

河南省建设协会网站微信网站搭建教程

前言 最近在和其他软件联合做一个本地图片选择传输功能,为此希望图片能够有序的呈现在客户端,简单的实现了一下功能,通过Mvvm模式进行呈现,过程简单通俗,话不多说直接上图。 处理过程 前台代码 你只需要粘贴到你的前台…

专业做网站app的公司哪家好百度网盘做自已网站

一次加入一个节点到我们的最下生成树中。加入哪个&#xff1f;跟着下面的步骤走一遍你就会了。 1. 把第一个节点A添加进来 2. 看两条边<A,B>,<A,E>,一个长度是3&#xff0c;一个长度是4&#xff0c;把长度短的边的另一个节点添加进来&#xff0c;也就是B 3. 再看A,…

atheme wordpress整站优化与关键词排名

随机森林超参数的网格优化&#xff08;机器学习的精华–调参&#xff09; 随机森林各个参数对算法的影响 影响力参数⭐⭐⭐⭐⭐几乎总是具有巨大影响力n_estimators&#xff08;整体学习能力&#xff09;max_depth&#xff08;粗剪枝&#xff09;max_features&#xff08;随机…

seo网站开发html交易网站设计实例

使用timeit模块进行速度测试,使用profile模块进行性能统计,使用dis模块进行字节码反汇编.下面的脚本演示了如何使用模块.从输出中注意到的一件事是函数调用的数量会影响整体性能(当然,字节码指令的数量也是如此).希望,(以及更多实验)应该为您提供有关如何提高功能效率的足够线索…

站酷网站的比赛网页谁做的上海网站推广定制

计算机、通信以及消费类电子产业的快速发展成为示波器发展的不竭动力&#xff1b;厂商不断从技术上对示波器进行改进更使其发展日新月异。 <?xml:namespace prefix o ns "urn:schemas-microsoft-com:office:office" />数字示波器自它诞生的第一天起&#xf…

网站上传后没有后台上海高端seo公司

一、明白社群运营的目的1、社群的目的确立任何一个社群(组织)成立的时分&#xff0c;都是承载着一定的目的的&#xff0c;这个目的就像是北极星一样&#xff0c;指引着我们的方向。确立运营目的的过程&#xff0c;也是在寻觅北极星的过程。社群运营属于触达用户的一种方式&…

网站备案幕布大小彩票网站的表格是如何做的

正题 题目大意 nnn个点的一棵树&#xff0c;每个节点有一个值valvalval和一个字符串SSS。对于每个点求∑x∈decp∑y∈decp(x<y)(valxxorvaly)∗∣LCP(Sx,Sy)∣\sum_{x\in dec_p}\sum_{y\in dec_p(x<y)}(val_x\ xor\ val_y)*|LCP(S_x,S_y)|x∈decp​∑​y∈decp​(x<y)…

综合网站开发设计中国建设银行网站用户是什么

在 XP 系统下不能建立叫 con 的文件! 不知其他朋友也是这样? 转载于:https://www.cnblogs.com/del/archive/2009/04/26/1444175.html

南通通州区网站制作wordpress 博客插件

1、手机触屏不灵敏小伙伴们经常说&#xff0c;我的手机偶尔会反应不灵敏。需要连续点击几次&#xff0c;有时候半天才有反应。这是为什么呢&#xff1f;这很可能是由于一些操作不当造成的&#xff1a;1.非原装充电器输出电压不稳定&#xff1b;2.屏幕保护膜导致触屏的不灵敏&am…

网站建设公司商务网站项目书网站建设专题页

承载16个量子点交叉条阵列的量子芯片&#xff0c;可无缝集成到棋盘图案&#xff08;图片来源&#xff1a;网络&#xff09; 由荷兰代尔夫特理工大学(TU Delft)和荷兰应用科学研究组织(TNO)组建的荷兰量子计算研究中心QuTech的研究人员开发了一种用相对较少的控制线来控制大量量…

童装网站建设目标宁波网站推广方案

文章目录 前言一、抽象类和接口对比二、举例说明三种情况1.接口实现类接口 2.抽象类实现类抽象类实现类(子类) 3.抽象类实现接口接口抽象类三个实现类 总结 前言 抽象类和接口其实都是抽象的一种,那么他俩有何异同呢? 抽象类实现接口的意义何在? 一、抽象类和接口对比 接口…

马鞍山建设网站马和人做人和牛做网站

告警解释 系统每天二十三点检查一次当前系统中的证书文件&#xff0c;如果当前时间距离过期时间不足告警阈值天数&#xff0c;则证书文件即将过期&#xff0c;产生该告警。告警阈值天数的配置请参考《管理员指南》的“配置证书即将过期告警阈值”章节。 当重新导入一个正常证…

广州开发小程序企业seo网络营销

Java内存模型: 内存分布情况及其关系: 主内存:Java内存模型规定所有的变量都保存在主内存中 工作内存:每个线程都有自己的工作内存,保存了该线程使用到的变量的主内存副本拷贝 主内存与工作内存的关系: 线程对变量的所有操作都必须在自己的工作内存中进行,不能直接读写主内存…

免费行情软件app网站大全下载u288北京住建局

智能优化算法应用&#xff1a;基于北方苍鹰算法无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于北方苍鹰算法无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.北方苍鹰算法4.实验参数设定5.算法结果6.参考…

移动网站建设生要女延吉哪家网站建设公司好

解决办法&#xff1a;上次我的复制 ctrl c不能用就是打开的QQ系统设置&#xff0c;只保留qq的截图快捷键&#xff0c;其他的都是无。 哎&#xff0c;一会又热键冲突了&#xff0c;卸载了搜狗输入法就没事了&#xff0c;原来搜狗的问题。转载于:https://www.cnblogs.com/xiaofa…

广州网站推广方案视频分享网站建设难吗

前面2期内容&#xff0c;主要给大家重点介绍了Runway视频生成技术的核心产品功能板块Gen1、Gen2、FI使用教程&#xff0c;还没有看过的小伙伴可以回看往期文章。除了视频生成AI技术外&#xff0c;Runway还具有图片、视频后期处理30多项单个功能&#xff0c;例如视频修复、视频主…