java中動態代理實現機制

前言:

  代理模式是常用的java設計模式,它的特征是代理類與委托類有同樣的接口,代理類主要負責為委托類預處理消息、過濾消息、把消息轉發給委托類,以及事后處理消息等。代理類與委托類之間通常會存在關聯關系,一個代理類的對象與一個委托類的對象關聯,代理類的對象本身并不真正實現服務,而是通過調用委托類的對象的相關方法,來提供特定的服務。?

JAVA各種動態代理實現的比較

接口

interface AddInterface{int add(int a, int b);
}interface SubInterface{int sub(int a, int b);
}

實現類

class Arithmetic implements AddInterface, SubInterface{@Overridepublic int sub(int a, int b) {return a-b;}@Overridepublic int add(int a, int b) {return a+b;}
}

方式1: JDK自帶的動態代理

實現方式

  Java在JDK1.3后引入的動態代理機制,使我們可以在運行期動態的創建代理類。使用動態代理實現AOP需要有四個角色:被代理的類,被代理類的接口,織入器,和InvocationHandler,而織入器使用接口反射機制生成一個代理類,然后在這個代理類中織入代碼。被代理的類是AOP里所說的目標,InvocationHandler是切面,它包含了Advice和Pointcut。?

InvocationHandler接口的實現

class JdkDPQueryHandler implements InvocationHandler{private Arithmetic real;public JdkDPQueryHandler(Arithmetic real){this.real = real;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {String methodName = method.getName();System.out.println(method);System.out.println("the method: " + methodName + "開始, 參數: "+Arrays.asList(args));Object result = method.invoke(real, args);System.out.println("the method: "+methodName+"結束, 結果: " + result);return result;}
}

創建代理類并且調用代理類

public class Main{private static int a = 4, b = 2;public static Object createJDKProxy(Arithmetic real){Object proxyArithmetic = Proxy.newProxyInstance(real.getClass().getClassLoader(),real.getClass().getInterfaces(), new JdkDPQueryHandler(real)); return proxyArithmetic;}public static void main(String[] args){Arithmetic real = new Arithmetic();Object proxyArithmetic = createJDKProxy(real);((AddInterface)proxyArithmetic).add(a, b);((SubInterface)proxyArithmetic).sub(a, b);}
}

方式2:動態字節碼生成(cglib)

實現方式

Enhancer和MethodInterceptor。Enhancer可以用來動態的生成一個類,這個類可以繼承指定的一個類,實現指定的一些接口。同時,Enhancer在生成一個類之前需要指定一個Callback,當類方法調用時,方法的執行被分配給這個Callback,MethodInterceptor是一個使用比較多的繼承自Callback的接口,它只有一個方法聲明。

接口InvocationHandler(jdk中)和接口MethodInterceptor(cglib中)對比

public interface MethodInterceptor extends Callback  {  public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args,MethodProxy proxy) throws Throwable;  
}  
public interface InvocationHandler {  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;  
}  
從參數構成上,methodInterceptor的輸入參數比Invocationhandler多1個,其實前3個參數對象的含義與Invocationhandler的含義是相同的。
第一個參數表示調用方法來自哪個對象;
第二個參數表示調用方法的Method對象;
第三個參數表示此次調用的輸入參數列表;
多出來的參數是MethodProxy 類型的,它應該是cglib生成用來代替Method對象的一個對象,使用MethodProxy比調用JDK自身的Method直接執行方法效率會有提升。

實現1

  MethodInterceptor接口的實現

class CglibDPQueryInterceptor implements MethodInterceptor{private Arithmetic real;public CglibDPQueryInterceptor(Arithmetic real){this.real = real;}@Overridepublic Object intercept(Object target, Method method, Object[] args, MethodProxy proxy) throws Throwable {String methodName = method.getName();System.out.println(method);System.out.println("the method: " + methodName + "開始, 參數: "+Arrays.asList(args));//Object result = method.invoke(real, args);//兩種方式都是可以得Object result = proxy.invoke(real, args);System.out.println("the method: "+methodName+"結束, 結果: " + result);return result;}
}

  創建代理類并調用代理類

public class Main{private static int a = 4, b = 2;public static Object createCglibProxy(Arithmetic real){Enhancer enhancer = new Enhancer();enhancer.setCallback(new CglibDPQueryInterceptor(real));enhancer.setInterfaces(real.getClass().getInterfaces());return enhancer.create();}public static void main(String[] args){Arithmetic real = new Arithmetic();      Object proxyArithmetic = createCglibProxy(real);        ((AddInterface)proxyArithmetic).add(a, b);((SubInterface)proxyArithmetic).sub(a, b);}
}

?  注意了,MethodProxy在對執行函數的時候,提供了2個方法

public Object invoke (Object obj, Object[] args) throws Throwable  
public Object invokeSuper(Object obj, Object[] args) throws Throwable

  其中,javadoc上說這個invoke()方法可以用于相同類中的其他對象的方法執行,也就是說這個方法中的obj需要傳入相同一個類的另一個對象(上述方法中就是傳入了Arithmetic類的不同對象,否則會進入無限遞歸循環(測試之后還真是出現了StackOverflowError)。仔細的想一想就會發現,public Object intercept(Object target, Method method, Object[] args, MethodProxy proxy)中的target是實現的代理類對象,通過target調用add()方法時會觸發intercept()方法被調用,如果在intercept()方法中再調用method.invoke(target, args),就相當于add()方法中又調用add()方法,導致無限的遞歸循環。但是如果執行method.invoke(real, args)則不會,因為real和target是同一個類不同對象,real是真實邏輯主題,target是真實主題real的代理。

  下面一個例子來模擬一下:

interface SolveInterface{void solve();
}class Real implements SolveInterface{public void solve(){System.out.println("Real Solve!");}
}class Target extends Real{private Object obj;public void setObject(Object obj){this.obj = obj;}private void invoke(){try {Method method = SolveInterface.class.getMethod("solve", new Class[]{});method.invoke(obj, new Class[]{});} catch (Exception e) {e.printStackTrace();}}public void solve(){System.out.println("Target Solve!");invoke();}
}

  

public class Main{public static void main(String[] args) throws Exception{     Target target = new Target();target.setObject(new Real());//正確
//target.setObject(target);//發生循環調用target.solve();} }

  其實Method的invoke()方法會根據obj的類型去調用對應的solve()方法,也就是多態性。

實現2

  MethodInterceptor接口的實現

class CglibDPQueryInterceptor implements MethodInterceptor{@Overridepublic Object intercept(Object target, Method method, Object[] args, MethodProxy proxy) throws Throwable {String methodName = method.getName();System.out.println(method);System.out.println("the method: " + methodName + "開始, 參數: "+Arrays.asList(args));//  打印類信息 :target.getClass();省略Object result = proxy.invokeSuper(target, args);System.out.println("the method: "+methodName+"結束, 結果: " + result);return result;}
}

  創建代理類并調用代理類

public class Main{private static int a = 4, b = 2;
public static Object createCglibProxy(){Enhancer enhancer = new Enhancer();enhancer.setCallback(new CglibDPQueryInterceptor());enhancer.setSuperclass(Arithmetic.class);return enhancer.create();}public static void main(String[] args){Arithmetic real = new Arithmetic();
Object proxyArithmetic = createCglibProxy();((AddInterface)proxyArithmetic).add(a, b);((SubInterface)proxyArithmetic).sub(a, b);}
}

  注意了,實現2中Enhancer 沒有設置接口,因為設置了Superclass了(也就是代理類的父類是Arithmetic),我們的代理類會繼承它,而Arithmetic已經實現了我們的接口。為了證明這一點,可以在MethodInterceptor的?intercept方法中打印?target.getClass()的類信息,你會發現cglib的兩種方式代理類的父類是不同的。如下:

  (如果需要打印類信息代碼,請參考:http://www.cnblogs.com/hujunzheng/p/5132943.html)

  實現1:

public class com.test.Arithmetic$$EnhancerByCGLIB$$4fa786eb extends java.lang.Object

  實現2:

public class com.test.Arithmetic$$EnhancerByCGLIB$$4fa786eb extends com.test.Arithmetic

方式3:javassist生成動態代理(代理工廠創建 或者 動態代碼創建)  

  Javassist是一個編輯字節碼的框架,可以讓你很簡單地操作字節碼。它可以在運行期定義或修改Class。使用Javassist實現AOP的原理是在字節碼加載前直接修改需要切入的方法。這比使用Cglib實現AOP更加高效,并且沒太多限制,實現原理如下圖:?

實現1:

接口的實現

class JavassistDPQueryHandler implements MethodHandler{@Overridepublic Object invoke(Object target, Method method, Method proxy, Object[] args) throws Throwable {String methodName = method.getName();System.out.println(method);System.out.println("the method: " + methodName + "開始, 參數: "+Arrays.asList(args));Object result = proxy.invoke(target, args);System.out.println("the method: "+methodName+"結束, 結果: " + result);return result;}
}

創建代理類并調用代理類

public class Main{private static int a = 4, b = 2;
public static Object createJavassistProxy() throws Exception{ProxyFactory factory = new ProxyFactory();factory.setSuperclass(Arithmetic.class);factory.setHandler(new JavassistDPQueryHandler());return factory.createClass().newInstance();}public static void main(String[] args) throws Exception{Arithmetic real = new Arithmetic();Object proxyArithmetic = createJavassistProxy();((AddInterface)proxyArithmetic).add(a, b);((SubInterface)proxyArithmetic).sub(a, b);}
}

  注意:MethodHandler接口中invoke方法的定義,如下:

public Object invoke(Object target, Method method, Method proxy, Object[] args)

  method代表調用方法的Method對象,proxy是代理類產生并代替method的對象,否則用method.invoke(target, args)會產生無限循環調用。

實現2:

  (來自:http://cuishen.iteye.com/blog/421464),代碼注釋很詳細,仔細研究一下就會懂了!

  javassist使用動態java代碼常見代理過程和前文的方法略有不同。javassist內部可以通過動態java代碼,生成字節碼。這種方式創建的動態代理可以非常靈活,甚至可以在運行時產生業務邏輯。

//自定義攔截器接口
interface
InterceptorHandler { /** * 調用動態代理對象的方法將反射本方法,可在本方法實現中添加類似AOP的事前事后操作,只有在本方法體中加入如下代碼 * 被代理的方法才會被執行,返回值將返回給代理最后返回給程序 * @param obj Object 被代理的對象 * @param method Method 被代理對象的方法 * @param args Object[] 被代理對象的方法的參數 * @return Object 被代理對象的方法執行后的返回值 * @throws Throwable */ public Object invoke(Object obj, Method method, Object[] args) throws Throwable; }
//攔截器的實現
class InterceptorHandlerImpl implements InterceptorHandler{@Overridepublic Object invoke(Object obj, Method method, Object[] args) throws Throwable {String methodName = method.getName();System.out.println(method);System.out.println("the method: " + methodName + "開始, 參數: "+Arrays.asList(args));Object result = method.invoke(obj, args);System.out.println("the method: "+methodName+"結束, 結果: " + result);return result;} }class MyProxyImpl { /** 動態代理類的類名后綴 */ private final static String PROXY_CLASS_NAME_SUFFIX = "$MyProxy_"; /** 攔截器接口 */ private final static String INTERCEPTOR_HANDLER_INTERFACE = "com.test.InterceptorHandler"; /** 動態代理類的類名索引,防止類名重復 */ private static int proxyClassIndex = 1; /** * 暴露給用戶的動態代理接口,返回某個接口的動態代理對象,注意本代理實現需和com.cuishen.myAop.InterceptorHandler攔截器配合 * 使用,即用戶要使用本動態代理,需先實現com.cuishen.myAop.InterceptorHandler攔截器接口 * @param interfaceClassName String 要動態代理的接口類名, e.g test.StudentInfoService * @param classToProxy String 要動態代理的接口的實現類的類名, e.g test.StudentInfoServiceImpl * @param interceptorHandlerImplClassName String 用戶提供的攔截器接口的實現類的類名 * @return Object 返回某個接口的動態代理對象 * @throws InstantiationException * @throws IllegalAccessException * @throws NotFoundException * @throws CannotCompileException * @throws ClassNotFoundException * @see com.cuishen.myAop.InterceptorHandler */ public static Object newProxyInstance(String interfaceClassName, String classToProxy, String interceptorHandlerImplClassName) throws InstantiationException, IllegalAccessException, NotFoundException, CannotCompileException, ClassNotFoundException { Class interfaceClass = Class.forName(interfaceClassName); Class interceptorHandlerImplClass = Class.forName(interceptorHandlerImplClassName); return dynamicImplementsInterface(classToProxy, interfaceClass, interceptorHandlerImplClass); } /** * 動態實現要代理的接口 * @param classToProxy String 要動態代理的接口的實現類的類名, e.g test.StudentInfoServiceImpl * @param interfaceClass Class 要動態代理的接口類, e.g test.StudentInfoService * @param interceptorHandlerImplClass Class 用戶提供的攔截器接口的實現類 * @return Object 返回某個接口的動態代理對象 * @throws NotFoundException * @throws CannotCompileException * @throws InstantiationException * @throws IllegalAccessException */ private static Object dynamicImplementsInterface(String classToProxy, Class interfaceClass, Class interceptorHandlerImplClass) throws NotFoundException, CannotCompileException, InstantiationException, IllegalAccessException { ClassPool cp = ClassPool.getDefault(); String interfaceName = interfaceClass.getName(); //動態指定代理類的類名 String proxyClassName = interfaceName + PROXY_CLASS_NAME_SUFFIX + proxyClassIndex++; //要實現的接口的包名+接口名 String interfaceNamePath = interfaceName; CtClass ctInterface = cp.getCtClass(interfaceNamePath); CtClass cc = cp.makeClass(proxyClassName); cc.addInterface(ctInterface); Method [] methods = interfaceClass.getMethods(); for(int i = 0; i < methods.length; i++) { Method method = methods[i]; dynamicImplementsMethodsFromInterface(classToProxy, cc, method, interceptorHandlerImplClass, i); } return (Object)cc.toClass().newInstance(); } /** * 動態實現接口里的方法 * @param classToProxy String 要動態代理的接口的實現類的類名, e.g test.StudentInfoServiceImpl * @param implementer CtClass 動態代理類的包裝 * @param methodToImpl Method 動態代理類里面要實現的接口方法的包裝 * @param interceptorClass Class 用戶提供的攔截器實現類 * @param methodIndex int 要實現的方法的索引 * @throws CannotCompileException */ private static void dynamicImplementsMethodsFromInterface(String classToProxy, CtClass implementer, Method methodToImpl, Class interceptorClass, int methodIndex) throws CannotCompileException { String methodCode = generateMethodCode(classToProxy, methodToImpl, interceptorClass, methodIndex); CtMethod cm = CtNewMethod.make(methodCode, implementer); implementer.addMethod(cm); } /** * 動態組裝方法體,當然代理里面的方法實現并不是簡單的方法拷貝,而是反射調用了攔截器里的invoke方法,并將接收到的參數進行傳遞 * @param classToProxy String 要動態代理的接口的實現類的類名, e.g test.StudentInfoServiceImpl * @param methodToImpl Method 動態代理類里面要實現的接口方法的包裝 * @param interceptorClass Class 用戶提供的攔截器實現類 * @param methodIndex int 要實現的方法的索引 * @return String 動態組裝的方法的字符串 */ private static String generateMethodCode(String classToProxy, Method methodToImpl, Class interceptorClass, int methodIndex) { String methodName = methodToImpl.getName(); String methodReturnType = methodToImpl.getReturnType().getName(); Class[] parameters = methodToImpl.getParameterTypes(); Class[] exceptionTypes = methodToImpl.getExceptionTypes(); StringBuffer exceptionBuffer = new StringBuffer(); //組裝方法的Exception聲明 if(exceptionTypes.length > 0) exceptionBuffer.append(" throws "); for(int i = 0; i < exceptionTypes.length; i++) { if(i != exceptionTypes.length - 1) exceptionBuffer.append(exceptionTypes[i].getName()).append(","); else exceptionBuffer.append(exceptionTypes[i].getName()); } StringBuffer parameterBuffer = new StringBuffer(); //組裝方法的參數列表 for(int i = 0; i < parameters.length; i++) { Class parameter = parameters[i]; String parameterType = parameter.getName(); //動態指定方法參數的變量名 String refName = "a" + i; if(i != parameters.length - 1) parameterBuffer.append(parameterType).append(" " + refName).append(","); else parameterBuffer.append(parameterType).append(" " + refName); } StringBuffer methodDeclare = new StringBuffer(); //方法聲明,由于是實現接口的方法,所以是public methodDeclare.append("public ").append(methodReturnType).append(" ").append(methodName).append("(").append(parameterBuffer).append(")").append(exceptionBuffer).append(" {\n"); String interceptorImplName = interceptorClass.getName(); //方法體 methodDeclare.append(INTERCEPTOR_HANDLER_INTERFACE).append(" interceptor = new ").append(interceptorImplName).append("();\n"); //反射調用用戶的攔截器接口 methodDeclare.append("Object returnObj = interceptor.invoke(Class.forName(\"" + classToProxy + "\").newInstance(), Class.forName(\"" + classToProxy + "\").getMethods()[" + methodIndex + "], "); //傳遞方法里的參數 if(parameters.length > 0) methodDeclare.append("new Object[]{"); for(int i = 0; i < parameters.length; i++) { //($w) converts from a primitive type to the corresponding wrapper type: e.g. //Integer i = ($w)5; if(i != parameters.length - 1) methodDeclare.append("($w)a" + i + ","); else methodDeclare.append("($w)a" + i); } if(parameters.length > 0) methodDeclare.append("});\n"); else methodDeclare.append("null);\n"); //對調用攔截器的返回值進行包裝 if(methodToImpl.getReturnType().isPrimitive()) { if(methodToImpl.getReturnType().equals(Boolean.TYPE)) methodDeclare.append("return ((Boolean)returnObj).booleanValue();\n"); else if(methodToImpl.getReturnType().equals(Integer.TYPE)) methodDeclare.append("return ((Integer)returnObj).intValue();\n"); else if(methodToImpl.getReturnType().equals(Long.TYPE)) methodDeclare.append("return ((Long)returnObj).longValue();\n"); else if(methodToImpl.getReturnType().equals(Float.TYPE)) methodDeclare.append("return ((Float)returnObj).floatValue();\n"); else if(methodToImpl.getReturnType().equals(Double.TYPE)) methodDeclare.append("return ((Double)returnObj).doubleValue();\n"); else if(methodToImpl.getReturnType().equals(Character.TYPE)) methodDeclare.append("return ((Character)returnObj).charValue();\n"); else if(methodToImpl.getReturnType().equals(Byte.TYPE)) methodDeclare.append("return ((Byte)returnObj).byteValue();\n"); else if(methodToImpl.getReturnType().equals(Short.TYPE)) methodDeclare.append("return ((Short)returnObj).shortValue();\n"); } else { methodDeclare.append("return (" + methodReturnType + ")returnObj;\n"); } methodDeclare.append("}"); System.out.println(methodDeclare.toString()); return methodDeclare.toString(); } } public class Main{ public static void main(String[] args) throws Exception{
     //分別對應 代理類要實現的接口類名需要代理類的類名用戶自定義攔截器實現類的類名Object proxyArithmetic
= MyProxyImpl.newProxyInstance("com.test.ArithmeticInterface", "com.test.Arithmetic",
                                    "com.test.InterceptorHandlerImpl");((ArithmeticInterface)proxyArithmetic).add(a, b);((ArithmeticInterface)proxyArithmetic).sub(a, b); } }

  打印一下動態實現接口的代碼如下:

public int add(int a0,int a1) {
com.test.InterceptorHandler interceptor = new com.test.InterceptorHandlerImpl();
Object returnObj = interceptor.invoke(Class.forName("com.test.Arithmetic").newInstance(), Class.forName("com.test.Arithmetic").getMethods()[0], new Object[]{($w)a0,($w)a1});
return ((Integer)returnObj).intValue();
}
public int sub(int a0,int a1) {
com.test.InterceptorHandler interceptor = new com.test.InterceptorHandlerImpl();
Object returnObj = interceptor.invoke(Class.forName("com.test.Arithmetic").newInstance(), Class.forName("com.test.Arithmetic").getMethods()[1], new Object[]{($w)a0,($w)a1});
return ((Integer)returnObj).intValue();
}

?

轉載于:https://www.cnblogs.com/hujunzheng/p/5134478.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/531346.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/531346.shtml
英文地址,請注明出處:http://en.pswp.cn/news/531346.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

libiconv庫簡單裁剪支持CP437編碼

有許多人在做項目的時候都會遇到字符編碼的不一致導致的亂碼問題&#xff0c;那如何去解決呢&#xff1f;在Linux系統上可以通過iconv函數族來進行編碼轉換&#xff0c;但有時候我們并不需要全部的字符集&#xff0c;因為可能會占用比較大的空間&#xff0c;本文主要支持CP437編…

簡單java在線測評程序

簡單java程序在線測評程序 一&#xff0e;前言 大家過年好&#xff01;今年的第一篇博客啊&#xff01;家里沒有網&#xff0c;到處蹭無線&#xff01;日子過得真糾結&#xff01;因為畢設的需求&#xff0c;簡單寫了一個java程序在線測評程序&#xff0c;當然也可以在本地測試…

指針強制類型轉換觸發內存自動對齊

C語言的指針是我們操作很靈活&#xff0c;但是也留下了不少坑&#xff0c;今天工作遇到了一個指針的坑&#xff0c;這里復現一下&#xff1a; 指針類型強制轉換,并進行解引用,引起的內存對齊問題.&#xff08;一&#xff09;問題復現&#xff1a; 運行環境&#xff1a;Ubuntu …

簡單文本編輯器

一、前言 聚天地之靈氣&#xff0c;集日月之精華&#xff01;一個簡單的java文本編輯器由此而生。畢設所需&#xff0c;很是無奈&#xff01; 二、界面預覽 三、實現思路 1.字體選擇器的實現 (1).字體類 class MyFont{private Font font;private Color color;public Font getFo…

u-boot新增命令后出現data abort

&#xff08;一&#xff09;問題描述 u-boot下新增了一條update的命令&#xff0c;直接輸入update沒有報錯&#xff0c;但是輸入up按TAB鍵補全時發現出現data abort&#xff0c;而且輸入不支持的命令也會有data abort &#xff08;二&#xff09;解決方法 最開始我包含的頭…

sublime text學習

Ctrl / ---------------------注釋 Ctrl 滾動 --------------字體變大/縮小 Ctrl N-------------------新建 軟件右下角可以選擇文檔語法模式 Ctrl Shift P ------------------命令模式 命令&#xff1a; sshtml模糊匹配-----語法切換到html模式&#xff0c;同理所得&am…

core文件如何分析

目錄(一&#xff09;什么是coredump(二)coredump產生的條件&#xff08;1&#xff09;coredump產生主要原因&#xff1a;&#xff08;2&#xff09;如何生成coredump(三&#xff09;gdb使用(四&#xff09;實例調試coredump文件(五&#xff09;總結(一&#xff09;什么是coredu…

SpringMVC+FreeMarker

前言&#xff1a; 最近在學習SpringMVC&#xff0c;模板引擎用的是FreeMarker&#xff0c;之前沒有接觸過。利用SpringMVC開發其實還有許多的步驟&#xff0c;比如控制層&#xff0c;服務層&#xff0c;持久化層&#xff0c;實體等等&#xff0c;先弄了一個小demo來總結一下Spr…

SpringMVC那點事

一、SpringMVC返回json數據的三種方式 1、第一種方式是spring2時代的產物&#xff0c;也就是每個json視圖controller配置一個Jsoniew。 如&#xff1a;<bean id"defaultJsonView" class"org.springframework.web.servlet.view.json.MappingJacksonJsonView&q…

js學習內容的整理

1、jquery動態添加Table中的一行 function addTableRow(tableId){var html <tr>\......\</tr>";//行首插入一行if($(#tableId).find(tr).length 1){$(html).insertAfter($(#tableId).find(tr).eq(0));} else { $(html).insertBefore($(#tableId).find(tr).e…

(一)最鄰近插值python實現

這里寫目錄標題&#xff08;一&#xff09;原始圖像&#xff08;二&#xff09;最鄰近插值實現&#xff08;三&#xff09;python實現1. 安裝庫2. python程序編寫3. 效果4. 工程文件&#xff08;一&#xff09;原始圖像 &#xff08;二&#xff09;最鄰近插值實現 一般情況下我…

(二)雙線性插值python實現

這里寫目錄標題&#xff08;一&#xff09;原始圖像&#xff08;二&#xff09;雙線性插值原理&#xff08;三&#xff09;python實現1. 安裝庫2. python程序編寫3. 效果4. 工程文件&#xff08;一&#xff09;原始圖像 &#xff08;二&#xff09;雙線性插值原理 一般情況下我…

js self = this的解釋

Demo 1: function Person(){this.name hjzgg;this.age 24;this.show function(){alert(name " " age);}}var p new Person();p.show(); 錯誤&#xff1a;name 和 age都沒有定義。 Demo 2: function Person(){this.name hjzgg;this.age 24;this.show functio…

(三)圖像轉灰度圖Python實現

這里寫目錄標題&#xff08;一&#xff09;原始圖像&#xff08;二&#xff09;轉換原理&#xff08;三&#xff09;python實現1. 安裝庫2. python程序編寫3. 效果4. 工程文件&#xff08;一&#xff09;原始圖像 &#xff08;二&#xff09;轉換原理 &#xff08;三&#xff…

SD卡實例分析fat32文件系統

目錄 環境描述 分析過程 1.SD卡格式化 2.使用winhex打開sd卡 3.MBR分析 4.DBR分析

java泛型上下限

前言&#xff1a; java的泛型上下限不是很好理解&#xff0c;尤其像我這種菜雞。反反復復看了好幾遍了...&#xff0c;真是... 一、簡單的繼承體系 class Person{}class Student extends Person{}class Worker extends Person{} 二、泛型上限&#xff08;extends 關鍵字&#x…

基于matlab的步進電機仿真(一)

這里寫目錄標題環境準備基礎準備模型參數輸入和輸出仿真原理圖仿真資源環境準備 MatLab2021b 基礎準備 打開Matlab&#xff0c;在幫助文檔里面搜索step motor,我們這里選擇如下模型 該模型實現了一個通用的步進電機模型&#xff1a; 可變磁阻步進電機永磁或混合步進電機 …

java自定義類加載器

前言 java反射&#xff0c;最常用的Class.forName()方法。做畢設的時候&#xff0c;接收到代碼字符串&#xff0c;通過 JavaCompiler將代碼字符串生成A.class文件&#xff08;存放在classpath下&#xff0c;也就是eclipse項目中的bin目錄里&#xff09;&#xff0c;然后通過jav…

常用網址

MDN : 一個不錯的前端學習網站 https://developer.mozilla.org/zh-CN/  https://developer.mozilla.org/en-US/ CodePen 是一個網站前端設計開發平臺&#xff0c;是一個針對網站前端代碼設計的開發工具。 RunJS - 在線編輯、展示、分享、交流你的 JavaScript 代碼 : http://r…

repo介紹(一)

repo簡介 Repo 是我們以 Git 為基礎構建的代碼庫管理工具,可以組織多個倉庫的上傳和下載。它是由一系列的Python腳本組成&#xff0c;封裝了一系列的Git命令&#xff0c;用來統一管理多個Git倉庫 一個大型的項目可能由很多小的倉庫組合而成的&#xff0c;為了方便統一管理各個…