java学习总结之反射

前言

在java中,反射就是在程序运行时动态的获取某一个类的元数据(metadata,描述数据的数据)的过程,这些元数据包括构造器、方法、成员变量、内部类、接口、父类等,通过反射,我们可以在程序运行时动态地去操作类的方法、成员变量等信息,所以,在java中,反射为我们提供了一种动态访问、修改类的能力,掌握反射,对我们加深java语言的理解很有帮助,反射大部分所使用到的类都在java.lang.reflect包下。

为什么使用反射

首先我们要知道在java中,对象分为编译时类型运行时类型,例如:Object obj = new String(“123”); 其中obj变量的编译时类型为Object类型,运行时类型为String类型,编译时类型在编译期就确定了变量的类型,而运行时类型则在运行期才确定变量的类型。

这时假设我需要通过obj 变量调用String中的length()方法,当我写下obj.length()这句代码时,编译就会报错,因为编译时会检查obj变量的编译时类型Object,发现它不存在length()方法,编译不通过,报错,这时的解决方法是:把obj强转成String,然后再调用length()方法,如:((String)obj).length();这时编译就通过了。

但是如果此时我并不知道obj变量的运行时类型是String,我怎么强转,我怎么调用它的length()方法?这时就要通过反射来实现了,例如下面的场景:

//外部调用getLen方法,把obj变量传入方法
public static void main(String[] args){
    Object obj = new String("123");
    System.out.println(getLen(obj));
}

//在方法内部的我们只知道obj的编译时类型为Object,并不知道它的运行时类型是什么?
public int getLen(Object obj){
    //通过反射调用obj的length()方法
    Method method = obj.getClass().getMethod("length");
    return (Integer) method.invoke(obj);
}

上述我们在执行一个getLen方法,方法传入了一个Object类型的对象,但是我们却需要调用对象运行时类型的方法length(),我们此时并不知道该对象的运行时类型,只知道对象编译时类型是Object,这时就需要通过反射来实现该方法的调用,obj变量在运行时是String类型,所以上述代码在运行时通过反射调用了obj的length()方法。

所以为什么要使用反射呢?这是因为有时候我们在编译时无法预知对象的运行时类型,但却需要使用对象的运行时类型的信息,这时就需要通过反射来实现。

反射的基本使用

1、获取Class对象

每个类被加载进JVM时,JVM会为这个类创建一个java.lang.Class对象,Class对象就是类的元数据,通过这个Class对象,我们可以在代码运行时访问到这个类的所有信息,Class对象是代码运行时访问类信息的唯一入口,所以要想使用反射,我们首先要获取到类的Class对象,获取Class对象主要有以下3种方法:

  • 1、使用Class的forName(String className)方法:该方法需要传入类的全限定名,通过类的全限定名加载这个类进入JVM,并返回该类的Class对象,如果无法加载这个类就抛出一个ClassNotFoundException异常;
  • 2、调用某个类的class属性:每个类都有一个class属性,调用类.class会返回该类的Class对象;
  • 3、调用某个对象的getClass方法:getClass方法是Object类中的一个方法,所以java中的所有对象都可以调用这个方法,getClass方法会返回该对象所属类的Class对象;

下面使用上面的3种方法获取String类的Class对象,如下:

public static void main(String[] args) throws ClassNotFoundException {
    //1、使用Class的forName(String className)方法
    Class<?> stringClass1 = Class.forName("java.lang.String");
    //2、调用String类的class属性
    Class<String> stringClass2 = String.class;
    //3、调用String对象的getClass方法
    Class<?> stringClass3 = new String().getClass();
}

在JDK5之后,Class添加了泛型支持,允许使用泛型来限制Class的类型,例如上面的String.class,它的Class类型为Class<String>,它表示Class对应的类为String类,如果Class对应的类未知,可以使用Class<?>或Class表示,在java中,通过forName或getClass方法获取的Class对象的对应的类是未知的,而通过某个类的class属性返回的Class对象的对应的类是确定的,通过泛型限制,可以避免使用反射创建对象时进行强制类型转换。

在java中,除了类有对应的Class对象外,8大基本数据类型+void类型都有自己的Class对象,但是这些基本类型并不是对象,所以它们不能通过getClass方法获取Class对象,同时基本类型也没有类名的概念,所以它们也不能通过forName方法获取Class对象,它们只能通过XX.class的方式获取相应的Class对象,如:int.class、byte.class、short.class、long.class、double.class、float.class、char.class、boolean.class和void.class,JVM中会内置这九大类型的Class对象,无需我们从外部加载,同时在这九大类型的对应的包装类中都有一个TYPE常量,这个TYPE常量就是对应基本类的Class对象,但是包装类的Class对象并不等于基本类的Class对象,以int为例,如下:

public static void main(String[] args) throws ClassNotFoundException {
    System.out.println(int.class == Integer.TYPE);//输出true
    System.out.println(int.class == Integer.class);//输出false
}

在java中,数组也是一个引用类型,也是一个对象,所以数组也有自己的Class对象,数组可以通过getClass方法和XX.class方式获取自己的Class对象,以int[]为例,如下:

public static void main(String[] args) throws ClassNotFoundException {
    int[] ints = {};
    //1、通过XX.class方式获取数组的Class对象
    Class<int[]> intsClass1 = int[].class;
    //2、通过getClass方法获取数组的Class对象
    Class<?> intsClass2 = ints.getClass();
}

2、通过Class对象获取类信息

一旦获取到类的Class对象后,就可以通过Class对象获取类中的所有信息,这些信息包括:构造器、方法、成员变量、内部类、外部类、接口、父类、注解、类的修饰符,类名、类所在包等,但是我们常用的类信息只有构造器、方法、成员变量,它们的元数据代表如下:

  • Constructor:代表类的构造器对象;
  • Method:代表类的方法对象;
  • Field:代表类的成员变量对象;

在Class类中都有这些元数据的相应的获取方法,通过Class对象获取这些元数据的相应方法的名称命名都有一个规律:如果获取方法中不带Declared关键字的,那么这个方法只能获取到类的public属性,包括继承的;而如果这个获取方法带Declared关键字,那么这个方法就能获取到类的所有public、protected、default、private属性,但不包括继承的,而子类是无法继承父类的构造器的,所以通过子类获取构造器时,是无法获取到父类的构造器,只能获取到属于子类的构造器,而对于方法、成员变量它们是可以继承自父类的。

下面分别介绍它们的获取方法和使用场景:

2.1、获取类的Constructor

下面的4个方法可以获取Class对应类的构造器对象Constructor:

  • 1、Constructor<T> getConstructor(Class<?>… parameterTypes):返回此Class对应类的、带指定形参列表的public构造器对象,传入的形参也要用对应类型的Class对象表示;
  • 2、Constructor<?>[] getConstructors():以数组的形式返回此Class对应类的所有public构造器对象;
  • 3、 Constructor<T> getDeclaredConstructor(Class<?>… parameterTypes):返回此Class对应类的、带指定形参列表的构造器对象,与访问权限无关,传入的形参也要用对应类型的Class对象表示;
  • 4、Constructor<?>[] getDeclaredConstructors():以数组的形式返回此Class对应类的所有构造器对象,与访问权限无关.

获得到构造器对象Constructor后,就可以调用Constructor的newInstance(Object … initargs)方法创建对应类的实例,newInstance方法的initargs参数就是构造器中需要传入的参数,它是一个可变参数,如下我通过反射创建了一个值为123的String对象:

//通过反射创建对象
public static void main(String[] args) throws IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchMethodException {
    //1、获取String类的Class对象
    Class<String> stringClass = String.class;
    //2、通过Class对象获取一个带String类型参数的构造器Constructor
    Constructor<String> stringConstructor = stringClass.getConstructor(String.class);
    //3、使用Constructor的newInstance方法创建String对象,并传入该构造器需要的参数“123”
    String string = stringConstructor.newInstance("123");
    //输出:123
    System.out.println(string);
}

如果你需要调用类的默认构造器来创建对象,这时调用Constructor的newInstance方法时就不需要传入参数,类的默认构造器就是没有参数的那个构造器,同时为了简化获取Constructor这一步骤,java允许我们直接使用Class对象的newInstance()方法来调用类的默认构造器来创建对象,如下我通过Class对象的newInstance方法创建了一个空String对象:

//通过反射创建对象
public static void main(String[] args) throws IllegalAccessException, InstantiationException{
    //1、获取String类的Class对象
    Class<String> stringClass = String.class;
    //2、调用String类的默认构造器来创建对象
    String string = stringClass.newInstance();
    System.out.println(string);
}

Class对象的newInstance方法从java9之后就被标记为Deprecated,java9之后推荐使用Constructor的newInstance方法来反射创建对象实例。

为什么要使用Constructor来创建对象呢,直接使用new不行吗?这是因为某些场景下我们无法直接调用某个类的构造器,例如在一些框架中,传给我们的都是关于类的字符串(类的全限定名),我们只能通过Class的forName方法获取到Class对象,然后获取到Constructor对象,然后通过Constructor对象来创建实例。

2.2、获取类的Method

下面的4个方法可以获取Class对应类的方法对象Method:

  • 1、Method getMethod(String name, Class<?>… parameterTypes) :返回此Class对应类的、指定名称的、带指定形参列表的public方法对象,传入的形参也要用对应类型的Class对象表示;
  • 2、Method[] getMethods() :以数组的形式返回此Class对应类的所有public方法对象;
  • 3、Method getDeclaredMethod(String name, Class<?>… parameterTypes):返回此Class对应类的、非继承的、指定名称的、带指定形参列表的方法对象,与访问权限无关,传入的形参也要用对应类型的Class对象表示;
  • 4、 Method[] getDeclaredMethods():以数组的形式返回此Class对应类的、非继承的所有方法对象,与访问权限无关.

获取到方法对象Method后,就可以通过Method的**invoke(Object obj, Object… args)**方法来调用Method对应的方法,其中invoke方法的返回值就是被调用方法的返回值,而invoke方法的obj参数表示被调用方法的所属对象,args参数表示被调用方法的需要传入的参数,如下我通过反射调用String对象的length方法:

//通过反射调用方法
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
     //1、获取String类的Class对象
    Class<String> stringClass = String.class;
    //2、通过Class对象获取名称为length、没有参数的方法对象method
    Method method = stringClass.getMethod("length");
    //3、通过invoke方法执行String对象的length方法,返回String的长度
    Integer len = (Integer) method.invoke(stringClass.newInstance());
    //输出:0
    System.out.println(len);
}

如果需要调用的是静态方法,那么invoke方法的第一个参数obj参数就要传入null,因为静态方法不属于任何对象,静态方法只属于对应的类,如下我通过反射调用String的valueOf(int)静态方法:

//通过反射调用静态方法
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
    //1、获取String类的Class对象
    Class<String> stringClass = String.class;
    //2、通过Class对象获取名称为valueOf、参数类型为int的方法对象method
    Method method = stringClass.getMethod("valueOf", int.class);
    //3、通过invoke方法执行String类的valueOf方法,返回字符串“123”
    //注意:这里第一个参数传入了null,而不是String实例
    String num = (String) method.invoke(null, 123);
    //输出:123
    System.out.println(num);
}

如果需要调用的方法有一个数组类型或可变参数类型的参数,那么调用时就需要注意,而可变参数底层实现其实就是数组,下面我以数组为例,如下:

public class Main {
    
    //通过反射调用参数含有数组类型的方法
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        //获取Main类的Class对象
        Class<Main> mainClass = Main.class;

        //情况1:数组的元素类型为基本数据类型
        Method method1 = mainClass.getMethod("doWork", int[].class);
        //method1.invoke(null, 1, 2, 3);//错误
        method1.invoke(null, new int[]{1, 2, 3});//正确
        method1.invoke(null, new Object[]{new int[]{1, 2, 3}});//正确

        //情况2:数组的元素类型为引用类型
        Method method2 = mainClass.getMethod("doWork", String[].class);
        //method2.invoke(null, "1", "2", "3");//错误
        //method2.invoke(null, new String[]{"1", "2", "3"});//错误
        method2.invoke(null, new Object[]{new String[]{"1", "2", "3"}});//正确
    }
    
    //数组的元素类型为基本数据类型
    public static void doWork(int[] nums){
        for(int num : nums){
            System.out.print(num + " ");
        }
    }

    //数组的元素类型为引用类型
    public static void doWork(String[] strs){
        for(String str : strs){
            System.out.print(str + " ");
        }
    }
}

可以看到反射调用的方法的参数含有数组类型,在执行invoke方法时传参会很容易出错,所以为了保险起见,建议以后反射调用参数含有数组类型的方法时,把被调用方法的实际参数统统作为Object数组的元素即可,如:Method对象.invoke(方法所属对象,new Object[]{参数1,参数2,…})

2.3、获取类的Field

下面的4个方法可以获取Class对应类的成员变量对象Field:

  • 1、Field getField(String name) :返回此Class对应类的、指定名称的public成员变量对象;
  • 2、Field[] getFields() :以数组的形式返回此Class对应类的所有public成员变量对象;
  • 3、Field getDeclaredField(String name):返回此Class对应类的、非继承的、指定名称的成员变量对象,与访问权限无关;
  • 4、Field[] getDeclaredFields():以数组的形式返回此Class对应类的、非继承的所有成员变量对象,与访问权限无关.

获得成员变量对象Field后,就可以通过Field的setXX(Object obj)方法和getXX(Object obj)方法为成员变量赋值和向成员变量取值,其中setXX或getXX方法中的obj参数表示成员变量所属的对象实例,而XX对应8种基本数据类型,如果成员变量为引用类型,则取消setXX或getXX方法中XX,例如下面我通过反射为一个引用类型变量和一个基本数据类型变量赋值和取值:

public class Main {

    //引用类型
    public String str = "123";
    //基本数据类型
    public int num = 123;

     //通过反射访问成员变量
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, InstantiationException {
        Main mainInstance = new Main();
        //1、获取Main类的Class对象
        Class<Main> mainClass = Main.class;
        //2、通过Class对象分别获取名为str和名为num的成员变量对象Field
        Field field1 = mainClass.getField("str");
        Field field2 = mainClass.getField("num");
        //3、分别通过Field的setXX方法赋值,getXX方法取值
        //重新赋值,如果是引用类型就直接调用set方法就行,如果是基本数据类型就可以调用setXX方法
        field1.set(mainInstance, "321");
        field2.setInt(mainInstance, 321);
        //取值,如果是引用类型就直接调用get方法就行,如果是基本数据类型就可以调用getXX方法
        String str = (String) field1.get(mainInstance);
        int num = (Integer)field2.getInt(mainInstance);
        //输出
        System.out.println(str);//输出:321
        System.out.println(num);//输出:321
    }
    
}

如果成员变量为静态成员变量,那么调用setXX或getXX方法时第一个参数就直接传null,如果这个成员变量是一个私有变量,那么还要调用**setAccessible(true)**方法后才能访问这个变量,如下我通过反射访问了一个名为data的私有变量:

public class Main {

    //私有成员变量
    private int data = 123;

    //通过反射访问私有成员变量
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, InstantiationException {
        Main mainInstance = new Main();
        //1、获取Main类的Class对象
        Class<Main> mainClass = Main.class;
        //2、通过Class对象获取名为data的成员变量对象Field,这里使用了getDeclaredField方法,因为getDeclaredField方法可以获取到私有变量
        Field field = mainClass.getDeclaredField("data");
        //注意:这里调用了setAccessible(true)方法把私有成员变量设置为可访问
        field.setAccessible(true);
        //3、通过Field的getXX方法取值
        int data = field.getInt(mainClass.newInstance());
        //输出:123
        System.out.println(data);
    }
}

对变量调用了setAccessible(true)后,就会在使用时关闭访问权限的检查,该变量就不会受访问权限的控制,我们就可以任意的修改和访问它。

其实不止Field可以调用setAccessible方法,对于Constructor、Method等也可以调用setAccessible方法,因为setAccessible方法是在AccessibleObject类中,而Field、Constructor、Method都继承自AccessibleObject,所以当以后想要通过Class对象访问private的Field、Constructor、Method时,都需要先调用**setAccessible(true)**关闭访问权限的检查后才能访问。

3、小结

以上就是反射的基本使用,下面再总结一下:

通过反射创建对象,需要通过下面三个步骤:

  • 1、获取该类的Class对象;
  • 2、利用Class对象的getConstructor或getDeclaredConstructor方法来获取指定的构造器对象Constructor;
  • 3、调用Constructor的newInstance方法来创建相应的对象.

通过反射调用方法,需要通过下面三个步骤:

  • 1、获取该类的Class对象;
  • 2、利用Class对象的getMethod或getDeclaredMethod方法来获取指定的方法对象Method;
  • 3、调用Method的invoke方法来执行相应的方法.

通过反射访问成员变量,需要通过下面三个步骤:

  • 1、获取该类的Class对象;
  • 2、利用Class对象的getField或getDeclaredField方法来获取指定的成员变量对象Field;
  • 3、调用Field的setXX和getXX方法来访问相应的成员变量。

如果通过Class对象获取到的属性是私有的,还要调用setAccessible(true)方法关闭访问权限的检查。

通过反射获取变量的类型

在JDK5之前,java中只有原始类型(raw types)和基本类型(primitive types),它们都用Class类表示,从JDK5开始引入了泛型(ParameterizedType),这时为了统一表示这些类型就引入了Type接口,同时为了扩展java的泛型,还引入了数组类型(GenericArrayType)、类型变量(TypeVariable)、通配符类型(WildcardType),这5种类型的父接口都是Type,他们的各自含义如下:

  • Class:原始类型或基本类型,原始类型表示我们平常所使用的类、注解、枚举、数组等,基本类型表示基本数据类型,如int、byte、short等,它们都用Class类表示;
  • ParameterizedType:参数化类型,表示java5引入的泛型类型,如List<String>、Map<String, Integer>等;
  • GenericArrayType:数组类型,表示带有泛型的数组,如T[]、List<String>[]等;
  • TypeVariable:类型变量,表示泛型的尖括号中的类型,如T、K、V等;
  • WildcardType:通配符类型,表示泛型尖括号中带有上限或下限的通配符表达式,如? extends String、? super String等.

泛型经过编译后会被自动擦除,所以最终JVM中运行的都是Class文件,Class类是反射的基础。

当一个成员变量被定义在类中的时候,它的编译时类型就已经确定,当我们通过Class对象获取到这个成员变量的Field对象后,就可以通过Field的getType方法返回它的编译时类型,即这个成员变量的数据类型,getType方法会返回一个Class对象,代表这个数据类型的Class对象,如下:

public class Main {

    public String str = "123";

    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, InstantiationException {
        Class<Main> mainClass = Main.class;
        Field field = mainClass.getField("str");
        //调用Field的getType方法返回Field对应变量的数据类型
        Class<?> type = field.getType();
        //输出:java.lang.String
        System.out.println(type.getName());
    }
}

但是如果这个成员变量的类型是一个泛型,那么Field的getType方法返回的Class对象就会丢失了泛型尖括号中的类型参数,例如:Map<Integer, String> map = new HashMap<>();获取到map变量的Field对象后,调用getType方法返回的Class类就是java.util.Map,丢失了尖括号中的类型参数信息,所以如果成员变量的类型是一个泛型,你可以通过Field的getGenericType方法返回它的参数化类型ParameterizedType,通过ParameterizedType就可以获取到泛型信息,如下:

public class Main {

    public Map<Integer, String> map = new HashMap<>();

    public static void main(String[] args) throws NoSuchFieldException{
        Class<Main> mainClass = Main.class;
        Field field = mainClass.getField("map");
        //调用Field的getGenericType方法返回Field对应变量的数据类型
        //注意:getGenericType方法的返回值是一个Type类型,这说明它可以向下转型为Class、ParameterizedType、GenericArrayType、TypeVariable、WildcardType中的一个
        Type type = field.getGenericType();
        //我们已经知道它是一个泛型
        if (type instanceof ParameterizedType) {
            //强转为参数化类型ParameterizedType
            ParameterizedType parameterizedType = (ParameterizedType) type;
            //通过ParameterizedType的getRawType方法返回它的原始类型
            Type rawType = parameterizedType.getRawType();
            //通过ParameterizedType的getActualTypeArguments方法返回泛型中的类型信息
            Type[] types = parameterizedType.getActualTypeArguments();
            System.out.println("原始类型:" + rawType.getTypeName());
            for(Type t : types){
                System.out.println("类型形参: " + t.getTypeName());
            }
            //输出:
            //原始类型:java.util.Map
            //类型形参: java.lang.Integer
            //类型形参: java.lang.String
        }
    }
}

可以看到,如果成员变量的类型是一个泛型,就可以通过ParameterizedType的getRawType方法返回它的没有泛型信息的原始类型,通过ParameterizedType的getActualTypeArguments方法返回它的泛型信息,getActualTypeArguments方法它的返回值是一个Type数组,因为泛型的<>中会有多个Type,所以如果泛型的<>中某个Type是一个类型变量T,那么就可以把这个Type转成TypeVariable,通过TypeVariable的相应方法获取更多关于T的信息,如果<>中某个Type是一个通配符类型? extends XX或?super XX,那么就可以把这个Type转成WildcardType,通过WildcardType的相应方法获取更多关于通配符的信息,GenericArrayType同理。

所以对于数据类型为基本类型或原始类型的成员变量来说,通过getType方法就可以获取它的相应类型的Class对象;对于数据类型含有泛型的成员变量来说,就可以通过getGenericType方法获取它的相应类型,它返回一个Type,这个Type可以向下转型为ParameterizedType、GenericArrayType、TypeVariable中的一个,通过实际类型的相应方法获取泛型信息。

结语

本文通过对Class、Constructor、Method、Field、Type的基本使用来简单的介绍了一下java的反射,正是由于反射的存在,才使得java具有动态性、灵活性,反射在一些java的框架上应用非常广泛,如Spring等,对于我们平常开发的应用,主要就是本文的内容,同时反射也会和一些设计模式结合使用,让设计模式具有更好的扩展性,具体可以查看以下两篇文章:

工厂方法模式

静态和动态代理模式

但同时反射也有它的缺点,反射的缺点就是执行效率低,因为反射需要按名称来检索类和方法,有一定的时间开销,同时反射会很容易破坏类的原本结构,所以在一些不必要的场景下不要频繁的使用反射。

以上就是本文的全部内容,希望大家有所收获!

参考资料:

java中的Type

了解神秘的Java反射机制

已标记关键词 清除标记
相关推荐
<p> <strong><span style="font-size:20px;color:#FF0000;">本课程主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的Java学习者</span></strong> </p> <p> <span style="color:#FF0000;"><strong><span style="font-size:18px;">1. 包含:<span style="color:#FFFF00;background-color:#FF0000;">项目源码、</span><span style="color:#FFFF00;background-color:#FF0000;">项目文档、数据库脚本、软件工具</span>等所有资料</span></strong></span> </p> <p> <span style="color:#FF0000;"><strong><span style="font-size:18px;">2. 手把手的带你从零开始部署运行本套系统</span></strong></span> </p> <p> <span style="color:#FF0000;"><strong><span style="font-size:18px;">3. 该项目附带的源码资料可作为毕设使用</span></strong></span> </p> <p> <span style="color:#FF0000;"><strong><span style="font-size:18px;">4. 提供技术答疑和远程协助指导</span></strong></span><strong><span style="font-size:18px;"></span></strong> </p> <p> <br /> </p> <p> <span style="font-size:18px;"><strong>项目运行截图:</strong></span> </p> <p> <strong><span style="font-size:18px;">1)系统登陆界面</span></strong> </p> <p> <strong><span style="font-size:18px;"><img src="https://img-bss.csdn.net/202002241015433522.png" alt="" /><br /> </span></strong> </p> <p> <strong><span style="font-size:18px;"><strong><span style="font-size:18px;">2)学生模块</span></strong></span></strong> </p> <p> <strong><span style="font-size:18px;"><img src="https://img-bss.csdn.net/202002241015575966.png" alt="" /></span></strong> </p> <p> <strong><span style="font-size:18px;"><strong><span style="font-size:18px;">3)教师模块</span></strong></span></strong> </p> <p> <strong><span style="font-size:18px;"><img src="https://img-bss.csdn.net/202002241016127898.png" alt="" /></span></strong> </p> <p> <strong><span style="font-size:18px;"><strong><span style="font-size:18px;">4)系统管理员</span></strong></span></strong> </p> <p> <strong><span style="font-size:18px;"><img src="https://img-bss.csdn.net/202002241016281177.png" alt="" /></span></strong> </p> <p> <strong><span style="font-size:18px;"><img src="https://img-bss.csdn.net/202002241016369884.png" alt="" /></span></strong> </p> <p> <strong><span style="font-size:18px;"><br /> </span></strong> </p> <p> <strong><span style="font-size:18px;"><strong><span style="font-size:18px;">更多Java毕设项目请关注我的毕设系列课程 <a href="https://edu.csdn.net/lecturer/2104">https://edu.csdn.net/lecturer/2104</a></span></strong></span></strong> </p> <p> <strong><span style="font-size:18px;"><br /> </span></strong> </p>
简介 笔者当初为了学习JAVA,收集了很多经典源码,源码难易程度分为初级、中级、高级等,详情看源码列表,需要的可以直接下载! 这些源码反映了那时那景笔者对未来的盲目,对代码的热情、执着,对IT的憧憬、向往!此时此景,笔者只专注Android、Iphone等移动平台开发,看着这些源码心中有万分感慨,写此文章纪念那时那景! Java 源码包 Applet钢琴模拟程序java源码 2个目标文件,提供基本的音乐编辑功能。编辑音乐软件的朋友,这款实例会对你有所帮助。 Calendar万年历 1个目标文件 EJB 模拟银行ATM流程及操作源代码 6个目标文件,EJB来模拟银行ATM机的流程及操作:获取系统属性,初始化JNDI,取得Home对象的引用,创建EJB对象,并将当前的计数器初始化,调用每一个EJB对象的count()方法,保证Bean正常被激活和钝化,EJB对象是用完毕,从内存中清除,从账户中取出amt,如果amt>账户余额抛出异常,一个实体Bean可以表示不同的数据实例,我们应该通过主键来判断删除哪个数据实例…… ejbCreate函数用于初始化一个EJB实例 5个目标文件,演示Address EJB的实现 ,创建一个EJB测试客户端,得到名字上下文,查询jndi名,通过强制转型得到Home接口,getInitialContext()函数返回一个经过初始化的上下文,用client的getHome()函数调用Home接口函数得到远程接口的引用,用远程接口的引用访问EJB。 EJB中JNDI的使用源码例子 1个目标文件,JNDI的使用例子,有源代码,可以下载参考,JNDI的使用,初始化Context,它是连接JNDI树的起始点,查找你要的对象,打印找到的对象,关闭Context…… ftp文件传输 2个目标文件,FTP的目标是:(1)提高文件的共享性(计算机程序和/或数据),(2)鼓励间接地(通过程序)使用远程计算机,(3)保护用户因主机之间的文件存储系统导致的变化,(4)为了可靠和高效地传输,虽然用户可以在终端上直接地使用它,但是它的主要作用是供程序使用的。本规范尝试满足大型主机、微型主机、个人工作站、和TACs 的不同需求。例如,容易实现协议的设计。 Java EJB中有、无状态SessionBean的两个例子 两个例子,无状态SessionBean可会话Bean必须实现SessionBean,获取系统属性,初始化JNDI,取得Home对象的引用,创建EJB对象,计算利息等;在有状态SessionBean中,用累加器,以对话状态存储起来,创建EJB对象,并将当前的计数器初始化,调用每一个EJB对象的count()方法,保证Bean正常被激活和钝化,EJB对象是用完毕,从内存中清除…… Java Socket 聊天通信演示代码 2个目标文件,一个服务器,一个客户端。 Java Telnet客户端实例源码 一个目标文件,演示Socket的使用。 Java 组播组中发送和接受数据实例 3个目标文件。 Java读写文本文件的示例代码 1个目标文件。 java俄罗斯方块 一个目标文件。 Java非对称加密源码实例 1个目标文件 摘要:Java源码,算法相关,非对称加密   Java非对称加密源程序代码实例,本例中使用RSA加密技术,定义加密算法可用 DES,DESede,Blowfish等。   设定字符串为“张三,你好,我是李四”   产生张三的密钥对(keyPairZhang)   张三生成公钥(publicKeyZhang)并发送给李四,这里发送的是公钥的数组字节   通过网络或磁盘等方式,把公钥编码传送给李四,李四接收到张三编码后的公钥,将其解码,李四用张三的公钥加密信息,并发送给李四,张三用自己的私钥解密从李四处收到的信息…… Java利用DES私钥对称加密代码实例 同上 java聊天室 2个目标文件,简单。 java模拟掷骰子2个 1个目标文件,输出演示。 java凭图游戏 一个目标文件,简单。 java求一个整数的因子 如题。 Java生成密钥的实例 1个目标文件 摘要:Java源码,算法相关,密钥   Java生成密钥、保存密钥的实例源码,通过本源码可以了解到Java如何产生单钥加密的密钥(myKey)、产生双钥的密钥对(keyPair)、如何保存公钥的字节数组、保存私钥到文件privateKey.dat、如何用Java对象序列化保存私钥,通常应对私钥加密后再保存、如何从文件中得到公钥编码的字节数组、如何从字节数组解码公钥。 Java数据压缩与传输实例 1个目标文件 摘要:Java源码,文件操作,数据压缩,文件传输   Java数据压缩与传输实例,可以学习一下实例化套按字、得到文件输入流、压缩输入流、文件输出流、实例化缓冲
<p> <span style="color:#0000ff;">需要学习ubuntu系统上YOLOv4的同学请前往:《YOLOv4目标检测实战:原理与源码解析》</span> </p> <h3> <span style="color:#3598db;">【为什么要学习这门课】</span> </h3> <p> Linux创始人Linus Torvalds有一句名言:Talk is cheap. Show me the code. <strong><span style="color:#ba372a;">冗谈不够,放码过来!</span></strong> </p> <p> 代码阅读是从基础到提高的必由之路。尤其对深度学习,许多框架隐藏了神经网络底层的实现,只能在上层调包使用,对其内部原理很难认识清晰,不利于进一步优化和创新。 </p> <p> YOLOv4是最近推出的基于深度学习的端到端实时目标检测方法。 </p> <p> YOLOv4的实现darknet是使用C语言开发的轻型开源深度学习框架,依赖少,可移植性好,可以作为很好的代码阅读案例,让我们深入探究其实现原理。 </p> <h3> <span style="color:#3598db;">【课程内容与收获】</span> </h3> <p> 本课程将解析YOLOv4的实现原理和源码,具体内容包括: </p> <p> - YOLOv4目标检测原理 </p> <p> - 神经网络及darknet的C语言实现,尤其是反向传播的梯度求解和误差计算 </p> <p> - 代码阅读工具及方法 </p> <p> - 深度学习计算的利器:BLAS和GEMM </p> <p> - GPU的CUDA编程方法及在darknet的应用 </p> <p> - YOLOv4的程序流程 </p> <p> - YOLOv4各层及关键技术的源码解析 </p> <p> 本课程将提供注释后的darknet的源码程序文件。 </p> <h3> <span style="color:#3598db;">【相关课程】</span> </h3> <p> 除本课程《Windows版YOLOv4目标检测:原理与源码解析》外,本人推出了有关YOLOv4目标检测的系列课程,包括: </p> <p> 《Windows版YOLOv4目标检测实战:训练自己的数据集》 </p> <p> 《Windows版YOLOv4-Tiny目标检测实战:训练自己的数据集》 </p> <p> 《Windows版YOLOv4目标检测实战:人脸口罩佩戴检测》<br /> 《Windows版YOLOv4目标检测实战:中国交通标志识别》 </p> <p> 建议先学习一门YOLOv4实战课程,对YOLOv4的使用方法了解以后再学习本课程。 </p> <h3> <span style="color:#3598db;">【YOLOv4网络模型架构图】</span> </h3> <p> 下图由白勇老师绘制<img src="https://img-bss.csdnimg.cn/202006291533009066.jpg" alt="" /> </p> <p>   </p>
<p> <strong><span style="font-size:16px;color:#003399;">会用Python分析金融数据 or 金融行业会用Python</span></strong> </p> <p> <strong><span style="font-size:16px;color:#003399;">职场竞争力更高</span></strong> </p> <p> <br /> </p> <p> <img src="https://img-bss.csdnimg.cn/202012231042221925.png" alt="" /> </p> <p> <br /> </p> <p> <br /> </p> <p> <strong><span style="font-size:16px;color:#003399;">Python金融数据分析入门到实战</span></strong> </p> <p> <strong><span style="font-size:16px;color:#003399;">Get√金融行业数据分析必备技能</span></strong> </p> <p> <img src="https://img-bss.csdnimg.cn/202012231042438069.png" alt="" /> </p> <p> <br /> </p> <p> <br /> </p> <p> <strong><span style="font-size:16px;color:#003399;">以股票量化交易为应用场景</span></strong> </p> <p> <strong><span style="font-size:16px;color:#003399;">完成技术指标实现的全过程</span></strong> </p> <p> <br /> </p> <p> <span style="font-size:14px;">课程选取股票量化交易为应用场景,由股票数据的获取、技术指标的实现,逐步进阶到策略的设计</span><span style="font-size:14px;">和回测,由浅入深、由技术到思维地为同学们讲解Python金融数据分析在股票量化交易中的应用</span><span style="font-size:14px;">。</span> </p> <p> <br /> </p> <p> <span style="font-size:14px;"><br /> </span> </p> <p> <img src="https://img-bss.csdnimg.cn/202012231043183686.png" alt="" /> </p> <p> <br /> </p> <p> <br /> </p> <p> <strong><span style="font-size:16px;color:#003399;">以Python为编程语言</span></strong> </p> <p> <strong><span style="font-size:16px;color:#003399;">解锁3大主流数据分析工具</span></strong> </p> <p> <br /> </p> <p> <span style="font-size:14px;">Python做金融具有先天优势,课程提取了Python数据分析工具NumPy、Pandas及可视化工具</span><span style="font-size:14px;">Matplotlib的关键点详细讲解,帮助同学掌握数据分析的关键技能。</span> </p> <p> <img src="https://img-bss.csdnimg.cn/202012231043472858.png" alt="" /> </p> <p> <strong><span style="font-size:16px;color:#003399;"><br /> </span></strong> </p> <p> <strong><span style="font-size:16px;color:#003399;">2大购课福利</span></strong> </p> <p> <strong><span style="font-size:16px;color:#003399;"><br /> </span></strong> </p> <p> <img src="https://img-bss.csdnimg.cn/202012300628195864.png" alt="" /> </p>
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页