反射
- Java除基本类型以外其他都是class(包括interface),class的本质也是数据类型(Type),实例化的时候即是将其赋值给变量
- class/interface的数据类型是
Class
,每加载一个class,JVM就为其创建一个Class类型的实例,该实例保存了class的完整信息 - 如果获取了某个Class的实例(类的实例),即获取到了对应class的所有信息,比如String类,当使用String类时,即获取了他的所有参数方法:
- 通过Class实例获取class信息的方法称为反射(Reflection)
- 反射的目的是当获得某个Object类实例时,我们可以获取这个class的定义信息
package Hello;
public class Student {
private String name;
public Student() {
this("unnamed");
}
public Student(String name) {
this.name = name;
}
public void hello() {
System.out.println("Hi, " + name + "!");
}
}
package Hello;
public class Main {
public static void main(String[] args) throws IllegalAccessException, InstantiationException {
// Class
Class cls = Student.class; // 获取Student类的Class
System.out.println("class name: " + cls.getName()); // Hello.Student
System.out.println("class simple name: " + cls.getSimpleName()); // Student
System.out.println("package name: " + cls.getPackage().getName()); // Hello
System.out.println("is interface? " + cls.isInterface()); // false
// Student s = new Student();
Student s = (Student) cls.newInstance(); // 通过Class进行类的实例化
s.hello(); // Hi, unnamed!
}
}
JVM总是动态加载class,可以在运行期根据条件控制加载class
Class cls;
if (true) {
cls = Class.forName("Hello.Student");
} else {
cls = Class.forName("Hello.Something"); // 虽然这个类不存在
}
访问字段——field
- Field对象封装了字段的所有信息
- 通过Class实例的方法可以获取Field实例
getField
getFields
getDeclaredField
// 获取非public字段getDeclaredFields
- 通过Field实例可以获取字段信息
getName
getType
getModifiers
// 修饰类型
- 通过Field实例可以读取或设置某个对象的字段
get(Object instance)
/set(Object instance, Object value)
- 通过设置setAccessible(true)来访问非public字段
// class Main
public static void main(String[] args) throws Exception {
// field字段
Student s = new Student(); // 实例化类
Class cls = s.getClass(); // 类的实例
// 类的实例的方法获取Field实例
Field f = cls.getField("name"); // public字段
// Field f = cls.getDeclaredField("address"); // private字段
printFieldInfo(f);
// f.setAccessible(true); // address为private,必须设置为可访问,f.get()才能执行
// f.set(s,"shanghai");
// System.out.println(f.get(s));
// Field f = cls.getDeclaredField("number"); // 同样可以访问static字段
// f.setAccessible(true);
// f.set(null,123);
// System.out.println(f.get(null)); // 不需要指定实例类,null即可
s.hello();
}
static void printFieldInfo(Field f) {
System.out.println("field name: " + f.getName());
System.out.println("field type: " + f.getType());
System.out.println("field modifier: " + f.getModifiers());
System.out.println("is public? " + Modifier.isPublic(f.getModifiers()));
System.out.println("is protected? " + Modifier.isProtected(f.getModifiers()));
System.out.println("is private? " + Modifier.isPrivate(f.getModifiers()));
System.out.println("is static? " + Modifier.isStatic(f.getModifiers()));
System.out.println("is final? " + Modifier.isFinal(f.getModifiers()));
}
// field name: age
// field type: int
// field modifier: 1
// is public? true
// is protected? false
// is private? false
// is static? false
// is final? false
// Hi, unnamed from beijing!
// Student.java
public class Student extends Person implements say {
public static int number = 0;
public String name;
private String address = "beijing";
public Student() {
this("unnamed");
}
public Student(String name) {
this.name = name;
number++;
}
public void hello() {
System.out.println("Hi, " + name + " from " + address + "!");
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
// Person.java
public class Person {
public int age;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
// say.java
interface say {
void hello();
}
setAccessible(true)
可能会失败,这是因为定义了SecurityManager,该规则阻止对Field设置accessible- 但是对于大多数自定义类和第三方类没有此限制
调用方法
- Method对象封装了方法的所有信息
- 通过Class实例可以获取Method实例
getMethod(name, Class...)
// 获取某个public属性的method(包括父类)getDeclaredMethod(name, Class...)
// 获取当前类的某个methodgetMethod()
// 获取所有public的methodgetDeclaredMethod()
// 获取当前类的所有method
- 通过Method可以调用对象的方法
invoke(Object instance, Object params)
- 同样,通过设置setAccessible(true)来访问非public属性方法
调用构造方法
- Constructor对象封装了构造方法的信息
- 通过Class实例可以获取Constructor实例
getConstrcutor(Class...)
// 获取某个public属性的constructorgetDeclaredConstructor()
// 获取某个constructorgetConstrcutors(Class...)
// 获取所有public属性的constructorgetDeclaredConstructors()
// 获取所有constructor- 不可能获取父类的constructor
// Main.java
public class Main {
public static void main(String[] args) throws Exception {
Class cls = Student.class;
// 类的带参数构造方法,传入参数类型的Class实例
Constructor c = cls.getDeclaredConstructor(String.class, int.class);
printConstructorInfo(c);
c.setAccessible(true);
Student s = (Student) c.newInstance("Xiao Ming", 12);
s.hello();
}
static void printConstructorInfo(Constructor c) {
System.out.println(c);
System.out.println("parameters: " + Arrays.toString(c.getParameterTypes()));
System.out.println("modifier: " + c.getModifiers());
}
}
// Student.java
public class Student extends Person implements Hello {
public static int number = 0;
public String name;
private String address = "beijing";
public Student() {
this("unnamed");
}
public Student(String name) {
this(name, 20);
}
private Student(String name, int age) {
this.name = name;
this.age = age;
number++;
}
public void hello() {
System.out.println("Hi, " + name + " from " + address + "!");
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
获取继承关系
- 获取父类的Class
Class sup = Integer.class.getSuperclass(); // Number.class
Object.class.getSuperclass(); // null
- 获取当前类直接实现的interface
Class[] ifs = Integer.class.getInterfaces(); // Comparable.class
- 判断一个向上转型是否成立
Number.class.isAssignableFrom(Integer.class); // true
以上部分基本介绍了反射及相关的方法,通过类的实例Class可以获取属性、方法等信息
注解
- 注解(Annotation)是Java语言使用工具处理时的标注
- 如何使用注解由工具决定,例如CLASS类型的注解由操作class类型的工具使用,但很少见;我们一般使用RUNTIME类型的注解,在运行期读取注解
- 注解本身对代码逻辑没有任何影响
- 注解可以配置参数,没有指定参数则使用默认值
定义注解
- 使用
@interface
定义注解 - 注解的参数类似无参数方法
- 把最常用的参数命名为
value
public @interface Report{
int type() default 0;
String level() default "info";
String value() default "";
}
- 使用@Target定义注解可以被应用于源码的哪些位置,属于元注解
ElementType.TYPE
// 类或接口ElementType.FIELD
// 子段ElementType.METHOD
// 方法ElementType.CONSTRUCTOR
// 构造方法ElementType.PARAMETER
// 方法参数
@Target(ElementType.METHOD)
public @interface Report{
int type() default 0;
String level() default "info";
String value() default "";
}
- 使用@Retention定义注解的生命周期,属于元注解
- RetentionPolicy.SOURCE // 仅编译期
- RetentionPolicy.CLASS // 仅class文件(默认)
- RetentionPolicy.RUNTIME // 运行期(推荐)
- 使用@inherited定义子类是否可继承父类定义的注解
@Target(ElementType.TYPE) // 仅针对@Target为TYPE类型的注解
public @interface Report{ // 仅针对类的继承,对interface的继承无效
int type() default 0;
String level() default "info";
String value() default "";
}
public class Person {
@NotNull
public String name;
@Range(max = 20)
public int age;
public person(@NotNull String name, int age){
this.name = name;
this.age = age;
}
}
// NotNull.java
import java.lang.annotation.ElementType;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.PARAMETER})
public @interface NotNull {
}
// Range.java
import java.lang.annotation.ElementType;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Range{
int min() default 1;
int max() default 100;
}
处理注解
- 如何读取RUNTIME类型的注解?使用反射API
public static void main(String[] args) {
Person p1 = new Person("Roy", 25);
Person p2 = new Person(null, 15);
checkPerson(p1);
checkPerson(p1);
}
static void checkPerson(Person p) throws Exception {
System.out.println("check "+ p + "..." );
Class cls = Person.class;
for (Field f : cls.getFields()) {
checkField(f,p);
}
}
static void checkField(Field f, Person p) throws Exception {
if(f.isAnnotationPresent(NotNull.class)) { // 注解的反射,判断当前是否使用注解
Object r = f.get(p);
if(r == null) {
System.out.println("Error:Field "+f.getName()+"is null");
}
}
if(f.isAnnotationPresent(Range.class)) {
Range range = f.getAnnotation(Range.class); // 使用反射实例化注解
int n = (Integer) f.get(p);
if(n<range.min() | n>range.max()) {
System.out.println("Error:field "+ f.getName() + "is out of range");
}
}
}
泛型
- 为什么使用泛型?
举例来说,ArrayList可以看做是变长数组,内部使用Object[] array,但是如果要存储String类型元素,需要强制转型很不方便。因此,要将ArrayList变成一个模板,ArrayList<T>,根据传入类型的不同,创建不同的ArrayList<>
- ArrayList<T>实现了List<T>接口,可以向上转型
List<String> list = new ArrayList<String>()
// 注:ArrayList<Number>与ArrayList<Integer>没有继承关系
- 泛型,即是编写模板代码来适应任意类型
- 使用泛型时,把泛型参数<T>替换为需要的class类型
- 不指定泛型参数时,会使胃Object类型
编写泛型
- 静态方法不能引用泛型类型<Y>,必须定义其他类型<K>来实现泛型
回顾:
静态变量被所有的对象所共享
静态变量和方法先于对象出现,所以习惯上使用类名调用
静态方法可以被继承,但不能被重写
- 定义泛型类型时可定义多种类型<T,K>
泛型的实现
通配符
extends
通配符
- 注:由于不能确定具体类型,所以不能使用set相关的方法;
super
通配符
// Pair.java
public class Pair<T> {
private T first;
private T last;
public Pair(T first, T last) {
this.first = first;
this.last = last;
}
public T getFirst() {
return first;
}
public void setFirst(T first) {
this.first = first;
}
public T getLast() {
return last;
}
public void setLast(T last) {
this.last = last;
}
public String toString() {
return "Pair(" + first + ", " + last + ")";
}
}
- 通过比较,我们很容易发现extends与super的区别:
- 可以理解为extends可以使用返回值,但是不可以为方法提供参数;super可以为方法提供参数,但是不能使用返回值;
- 无限定通配符
相当于加上了extends和super的限制
- 不同的通配符总的来说限定了使用泛型时的一些继承关系,只需要知道不同通配符的特点即可:由于类型的不确定(类型擦除),
extends
不能set,super
不能get - 部分反射API是泛型:
- 泛型数组
可以声明带泛型的数组,但不能直接使用new创建带泛型的数组
Pair<String>[] ps = null;
// 必须通过强制转型实现带泛型的数组
Pair<String>[] ps = (Pair<String>[]) new Pair[2];
import java.lang.reflect.Constructor;
import java.util.Arrays;
public class Main {
public static void main(String[] args) throws Exception {
Class<String> clazz = String.class; // 反射
String s1 = clazz.newInstance(); // 得到实例化
System.out.println(s1);
Constructor<String> cons = clazz.getConstructor(String.class);// 构造方法传参
String s2 = cons.newInstance("Hello"); // 通过构造方法实例化
System.out.println(s2);
@SuppressWarnings("unchecked")
Pair<String>[] ps = (Pair<String>[]) new Pair[2];// 必须通过强制转型实现带泛型的数组
ps[0] = new Pair<>("a", "b");
ps[1] = new Pair<>("x", "y");
System.out.println(Arrays.toString(ps));
}
}
小结
以上主要总结了Java中反射、注解和泛型的概念和基本操作,泛型是最常见的操作,和反射相关联,需要熟练掌握。可以参看一下 其他的文章进一步了解!
注:部分截图来自廖雪峰老师的教程;