JAVA反射机制详解,看完还学不会你来捶我

大家好,我是猿人(猿码天地创始人),今天给码农们或即将成为码农或想成为码农的朋友讲讲JAVA反射机制详解,看完还学不会你来捶我,现在是深夜23:00分,猿人最擅长熬夜,就是不怕掉头发!一切都是为了亲爱的粉丝朋友能学到知识,猿人熬夜也是值得的!

好,废话不多说,直接干,进入正题:Good Good Study,Day Day Up!

我是「猿码天地」,一个热爱技术、热爱编程的IT猿。技术是开源的,知识是共享的!

写作是对自己学习的总结和记录,如果您对 Java、分布式、微服务、中间件、Spring Boot、Spring Cloud等技术感兴趣,可以关注我的动态,我们一起学习,一起成长!

用知识改变命运,让家人过上更好的生活,互联网人一家亲!

JAVA反射机制详解,看完还学不会你来捶我_第1张图片

1. 什么是反射?

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

2. 用途

在日常的第三方应用开发过程中,经常会遇到某个类的某个成员变量、方法或是属性是私有的或是只对系统应用开放,这时候就可以利用Java的反射机制通过反射来获取所需的私有成员或是方法。

一般而言,当用户使用一个类的时候,应该获取这个类,而后通过这个类实例化对象,但是使用反射则可以相反的通过对象获取类中的信息。

通俗的讲反射就是可以在程序运行的时候动态装载类,查看类的信息,生成对象,或操作生成的对象。它允许运行中的 Java 程序获取自身的信息。

3. 反射机制相关的类

 

类名 用途
Class类 代表类的实体,在运行的Java应用程序中表示类和接口
Field类 代表类的成员变量(成员变量也称为类的属性)
Method类 代表类的方法
Constructor类 代表类的构造方法
  • Class类

Class代表类的实体,在运行的Java应用程序中表示类和接口。在这个类中提供了很多有用的方法,这里对他们简单的分类介绍。

获得类相关的方法

JAVA反射机制详解,看完还学不会你来捶我_第2张图片

获得类中属性相关的方法

JAVA反射机制详解,看完还学不会你来捶我_第3张图片

获得类中注解相关的方法

JAVA反射机制详解,看完还学不会你来捶我_第4张图片

获得类中构造器相关的方法

JAVA反射机制详解,看完还学不会你来捶我_第5张图片

获得类中方法相关的方法

JAVA反射机制详解,看完还学不会你来捶我_第6张图片

类中其他重要的方法

JAVA反射机制详解,看完还学不会你来捶我_第7张图片
  • Field类

Field代表类的成员变量(成员变量也称为类的属性)

JAVA反射机制详解,看完还学不会你来捶我_第8张图片
  • Method类

Method代表类的方法。

  • Constructor类

Constructor代表类的构造方法。

4. Java 反射机制相关面试题

反射机制是程序在运行时能够获取自身的信息。在java中,只要给定类的名字,那么就可以通过反射机制来获得类的所有信息。

  • java反射的作用是什么?

反射机制是在运行时,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意个对象,都能够调用它的任意一个方法。在java中,只要给定类的名字,就可以通过反射机制来获得类的所有信息。这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

  • Java反射创建对象效率高还是通过new创建对象的效率高?

通过new创建对象的效率比较高。通过反射时,先找查找类资源,使用类加载器创建,过程比较繁琐,所以效率较低。

  • 除了使用new创建对象之外,还可以用什么方法创建对象?

使用Java反射可以创建对象。

  • 反射的实现方式都有什么?

获取Class对象,有4种方法:(1)Class.forName(“类的路径”);(2)类名.class;(3)对象名.getClass();(4)基本类型的包装类,可以调用包装类的Type属性来获得该包装类的Class对象。

  • 实现java反射的类有什么?

(1)Class:表示正在运行的Java应用程序中的类和接口,注意所有获取对象的信息都需要Class类来实现;(2)Field:提供有关类和接口的属性信息,以及对它的动态访问权限;(3)Constructor:提供关于类的单个构造方法的信息以及它的访问权限;(4)Method:提供类或接口中某个方法的信息。

  • 反射机制的优缺点

优点:(1)能够运行时动态获取类的实例,提高灵活性;(2)与动态编译结合Class.forName('com.mysql.jdbc.Driver.class');//加载MySQL的驱动类

缺点:使用反射性能较低,需要解析字节码,将内存中的对象进行解析。

其解决方案是:通过setAccessible(true)关闭JDK的安全检查来提升反射速度;多次创建一个类的实例时,有缓存会快很多;ReflflectASM工具类,通过字节码生成的方式加快反射速度。

  • Java反射API有几类?

反射 API 用来生成 JVM 中的类、接口或则对象的信息。

(1)Class 类:反射的核心类,可以获取类的属性,方法等信息。

(2)Field 类:Java.lang.reflec 包中的类,表示类的成员变量,可以用来获取和设置类之中的属性值。

(3)Method 类:Java.lang.reflec 包中的类,表示类的方法,它可以用来获取类中的方法信息或者执行方法。

(4)Constructor 类:Java.lang.reflec 包中的类,表示类的构造方法。

  • 反射使用步骤(获取Class对象、调用对象方法)

(1)获取想要操作的类的Class对象,他是反射的核心,通过Class对象我们可以任意调用类的方法。

(2)调用 Class 类中的方法,既就是反射的使用阶段。

(3)使用反射 API 来操作这些信息。

  • Java反射机制的作用有什么?

(1)在运行时判断任意一个对象所属的类;(2)在运行时构造任意一个类的对象;(3)在运行时判断任意一个类所具有的成员变量和方法;(4)在运行时调用任意一个对象的方法。

  • 反射中,Class.forName 和 ClassLoader 区别?

Class.forName(className)方法,其实调用的方法是 Class.forName(className,true,classloader); 第 2 个参数表示在 loadClass 后必须初始化。根据 JVM 加载类的知识,我们知道在执行过此方法后,目标对象的 static 块代码已经被执行,static 参数也已经被初始化。

而 ClassLoader.loadClass(className) 方法,实际调用的方法是 ClassLoader.loadClass(className,false); 第 2 个参数表示目标对象被装载后不进行链接,这就意味着不会去执行该类静态块中间的内容。

例如,在 JDBC 使用中,常看到这样的用法,Class.forName("com.mysql.jdbc.Driver") 如果换成了 getClass().getClassLoader().loadClass("com.mysql.jdbc.Driver"),就不行。根据 com.mysql.jdbc.Driver 的源代码,Driver 在 static 块中会注册自己到 java.sql.DriverManager。而 static 块就是在 Class 的初始化中被执行。所以这个地方就只能用 Class.forName(className)。

com.mysql.jdbc.Driver 的源代码:

// Register ourselves with the DriverManager
static {
    try {
        java.sql.DriverManager.registerDriver(new Driver());
    } catch (SQLException E) {
        throw new RuntimeException("Can't register driver!");
    }
} 
  • Java 类的加载过程

在 Java 中,类装载器把一个类装入 Java 虚拟机中,要经过三个步骤来完成:装载、链接和初始化,其中链接又可以分成校验、准备和解析三步:

装载:查找和导入类或接口的二进制数据;
链接:执行下面的校验、准备和解析步骤,其中解析步骤是可以选择的;
校验:检查导入类或接口的二进制数据的正确性;
准备:给类的静态变量分配并初始化存储空间;
解析:将符号引用转成直接引用;
初始化:激活类的静态变量的初始化 Java 代码和静态 Java 代码块。

  • 描述动态代理的几种实现方式,分别说出相应的优缺点

JDK 动态代理和 CGLIB 动态代理。JDK 动态代理是由 Java 内部的反射机制来实现的,CGLIB 动态代理底层则是借助 ASM 框架来实现的,实现了无反射机制进行代理,利用空间来换取了时间,代理效率高于 JDK 。总的来说,反射机制在生成类的过程中比较高效,而 ASM 在生成类之后的相关执行过程中比较高效(可以通过将 ASM 生成的类进行缓存,这样解决 ASM 生成类过程低效问题)。还有一点必须注意:JDK 动态代理的应用前提,必须是目标类基于统一的接口。如果没有上述前提,JDK 动态代理不能应用。由此可以看出,JDK 动态代理有一定的局限性,CGLIB 这种第三方类库实现的动态代理应用更加广泛,且在效率上更有优势。

5. 总结

很多人认为反射在实际的 Java 开发应用中并不广泛,其实不然。我们常用的 JDBC 中有一行代码:

Class.forName('com.MySQL.jdbc.Driver.class').newInstance();

这里用到的就是 Java 反射机制,除此之外很多框架也都用到反射机制。

实际上反射机制就是非常规模式,如果说方法的调用是 Java 正确的打开方式,那反射机制就是 Java 的后门。为什么会有这个后门呢?这涉及到了静态和动态的概念:

  • 静态编译:在编译时确定类型,绑定对象

  • 动态编译:运行时确定类型,绑定对象

两者的区别在于,动态编译可以最大程度地支持多态,多态最大的意义在于降低类的耦合性,而 Java 语言确是静态编译(如:int a=3;要指定类型),Java 反射很大程度上弥补了这一不足:解耦以及提高代码的灵活性,所以 Java 也被称为是准动态语言。

尽管反射有诸多优点,但是反射也有缺点:

  • 由于反射会额外消耗一定的系统资源,因此,反射操作的效率要比那些非反射操作低得多。

  • 由于反射允许代码执行一些在正常情况下不被允许的操作(比如访问私有的属性和方法),所以使用反射可能会导致意料之外的副作用(代码有功能上的错误)。所以能用其它方式实现的就尽量不去用反射。

6. 源码实战

package com.bowen.demo.demo005;

import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

/**
 * 

java-study

 * 

ReflectClass 

 * @author : zhang.bw  * @date : 2021-03-19 18:10  **/ @Slf4j public class ReflectClass {  // 创建对象  public static void reflectNewInstance() {   try {    // 根据类名返回类的对象    Class classBook = Class.forName("com.bowen.demo.demo005.Book");    // 创建类的实例    Object objectBook = classBook.newInstance();    Book book = (Book) objectBook;    book.setName("猿码天地");    book.setAuthor("猿人");    System.out.println(book.toString());   } catch (Exception ex) {    ex.printStackTrace();   }  }  // 反射私有构造方法  public static void reflectPrivateConstructor() {   try {    Class classBook = Class.forName("com.bowen.demo.demo005.Book");    Constructor declaredConstructorBook = classBook.getDeclaredConstructor(String.class,String.class);    declaredConstructorBook.setAccessible(true);    Object objectBook = declaredConstructorBook.newInstance("猿码天地","猿人");    Book book = (Book) objectBook;    System.out.println(book.toString());   } catch (Exception ex) {    ex.printStackTrace();   }  }  // 反射私有属性  public static void reflectPrivateField() {   try {    Class classBook = Class.forName("com.bowen.demo.demo005.Book");    Object objectBook = classBook.newInstance();    Field fieldTag = classBook.getDeclaredField("TAG");    fieldTag.setAccessible(true);    String tag = (String) fieldTag.get(objectBook);    System.out.println(tag);   } catch (Exception ex) {    ex.printStackTrace();   }  }  // 反射私有方法  public static void reflectPrivateMethod() {   try {    Class classBook = Class.forName("com.bowen.demo.demo005.Book");    Method methodBook = classBook.getDeclaredMethod("declaredMethod",int.class);    methodBook.setAccessible(true);    Object objectBook = classBook.newInstance();    String string = (String) methodBook.invoke(objectBook,0);    System.out.println(string);   } catch (Exception ex) {    ex.printStackTrace();   }  }  /**   * 将对象转换为Map键值对   * @param obj   * @return   */  public static Map reflectKeyAndValue(Object obj) {   Map result = new HashMap();   // 得到类对象   Class cla = (Class) obj.getClass();   /* 得到类中的所有属性集合 */   Field[] fs = cla.getDeclaredFields();   for (int i = 0; i < fs.length; i++) {    Field f = fs[i];    // 设置哪些属性是可以访问的    f.setAccessible(true);    Object val = new Object();    try {     val = f.get(obj);     // 设置键值     result.put(f.getName(), val);    } catch (IllegalArgumentException e) {     log.error("非法参数异常", e);    } catch (IllegalAccessException e) {     log.error("非法访问异常", e);    }   }   log.debug("获取对象的所有键值: {}", result);   return result;  }  /**   * 使用 Java 反射获取 Class 对象(三种方式)   *   * 方式1:通过 Class 类的静态方法获取 Class 类对象 (推荐)   * 方式2:因为所有类都继承 Object 类。因而可通过调用 Object 类中的 getClass 方法来获取   * 方式3:任何数据类型(包括基本数据类型)都有一个“静态”的 class 属性   * @throws Exception   */  public static void test1() throws Exception {   Class class1 = null;   Class class2 = null;   Class class3 = null;   class1 = Class.forName("com.bowen.demo.demo005.Book");   class2 = new Book().getClass();   class3 = Book.class;   System.out.println("类名称 1:  " + class1.getName());   System.out.println("类名称 2:  " + class2.getName());   System.out.println("类名称 3:  " + class3.getName());  }  /**   * 使用 Java 反射获取一个对象的父类和实现的接口   * @throws Exception   */  public static void test2() throws Exception{   Class clazz = Class.forName("com.bowen.demo.demo005.Book");   // 取得父类   Class parentClass = clazz.getSuperclass();   System.out.println("父类为:" + parentClass.getName());   // 获取所有的接口   Class intes[] = clazz.getInterfaces();   System.out.println("实现的接口有:");   for (int i = 0; i < intes.length; i++) {    System.out.println((i + 1) + ":" + intes[i].getName());   }  }  /**   * 使用 Java 反射获取某个类的全部构造函数并调用私有构造方法   */  public static void test3() throws Exception{   //1.加载Class对象   Class clazz = Class.forName("com.bowen.demo.demo005.Student");   //2.获取所有公有构造方法   System.out.println("所有公有构造方法");   Constructor[] conArray = clazz.getConstructors();   for(Constructor c : conArray){    System.out.println(c);   }   System.out.println("所有的构造方法(包括:私有、受保护、默认、公有)");   conArray = clazz.getDeclaredConstructors();   for(Constructor c : conArray){    System.out.println(c);   }   System.out.println("获取公有、无参的构造方法");   //1、因为是无参的构造方法所以类型是一个null,不写也可以:这里需要的是一个参数的***类型***,   //2、返回的是描述这个无参构造函数的类对象。   Constructor con = clazz.getConstructor(null);   System.out.println("con = " + con);   //调用构造方法   Object obj = con.newInstance();   System.out.println("obj = " + obj);   System.out.println("获取私有构造方法,并调用");   con = clazz.getDeclaredConstructor(int.class);   System.out.println(con);   //调用构造方法   con.setAccessible(true);//暴力访问(忽略掉访问修饰符)   obj = con.newInstance(26);  }  /**   * 使用 Java 反射获取某个类的全部属性   */  public static void test4() throws Exception{   //1.获取Class对象   Class stuClass = Class.forName("com.bowen.demo.demo005.Student");   //2.获取字段   System.out.println("获取所有公有的字段");   Field[] fieldArray = stuClass.getFields();   for(Field f : fieldArray){    System.out.println(f);   }   System.out.println();   System.out.println("获取所有的字段(包括私有、受保护、默认的)");   fieldArray = stuClass.getDeclaredFields();   for(Field f : fieldArray){    System.out.println(f);   }   System.out.println();   System.out.println("获取公有字段并调用");   Field f = stuClass.getField("name");   System.out.println(f);   //获取一个对象   Object obj = stuClass.getConstructor().newInstance();   f.set(obj, "laughitover");   //验证   Student stu = (Student)obj;   System.out.println("验证姓名:" + stu.name);   System.out.println();   System.out.println("获取私有字段并调用");   f = stuClass.getDeclaredField("phoneNum");   System.out.println(f);   f.setAccessible(true);//暴力反射,解除私有限定   f.set(obj, "13812345678");   System.out.println("验证电话:" + stu);  }  /**   * 使用 Java 反射获取某个类的全部方法   */  public static void test5() throws Exception{   //1.获取Class对象   Class stuClass = Class.forName("com.bowen.demo.demo005.Student");   //2.获取所有公有方法   System.out.println("获取所有的”公有“方法");   stuClass.getMethods();   Method[] methodArray = stuClass.getMethods();   for(Method m : methodArray){    System.out.println(m);   }   System.out.println("获取所有的方法,包括私有的");   methodArray = stuClass.getDeclaredMethods();   for(Method m : methodArray){    System.out.println(m);   }   System.out.println("获取公有的show1()方法");   Method m = stuClass.getMethod("show1", String.class);   System.out.println(m);   //实例化一个Student对象   Object obj = stuClass.getConstructor().newInstance();   m.invoke(obj, "laughitover");   System.out.println("获取私有的show4()方法");   m = stuClass.getDeclaredMethod("show4", int.class);   System.out.println(m);   m.setAccessible(true);//解除私有限定   Object result = m.invoke(obj, 20);//需要两个参数,一个是要调用的对象(获取有反射),一个是实参   System.out.println("返回值:" + result);  }  /**   *  使用 Java 反射调用某个类的方法   *  Java 反射获取 Class 对象并使用invoke()方法调用方法 reflect1() 和 reflect2()   */  public static void test6() throws Exception{   Class clazz = Class.forName("com.bowen.demo.demo005.ReflectClass");   Method method = clazz.getMethod("reflect1");   method.invoke(clazz.newInstance());   method = clazz.getMethod("reflect2", int.class, String.class);   method.invoke(clazz.newInstance(), 20, "张三");  }  public void reflect1() {   System.out.println("Java 反射机制 - 调用某个类的方法1.");  }  public void reflect2(int age, String name) {   System.out.println("Java 反射机制 - 调用某个类的方法2.");   System.out.println("age -> " + age + ". name -> " + name);  }  /**   *  通过反射越过泛型检查   *  泛型作用在编译期,编译过后泛型擦除(消失掉)。所以是可以通过反射越过泛型检查的   */  public static void test7() throws Exception{   ArrayList list = new ArrayList();   list.add(100);   list.add(200);   Method method = list.getClass().getMethod("add", Object.class);   method.invoke(list, "Java反射机制实例。");   //遍历集合   for(Object obj : list){    System.out.println(obj);   }  }  /**   *  通过反射机制获得数组信息并修改数组的大小和值   *  通过反射机制分别修改int和String类型的数组的大小并修改int数组的第一个值   */  public static void test8() throws Exception{   int[] temp = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };   int[] newTemp = (int[]) arrayInc(temp, 15);   print(newTemp);   Array.set(newTemp, 0, 100);   System.out.println("修改之后数组第一个元素为: " + Array.get(newTemp, 0));   print(newTemp);   String[] atr = { "a", "b", "c" };   String[] str1 = (String[]) arrayInc(atr, 8);   print(str1);  }  // 修改数组大小  public static Object arrayInc(Object obj, int len) {   Class arr = obj.getClass().getComponentType();   Object newArr = Array.newInstance(arr, len);   int co = Array.getLength(obj);   System.arraycopy(obj, 0, newArr, 0, co);   return newArr;  }  // 打印  public static void print(Object obj) {   Class c = obj.getClass();   if (!c.isArray()) {    return;   }   Class arr = obj.getClass().getComponentType();   System.out.println("数组类型: " + arr.getName());   System.out.println("数组长度为: " + Array.getLength(obj));   for (int i = 0; i < Array.getLength(obj); i++) {    System.out.print(Array.get(obj, i) + " ");   }   System.out.println();  }  /**   * 通过 Java 反射机制获取 ClassLoader 类加载器   */  public static void test9() throws Exception{   //1、获取一个系统的类加载器   ClassLoader classLoader = ClassLoader.getSystemClassLoader();   System.out.println("系统的类加载器-->" + classLoader);   //2、获取系统类加载器的父类加载器(扩展类加载器(extensions classLoader))   classLoader = classLoader.getParent();   System.out.println("扩展类加载器-->" + classLoader);   //3、获取扩展类加载器的父类加载器   //输出为Null,引导类加载器无法被Java程序直接引用   classLoader = classLoader.getParent();   System.out.println("启动类加载器-->" + classLoader);   //4、测试当前类由哪个类加载器进行加载 ,结果就是系统的类加载器   classLoader = Class.forName("com.channelsoft.ssm.controller.ReflectTest").getClassLoader();   System.out.println("当前类由哪个类加载器进行加载-->"+classLoader);   //5、测试JDK提供的Object类由哪个类加载器负责加载的   classLoader = Class.forName("java.lang.Object").getClassLoader();   System.out.println("JDK提供的Object类由哪个类加载器加载-->" + classLoader);  }  public static void main(String[] args) {   try {    // 创建对象    ReflectClass.reflectNewInstance();    // 反射私有构造方法    ReflectClass.reflectPrivateConstructor();    // 反射私有属性    ReflectClass.reflectPrivateField();    // 反射私有方法    ReflectClass.reflectPrivateMethod();    // 将对象转换为Map键值对    Book book = new Book();    book.setName("猿码天地");    book.setAuthor("猿人");    Map map = ReflectClass.reflectKeyAndValue(book);    System.out.println(map);    // 使用 Java 反射获取 Class 对象(三种方式)    ReflectClass.test1();    // 使用 Java 反射获取一个对象的父类和实现的接口    ReflectClass.test2();    // 使用 Java 反射获取某个类的全部构造函数并调用私有构造方法    ReflectClass.test3();    // 使用 Java 反射获取某个类的全部属性    ReflectClass.test4();    // 使用 Java 反射获取某个类的全部方法    ReflectClass.test5();    // 使用 Java 反射调用某个类的方法    ReflectClass.test6();    // 通过反射越过泛型检查    ReflectClass.test7();    // 通过反射机制获得数组信息并修改数组的大小和值    ReflectClass.test8();    // 通过 Java 反射机制获取 ClassLoader 类加载器    ReflectClass.test9();   } catch (Exception ex) {    ex.printStackTrace();   }  } }
package com.bowen.demo.demo005;

/**
 * 

java-study

 * 

Book

 * @author : zhang.bw  * @date : 2021-03-19 18:08  **/ public class Book{  private final static String TAG = "BookTag";  private String name;  private String author;  @Override  public String toString() {   return "Book{" +     "name='" + name + '\'' +     ", author='" + author + '\'' +     '}';  }  public Book() {  }  private Book(String name, String author) {   this.name = name;   this.author = author;  }  public String getName() {   return name;  }  public void setName(String name) {   this.name = name;  }  public String getAuthor() {   return author;  }  public void setAuthor(String author) {   this.author = author;  }  private String declaredMethod(int index) {   String string = null;   switch (index) {    case 0:     string = "I am declaredMethod 1 !";     break;    case 1:     string = "I am declaredMethod 2 !";     break;    default:     string = "I am declaredMethod 3 1 !";   }   return string;  } }
package com.bowen.demo.demo005;

/**
 * 

java-study

 * 

Student 

 * @author : zhang.bw  * @date : 2021-03-21 00:24  **/ public class Student {  public String name;  protected int age;  char sex;  private String phoneNum;  @Override  public String toString() {   return "Student [name=" + name + ", age=" + age + ", sex=" + sex     + ", phoneNum=" + phoneNum + "]";  }  //--------------------------------------------  public Student(){   System.out.println("调用了公有、无参构造方法。。。");  }  public Student(char name){   System.out.println("姓名:" + name);  }  public Student(String name ,int age){   System.out.println("姓名:"+name+"年龄:"+ age);  }  //受保护的构造方法  protected Student(boolean b){   System.out.println("受保护的构造方法 b = " + b);  }  //私有构造方法  private Student(int age){   System.out.println("私有的构造方法,年龄:"+ age);  }  //--------------------------------------------  public void show1(String s){   System.out.println("调用了:公有的,String参数的show1(): s = " + s);  }  protected void show2(){   System.out.println("调用了:受保护的,无参的show2()");  }  void show3(){   System.out.println("调用了:默认的,无参的show3()");  }  private String show4(int age){   System.out.println("调用了,私有的,并且有返回值的,int参数的show4(): age = " + age);   return "abcd";  } }

猿人于2021年3月20日 0点30分 于深圳整理,整理完这篇文章头发还有10万零547根,今天掉了1根头发,持续记录头发根数,加油!

 

JAVA反射机制详解,看完还学不会你来捶我_第9张图片

你多学一样本事,就少说一句求人的话,现在的努力,是为了以后的不求别人,实力是最强的底气。记住,活着不是靠泪水博得同情,而是靠汗水赢得掌声。——《写给程序员朋友》 

点赞&在看是最大的支持

你可能感兴趣的:(跟着猿人学Java,Java,面试技巧,java,反射,编程语言,jvm)