Class

  • java除了基本类型外,其他均为Class类型,包括接口,数组
    • 一个类的class实例中保存了该class的所有信息,包括类名、包名、父类、实现的接口、所有方法、字段等
    • 通过Class实例获取class信息的方法称为反射(Reflection)
    • 基本类型也能获取class实例,但是只能通过隐藏的class属性方式获取
  • 动态加载:JVM在执行Java程序的时候,并不是一次性把所有用到的class全部加载到内存,而是第一次需要用到class时才加载。每加载一种class,JVM就为其创建一个Class类型的实例
    • 动态加载class的特性对于Java程序非常重要。利用JVM动态加载class的特性,我们才能在运行期根据条件加载不同的实现类,见代码块
    • Class类的构造方法是private,只有JVM能创建Class实例
    • 一个类的class实例在jvm中是唯一的,不同方式获取一个类的多个实例**==**为true
    • 有继承关系的普通实例instanceof可以是true,如Integer实例instanceof Number=true,但是Integer.class instanceof Number.class=false
      1. public final class Class {
      2. private Class() {} //Class类的构造方法是private,只有JVM能创建Class实例
      3. }
      4. //加载一个类,jvm执行下面以创建class实例:
      5. Class cls = new Class(String);
      ```java //如果log4j存在则直接创建,否则使用jul LogFactory factory = null; if (isClassPresent(“org.apache.logging.log4j.Logger”)) { factory = createLog4j(); // } else { factory = createJdkLog(); }

boolean isClassPresent(String name) { try { Class.forName(name); return true; } catch (Exception e) { return false; } }

  1. <a name="wraf4"></a>
  2. ## 获取class实例
  3. ```java
  4. //1:通过一个类编译时自动生成的class属性获取,这个class跟jvm生成的有点区别,还没有被加载到内存中。基本类型只能通过第一种方式获取
  5. Class cls = String.class;
  6. //2:通过一个类隐藏的getClass()方法获取
  7. String s = "Hello";
  8. Class cls = s.getClass();
  9. //3:通过Class类的Class.forName()方法获取,参数为完整类名
  10. Class cls = Class.forName("java.lang.String");
  11. //4:通过类加载器,传个类路径
  12. ClassLoader.getSystemClassLoader().loadClass("cn.javaguide.TargetObject");

获取class的信息

  • getName()得到class的完整类名,如java.lang.String,如果为数组[Ljava.lang.String
  • getSimpleName()得到简单类名,如String,如果为数组String[]
  • getPackage()得到包信息,注意包信息可能为null

    • getPackage().getName()得到包名,如java.lang
  • isInterface() class是否为接口

  • isEnum() class是否为枚举
  • isArray() class是否为数组
  • isPrimitive() 是否为基本类型

    通过class实例创建类的普通实例

  • 通过Class的newInstance()方法可以创建类的普通实例,它的本质是:调用类的public的无参数构造方法。所以对于非public和有参构造无能为力

    // 获取String的Class实例:
    Class cls = String.class;
    // 创建一个String实例:
    String s = (String) cls.newInstance();
    

    获取class的字段信息

  • Field getField(name):根据字段名获取某个public的field(包括继承自父类)

  • Field[] getFields():获取所有public的field(包括父类)
  • Field getDeclaredField(name):根据字段名获取当前类的某个field,包括私有(不包括父类)
  • Field[] getDeclaredFields():获取当前类的所有field,包括私有(不包括父类)

    • 前2个能获取到继承来的,但是只能获取父类或者当前类public属性
    • 后2个能获取任意访问类型,但是只能获取当前类

      获取Field详细信息

  • 通过class实例和上述方法获取

    • getName():返回字段名称
    • getType():返回字段类型,也是一个Class实例,例如,返回class [C表示这是个char数组
    • getModifiers():返回字段的修饰符,它是一个int,不同的bit表示不同的含义。
      输出一个String[]类型的字段的Field
      public java.lang.String[] Person.name
      访问类型  完整类名(无包则只输出简单类名)   属性
      
      ```java public final class String { private final byte[] value; }

Field f = String.class.getDeclaredField(“value”); f.getName(); // “value” f.getType(); // class [B 表示byte[]类型 int m = f.getModifiers(); Modifier.isFinal(m); // true 是否是final修饰 Modifier.isPublic(m); // false 是否是public Modifier.isProtected(m); // false Modifier.isPrivate(m); // true Modifier.isStatic(m); // false

<a name="AemYu"></a>
## 获取与设置字段值

- 通过`field的get(对象)`方法得到一个字段的值,对于私有字段无法访问,可以使用`setAccessible(true)`强制允许访问
   - 正常情况下,我们总是通过p.name来访问Person的name字段,编译器会根据public、protected和private决定是否允许访问字段,这样就达到了数据封装的目的。
   - 而反射是一种非常规的用法,使用反射,首先代码非常繁琐,其次,它更多地是给工具或者底层框架来使用,目的是在不知道目标实例任何信息的情况下,获取特定字段的值。
   - 此外,setAccessible(true)可能会失败。如果JVM运行期存在SecurityManager,那么它会根据规则进行检查,有可能阻止setAccessible(true)。例如,某个SecurityManager可能不允许对java和javax开头的package的类调用setAccessible(true),这样可以保证JVM核心库的安全。
```java
Person p = new Person();
Class c = p.getClass();
Field f = c.getDeclaredField("name");
f.setAccessible(true);  //强制访问私有字段
Object value = f.get(p); //小明
f.set(p, "小红");
sout(p.name);  //小红
class Person {
    private String name="小明";
}

方法

Class …参数为方法的参数的class,如方法参数为string,则传个string.class.传的个数与类型要对应

  • Method getMethod(name, Class...):获取某个public的Method(包括父类)
  • Method getDeclaredMethod(name, Class...):获取当前类的某个Method(不包括父类)
  • Method[] getMethods():获取所有public的Method(包括父类)
  • Method[] getDeclaredMethods():获取当前类的所有Method(不包括父类)

    获取Method详细信息

  • getName():返回方法名称,例如:”getScore”;

  • getReturnType():返回方法返回值类型,也是一个Class实例,例如:String.class;
  • getParameterTypes():返回方法的参数类型,是一个Class数组,例如:{String.class, int.class};
  • getModifiers():返回方法的修饰符,它是一个int,不同的bit表示不同的含义。

    调用方法

  • 对Method实例调用invoke就相当于调用该方法,invoke的第一个参数是对象实例,即在哪个实例上调用该方法,后面的可变参数要与方法参数一致,否则将报错。

    • 调用静态方法时,由于无需指定实例对象,所以invoke方法传入的第一个参数永远为null
    • 调用非public需要设置Method.setAccessible(true)强制允许访问
      • 此外,setAccessible(true)可能会失败。如果JVM运行期存在SecurityManager,那么它会根据规则进行检查,有可能阻止setAccessible(true)。例如,某个SecurityManager可能不允许对java和javax开头的package的类调用setAccessible(true),这样可以保证JVM核心库的安全。
  • 调用方法同样遵循多态 ```java String s=”hello world”; String r=s.subtring(6); //world

Method m = String.class.getMethod(“substring”, int.class); // 在s对象上调用该方法并获取结果: String r = (String) m.invoke(s, 6);

```java
 // 获取Integer.parseInt(String)方法,参数为String:
        Method m = Integer.class.getMethod("parseInt", String.class);
        // 调用该静态方法并获取结果:
        Integer n = (Integer) m.invoke(null, "12345");
        Person p = new Person();
        Method m = p.getClass().getDeclaredMethod("setName", String.class);
        m.setAccessible(true);
        m.invoke(p, "Bob");
        System.out.println(p.name);
    }

class Person {
    String name;
    private void setName(String name) {
        this.name = name;
    }
}

调用构造方法

  • 类的class调用newInstance()即是调用构造方法
    • 只能调用该类的public无参数构造方法
  • 为了调用任意的构造方法,Java的反射API提供了Constructor对象,它包含一个构造方法的所有信息,可以创建一个实例。Constructor对象和Method非常类似,不同之处仅在于它是一个构造方法,并且,调用结果总是返回实例:
    • getConstructor(Class…):获取某个public的Constructor;
    • getDeclaredConstructor(Class…):获取某个Constructor;
    • getConstructors():获取所有public的Constructor;
    • getDeclaredConstructors():获取所有Constructor。
  • 注意Constructor总是当前类定义的构造方法,和父类无关,因此不存在多态的问题。

    • 调用非public的Constructor时,必须首先通过setAccessible(true)设置允许访问。setAccessible(true)可能会失败。
      Person p = Person.class.newInstance();
      
      // 获取构造方法Integer(int):
         Constructor cons1 = Integer.class.getConstructor(int.class);
         // 获取构造方法Integer(String)  ,如果是基本类型参数,可以直接传int.class形式
         Constructor cons2 = Integer.class.getConstructor(String.class);
         Integer n2 = (Integer) cons2.newInstance("456");
      

      获取继承关系

      获取父类

  • Class.getSuperclass()返回该class的父类class。只会返回直接父类,如Integer的直接父类为Number,而非Object

    获取父接口

  • 注意java中的接口不可以实现接口,但是可以继承接口

  • 对所有interface的Class调用getSuperclass()返回的是null,获取接口的父接口要用getInterfaces()

    • getInterfaces()只返回当前类直接实现的接口类型,并不包括其父类实现的接口类型
    • 如果一个类没有实现任何interface,那么getInterfaces()返回空数组。
      Class s = Integer.class;
         Class[] is = s.getInterfaces();
         for (Class i : is) {
             System.out.println(i);
         }
      

      判断Class继承关系

  • 如果是两个Class实例,要判断一个向上转型是否成立,可以调用isAssignableFrom():

    // Integer i = ?
    Integer.class.isAssignableFrom(Integer.class); // true,因为Integer可以赋值给Integer
    // Number n = ?
    Number.class.isAssignableFrom(Integer.class); // true,因为Integer可以赋值给Number
    // Object o = ?
    Object.class.isAssignableFrom(Integer.class); // true,因为Integer可以赋值给Object
    // Integer i = ?
    Integer.class.isAssignableFrom(Number.class); // false,因为Number不能赋值给Integer
    

    Type

  • Type 是 Java 语言中所有类型的公共父接口

    • Class 就是 Type 的一个直接实现类。Type 和 Class,以及 Type 的其它子接口( 和实现类 )组成了 Java 的类型体系。
  • 先有Class,再有Type(jdk1.5) 。因为jdk1.5引入了泛型,而Class是不带泛型信息的,因此jdk1.5还引入了Type
  • Type有如下实现类与子接口,以**HashMap<K,V>**为例
    • ClassHashMap
    • ParameterizedType泛型与Class整个的类型,即HashMap<K, V>这个整体的类型
    • TypeVariable泛型类的泛型类型。这个和WildcardType的区别还不清楚
    • WildcardType泛型方法的泛型参数
    • GenericArrayType泛型数组类型 ```java

Method method= cl.getMethod(“demo”,Object.class,Object[].class,String.class); Arrays.stream(method.getParameterTypes()).forEach(System.out::println); class java.lang.Object //TypeVariable class [Ljava.lang.Object; //GenericArrayType class java.lang.String //Class

```java
public static <T> <T> HashSet<T> demo(T arg0, T[] arg1, String arg2) {return new HashSet<>();}

//注意这些方法获取的都是Class

//---------获取方法参数的Type数组
//getParameterTypes会执行泛型擦除,如HashMap.put(K,V)方法擦除为Object,而getGenericParameterTypes返回的是K,V
method.getGenericParameterTypes () //[K,V]

//-----------获取带泛型的返回值类型,类型是TypeVariable
method.getGenericReturnType() //java.util.HashSet<T>
//如果是getReturnType ,则返回的class java.util.HashSet
public class StringLinkedList<T> extends LinkedList<String> {
    ...
}
//获取泛型类的泛型类型
StringLinkedList.class.getTypeParameters()  //[T]  
//返回泛型类的父类。这里会有泛型擦除
StringLinkedList.class.getSuperclass()    // LinkedList
//返回泛型类的父类。保留父类的泛型信息。
StringLinkedList.class.getGenericSuperclass()   // LinkedList<String>
//获取泛型类的父类的泛型类型
ParameterizedType superclass = (ParameterizedType) StringLinkedList.class.getGenericSuperclass();
for (Type cur : superclass.getActualTypeArguments()) {
    System.out.println(cur);    // 这个例子中只有一个:String
}