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
```java //如果log4j存在则直接创建,否则使用jul LogFactory factory = null; if (isClassPresent(“org.apache.logging.log4j.Logger”)) { factory = createLog4j(); // } else { factory = createJdkLog(); }public final class Class {
private Class() {} //Class类的构造方法是private,只有JVM能创建Class实例
}
//加载一个类,jvm执行下面以创建class实例:
Class cls = new Class(String);
boolean isClassPresent(String name) { try { Class.forName(name); return true; } catch (Exception e) { return false; } }
<a name="wraf4"></a>
## 获取class实例
```java
//1:通过一个类编译时自动生成的class属性获取,这个class跟jvm生成的有点区别,还没有被加载到内存中。基本类型只能通过第一种方式获取
Class cls = String.class;
//2:通过一个类隐藏的getClass()方法获取
String s = "Hello";
Class cls = s.getClass();
//3:通过Class类的Class.forName()方法获取,参数为完整类名
Class cls = Class.forName("java.lang.String");
//4:通过类加载器,传个类路径
ClassLoader.getSystemClassLoader().loadClass("cn.javaguide.TargetObject");
获取class的信息
getName()
得到class的完整类名,如java.lang.String
,如果为数组[Ljava.lang.String
getSimpleName()
得到简单类名,如String
,如果为数组String[]
getPackage()
得到包信息,注意包信息可能为nullgetPackage().getName()
得到包名,如java.lang
isInterface()
class是否为接口isEnum()
class是否为枚举isArray()
class是否为数组-
通过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,包括私有(不包括父类)通过class实例和上述方法获取
getName()
:返回字段名称getType()
:返回字段类型,也是一个Class实例,例如,返回class [C
表示这是个char数组getModifiers()
:返回字段的修饰符,它是一个int,不同的bit表示不同的含义。
```java public final class String { private final byte[] value; }输出一个String[]类型的字段的Field public java.lang.String[] Person.name 访问类型 完整类名(无包则只输出简单类名) 属性
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");
获取继承关系
获取父类
- 调用非public的Constructor时,必须首先通过setAccessible(true)设置允许访问。setAccessible(true)可能会失败。
Class.getSuperclass()
返回该class的父类class。只会返回直接父类,如Integer的直接父类为Number,而非Object获取父接口
注意java中的接口不可以实现接口,但是可以继承接口
对所有interface的Class调用getSuperclass()返回的是null,获取接口的父接口要用getInterfaces()
如果是两个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>**
为例Class
HashMapParameterizedType
泛型与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
}