Class对象  
在JVM底层原理中我们已经知道了:
当我们实例化一个新的对象时,JVM会先通过classLoader搜寻对应的class文件并创建一个Class对象,一个Class对象包含了关于这个类的详细信息,然后JVM会根据这个Class对象去初始化实例对象。
一个Class对象包含了这个类的 全限定名(name)、包名(package)、父类(super)、实现接口(interface)、成员(fields)、方法(methods)  等信息,这些信息是用来描述这个类本身的元信息 ,反过来说,我们也可以通过得到一个类的Class对象来获取关于这个类的元信息 
通过Class获取类的元信息的操作我们叫做反射 (Reflection)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class Main {     public static void main(String[] args){         Integer n = Integer.valueOf(123);                  System.out.println(n instanceof Integer);         System.out.println(n instanceof Number);         System.out.println(n.getClass() == Integer.class);         System.out.println(n.getClass() == Number.class);          System.out.println(n.getClass().getSuperclass() == Number.class);     } } stdout: true    //n是Integer,Integer自然是Integer true    //n是Integer,自然也是Number true    //n是Integer,Integer.class自然是Integer.class false   //n是Integer,Integer.class!=Number.class,实际上有的编译器会直接在这一步报错 true    //n是Integer,Integer的父类是Number,Number.class自然是Number.class 
 
通过上述代码可以知道,每个类的Class对象都是独立的,即Integer虽然在继承关系上属于Number,但是Integer.class和Number.class是不同的两个Class对象,但我们仍可以通过Integer.class.getSuperclass()来获取Integer父类的Class对象
获取Class 我们可以通过多种方法得到一个类的Class对象,由于Class对象在JVM中具有唯一性 ,对于同一个类获取的Class对象永远是同一个
声明Class引用 1 Class<String> cls=String.class; 
 
要注意的是,这里我们只是创建了一个class的引用,并没有显式地创建一个新的Class对象
调用实例对象的getClass方法 1 2 String s="123"; Class<? extends String> cls=s.getClass(); 
 
调用Class的forName静态方法 1 Class<?> cls = Class.forName("java.lang.String"); 
 
如你所见,这种方法需要知道类的全限定名 才能获取
Class提供的方法 在我们获取到一个Class对象后,可以用Class对象的许多get方法获取关于类的信息
有趣的是,当我们试图打印一些数据类型的ClassName时,如
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class Main {     public static void main(String[] args) {         int[] array=new int[5];         Integer i=Integer.valueOf(5);         String[] sArr={"123"};         System.out.println("Class name: " + array.getClass().getName());         System.out.println("Class name: " + i.getClass().getName());         System.out.println("Class name: " + sArr.getClass().getName());     } } stdout: Class name: [I Class name: java.lang.Integer Class name: [Ljava.lang.String; 
 
JVM给不同的数据类型(基本类型、对象、数组)设置了一套描述符,在字节码中用一系列大写字母来表示不同数据类型
描述符 
数据类型 
描述符 
数据类型 
 
 
B 
byte 
J 
long 
 
C 
char 
S 
short 
 
D 
double 
V 
void 
 
F 
float 
Z 
boolean 
 
I 
int 
 
 
 
L* 
Object 
[* 
数组类型 
 
于是从上面的输出我们会发现:
[I 说明了这是一个整数类型的数组
第一行输出中大写字母I 指的则是int类型数据,**[** 则指的是这是一个int类型的数组 
 
 
而Integer的ClassName则是java.lang.Integer,即Integer对象的全限定名 
当我们试图输出String[]的ClassName时得到的是[Ljava.lang.String,结合第一点和第二点,实际上这个name分为两部分
[L 说明了这是一个对象类型的数组 
java.lang.String则是这个对象的全限定名 的符号引用 
 
 
 
字段  
Field(字段)对象 是一个用于描述类的成员的对象,如String源码下的private final byte[] value就是String的一个字段
获取字段 Class提供了如下几个方式来获得某个class对象的指定field
1 2 3 4 public  Field getField (String name)              public  Field getDeclaredField (String name)      public  Field[] getFields()                     public  Field[] getDeclaredFields()             
 
在获取到一个字段后,我们可以通过一系列get获取到关于该字段的信息
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 public  class  Main  {    public  static  void  main (String[] args)  throws  NoSuchFieldException {         String sArr="123" ;         Field f=sArr.getClass().getDeclaredField("value" );         System.out.println("getName:" +f.getName());         System.out.println("getType:" +f.getType());         System.out.println("getModifiers:" +f.getModifiers());         System.out.println("isPublic:" +Modifier.isPublic(f.getModifiers()));         System.out.println("isPrivate:" +Modifier.isPrivate(f.getModifiers()));     } } stdout: getName:value                            getType:class [B                         getModifiers:18                           isPublic:false                            isPrivate:true                            ```    ### 操作字段 在获取到一个字段后,我们就可以对一个对象的该指定字段进行操作 ```java public  Object get (Object obj)                public  void  set (Object obj, Object value)    
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public  class  test  {    private  int  a;     public  test () {         a=10 ;     } } public  class  Main  {    public  static  void  main (String[] args)  throws  NoSuchFieldException, IllegalAccessException {         test t=new  test ();         Field f=t.getClass().getDeclaredField("a" );         f.setAccessible(true );                   System.out.println(f.get(t));            f.set(t,20 );                             System.out.println(f.get(t));     } } stdout: 10 20 
 
反射 是获取类内成员信息的一种非常规 用法,可以让我们在不知道一个对象内任何信息(即黑盒状态)的情况下,获取/修改一个特定字段 的值,常用于底层框架中。
此外,有一些Java核心类 的SecurityManager会阻止setAccessible,如String的private final byte[] value字段,在使用setAccessible(true)时则会抛出异常
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public  class  Main  {    public  static  void  main (String[] args)  throws  NoSuchFieldException, IllegalAccessException {         String sArr="123" ;         Field f=sArr.getClass().getDeclaredField("value" );         f.setAccessible(true );         System.out.println(f.get(sArr));     } } stderr: Exception in thread "main"  java.lang.reflect.InaccessibleObjectException: Unable to make field private  final  byte [] java.lang.String.value accessible: module  java.base does not "opens java.lang"  to unnamed module  @16b98e56 	at java.base/java.lang.reflect.AccessibleObject.throwInaccessibleObjectException(AccessibleObject.java:387 ) 	at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:363 ) 	at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:311 ) 	at java.base/java.lang.reflect.Field.checkCanSetAccessible(Field.java:180 ) 	at java.base/java.lang.reflect.Field.setAccessible(Field.java:174 ) 	at Main.main(Main.java:9 ) 
 
方法  
要注意,通过反射获取的方法永远是遵循多态规则的,即总是优先选择被子类覆写的方法 
获取方法 和字段同理,我们也可以通过如下几个方式来获得某个class对象的指定field
1 2 3 4 public  Method getMethod (String name, Class...)              public  Method getDeclaredMethod (String name, Class...)      public  Method[] getMethods()                        public  Method[] getDeclaredMethods()                
 
在获取到一个方法后,我们可以通过一系列get获取到关于该方法的信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public  class  Main  {    public  static  void  main (String[] args)  throws  NoSuchFieldException, NoSuchMethodException {         String sArr="123" ;         Method f=sArr.getClass().getMethod("charAt" , int .class);         System.out.println("getName:" +f.getName());         System.out.println("getParameterTypes:" +f.getParameterTypes());         System.out.println("getParameterTypes:" + Arrays.toString(f.getParameterTypes()));         System.out.println("getReturnType:" +f.getReturnType());         System.out.println("getModifiers:" +f.getModifiers());         System.out.println("isPublic:" +Modifier.isPublic(f.getModifiers()));         System.out.println("isPrivate:" +Modifier.isPrivate(f.getModifiers()));     } } stdout: getName:charAt                                   getParameterTypes:[Ljava.lang.Class;@27d6c5e0    getParameterTypes:[int ] getType:char                                      getModifiers:1                                    isPublic:true  isPrivate:false  
 
操作方法 我们可以通过invoke调用一个对象的指定方法,同理的,我们也可以通过setAccessible来操作private方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public  class  Main  {    public  static  void  main (String[] args)  throws  NoSuchFieldException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {         String sArr="123" ;         Method f=sArr.getClass().getMethod("charAt" , int .class);         Method m=Integer.class.getMethod("valueOf" , int .class);         System.out.println(f.invoke(sArr,0 ));         System.out.println(f.invoke(sArr,1 ));         System.out.println(f.invoke(sArr,2 ));         System.out.println(m.invoke(null ,10 ));       } } stdout: 1 2 3 10 
 
实际上,invoke非常接近JVM 实际调用非静态方法的情形,当调用一个非静态方法 时,实际上隐藏了一个参数,即当前对象的引用,当我们执行s.charAt(0)时,JVM看到的实际上是String.charAt(s,0);但是在class文件中,static方法并非传入了一个null ,而是压根就没有 这个参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public  class  test  {    static  public  int  get5 () {         return  5 ;     } } > javap -c -v test.class ···   public  static  int  get5 () ;     descriptor: ()I     flags: (0x0009 ) ACC_PUBLIC, ACC_STATIC     Code:       stack=1 , locals=0 , args_size=0           0 : iconst_5          1 : ireturn       LineNumberTable:         line 8 : 0  } ··· 
 
构造器  
获取构造器 1 2 3 4 public  getConstructor (Class...)              public  getDeclaredConstructor (Class...)      public  getConstructors ()                     public  getDeclaredConstructors ()             
 
而public newInstance())这个获取零参构造器的方法在Java9中已经被弃用了
1 2 3 4 @Deprecated(since = "9")  @NotNull   public  T newInstance () throws  InstantiationException, IllegalAccessException
 
调用构造器 我们可以通过newInstance来调用构造器并实例化一个新的对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public  class  Main  {    public  static  void  main (String[] args)  throws  NoSuchFieldException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {         String sArr="123" ;         Constructor c=sArr.getClass().getConstructor(String.class);                   System.out.println(sArr);         String sAnother= (String) c.newInstance("456" );         System.out.println(sAnother);     } } stdout: 123 456