转载地址:https://blog.csdn.net/danchu/article/details/70238002

什么是CGLIB

CGLIB是一个强大的、高性能的代码生成库。其被广泛应用于AOP框架(Spring、dynaop)中,用以提供方法拦截操作。Hibernate作为一个比较受欢迎的ORM框架,同样使用CGLIB来代理单端(多对一和一对一)关联(延迟提取集合使用的另一种机制)。CGLIB作为一个开源项目,其代码托管在github,地址为:https://github.com/cglib/cglib

为什么使用CGLIB

CGLIB代理主要通过对字节码的操作,为对象引入间接级别,以控制对象的访问。我们知道Java中有一个动态代理也是做这个事情的,那我们为什么不直接使用Java动态代理,而要使用CGLIB呢?答案是CGLIB相比于JDK动态代理更加强大,JDK动态代理虽然简单易用,但是其有一个致命缺陷是,只能对接口进行代理。如果要代理的类为一个普通类、没有接口,那么Java动态代理就没法使用了。关于Java动态代理,可以参者这里Java动态代理分析

CGLIB组成结构

CGLIB底层使用了ASM(一个短小精悍的字节码操作框架)来操作字节码生成新的类。除了CGLIB库外,脚本语言(如Groovy何BeanShell)也使用ASM生成字节码。ASM使用类似SAX的解析器来实现高性能。我们不鼓励直接使用ASM,因为它需要对Java字节码的格式足够的了解

例子

说了这么多,可能大家还是不知道CGLIB是干什么用的。下面我们将使用一个简单的例子来演示如何使用CGLIB对一个方法进行拦截。
首先,我们需要在工程的POM文件中引入cglib的dependency,这里我们使用的是2.2.2版本

<dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>2.2.2</version>
</dependency>
  • 1
  • 2
  • 3
  • 4
  • 5

依赖包下载后,我们就可以干活了,按照国际惯例,写个hello world

public class SampleClass {public void test(){System.out.println("hello world");}public static void main(String[] args) {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(SampleClass.class);enhancer.setCallback(new MethodInterceptor() {@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {System.out.println("before method run...");Object result = proxy.invokeSuper(obj, args);System.out.println("after method run...");return result;}});SampleClass sample = (SampleClass) enhancer.create();sample.test();}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

在mian函数中,我们通过一个Enhancer和一个MethodInterceptor来实现对方法的拦截,运行程序后输出为:

before method run...
hello world
after method run...
  • 1
  • 2
  • 3

在上面的程序中,我们引入了Enhancer和MethodInterceptor,可能有些读者还不太了解。别急,我们后面将会一一进行介绍。就目前而言,一个使用CGLIB的小demo就完成了

常用的API

目前网络上对CGLIB的介绍资料比较少,造成对cglib的学习困难。这里我将对cglib中的常用类进行一个介绍。为了避免解释的不清楚,我将为每个类都配有一个demo,用来做进一步的说明。首先就从Enhancer开始吧。

Enhancer

Enhancer可能是CGLIB中最常用的一个类,和Java1.3动态代理中引入的Proxy类差不多(如果对Proxy不懂,可以参考这里)。和Proxy不同的是,Enhancer既能够代理普通的class,也能够代理接口。Enhancer创建一个被代理对象的子类并且拦截所有的方法调用(包括从Object中继承的toString和hashCode方法)。Enhancer不能够拦截final方法,例如Object.getClass()方法,这是由于Java final方法语义决定的。基于同样的道理,Enhancer也不能对fianl类进行代理操作。这也是Hibernate为什么不能持久化final class的原因。

public class SampleClass {public String test(String input){return "hello world";}
}
  • 1
  • 2
  • 3
  • 4
  • 5

下面我们将以这个类作为主要的测试类,来测试调用各种方法

@Test
public void testFixedValue(){Enhancer enhancer = new Enhancer();enhancer.setSuperclass(SampleClass.class);enhancer.setCallback(new FixedValue() {@Overridepublic Object loadObject() throws Exception {return "Hello cglib";}});SampleClass proxy = (SampleClass) enhancer.create();System.out.println(proxy.test(null)); //拦截test,输出Hello cglibSystem.out.println(proxy.toString()); System.out.println(proxy.getClass());System.out.println(proxy.hashCode());
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

程序的输出为:

Hello cglib
Hello cglib
class com.zeus.cglib.SampleClass$$EnhancerByCGLIB$$e3ea9b7java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Numberat com.zeus.cglib.SampleClass$$EnhancerByCGLIB$$e3ea9b7.hashCode(<generated>)...
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

上述代码中,FixedValue用来对所有拦截的方法返回相同的值,从输出我们可以看出来,Enhancer对非final方法test()、toString()、hashCode()进行了拦截,没有对getClass进行拦截。由于hashCode()方法需要返回一个Number,但是我们返回的是一个String,这解释了上面的程序中为什么会抛出异常。

Enhancer.setSuperclass用来设置父类型,从toString方法可以看出,使用CGLIB生成的类为被代理类的一个子类,形如:SampleClass</span><span></span><span><script type="math/tex" id="MathJax-Element-1"></script>EnhancerByCGLIB</span><span></span><span><script type="math/tex" id="MathJax-Element-2"></script>e3ea9b7

Enhancer.create(Object…)方法是用来创建增强对象的,其提供了很多不同参数的方法用来匹配被增强类的不同构造方法。(虽然类的构造放法只是Java字节码层面的函数,但是Enhancer却不能对其进行操作。Enhancer同样不能操作static或者final类)。我们也可以先使用Enhancer.createClass()来创建字节码(.class),然后用字节码动态的生成增强后的对象。

可以使用一个InvocationHandler(如果对InvocationHandler不懂,可以参考这里)作为回调,使用invoke方法来替换直接访问类的方法,但是你必须注意死循环。因为invoke中调用的任何原代理类方法,均会重新代理到invoke方法中。

public void testInvocationHandler() throws Exception{Enhancer enhancer = new Enhancer();enhancer.setSuperclass(SampleClass.class);enhancer.setCallback(new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {if(method.getDeclaringClass() != Object.class && method.getReturnType() == String.class){return "hello cglib";}else{throw new RuntimeException("Do not know what to do");}}});SampleClass proxy = (SampleClass) enhancer.create();Assert.assertEquals("hello cglib", proxy.test(null));Assert.assertNotEquals("Hello cglib", proxy.toString());
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

为了避免这种死循环,我们可以使用MethodInterceptor,MethodInterceptor的例子在前面的hello world中已经介绍过了,这里就不浪费时间了。

有些时候我们可能只想对特定的方法进行拦截,对其他的方法直接放行,不做任何操作,使用Enhancer处理这种需求同样很简单,只需要一个CallbackFilter即可:

@Test
public void testCallbackFilter() throws Exception{Enhancer enhancer = new Enhancer();CallbackHelper callbackHelper = new CallbackHelper(SampleClass.class, new Class[0]) {@Overrideprotected Object getCallback(Method method) {if(method.getDeclaringClass() != Object.class && method.getReturnType() == String.class){return new FixedValue() {@Overridepublic Object loadObject() throws Exception {return "Hello cglib";}};}else{return NoOp.INSTANCE;}}};enhancer.setSuperclass(SampleClass.class);enhancer.setCallbackFilter(callbackHelper);enhancer.setCallbacks(callbackHelper.getCallbacks());SampleClass proxy = (SampleClass) enhancer.create();Assert.assertEquals("Hello cglib", proxy.test(null));Assert.assertNotEquals("Hello cglib",proxy.toString());System.out.println(proxy.hashCode());
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

ImmutableBean

通过名字就可以知道,不可变的Bean。ImmutableBean允许创建一个原来对象的包装类,这个包装类是不可变的,任何改变底层对象的包装类操作都会抛出IllegalStateException。但是我们可以通过直接操作底层对象来改变包装类对象。这有点类似于Guava中的不可变视图

为了对ImmutableBean进行测试,这里需要再引入一个bean

public class SampleBean {private String value;public SampleBean() {}public SampleBean(String value) {this.value = value;}public String getValue() {return value;}public void setValue(String value) {this.value = value;}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

然后编写测试类如下:

@Test(expected = IllegalStateException.class)
public void testImmutableBean() throws Exception{SampleBean bean = new SampleBean();bean.setValue("Hello world");SampleBean immutableBean = (SampleBean) ImmutableBean.create(bean); //创建不可变类Assert.assertEquals("Hello world",immutableBean.getValue()); bean.setValue("Hello world, again"); //可以通过底层对象来进行修改Assert.assertEquals("Hello world, again", immutableBean.getValue());immutableBean.setValue("Hello cglib"); //直接修改将throw exception
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

Bean generator

cglib提供的一个操作bean的工具,使用它能够在运行时动态的创建一个bean。

@Test
public void testBeanGenerator() throws Exception{BeanGenerator beanGenerator = new BeanGenerator();beanGenerator.addProperty("value",String.class);Object myBean = beanGenerator.create();Method setter = myBean.getClass().getMethod("setValue",String.class);setter.invoke(myBean,"Hello cglib");Method getter = myBean.getClass().getMethod("getValue");Assert.assertEquals("Hello cglib",getter.invoke(myBean));
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

在上面的代码中,我们使用cglib动态的创建了一个和SampleBean相同的Bean对象,包含一个属性value以及getter、setter方法

Bean Copier

cglib提供的能够从一个bean复制到另一个bean中,而且其还提供了一个转换器,用来在转换的时候对bean的属性进行操作。

public class OtherSampleBean {private String value;public String getValue() {return value;}public void setValue(String value) {this.value = value;}
}@Test
public void testBeanCopier() throws Exception{BeanCopier copier = BeanCopier.create(SampleBean.class, OtherSampleBean.class, false);//设置为true,则使用converterSampleBean myBean = new SampleBean();myBean.setValue("Hello cglib");OtherSampleBean otherBean = new OtherSampleBean();copier.copy(myBean, otherBean, null); //设置为true,则传入converter指明怎么进行转换assertEquals("Hello cglib", otherBean.getValue());
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

BulkBean

相比于BeanCopier,BulkBean将copy的动作拆分为getPropertyValues和setPropertyValues两个方法,允许自定义处理属性

@Test
public void testBulkBean() throws Exception{BulkBean bulkBean = BulkBean.create(SampleBean.class,new String[]{"getValue"},new String[]{"setValue"},new Class[]{String.class});SampleBean bean = new SampleBean();bean.setValue("Hello world");Object[] propertyValues = bulkBean.getPropertyValues(bean);assertEquals(1, bulkBean.getPropertyValues(bean).length);assertEquals("Hello world", bulkBean.getPropertyValues(bean)[0]);bulkBean.setPropertyValues(bean,new Object[]{"Hello cglib"});assertEquals("Hello cglib", bean.getValue());
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

使用注意:
1. 避免每次进行BulkBean.create创建对象,一般将其声明为static的
2. 应用场景:针对特定属性的get,set操作,一般适用通过xml配置注入和注出的属性,运行时才确定处理的Source,Target类,只需要关注属性名即可。

BeanMap

BeanMap类实现了Java Map,将一个bean对象中的所有属性转换为一个String-to-Obejct的Java Map

@Test
public void testBeanMap() throws Exception{BeanGenerator generator = new BeanGenerator();generator.addProperty("username",String.class);generator.addProperty("password",String.class);Object bean = generator.create();Method setUserName = bean.getClass().getMethod("setUsername", String.class);Method setPassword = bean.getClass().getMethod("setPassword", String.class);setUserName.invoke(bean, "admin");setPassword.invoke(bean,"password");BeanMap map = BeanMap.create(bean);Assert.assertEquals("admin", map.get("username"));Assert.assertEquals("password", map.get("password"));
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

我们使用BeanGenerator生成了一个含有两个属性的Java Bean,对其进行赋值操作后,生成了一个BeanMap对象,通过获取值来进行验证

keyFactory

keyFactory类用来动态生成接口的实例,接口需要只包含一个newInstance方法,返回一个Object。keyFactory为构造出来的实例动态生成了Object.equals和Object.hashCode方法,能够确保相同的参数构造出的实例为单例的。

public interface SampleKeyFactory {Object newInstance(String first, int second);
}
  • 1
  • 2
  • 3

我们首先构造一个满足条件的接口,然后进行测试

@Test
public void testKeyFactory() throws Exception{SampleKeyFactory keyFactory = (SampleKeyFactory) KeyFactory.create(SampleKeyFactory.class);Object key = keyFactory.newInstance("foo", 42);Object key1 = keyFactory.newInstance("foo", 42);Assert.assertEquals(key,key1);//测试参数相同,结果是否相等
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

Mixin(混合)

Mixin能够让我们将多个对象整合到一个对象中去,前提是这些对象必须是接口的实现。可能这样说比较晦涩,以代码为例:

public class MixinInterfaceTest {interface Interface1{String first();}interface Interface2{String second();}class Class1 implements Interface1{@Overridepublic String first() {return "first";}}class Class2 implements Interface2{@Overridepublic String second() {return "second";}}interface MixinInterface extends Interface1, Interface2{}@Testpublic void testMixin() throws Exception{Mixin mixin = Mixin.create(new Class[]{Interface1.class, Interface2.class,MixinInterface.class}, new Object[]{new Class1(),new Class2()});MixinInterface mixinDelegate = (MixinInterface) mixin;assertEquals("first", mixinDelegate.first());assertEquals("second", mixinDelegate.second());}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

Mixin类比较尴尬,因为他要求Minix的类(例如MixinInterface)实现一些接口。既然被Minix的类已经实现了相应的接口,那么我就直接可以通过纯Java的方式实现,没有必要使用Minix类。

String switcher

用来模拟一个String到int类型的Map类型。如果在Java7以后的版本中,类似一个switch语句。

@Test
public void testStringSwitcher() throws Exception{String[] strings = new String[]{"one", "two"};int[] values = new int[]{10,20};StringSwitcher stringSwitcher = StringSwitcher.create(strings,values,true);assertEquals(10, stringSwitcher.intValue("one"));assertEquals(20, stringSwitcher.intValue("two"));assertEquals(-1, stringSwitcher.intValue("three"));
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

Interface Maker

正如名字所言,Interface Maker用来创建一个新的Interface

@Test
public void testInterfaceMarker() throws Exception{Signature signature = new Signature("foo", Type.DOUBLE_TYPE, new Type[]{Type.INT_TYPE});InterfaceMaker interfaceMaker = new InterfaceMaker();interfaceMaker.add(signature, new Type[0]);Class iface = interfaceMaker.create();assertEquals(1, iface.getMethods().length);assertEquals("foo", iface.getMethods()[0].getName());assertEquals(double.class, iface.getMethods()[0].getReturnType());
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

上述的Interface Maker创建的接口中只含有一个方法,签名为double foo(int)。Interface Maker与上面介绍的其他类不同,它依赖ASM中的Type类型。由于接口仅仅只用做在编译时期进行类型检查,因此在一个运行的应用中动态的创建接口没有什么作用。但是InterfaceMaker可以用来自动生成代码,为以后的开发做准备。

Method delegate

MethodDelegate主要用来对方法进行代理

interface BeanDelegate{String getValueFromDelegate();
}@Test
public void testMethodDelegate()  throws Exception{SampleBean bean = new SampleBean();bean.setValue("Hello cglib");BeanDelegate delegate = (BeanDelegate) MethodDelegate.create(bean,"getValue", BeanDelegate.class);assertEquals("Hello cglib", delegate.getValueFromDelegate());
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

关于Method.create的参数说明:
1. 第二个参数为即将被代理的方法
2. 第一个参数必须是一个无参数构造的bean。因此MethodDelegate.create并不是你想象的那么有用
3. 第三个参数为只含有一个方法的接口。当这个接口中的方法被调用的时候,将会调用第一个参数所指向bean的第二个参数方法

缺点:
1. 为每一个代理类创建了一个新的类,这样可能会占用大量的永久代堆内存
2. 你不能代理需要参数的方法
3. 如果你定义的接口中的方法需要参数,那么代理将不会工作,并且也不会抛出异常;如果你的接口中方法需要其他的返回类型,那么将抛出IllegalArgumentException

MulticastDelegate

  1. 多重代理和方法代理差不多,都是将代理类方法的调用委托给被代理类。使用前提是需要一个接口,以及一个类实现了该接口
  2. 通过这种interface的继承关系,我们能够将接口上方法的调用分散给各个实现类上面去。
  3. 多重代理的缺点是接口只能含有一个方法,如果被代理的方法拥有返回值,那么调用代理类的返回值为最后一个添加的被代理类的方法返回值
public interface DelegatationProvider {void setValue(String value);
}public class SimpleMulticastBean implements DelegatationProvider {private String value;@Overridepublic void setValue(String value) {this.value = value;}public String getValue() {return value;}
}@Test
public void testMulticastDelegate() throws Exception{MulticastDelegate multicastDelegate = MulticastDelegate.create(DelegatationProvider.class);SimpleMulticastBean first = new SimpleMulticastBean();SimpleMulticastBean second = new SimpleMulticastBean();multicastDelegate = multicastDelegate.add(first);multicastDelegate  = multicastDelegate.add(second);DelegatationProvider provider = (DelegatationProvider) multicastDelegate;provider.setValue("Hello world");assertEquals("Hello world", first.getValue());assertEquals("Hello world", second.getValue());
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

Constructor delegate

为了对构造函数进行代理,我们需要一个接口,这个接口只含有一个Object newInstance(…)方法,用来调用相应的构造函数

interface SampleBeanConstructorDelegate{Object newInstance(String value);
}/*** 对构造函数进行代理* @throws Exception*/
@Test
public void testConstructorDelegate() throws Exception{SampleBeanConstructorDelegate constructorDelegate = (SampleBeanConstructorDelegate) ConstructorDelegate.create(SampleBean.class, SampleBeanConstructorDelegate.class);SampleBean bean = (SampleBean) constructorDelegate.newInstance("Hello world");assertTrue(SampleBean.class.isAssignableFrom(bean.getClass()));System.out.println(bean.getValue());
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

Parallel Sorter(并行排序器)

能够对多个数组同时进行排序,目前实现的算法有归并排序和快速排序

@Test
public void testParallelSorter() throws Exception{Integer[][] value = {{4, 3, 9, 0},{2, 1, 6, 0}};ParallelSorter.create(value).mergeSort(0);for(Integer[] row : value){int former = -1;for(int val : row){assertTrue(former < val);former = val;}}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

FastClass

顾明思义,FastClass就是对Class对象进行特定的处理,比如通过数组保存method引用,因此FastClass引出了一个index下标的新概念,比如getIndex(String name, Class[] parameterTypes)就是以前的获取method的方法。通过数组存储method,constructor等class信息,从而将原先的反射调用,转化为class.index的直接调用,从而体现所谓的FastClass。

@Test
public void testFastClass() throws Exception{FastClass fastClass = FastClass.create(SampleBean.class);FastMethod fastMethod = fastClass.getMethod("getValue",new Class[0]);SampleBean bean = new SampleBean();bean.setValue("Hello world");assertEquals("Hello world",fastMethod.invoke(bean, new Object[0]));
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

注意

由于CGLIB的大部分类是直接对Java字节码进行操作,这样生成的类会在Java的永久堆中。如果动态代理操作过多,容易造成永久堆满,触发OutOfMemory异常。

CGLIB和Java动态代理的区别

  1. Java动态代理只能够对接口进行代理,不能对普通的类进行代理(因为所有生成的代理类的父类为Proxy,Java类继承机制不允许多重继承);CGLIB能够代理普通类;
  2. Java动态代理使用Java原生的反射API进行操作,在生成类上比较高效;CGLIB使用ASM框架直接对字节码进行操作,在类的执行过程中比较高效
  3. 3.

CGLIB相关的文章:
- http://jnb.ociweb.com/jnb/jnbNov2005.html
- http://www.iteye.com/topic/799827
- http://mydailyjava.blogspot.kr/2013/11/cglib-missing-manual.html

                                            <link rel="stylesheet" href="https://csdnimg.cn/release/phoenix/production/markdown_views-68a8aad09e.css"></div>

CGLIB详解(最详细)相关推荐

  1. NLP:Transformer的架构详解之详细攻略(持续更新)

    NLP:Transformer的架构详解之详细攻略(持续更新) 目录 Transformer的架构详解 1. Encoder 1.1.Positional Encoding-数据预处理的部分 1.2. ...

  2. NLP:Transformer的简介(优缺点)、架构详解之详细攻略

    NLP:Transformer的简介(优缺点).架构详解之详细攻略 目录 Transformer的简介(优缺点).架构详解之详细攻略 1.Transformer的简介 (1).Transforme的四 ...

  3. Python之pandas:pandas中to_csv()、read_csv()函数的index、index_col(不将索引列写入)参数详解之详细攻略

    Python之pandas:pandas中to_csv().read_csv()函数的index.index_col(不将索引列写入)参数详解之详细攻略 目录 pandas中to_csv().read ...

  4. Python之pandas:pandas中缺失值与空值处理的简介及常用函数(drop()、dropna()、isna()、isnull()、fillna())函数详解之详细攻略

    Python之pandas:pandas中缺失值与空值处理的简介及常用函数(drop().dropna().isna().isnull().fillna())函数详解之详细攻略 目录 pandas中缺 ...

  5. android Json解析详解(详细代码)

    android Json解析详解(详细代码)   JSON的定义: 一种轻量级的数据交换格式,具有良好的可读和便于快速编写的特性.业内主流技术为其提供了完整的解决方案(有点类似于正则表达式 ,获得了当 ...

  6. Computer:字符编码(ASCII编码/GBK编码/BASE64编码/UTF-8编码)的简介、案例应用(python中的编码格式及常见编码问题详解)之详细攻略

    Computer:字符编码(ASCII编码/GBK编码/BASE64编码/UTF-8编码)的简介.案例应用(python中的编码格式及常见编码问题详解)之详细攻略 目录 符串编码(ASCII编码/GB ...

  7. ML之shap:分析基于shap库生成的力图、鸟瞰图、散点图等可视化图的坐标与内容详解之详细攻略

    ML之shap:分析基于shap库生成的力图.鸟瞰图.散点图等可视化图的坐标与内容详解之详细攻略 目录 一.力图可视化 1.单个样本力图可视化

  8. Java 泛型详解(超详细的java泛型方法解析)

    Java 泛型详解(超详细的java泛型方法解析) 1. 什么是泛型 泛型:是一种把明确类型的工作推迟到创建对象或者调用方法的时候才去明确的特殊的类型.也就是说在泛型使用过程中,操作的数据类型被指定为 ...

  9. python中装饰器的作用_Python装饰器详解,详细介绍它的应用场景

    装饰器的应用场景附加功能 数据的清理或添加:函数参数类型验证 @require_ints 类似请求前拦截数据格式转换 将函数返回字典改为 JSON/YAML 类似响应后篡改为函数提供额外的数据 moc ...

最新文章

  1. C++_向函数传递对象
  2. python day2 python基础 列表、元组操作 字符串操作 字典操作 集合操作 文件操作 字符编码与转码...
  3. 记录:SqlParamater要点小结
  4. 如何通过 Linq 将集合拆成多个块?
  5. c# 插入树形数据#_C#数据类型能力问题 套装1
  6. duration java_Java Duration类| toHours()方法与示例
  7. 06-02 Jenkins job 机制
  8. 开发函数计算的正确姿势 —— 排查超时问题
  9. html单页倒计时,单页面vue实例带倒计时功能
  10. 【C#】1.2 控制台应用程序学习要点
  11. 腾讯这几年成果还真不少!抢了不少群众资源。。。
  12. java如何面试别人_Java面试如何考察候选人
  13. mac 学习 java_Mac 新手从零学习JAVA 环境配置篇
  14. 2021-09-08 网安实验-编码解码-九宫格键盘编码
  15. 大数据小结(1-4)
  16. 未明学院数据分析报告:金融专业如何找实习?数据分析告诉你整个金融实习市场全貌!
  17. Flutter 启动白屏
  18. 计算机网络与新媒体是什么,网络与新媒体专业课程是什么
  19. C++普通函数指针和类成员函数指针
  20. 智能合约开发——TypeScript 基础(全)

热门文章

  1. springboot jpa 配置多数据源
  2. C# OPC客户端访问讯饶OPC服务器访问SunFull.X2OPC.1 报错
  3. 敬业!华为23级大佬消耗巨资整理出2000页网络协议最全笔记
  4. SECS/GEM300半导体12寸标准通讯设计方案,300mm标准
  5. Window 10无法登陆微软账户解决方案
  6. c++ delete
  7. 在win2008R2上使用(NLB)网络负载均衡
  8. 初等数论四大定理之——费马小定理
  9. php和js调试,JS调试使用详解
  10. POJ ACM题分类