• 07.Java反射技术


    什么是反射技术

    1.Java反射机制的核心是在程序运行时动态加载类并获取类的详细信息,从而操作类或对象的属性和方法。本质是JVM得到class对象之后,再通过class对象进行反编译,从而获取对象的各种信息。

    2.Java属于先编译再运行的语言,程序中对象的类型在编译期就确定下来了,而当程序在运行时可能需要动态加载某些类,这些类因为之前用不到,所以没有被加载到JVM。通过反射,可以在运行时动态地创建对象并调用其属性,不需要提前在编译期知道运行的对象是谁。

    Java反射机制可以动态方式获取到 class相关信息 class中成员方法、属性 ,反射技术灵活调用方法 或者

    给我们成员属性赋值,class.forName 初始化对象(创建我们的对象)

    类加载器:第九期Java进阶课程中 类加载器

    简单回顾双亲委派机制

    1.启动(Bootstrap)类加载器:加载JVM自身工作需要的类,它由JVM自己实现。它会加载$JAVA_HOME/jre/lib下的文件 底层是C语言实现

    2.扩展(Extension)类加载器:它是JVM的一部分,由sun.misc.LauncherExtClassLoader实现,他会加载ExtClassLoader实现,他会加载ExtClassLoader实现,他会加载JAVA_HOME/jre/lib/ext目录中的文件(或由System.getProperty(“java.ext.dirs”)所指定的文件)。 底层是Java实现

    3.(应用)AppClassLoader 类加载器:应用类加载器,我们工作中接触最多的也是这个类加载器,它由sun.misc.Launcher$AppClassLoader实现。他加载我们工程目录classpath下的class及jar包 底层是java实现

    4.自定义类加载器: 也就是用户自己定义的类加载器

    反射技术的原理

    Class对象的由来是将.class文件读入内存,并为之创建一个Class对象。

         // 1.获取class方式 直接类名称.
            Class<MayiktUserEntity> mayiktUserEntityClass = MayiktUserEntity.class;
            // 2.new 对象 通过对象获取class  springioc容器 根据class获取对象
            MayiktUserEntity mayiktUserEntity = new MayiktUserEntity();
            Class<? extends MayiktUserEntity> aClass = mayiktUserEntity.getClass();
            //类的完整路径地址 包的名称+类名称组合  第三种 企业使用最多的
            Class<?> aClass1 = Class.forName("com.mayikt.entity.MayiktUserEntity");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    反射的优缺点

    优点:在运行时获得类的各种内容,进行反编译,对于Java这种先编译再运行的语言,能够让我们很方便的创建灵活的代码,这些代码可以在运行时装配,无需在组件之间进行源代码的链接,更加容易实现面向对象。

    缺点:

    1.反射会消耗一定的系统资源,因此,如果不需要动态地创建一个对象,那么就不需要用反射;

    2.反射调用方法时可以忽略权限检查,因此可能会破坏封装性而导致安全问题。

    反射机制应用场景

    1.JDBC 中Class.forName(“com.mysql.jdbc.Driver”)-----反射技术加载 mysql驱动

    2.Spring底层基于反射初始化对象

    3.(写一套自己 )第三方框架扩展功能 代理设计模式 反射技术

    反射机制核心类

    Java.lang.Class;

    Java.lang.reflect.Constructor;

    Java.lang.reflect.Field;

    Java.lang.reflect.Method;

    Java.lang.reflect.Modifier;

    反射机制API使用

    获取Class

    1、获得Class:主要有三种方法:

    (1)Object–>getClass

    (2)任何数据类型(包括基本的数据类型)都有一个“静态”的class属性

    (3)通过class类的静态方法:forName(String className)(最常用)

    package com.mayikt;
    
    import com.mayikt.entity.MayiktUserEntity;
    
    
    /**
     * @author 余胜军
     * @ClassName Test01
     * @qq 644064779
     * @addres www.mayikt.com
     * 微信:yushengjun644
     */
    public class Test01 {
        public static void main(String[] args) throws ClassNotFoundException {
            // 1.方式1 通过对象名称.getClass();
            MayiktUserEntity user1 = new MayiktUserEntity();
            System.out.println(user1.getClass());
            System.out.println(user1.getClass().getName());
            // 2.方式2: 直接通过类名称.class;
            System.out.println(MayiktUserEntity.class);
            // 3.方式3 :class.forName("类的完整路径地址")
            Class<?> aClass = Class.forName("com.mayikt.entity.MayiktUserEntity");
            System.out.println(aClass==user1.getClass());
        }
    }
    
    • 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

    注意就是在运行期间,一个类只有一个Class对象产生,所以打印结果都是true

    判断是否为某个类的示例

    在之前我们使用instanceof 关键字来判断是否为某个类的实例。同时我们也可以借助反射中Class对象的isInstance()方法来判断时候为某个类的实例,它是一个native方法。

        public native boolean isInstance(Object obj);
    package com.mayikt;
    
    import com.mayikt.entity.MayiktUserEntity;
    
    
    /**
     * @author 余胜军
     * @ClassName Test01
     * @qq 644064779
     * @addres www.mayikt.com
     * 微信:yushengjun644
     */
    public class Test01 {
        public static void main(String[] args) throws ClassNotFoundException {
            // 1.方式1 通过对象名称.getClass();
            MayiktUserEntity user1 = new MayiktUserEntity();
            System.out.println(user1.getClass());
            System.out.println(user1.getClass().getName());
            // 2.方式2: 直接通过类名称.class;
            System.out.println(MayiktUserEntity.class);
            // 3.方式3 :class.forName("类的完整路径地址")
            Class<?> aClass = Class.forName("com.mayikt.entity.MayiktUserEntity");
            System.out.println(aClass == user1.getClass());
            boolean instance = aClass.isInstance(new Object());
            System.out.println(instance);
        }
    }
    
    • 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

    通过反射创建对象

    反射使用无参构造方法初始化对象

    使用Class对象的newInstance()方法来创建Class对象对应类的实例。

    package com.mayikt.entity;
    
    /**
     * @author 余胜军
     * @ClassName UserEntity
     * @qq 644064779
     * @addres www.mayikt.com
     * 微信:yushengjun644
     */
    public class MayiktUserEntity {
        private String userName;
        private Integer age;
    
        public MayiktUserEntity() {
            System.out.println("无参构造方法初始化....");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    反射使用有参构造方法初始化对象

    // 2.1 获取到构造方法  调用有参构造方法初始化对象
    Constructor<?> constructor = aClass.getConstructor(String.class, Integer.class);
     MayiktUserEntity user2 = (MayiktUserEntity) constructor.newInstance("mayikt", 22);
    System.out.println(user2);
    
      public MayiktUserEntity(String userName, Integer age) {
            this.userName = userName;
            this.age = age;
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    反射获取构造方法

    1)批量获取的方法:
    public Constructor[] getConstructors():所有"公有的"构造方法
    public Constructor[] getDeclaredConstructors():获取所有的构造方法(包括私有、受保护、默认、公有)2)单个获取的方法,并调用:
    public Constructor getConstructor(Class... parameterTypes):获取单个的"公有的"构造方法:
    public Constructor getDeclaredConstructor(Class... parameterTypes):获取"某个构造方法"可以是私有的,或受保护、默认、公有;
    (3) 调用构造方法:
    Constructor-->newInstance(Object... initargs)
    newInstance是 Constructor类的方法(管理构造函数的类)
    api的解释为:newInstance(Object... initargs) ,使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。
    它的返回值是T类型,所以newInstance是创建了一个构造方法的声明类的新实例对象,并为之调用。
    package com.mayikt;
    
    import java.lang.reflect.Constructor;
    import java.util.Arrays;
    
    /**
     * @author 余胜军
     * @ClassName Test03
     * @qq 644064779
     * @addres www.mayikt.com
     * 微信:yushengjun644
     */
    public class Test03 {
        public static void main(String[] args) throws ClassNotFoundException {
            Class<?> aClass = Class.forName("com.mayikt.entity.MayiktUserEntity");
            // 1.所有"公有的"构造方法
            Constructor<?>[] constructors1 = aClass.getConstructors();
            for (int i = 0; i < constructors1.length; i++) {
                System.out.println(constructors1[i]);
            }
            System.out.println("--------------------------");
            //2.获取所有的构造方法(包括私有、受保护、默认、公有)
            Constructor<?>[] constructors2 = aClass.getDeclaredConstructors();
            for (int i = 0; i < constructors2.length; i++) {
                System.out.println(constructors2[i]);
            }
            
        }
    }
    
    • 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
    • 36
    • 37
    • 38
    • 39
    • 40

    反射获取成员属性

    获取成员变量并调用:

    1.批量的

    ​ 1.1.Field[] getFields():获取所有的"公有字段"

    ​ 1.2.Field[] getDeclaredFields():获取所有字段,包括:私有、受保护、默认、公有;

    2.获取单个的:

    2.1.public Field getField(String fieldName):获取某个"公有的"字段;

    2.2.public Field getDeclaredField(String fieldName):获取某个字段(可以是私有的)

    3.设置字段的值 需要注意权限问题:

    3.1.Field --> public void set(Object obj,Object value):

    3.2.参数说明:

    3.3.obj:要设置的字段所在的对象;

    相关代码:

    package com.mayikt;
    
    import java.lang.reflect.Field;
    
    /**
     * @author 余胜军
     * @ClassName Test04
     * @qq 644064779
     * @addres www.mayikt.com
     * 微信:yushengjun644
     */
    public class Test04 {
        public static void main(String[] args) throws ClassNotFoundException {
            //1.获取所有的"公有字段"
            Class<?> aClass = Class.forName("com.mayikt.entity.MayiktUserEntity");
            Field[] fields = aClass.getFields();
            for (int i = 0; i < fields.length; i++) {
                System.out.println(fields[i]);
            }
            //2.获取所有字段,包括:私有、受保护、默认、公有;
            Field[] declaredFields = aClass.getDeclaredFields();
            for (int i = 0; i < declaredFields.length; i++) {
                System.out.println(declaredFields[i]);
            }
    
        }
    }
    
    • 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

    使用反射给类中成员属性赋值

    package com.mayikt;
    
    import com.mayikt.entity.MayiktUserEntity;
    
    import java.lang.reflect.Field;
    
    /**
     * @author 余胜军
     * @ClassName Test05
     * @qq 644064779
     * @addres www.mayikt.com
     * 微信:yushengjun644
     */
    public class Test05 {
        public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, InstantiationException {
            Class<?> aClass = Class.forName("com.mayikt.entity.MayiktUserEntity");
            //1.反射使用无参构造方法初始化对象
            MayiktUserEntity mayiktUserEntity = (MayiktUserEntity) aClass.newInstance();
            Field userNameField = aClass.getDeclaredField("userName");
            userNameField.set(mayiktUserEntity, "mayikt");
            //2.获取该对象的成员userName值
            System.out.println(mayiktUserEntity.getUserName());
        }
    }
    
    错误提示:
    Exception in thread "main" java.lang.IllegalAccessException: Class com.mayikt.Test05 can not access a member of class com.mayikt.entity.MayiktUserEntity with modifiers "private"
    	at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102)
    	at java.lang.reflect.AccessibleObject.slowCheckMemberAccess(AccessibleObject.java:296)
    	at java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:288)
    	at java.lang.reflect.Field.set(Field.java:761)
    	at com.mayikt.Test05.main(Test05.java:20)
     反射在默认的情况下不允许给私有属性赋值 需要设置权限
     
     package com.mayikt;
    
    import com.mayikt.entity.MayiktUserEntity;
    
    import java.lang.reflect.Field;
    
    /**
     * @author 余胜军
     * @ClassName Test05
     * @qq 644064779
     * @addres www.mayikt.com
     * 微信:yushengjun644
     */
    public class Test05 {
        public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, InstantiationException {
            Class<?> aClass = Class.forName("com.mayikt.entity.MayiktUserEntity");
            //1.反射使用无参构造方法初始化对象
            MayiktUserEntity mayiktUserEntity = (MayiktUserEntity) aClass.newInstance();
            Field userNameField = aClass.getDeclaredField("userName");
            userNameField.setAccessible(true);
            userNameField.set(mayiktUserEntity, "mayikt");
            //2.获取该对象的成员userName值
            System.out.println(mayiktUserEntity.getUserName());
        }
    }
    
     
    
    • 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
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61

    反射获取成员方法

    获取成员方法并调用:

    1.所有的方法:

    1.1.public Method[] getMethods():获取所有"公有方法";(包含了父类的方法也包含Object类)

    1.2.public Method[] getDeclaredMethods():获取所有的成员方法,包括私有的(不包括继承的)

    2.获取单个的方法:

    ​ 2.1.public Method getMethod(String name,Class<?>… parameterTypes):

    				参数:
    
    					name : 方法名;
    
    					Class ... : 形参的Class类型对象
    
    	public Method getDeclaredMethod(String name,Class<?>... parameterTypes)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    ​ 调用方法:

    	Method --> public Object invoke(Object obj,Object... args):
    
    				参数说明:
    
    				obj : 要调用方法的对象;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    package com.mayikt.entity;
    
    /**
     * @author 余胜军
     * @ClassName UserEntity
     * @qq 644064779
     * @addres www.mayikt.com
     * 微信:yushengjun644
     */
    public class MayiktUserEntity extends ParentEntity {
        private String userName;
        private Integer age;
    
        public MayiktUserEntity() {
            System.out.println("无参构造方法初始化....");
        }
    
        public MayiktUserEntity(String userName, Integer age) {
            this.userName = userName;
            this.age = age;
        }
    
        MayiktUserEntity(String userName) {
            this.userName = userName;
            this.age = age;
        }
    
        public String getUserName() {
            return userName;
        }
    
        public void setUserName(String userName) {
            this.userName = userName;
        }
    
        public Integer getAge() {
            return age;
        }
    
        public void setAge(Integer age) {
            this.age = age;
        }
    
        @Override
        public String toString() {
            return "MayiktUserEntity{" +
                    "userName='" + userName + '\'' +
                    ", age=" + age +
                    '}';
        }
        private void addUser(){
            System.out.println("addUser");
        }
    }
    
    
    package com.mayikt.entity;
    
    /**
     * @author 余胜军
     * @ClassName UserEntity
     * @qq 644064779
     * @addres www.mayikt.com
     * 微信:yushengjun644
     */
    public class MayiktUserEntity extends ParentEntity {
        private String userName;
        private Integer age;
    
        public MayiktUserEntity() {
            System.out.println("无参构造方法初始化....");
        }
    
        public MayiktUserEntity(String userName, Integer age) {
            this.userName = userName;
            this.age = age;
        }
    
        MayiktUserEntity(String userName) {
            this.userName = userName;
            this.age = age;
        }
    
        public String getUserName() {
            return userName;
        }
    
        public void setUserName(String userName) {
            this.userName = userName;
        }
    
        public Integer getAge() {
            return age;
        }
    
        public void setAge(Integer age) {
            this.age = age;
        }
    
        @Override
        public String toString() {
            return "MayiktUserEntity{" +
                    "userName='" + userName + '\'' +
                    ", age=" + age +
                    '}';
        }
        private void addUser(){
            System.out.println("addUser");
        }
    }
    
    • 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
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110

    使用反射调用类中成员方法

    反射调用类中的方法

    package com.mayikt;
    
    import com.mayikt.entity.MayiktUserEntity;
    
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    
    /**
     * @author 余胜军
     * @ClassName Test07
     * @qq 644064779
     * @addres www.mayikt.com
     * 微信:yushengjun644
     */
    public class Test07 {
        public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
            Class<?> aClass = Class.forName("com.mayikt.entity.MayiktUserEntity");
            MayiktUserEntity mayiktUserEntity = (MayiktUserEntity) aClass.newInstance();
            Method addUserMethod = aClass.getDeclaredMethod("addUser", Integer.class);
            addUserMethod.setAccessible(true);
            Object result = addUserMethod.invoke(mayiktUserEntity, 10);
            System.out.println(result);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    反射机制综合案例

    思路:

    1.当我们springboot项目启动完成之后,获取当前有哪些观察者? MsgObServer 所有实现class

    2.在根据该class去ioc容器中查找到该bean对象 存入到 msg主题中;

    结合上节课观察者模式

    maven依赖

      <dependencies>
            <dependency>
                <groupId>org.reflections</groupId>
                <artifactId>reflections</artifactId>
                <version>0.9.10</version>
            </dependency>
        </dependencies>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    反射工具类

    package com.mayikt.utils;
    
    import com.mayikt.observer.ObServer;
    import com.mayikt.observer.impl.MsgObServer;
    import org.reflections.Reflections;
    
    import java.util.List;
    import java.util.Set;
    
    /**
    * @author 余胜军
    * @ClassName ReflexUtils
    * @qq 644064779
    * @addres www.mayikt.com
    * 微信:yushengjun644
    */
    public class ReflexUtils {
        public static Set<Class> getInterfaceImpls(Class c) {
            Reflections reflections = new Reflections("com.mayikt");
            //Filter是个接口,获取在指定包扫描的目录所有的实现类
            Set<Class> classes = reflections.getSubTypesOf(c);
            return classes;
        }
        
        public static void main(String[] args) {
            //        ReflexUtils.getInterfaceImpls(MsgObServer.class)
            //        interfaceImpls.forEach((t) -> {
            //            System.out.println("name:" + t.getSimpleName());
            //        });
        }
    }
    package com.mayikt.init;
    
    import com.mayikt.observer.ObServer;
    import com.mayikt.observer.impl.MsgObServer;
    import com.mayikt.observer.impl.MsgSubject;
    import com.mayikt.utils.ReflexUtils;
    import com.mayikt.utils.SpringUtils;
    import org.springframework.boot.ApplicationArguments;
    import org.springframework.boot.ApplicationRunner;
    import org.springframework.context.ApplicationContext;
    import org.springframework.stereotype.Component;
    
    import java.util.ArrayList;
    import java.util.Set;
    
    @Component
    public class SpringBootInitialization implements ApplicationRunner {
        
        @Override
        public void run(ApplicationArguments args) throws Exception {
            // 1.使用java反射机制 获取我们自定义的 ObServer下 有哪些实现类
            Set<Class> interfaceImpls = ReflexUtils.getInterfaceImpls(MsgObServer.class);
            // 2.在根据类名称从IOC容器中查找bean对象 存入到集合中
            ArrayList<ObServer> obServers = new ArrayList<>();
            interfaceImpls.forEach((c) -> {
                ObServer obServer = (ObServer) SpringUtils.getBean(c);
                //存入到集合中
                obServers.add(obServer);
            });
            // 3.赋值给主题
            MsgSubject msgSubject = SpringUtils.getBean("msgSubject", MsgSubject.class);
            msgSubject.addObServerAll(obServers);
            
        }
        
        
    }
    
    • 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
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68

    相关代码

    📎反射代码.rar

  • 相关阅读:
    Nacos报错 Can‘t find dependent libraries(坑!)
    easycms v5.5 分析 | Bugku S3 AWD排位赛
    RabbitMQ事务机制和确认机制
    算法与数据结构-图
    乐观模式下分库分表合并迁移
    Jpa JdbcTemplate 批量插入效率对比
    程序员找不到工作,先去外包过渡2年?千万别去
    天空卫士加入工信部重点实验室大数据安全工作组
    hadoop集群搭建
    Sublime下运行javascript,并带彩色提示
  • 原文地址:https://blog.csdn.net/u014001523/article/details/125531496