zoukankan      html  css  js  c++  java
  • Java 反射

    说起反射,每次学习后每次忘,在现在的工作中可能没他关注过,希望这次学习后,能明白现在哪些code是可以进行优化的。

    JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

    要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象. 每一个类都会生成一个Class类型的对象。

    要正确使用Java反射机制就得使用java.lang.Class 这个类。它是Java反射机制的起源。当一个类被加载以后,Java虚拟机就会自动产生一个Class对象。通过这个Class对象我们就能获得加载到虚拟机当中这个Class对象对应的方法、成员以及构造方法的声明和定义等信息。

    1、Class类

    Class是一个类,封装了当前对象所对应的类的信息一个类中有属性,方法,构造器等,比如说有一个Person类,一个Order类,一个Book类,这些都是不同的类,现在需要一个类,用来描述类,这就是Class,它应该有类名,属性,方法,构造器等。Class是用来描述类的类。

    对于每个类而言,JRE 都为其保留一个不变的 Class 类型的对象。一个 Class 对象包含了特定某个类的有关信息。

    Class 对象只能由系统建立对象,一个类(而不是一个对象)在 JVM 中只会有一个Class实例

    获取Class对象的三种方式:

    1:通过类名获取  类名.class()

    2:通过对象获取   对象.getClass()

    3:通过全类名获取   Class.forName(全类名)

     1 package lesson1214;
     2 
     3 public class Person {
     4     
     5     private String name;
     6     private int age;
     7     
     8     public Person() {
     9         super();
    10         // TODO Auto-generated constructor stub
    11     }
    12     
    13     public Person(String name, int age){
    14         super();
    15         this.name = name;
    16         this.age = age;        
    17     }
    18 
    19     /**
    20      * @return the name
    21      */
    22     public String getName() {
    23         return name;
    24     }
    25 
    26 
    27     /**
    28      * @param name the name to set
    29      */
    30     public void setName(String name) {
    31         this.name = name;
    32     }
    33 
    34 
    35     /**
    36      * @return the age
    37      */
    38     public int getAge() {
    39         return age;
    40     }
    41 
    42 
    43     /**
    44      * @param age the age to set
    45      */
    46     public void setAge(int age) {
    47         this.age = age;
    48     }
    49 
    50 }
    51 
    52 package lesson1214;
    53 
    54 import java.lang.reflect.Field;
    55 
    56 public class ReflectionTest {
    57 
    58     public static void main(String[] args) throws ClassNotFoundException {
    59         // TODO Auto-generated method stub
    60         Class clazz = null;
    61         
    62         //1:通过类名.class获取Class对象
    63         clazz = Person.class;        
    64         Field []field = clazz.getDeclaredFields();
    65         
    66         //2:通过对象获取Class对象
    67         Person p1 = new Person();
    68         //这种方式是用在传进来一个对象,却不知道对象类型的时候使用
    69         Class clazz1 = p1.getClass();
    70         
    71         //上面这个例子的意义不大,因为已经知道person类型是Person类,再这样写就没有必要了
    72         //如果传进来是一个Object类,这种做法就是应该的
    73         Object obj = new Person();
    74         clazz1 = obj.getClass();
    75         
    76         //3.通过全类名(会抛出异常)
    77         //一般框架开发中这种用的比较多,因为配置文件中一般配的都是全类名,通过这种方式可以得到Class实例
    78         String name = "lesson1214.Person";
    79         Class clazz2 = Class.forName(name);   
    80         
    81         System.out.println(clazz2.getName());  //lesson1214.Person
    82         System.out.println(clazz==clazz1);  //true  在运行期间,每个类只有一个Class对象产生。
            System.out.println(clazz==clazz2); //true 83 //字符串的例子 84 clazz = String.class; 85 clazz = "javaTest".getClass(); 86 clazz = Class.forName("java.lang.String"); 87 88 System.out.println(); 89 90 } 91 92 }

    Class 对象就是放在方法区中。并且只有一个。

    Class类的常用方法

    方法名

    功能说明

    static Class forName(String name)

    返回指定类名 name 的 Class 对象

    Object newInstance()

    调用缺省构造函数,返回该Class对象的一个实例

    Object newInstance(Object []args)

    调用当前格式构造函数,返回该Class对象的一个实例    (没有这个方法)

    getName()

    返回此Class对象所表示的实体(类、接口、数组类、基本类型或void)名称

    Class getSuperClass()

    返回当前Class对象的父类的Class对象

    Class [] getInterfaces()

    获取当前Class对象的接口

    ClassLoader getClassLoader()

    返回该类的类加载器

    Class getSuperclass()

    返回表示此Class所表示的实体的超类的Class

     Class类的newInstance()方法

    1     public static void testRF() throws ClassNotFoundException, InstantiationException, IllegalAccessException{
    2         //1.获取Class对象
    3         Class clazz = Class.forName("lesson1214.Person");
    4         //2. 利用Class对象的newInstance方法创建一个类的实例
    5         Object obj = clazz.newInstance();  //对应Person类的无参构造函数,若没有无参构造函数,会出现抛异常。
    6         System.out.println(obj);
    7         
    8     }

    可以看出确实是创建了一个Person实例
      但是Person类有两个构造方法,到底是调用的哪一个构造方法呢?

      实际调用的是类的无参数的构造器。所以在我们在定义一个类的时候,定义一个有参数的构造器,作用是对属性进行初始化,还要写一个无参数的构造器,作用就是反射时候用。一般地、一个类若声明一个带参的构造器,同时要声明一个无参数的构造器。想要通过newInstance()来创建类对象,必须有无参构造函数。

    到现在大家对Class类应该有一点了解了。下面说Classloader

    2: ClassLoader

     关于ClassLoader可以查看另一片介绍Java ClassLoader,里面解析清晰。这个地方只给出一个例子。理解双亲委托机制。

     1 package lesson1214;
     2 
     3 public class TestClassLoader {
     4 
     5     public static void main(String[] args) throws ClassNotFoundException {
     6         
     7         //1、获取一个系统类加载器,当前类就是通过它加载的        
     8         ClassLoader classLoader = ClassLoader.getSystemClassLoader();
     9         System.out.println("系统类加载器: " + classLoader.getClass().getSimpleName());  //注意下这个打印
    10         
    11         //2、获取系统类加载器的父类加载器:扩展类加载器
    12         classLoader = classLoader.getParent();
    13         System.out.println("系统类加载器的父类加载器: " + classLoader);
    14         
    15         //3、获取扩展类加载器的父类加载器:引导类加载器 (不可获取,C语言编写)
    16         classLoader = classLoader.getParent();
    17         System.out.println("扩展类加载器的父类加载器: " + classLoader);
    18         
    19         //4、测试当前类由哪个加载器加载:系统类加载器
    20         classLoader = TestClassLoader.class.getClassLoader();
    21         System.out.println("当前类用的加载器: " + classLoader);
    22         
    23         //5. 测试 JDK 提供的 Object 类由哪个类加载器负责加载(引导类,不可获取)
    24         classLoader = Class.forName("java.lang.Object").getClassLoader();
    25         System.out.println("JDK 提供的 Object 类加载器 :" + classLoader);     //引导类,获取不到,输出为null
    26 
    27     }
    28 }

    运行结果:看过ClassLoader总结后,应该对这个输出结果理解。

    系统类加载器: AppClassLoader
    系统类加载器的父类加载器: sun.misc.Launcher$ExtClassLoader@305f387c
    扩展类加载器的父类加载器: null
    当前类用的加载器: sun.misc.Launcher$AppClassLoader@4d885088
    JDK 提供的 Object 类加载器 :null

    3:反射

    Reflection(反射)是Java被视为动态语言的关键。反射机制允许程序在执行期间借助于Reflection API取得任何类的内部信息,并且直接操作任意对象的内部属性及方法。

    Java反射机制主要提供了一下功能:

    1)在运行时构造任意一个类的对象

    2)在运行时获取任意一个类的所具有的成员变量和方法

    3)生成动态代理

    Class是一个类,一个描述类的类。封装了描述方法的Method, 描述字段的Filed,描述构造器的Constructor等属性。

    3.1 如何描述字段filed

      getField(String name);         getDeclaredField(String name);         getFields();         getDeclaredFields();

    其中带有Declared的表示获取所有申明的字段,不管是"public","protected",默认的还是"private",

    而不带Declared的表示获取的是申明为"public"的内容。

    提供的构造方法不能获取父类的字段,如果字段是私有的,不管是读值还是写值,都必须先调用setAccessable(true)方法

     1 package lesson1214;
     2 
     3 import java.lang.reflect.Field;
     4 
     5 public class TestFiled {
     6 
     7     public static void main(String[] args) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
     8         //通过类名.class获取Class对象
     9         Class clazz = Person.class;
    10         
    11         //1、获取字段,获取所有字段,不管是public和private. 但不能获取父类字段
    12         Field [] fields = clazz.getDeclaredFields();
    13         for(Field field : fields){
    14             System.out.println(field.getName());   //name  age
    15         }
    16         
    17         //1.2可以获取指定字段
    18         Field field = clazz.getDeclaredField("name");
    19         System.out.println(field.getName());    //name
    20         
    21         
    22         Person person = new Person("zhangsan", 20);        
    23         //2 、使用字段
    24         //2.1获取指定对象的指定字段
    25         Object val = field.get(person);
    26         System.out.println(val);    //zhangsan
    27         
    28         //2.2设置指定对象的指定字段
    29         field.set(person, "zhaosi");
    30         System.out.println(person.getName());   //zhaosi
    31         
    32         //2.3如果字段是私有的,不管是读值还是写值,都必须先调用setAccessable(true)方法            
    33         field = clazz.getDeclaredField("age");  //注意age是private
    34         //System.out.println(field.getName()); //会报指正异常,无法访问。必须先调用setAccessable(true)
    35 
    36         field.setAccessible(true);
    37         System.out.println(field.getName());        //age
    38         System.out.println(field.get(person));      //20   
    39         
    40         //如果要访问父类中的变量,就必须自己实现
    41     }
    42 
    43 }

    3.2 描述构造

     1 package lesson1214;
     2 
     3 import java.lang.reflect.Constructor;
     4 import java.lang.reflect.InvocationTargetException;
     5 
     6 public class TestConstructor {
     7 
     8     public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
     9         //获取Class
    10         Class<Person> clazz = Person.class;
    11         
    12         //1.1通过getDeclaredConstructors()得到所有构造函数
    13         Constructor<Person>[] conds = (Constructor<Person>[]) clazz.getDeclaredConstructors();
    14         System.out.println("getDeclaredConstructors():");
    15         for(Constructor<Person> con : conds){            
    16             System.out.println(con);
    17         }
    18         
    19         System.out.println("getDeclaredConstructor():");
    20         //1.2通过getDeclaredConstructor()得到无参构造函数
    21         Constructor<Person> cond  = clazz.getDeclaredConstructor();
    22         System.out.println(cond);
    23         //1.3通过getDeclaredConstruetor(Class<?>... parameterTypes) 获取指定有参构造函数
    24         cond = clazz.getDeclaredConstructor(String.class, int.class);
    25         System.out.println(cond);
    26         cond = clazz.getDeclaredConstructor(String.class);  //private构造函数
    27         System.out.println(cond);        
    28         
    29         
    30         System.out.println("getConstructors():");
    31         //1.4通过getConstructors()得到所有构造函数
    32         Constructor<Person>[] cons2 = (Constructor<Person>[]) clazz.getConstructors();
    33         for(Constructor<Person> con : cons2){
    34             System.out.println(con);
    35         }
    36         
    37         System.out.println("getConstructor():");
    38         //1.5通过getConstructor()获取无参构造函数
    39         Constructor<Person> con = clazz.getConstructor();
    40         System.out.println(con);
    41         //调用构造器的newInstance()方法创建对象
    42         Object obj = con.newInstance();
    43         
    44         //1.6通过getConstructor(Class<?>... parameterTypes)获取指定有参构造函数
    45         con = clazz.getConstructor(String.class,int.class);
    46         System.out.println(con);        
    47         //调用构造器的newInstance()方法创建对象        
    48         Object obj2 = con.newInstance("zhangsan",30);
    49         
    50         //con = clazz.getConstructor(String.class);  抛异常,get不到private 构造函数
    51         //System.out.println(con);    
    52 
    53     }
    54 
    55 }

    上面运行结果:

    getDeclaredConstructors():==================
    private lesson1214.Person(java.lang.String)
    public lesson1214.Person(java.lang.String,int)
    public lesson1214.Person()
    getDeclaredConstructor():===================
    public lesson1214.Person()
    public lesson1214.Person(java.lang.String,int)
    private lesson1214.Person(java.lang.String)
    getConstructors():==========================
    public lesson1214.Person(java.lang.String,int)
    public lesson1214.Person()
    getConstructor():===========================
    public lesson1214.Person()
    public lesson1214.Person(java.lang.String,int)

    种是获取所有的构造函数,getConstructors()和getDeclaredContructors()。

    种是获取指定的构造函数,getConstructor()和getDeclaredConstructor()。

    在这么多的方法中,其中带有Declared的表示获取所有申明的构造方法,不管是"public","protected",默认的还是"private",

                                     而不带Declared的表示获取的是申明为"public"的内容。不带参数的表示获取所有能获取的构造方法,而带参数的表示构造方法的参数类型。

    Class类中的newInstance()只能够调用无参的构造函数。 但是通过反射得到构造函数类Constructor中的newInstance(args[])是可以带参数的。

    3.3 如何描述方法

       getMethod(String name,Class<?>...parameterTypes);

             getDeclaredMethod(String name,Class<?>...parameterTypes);

             getMethods();

             getDeclaredMethods();

             和前面介绍的差不多,这里介绍一下参数,第一个String name表示的是方法名,第二个是参数类型,若是无参函数,可以写null。

     1 package lesson1214;
     2 
     3 import java.lang.reflect.Method;
     4 
     5 public class TestMethod {
     6 
     7     public static void main(String[] args) throws Exception {
     8         Class clazz = Class.forName("lesson1214.Person");
     9         
    10         //1.1通过getDlaredMethods()获取方法
    11         System.out.println("'getDeclaredMethods()=====all method=========");
    12         Method [] methods = clazz.getDeclaredMethods();
    13         for(Method method : methods){            
    14             System.out.println(method);            
    15         }
    16         
    17         //1.2通过getDeclaredMethod()获取指定方法
    18         System.out.println("getDeclaredMethod()=====specific method===========");
    19         Method methodd = clazz.getDeclaredMethod("getAge", null);
    20         System.out.println(methodd);    
    21         
    22         //1.3通过getMethods()获取所有public方法
    23         System.out.println("getMethods()===public all method=============");
    24         methods = clazz.getMethods();
    25         for(Method method : methods){            
    26             System.out.println(method);            
    27         }
    28         
    29         //1.4通过getMethod()获取指定public方法
    30         System.out.println("getMethod()===public specific method========");
    31         methodd = clazz.getMethod("setName", String.class);
    32         System.out.println(methodd);
    33         
    34     }
    35 }

    运行结果:

    'getDeclaredMethods()=====all method=========
    public java.lang.String lesson1214.Person.getName()
    public void lesson1214.Person.setName(java.lang.String)
    public int lesson1214.Person.getAge()
    public void lesson1214.Person.setAge(int)
    private void lesson1214.Person.getprivatemethod()
    getDeclaredMethod()=====specific method===========
    public int lesson1214.Person.getAge()
    getMethods()===public all method=============
    public java.lang.String lesson1214.Person.getName()
    public void lesson1214.Person.setName(java.lang.String)
    public int lesson1214.Person.getAge()
    public void lesson1214.Person.setAge(int)
    public java.lang.String lesson1214.BasePerson.getAddress()
    public void lesson1214.BasePerson.setAddress(java.lang.String)
    public int lesson1214.BasePerson.getNum()
    public void lesson1214.BasePerson.setNum(int)
    public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
    public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
    public final void java.lang.Object.wait() throws java.lang.InterruptedException
    public boolean java.lang.Object.equals(java.lang.Object)
    public java.lang.String java.lang.Object.toString()
    public native int java.lang.Object.hashCode()
    public final native java.lang.Class java.lang.Object.getClass()
    public final native void java.lang.Object.notify()
    public final native void java.lang.Object.notifyAll()
    getMethod()===public specific method========
    public void lesson1214.Person.setName(java.lang.String)
     

    从运行结果可以得出:

       getMethods()--获取取clazz对应类中的所有方法,不能获取private方法,且获取从父类继承来的所有方法

    getDeclaredMethods()--获取所有方法,包括私有方法。但不能获取父类方法。

    紧接着上面的code, get出来的方法怎么执行:

    1         //1.5 方法执行
    2         //  invoke第一个参数表示执行哪个对象的方法,剩下的参数是执行方法时需要传入的参数
    3         Person p1= (Person) clazz.newInstance();
    4         methodd.invoke(p1, "zhangsan");
    5         System.out.println(p1.getName());
    6         //私有方法的执行,必须在调用invoke之前加上一句method.setAccessible(true);

    调用方法通过methodd.invoke(obj, args) 

    第一个参数表示执行哪个对象的方法,剩下的参数是执行方法时需要传入的参数。如果是静态函数,可以填null.下面给出例子:

    私有方法的执行,必须在调用invoke之前加上一句method.setAccessible(true); field也是这个样子。

    反射main方法

     1 package lesson1214;
     2 
     3 public class Person extends BasePerson {
     4     
     5     public static void main(String[] args) {
     6         System.out.println("main方法执行了。。。。。。。");
     7     }
     8 }
     9 
    10 package lesson1214;
    11 
    12 import java.lang.reflect.Method;
    13 
    14 public class TestMethod {
    15 
    16     public static void main(String[] args) throws Exception {
    17         Class clazz = Class.forName("lesson1214.Person");
    18         Method methodd = clazz.getMethod("main", String[].class);   //怎么传入参数
    19         System.out.println(String[].class);
    20         System.out.println(methodd);        
    21 
    22         //1.5 方法执行
    23         //  invoke第一个参数表示执行哪个对象的方法,剩下的参数是执行方法时需要传入的参数
    24         Person p1= (Person) clazz.newInstance();
    25         methodd.invoke(null, (Object)new String[]{"a","b","c"});   //这个地方必须强转
    26         //第一个参数,对象类型,因为方法是static静态的,所以为null可以,
    27         //第二个参数是String数组,这里要注意在jdk1.4时是数组,jdk1.5之后是可变参数
    28         //这里拆的时候将  new String[]{"a","b","c"} 拆成3个对象。。。所以需要将它强转。        
    29         
    30     }
    31 }

    控制台输出:

    main方法执行了。。。

    反射小结

     1. Class: 是一个类; 一个描述类的类.

      封装了描述方法的 Method,

           描述字段的 Filed,

                  描述构造器的 Constructor 等属性.
     
     2. 如何得到 Class 对象:
        2.1 Person.class
        2.2 person.getClass()
        2.3 Class.forName("com.atguigu.javase.Person")
      
     3. 关于 Method:
        3.1 如何获取 Method:
          1). getDeclaredMethods: 得到 Method 的数组.
          2). getDeclaredMethod(String methondName, Class ... parameterTypes)
      
        3.2 如何调用 Method
          1). 如果方法时 private 修饰的, 需要先调用 Method 的 setAccessible(true), 使其变为可访问
          2). method.invoke(obj, Object ... args);
      
      4. 关于 Field:
        4.1 如何获取 Field: getField(String fieldName)
        4.2 如何获取 Field 的值: 
          1). setAccessible(true)
          2). field.get(Object obj)
        4.3 如何设置 Field 的值:
          field.set(Obejct obj, Object val)
      
      5. 了解 Constructor 和 Annotation

    上面这个是反射最基本的知识,后面争取添加一个反射的项目例子。

    学到这,下面我们给出两个反射的例子,加深印象。

    4.1反射方法的其它使用之---通过反射运行配置文件内容

    Person类:

     1 package lesson1214;
     2 
     3 public class Person extends BasePerson {
     4     private int age;
     5 
     6     /**
     7      * @return the age
     8      */
     9     public int getAge() {
    10         System.out.println("hehe.....");
    11         return age;
    12     }
    13 }

    配置文件以txt文件为例子(pro.txt)

    1 className=lesson1214.Person
    2 methodName=getAge

    测试类:

     1 package lesson1214;
     2 
     3 import java.io.File;
     4 import java.io.FileInputStream;
     5 import java.io.FileReader;
     6 import java.io.IOException;
     7 import java.lang.reflect.Method;
     8 import java.util.Properties;
     9 
    10 public class Demo {
    11 
    12     public static void main(String[] args) throws Exception {
    13         //通过反射获取Class对象
    14         Class personClass = Class.forName(getValue("className"));
    15         //通过反射获取方法
    16         Method method = personClass.getDeclaredMethod(getValue("methodName"));
    17         //调用方法        
    18         method.invoke(personClass.newInstance(),null);  
    19         method.invoke(personClass.newInstance());//若参数不需要传进去,可以不传
    20         
    21     }
    22     
    23     //此方法接收一个key,在配置文件中获取相应的value
    24     public static String getValue(String key) throws IOException{
    25         Properties pro = new Properties();
    26                 
    27         //相对路径:相对路径名必须使用取自其他路径名的信息进行解释。
    28         //java.io 包中的类总是根据当前用户目录来解析相对路径名。此目录可以通过System.getProperty("user.dir");获取
    29         System.out.println(System.getProperty("user.dir"));   //  C:WorkspaceJava01
    30         //通过字符流和字节流都可以,还不知道两者在这个地方区别
    31         //FileReader in = new FileReader("C:/Workspace/Java01/src/lesson1214/pro.txt");
    32         FileInputStream fis = new FileInputStream("src/lesson1214/pro.txt");  //从src目录开始        
    33         
    34         pro.load(fis);
    35         fis.close();            
    36         return pro.getProperty(key);        
    37     }
    38 }

    控制台输出:

    C:WorkspaceJava01
    C:WorkspaceJava01
    hehe.....
    hehe.....

    如果我们系统升级了,不需要Person类了,而需要写一个Person2时,这时只需要更改pro.txt的文件。

    4.2、反射方法的其它使用之---通过反射越过泛型检查

    泛型用在编译期间,编译过后泛型消失。 所以可以通过反射越过泛型检查。

    测试类:

     1 package lesson1214;
     2 
     3 import java.io.FileInputStream;
     4 import java.io.IOException;
     5 import java.lang.reflect.Method;
     6 import java.util.ArrayList;
     7 import java.util.Iterator;
     8 import java.util.List;
     9 import java.util.Properties;
    10 //例如:有一个String泛型的集合,怎样能向这个集合中添加一个Integer类型的值?
    11 public class Demo {
    12     public static void main(String[] args) throws Exception {
    13         
    14         List<String> lists = new ArrayList<String>();
    15         lists.add("abc");
    16         lists.add("efg");
    17         //list.add(100);  
    18         //The method add(int, String) in the type List<String> is not applicable for the arguments (int)
    19         
    20         Class clazz = lists.getClass();
    21         Method method = clazz.getDeclaredMethod("add", Object.class);
    22         method.invoke(lists, 100);  //注意这个地方写的是lists这个类
    23         
    24         //用迭代遍历集合
    25         Iterator it = lists.iterator();        
    26         while(it.hasNext()){
    27             System.out.println(it.next());            
    28         }
    29         //遍历集合
    30         for(Object list : lists){
    31             System.out.println(list);
    32         }
    33         
    34     }
    35 }
    控制台输出:
    abc
    efg
    100
    abc
    efg
    100
    例如:有一个String泛型的集合,怎样能向这个集合中添加一个Integer类型的值?上面就是典型例子,通过反射越过泛型检查。

    转载:

    https://www.cnblogs.com/tech-bird/p/3525336.html   关于反省和反射,这个地方没看。

    http://www.coin163.com/it/3275193761898137281

    https://blog.csdn.net/sinat_38259539/article/details/71799078   给出的反射例子,经典。

  • 相关阅读:
    input file上传文件弹出框的默认格式设置
    sql server中raiserror的用法(动态参数传值)
    Vue 安装“npm install -g @vue/cli”发生npm WARN deprecated request@2.88.2: request has been deprecated,的错误解决方法
    jquery奇数、偶数选择器
    使用float时造成里层的div无法撑开外层的div的解决办法
    Vue监听到被赋值后执行某个动作
    使用Jquery的$.unique去重时,注意先排序再去重
    DbFunctions.DiffDays(DateTime? dateValue1, DateTime? dateValue2)说明
    jquery利用sort方法对json数据排序
    jQuery closest() /parents()/parent() 方法说明
  • 原文地址:https://www.cnblogs.com/beilou310/p/10113946.html
Copyright © 2011-2022 走看看