Java反射:
class类:
Java除基本类型外其他都是class(包括interface)。
class的本质是数据类型(Type)。
没有继承关系的数据类型无法赋值。
class/interface的数据类型都是Class。
每加载一个class,JVM就为其创建一个Class类型的实例,并关联起来。JVM持有的每个Class实例都指向一个数据类型(class或interface)。
如:
一个class实例包含了该class的完整信息。
如:
JVM为每个加载的class创建对应的Class实例,并在实例中保存该class的所有信息。如果获取了某个Class实例,则可以获取到该实例对应的class的所有信息。
通过Class实例获取class信息的方法称为反射(Reflection)。
反射的目的是当获得某个Object实例时,我们可以获取该Object的class的所有信息。
如何获取一个class的Class的实例?
Type.class;
getClass();
Class.forName();
如:
Class实例在JVM中是唯一的。我们可以用==比较两个Class实例。
如:
Class实例比较和instanceof的区别:
instanceof比较不但匹配当前类型,还匹配当前类型的子类。
Class实例比较只匹配当前类型。
如:
通常我们用instanceof来判断。
从Class实例可以获取下列class信息:
getName();//获得完整类名
getSimpleName();//获得类名
getPackage();//获得包名
如:
从Class实例还可以判断class类型:
isInterface();
isEnum();
isArray();
isPrimitive();//判断是不是一个基本类型
利用JVM动态加载class的特性,我们可以在运行期间根据条件加载不同的实现类。
如:
Class.forName()判断一个类是否存在。
总结:
JVM为每个加载的class创建对应的Class实例来保存class的所有信息;
获取一个class对应的Class实例后,就可以获取该class的所有信息;
通过Class实例获取class信息的方法称为反射(Reflection);
JVM总是动态加载class,可以在运行期间根据条件控制加载class。
访问字段:
我们通过Class实例可以获取field信息:
getField(name):获取某个public的field(包括父类);
getDeclaredField(name):获取当前类的某个field(不包括父类);
getFields():获取所有public的field(包括父类);
getDeclaredFields():获取当前类的所有field(不包括父类)。
Field对象包含一个field的所有信息:
getName();//获得名称
getType();//获得类型
getModifiers();//获得修饰符
获取field的值:
get(Object);//获取一个实例的该字段的值
如:
设置field的值:
set(Object, Object);//设置一个实例的该字段的值
如:
总结:
Field对象封装了字段的所有信息;
通过Class实例的方法可以获取Field实例;
通过Field实例的方法可以获取字段信息;
通过Field实例可以读取或设置某个对象的字段;
通过设置setAccessible(true)来访问非public字段。
调用方法:
通过Class实例获取方法Method信息:
getMethod(name, Class...):获取某个public的method(包括父类);
getDeclaredMethod(name, Class...):获取当前类的某个method(不包括父类);
getMethods():获取所有public的method(包括父类);
getDeclaredMethods():获取当前类的所有method(不包括父类);
Method对象包含一个method的所有信息:
getName();//返回方法名称
getReturnType();//返回方法的返回类型
getParameterTypes();//返回方法的参数类型
getModifiers();//返回方法的修饰符
如:
调用无参数的Method:
Object invoke(Object obj)
如:
调用带参数的Method:
Object invoke(Object obj, Object... args)
如:
我们再看一个例子:
多态:
从Person.class获取的Method,作用于Student实例时:
实际调用方法使Student覆写的方法,这保证了多态的正确性。
如:
总结:
Method对象封装了方法的所有信息;
通过Class实例的方法可以获取Method实例;
通过Method实例可以获取方法信息;
通过Method实例可以调用某个对象的方法;
通过设置setAccessible(true)来访问非public方法。
调用构造方法:
Class.newInstance()只能调用public无参数构造方法。
如:
Integer没有定义无参数的构造方法。
Constructor对象包含一个构造方法的所有信息,可以创建一个实例:
通过Class实例获取Constructor信息:
getConstructor(Class...):获取某个public的Constructor;
getDeclaredConstructor(Class...):获取某个Constructor;
getConstructors():获取所有public的Constructor;
getDeclaredConstructors():获取所有Constructor;
总结:
Constructor对象封装了构造方法的所有信息;
通过Class实例的方法可以获取Constructor实例;
通过Constructor实例可以创建一个实例对象;
通过设置setAccessible(true)来访问非public构造方法。
获取继承关系:
获取父类的Class:
Class getSuperclass();
Object的父类是null;
interface的父类是null;
如:
获取当前类直接实现的interface:
Class[] getInterfaces();
不包括间接实现的interface;
没有interface的class返回空数组;
interface返回继承的interface;
如:
判断一个向上转型是否成立:
bool isAssignableFrom(Class)
如:
总结:
通过Class对象可以获取继承关系:getSuperclass()、getInterfaces();
通过Class对象的isAssignableFrom()方法可以判断一个向上转型是否正确。
Java注解:
使用注解:
注解(Annotation)是放在Java源码的类、方法、字段、参数前的一种标签。
如:
注解本身对代码逻辑没有任何影响,如何使用注解由工具决定。
编译器可以使用的注解:
@Override:让编译器检查该方法是否正确实现覆写;
@Deprecated:告诉编译器该方法已经被标记为“作废”,其他地方引用将会出现编译警告;
@SuppressWarnings。
如:
注解可以定义配置参数:
配置参数由注解类型定义;
配置参数可以包括:所有基本类型、String、枚举类型、数组;
配置参数必须是常量。
如:
使用注解时,缺少某个配置参数将使用默认值;如果只写常量,相当于省略了value参数;如果只写注解,相当于全部使用默认值。
如:
总结:
注解(Annotation)是Java语言用于工具处理的标注;
注解可以配置参数,没有指定配置的参数将使用默认值;
如果参数名称是value,可以省略参数名称。
定义注解:
使用@interface定义注解(Annotation):
注解的参数类似无参数方法;
可以设定一个默认值(推荐);
把最常用的参数命名为value(推荐)。
如:
元注解:可以修饰其他注解的注解。
使用@Target定义Annotation可以被应用于源码的哪些位置:
类或接口:ElementType.TYPE;
字段:ElementType.FIELD;
方法:ElementType.METHOD;
构造方法:ElementType.CONSTRUCTOR;
方法参数:ElementType.PARAMETER。
如:
使用@Retention定义Annotation的声明周期:
仅编译器:RetentionPolicy.SOURCE;//此时Annotation在编译器编译时直接丢弃
仅class文件:RetentionPolicy.CLASS;//此Annotation仅存储在class文件中
仅运行期:RetentionPolicy.RUNTIME;//在运行期间可以读取该Annotation
如果@Retention不存在,则该Annotation默认为CLASS。通常自定义的Annotation都是RUNTIME。
如:
使用@Repeatable定义Annotation是否可重复(JDK>=1.8)。
如:
使用@Inherited定义子类是否可继承父类定义的Annotation:
仅针对@Target为TYPE类型的Annotation;
仅针对class的继承,对interface的继承无效。
如:
定义Annotation的步骤:
1、用@interface定义注解;
2、用元注解(meta annotation)配置注解:
Target:必须设置
Retention:一般设置为RUNTIME
通常不必写@Inherited, @Repeatable等等
3、定义注解参数和默认值。
如:
总结:
使用@interface定义注解;
可定义多个参数和默认值,核心参数使用value名称;
必须设置@Target来指定Annotation可以应用的范围;
应当设置@Retention为RUNTIME便于运行期间读取该Annotation。
处理注解:
如何读取RUNTIME类型的注解?
Annotation也是class;
所有Annotation继承自java.lang.annotation.Annotation;
使用反射API判断Annotation是否存在:
Class.isAnnotationPresent(Class)
Field.isAnnotationPresent(Class)
Method.isAnnotationPresent(Class)
Constructor.isAnnotationPresent(Class)
如:
使用反射API获取Annotation:
Class.getAnnotation(Class)
Field.getAnnotation(Class)
Method.getAnnotation(Class)
Constructor.getAnnotation(Class)
getParameterAnnotations()
如:
读取方法参数的Annotation:
总结:
我们可以在运行期间通过反射读取RUNTIME类型的注解(注意不要漏写@Retention(RetentionPolicy.RUNTIME));
我们可以通过工具处理注解来实现相应的功能:对JavaBean的属性值按规则进行检查;JUnit会自动运行@Test注解的测试方法。
Java泛型:
什么是泛型:
泛型(Generic)就是定义一种模板,例如ArrayList<T>。
为什么需要泛型?
如:
JDK提供了ArrayList,可以看做“可变长度”的数组:比数组要方便。
但是如果用ArrayList存储String类型,则需要强制转型,不方便也容易出错。
我们可以通过单独为String编写一种ArrayList来解决。
如:
但是新的问题是,我们还要为Integer单独编写一种ArrayList,还需要为其他所有class单独编写一种ArrayList。
这是非常麻烦的。
我们必须把ArrayList变成一种模板:
ArrayList<T>,T可以是任意的class,这样就实现了编写一个模板就可以存储各种类型的class。
所以,泛型(Generic)就是定义一种模板,例如ArrayList<T>。
在代码中为用到的类创建对应的ArrayList<类型>:
如:
ArrayList<String> strList = new ArrayList<String>();
编译器会针对泛型类型作检查。 我们传入的参数只能是String。
如:
泛型的继承关系:
ArrayList<T>实现了List<T>接口,可以向上转型。
如:
注意向上转型时class类型不能变,原来是String,向上转型也得是String。
AArrayList<Number>和ArrayList<Integer>两者没有继承关系!!
如:
总结:
泛型就是编写模板代码来适应任意类型;
不必对类型进行强制转换;
编译器将对类型进行检查;
注意泛型的继承关系(T不能变!)。
使用泛型:
不使用泛型时,List的接口变为Object类型,list方法的类型都变为Object。
定义一个泛型类型<String>时,List<T>的泛型接口变为强类型:void add(String);String get(int)。
如:
使用泛型时,我们可以省略编译器能自动推断出的类型。
如:
例子:
Arrays.sort()可以对Object元素排序。
待排序的元素需要实现Comparable<T>泛型接口:
总结:
使用泛型时,我们要把泛型参数<T>替换成需要的class类型;
我们可以省略编译器能自动推断出的类型;
不指定泛型参数类型时,编译器会给出警告,且只能将<T>视为Object类型。
编写泛型:
泛型(Generic)一般用在集合类中。
如何编写一个泛型类:
按照某种类型(如String)编写类;
标记出所有的特定类型(如String);
把特定类型替换为T,并申明<T>。
如:
注意:
编写泛型时,需要定义泛型类型<T>:
public class Pair<T> { … }
静态方法不能引用泛型类型<T>,这会导致编译错误,编译器无法在静态字段或静态方法中使用泛型类型<T>。
如:
我们可以使用另一个类型<K>,就可以把静态方法单独改写为“泛型”方法。
如:
即形式:public static <K> Pair<K> create(K first, K last) { … }
泛型可以同时定义多种类型<T, K>。
总结:
编写泛型时,需要定义泛型类型<T>:public class Pair<T>{...};
静态方法不能引用泛型类型<T>,必须定义其他类型<K>来实现“泛型”:public static <K> Pair<K> create(K first, K last) { … };
泛型可以同时定义多种类型<T, K>:public class Pair<T,K>{...}。
擦拭法:
Java的泛型(Generic)是采用擦拭法(Type Erasure)实现的。
在泛型代码编译时,编译器实际上把所有泛型类型<T>统一视为Object类型。
如:
编译器根据<T>实现安全的强制转型。
擦拭法的局限:
<T>不能是基本类型,例如int;
Object字段无法持有基本类型;
无法取得带泛型的Class;
如:
所有泛型类型,无论T是什么,返回的都是同一个类型的class。
无法判断带泛型的Class;
不能实例化 T 类型,因为擦拭后实际上是new Object();
如:
实例化T类型必须借助Class<T>;
如:
泛型的继承:
可以继承自泛型类:
public class IntPair extends Pair<Integer> {
}
父类的类型是Pair<Integer> ,子类的类型是IntPair。子类可以获取父类的泛型类型Integer。
如:
继承关系:
总结:
Java的泛型采用擦拭法实现;
擦拭法决定了泛型<T>:
不能是基本类型,例如int;
不能取得带泛型的Class;
不能判断带泛型的Class;
不能实例化 T 类型;
泛型方法要防止重复定义方法;
子类可以获取父类的泛型类型<T>。
extends通配符:
前面提到泛型的继承关系:
Pair<Integer>不是Pair<Number>的子类;
add()不接受Pair<Integer>;
如:
这样一来就不太方便。
我们可以使用<? extends Number>使方法接收所有泛型类型为Number或Number子类的Pair类。
如:
当对Pair<? extends Number>调用getFirst()方法:
方法签名:? extends Number getFirst();
因此调用getFirst方法我们可以安全赋值给Number类型的变量:Number x=p.getFirst();
但是我们不能预测实际类型就是Integer:Integer x=p.getFirst()。
如:
实际传入的是Double类型。
如果我们对Pair<? extends Number>调用setFirst()方法:
方法签名为:void setFirst(? extends Number);
无法传递任何Number类型给setFirst(? extends Number)。
如:
编译会报错。
因此,<? extends Number>的通配符:
允许调用get方法获得Number的引用;
不允许调用set方法传入Number的引用;
唯一例外:可以调用setFirst(null)。
<T extends Number>的通配符:
限定定义Pair<T>时只能是Number或Number的子类。
如:
总结:
使用类似<? extends Number>通配符作为方法参数时表示:
方法内部可以调用获取Number引用的方法;
方法内部无法调用传入Number引用的方法(null除外);
使用类似<T extends Number>定义泛型类时表示:
泛型类型限定为Number或Number的子类。
super通配符:
泛型的继承关系:
Pair<Integer>不是Pair<Number>的子类;
set()不接受Pair<Number>;
如:
我们可以使用<? super Integer>使方法接收所有泛型类型为Integer或Integer超类的Pair类。
如:
对Pair<? super Integer>调用setFirst()方法:
方法签名:void setFirst(? super Integer);
可以安全传入Integer类型的变量:p.setFirst(new Integer(123));
如:
对Pair<? super Integer>调用getFirst()方法:
方法签名:?super Integer getFirst();
无法赋值给Integer类型的变量。
如:
<? super Integer>的通配符:
允许调用set方法传入Integer的引用;
不允许调用get方法获得Integer的引用;
唯一例外:可以获取Object引用Object o=p.getFirst()。
类似地,如果List使用<? super Integer>参数:
<T super Integer>的通配符:
限定定义Pair<T>时只能是Integer或Integer的超类。
如:
方法参数<? extends T>和<? super T>的区别:
<? extends T>允许调用方法获取T的引用;
<? super T>允许调用方法传入T的引用。
如:
如果反过来定义,则:
无限定通配符<?>:
?既包含extends的限制,又包含super的限制;
不允许调用set方法(null除外);
只能调用get方法获取Object引用;
Pair<?>和Pair不同。
如:
可以引用泛型参数<T>来消除<?>。
总结:
使用类似<? super Integer>的通配符作为方法参数时表示:
方法内部允许调用set方法传入Integer的引用;
方法内部不允许调用get方法获得Integer的引用;
唯一例外:可以获取Object引用Object o=p.getFirst()。
使用类似<T super Integer>定义泛型时表示:
泛型类型限定为Integer或Integer的超类。
无限定通配符<?>很少使用,可以用<T>替换。
泛型和反射:
部分反射API是泛型:
Class<T>是泛型;
如:
Constructor<T>也是泛型;
如:
我们可以声明带泛型的数组,但不能直接创建带泛型的数组,必须强制转型。
如:
我们必须通过强制转型实现带泛型的数组。
如:
不安全地使用带泛型的数组:
安全地使用带泛型的数组:
带泛型的数组实际上是编译器的类型擦除:
我们不能直接创建T[]数组,因为擦拭后代码变为new Object[5],我们必须借助Class<T>。
如:
我们还可以利用可变参数创建T[]数组:
@SafeVarargs消除编译器警告。
如:
总结:
部分反射API是泛型:Class<T>和Constructor<T>;
可以声明带泛型的数组,但是不能直接创建带泛型的数组,必须强制转型;
可以用过Array.newInstance(Class<T>,int)创建T[]数组,需要强制转型。