* 本文主要介绍反射相关:
* 反射:(Reflection) 是java被视为动态语言的关键,反射机制允许程序在动态执行期间借助ReflectionAPI动态的获取类的任何
* 内部信息,并能直接操作对象的任何内部属性及方法。
* 通过类加载器,加载完类之后,在堆内存的方法区中就产生了Class类型的对象(对于相同的类只有一个Class对象),这个对象包含了
* 完整的类的结构信息,我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子,我们可以看到类的结构,所以,我们形象地称之为:反射.
* 正常情况下,我们通过new关键字来实例化并获取实例对象
* 反射,通过实例化获取完整的类的内部结构
* 缺点:
* 对性能影响极大,这类操作总是慢于直接执行相同的操作。
* java.lang.Class
* java.lang.reflect.Method 类的方法
* java.lang.reflect.Field 类的成员变量
* java.lang.reflect.Constructor 类的构造器
public class OperationsOfReflection {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException,
NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
// 通过反射来获取类的class对象, 此处需要处理异常。
// 获取反射的Class对象方式1 Class.forName(全类名)
// forName()里面是全类名 异常 ClassNotFoundException,
Class c1 = Class.forName("cn.JAVandReflection.Reflection.Person");
// 获取方式2, 用某个类的对象的 对象.getClass()获取对应的Class对象
Person person = new Person();
Class c2 = person.getClass();
/**
* 一个类一定只有一个class对象,验证:用反射获取的对象和这个类的对象的getClass()创建的对象比较其hashCode
*/
System.out.println(c2.hashCode() == c1.hashCode()); // true
// 获取方式3 : 用类的class属性获取,该获取方式最快最可靠
Class c3 = Person.class;
// 简介获取方式: 通过子类class对象获取父类的Class对象
/**
* Man extend Person
*/
Class c4 = Man.class;
Class c5 = c4.getSuperclass();
System.out.println(c5.hashCode() == c3.hashCode()); // true
/**
* Class对象反操作类的一些方法, 如下:
*/
// 获取类名 以Person的Class类为例 c3 或者 c5
// 获取到的是全类名
String allName = c3.getName();
System.out.println(allName);
// 获取到的是仅仅类名
String simpleName = c3.getSimpleName();
System.out.println(simpleName);
/**
* 输出:
* cn.JAVandReflection.Reflection.Person
* Person
*/
// 根据Class对象获取类属性操作
// getFields()
Field[] fields = c3.getFields();
Field[] fieldsAll = c3.getDeclaredFields();
// 打印查看区别
System.out.println(Arrays.toString(fields));
System.out.println(Arrays.toString(fieldsAll));
/**
*[public java.lang.String cn.JAVandReflection.Reflection.Person.name,
* public char cn.JAVandReflection.Reflection.Person.sex]
* 第二个
* [public java.lang.String cn.JAVandReflection.Reflection.Person.name,
* public char cn.JAVandReflection.Reflection.Person.sex,
* private java.lang.String cn.JAVandReflection.Reflection.Person.id,
* private int cn.JAVandReflection.Reflection.Person.age]
* 可以看出,每一行第一个是修饰符,最后一个是字段名,
* 第一个获取到的都是poublic修饰的
* 第二个获取到的是public 和 private 其实它可以获取所有修饰的属性,包括private 私有属性
*/
// 以上是获取属性列表,那么如何获取单个属性呢?
// 同样的 加上Declared就是获取所有属性的而没有这个的方法获取到的都是publc修饰的
// NoSuchFieldException
// 像这样,获取的name属性必须是public修饰的,不然会抛出 NoSuchFieldException异常
Field name = c3.getField("name");
// 这样的可以获取任意修饰符修饰的属性
Field name1 = c3.getDeclaredField("name");
/**
* 获取方法
*/
// 获取所有public的方法和父类的public方法, 注意 c4是Man的class
Method[] methods = c4.getMethods();
Method[] declaredMethods = c4.getDeclaredMethods();
System.out.println(Arrays.toString(methods));
System.out.println(Arrays.toString(declaredMethods));
/**
*注意。因为Man继承了person类,而person类继承了Object类,所以它列出了所有的public方法,很多
* [public static void cn.JAVandReflection.Reflection.Man.main(java.lang.String[]),
* public java.lang.String cn.JAVandReflection.Reflection.Person.toString(),
* public java.lang.String cn.JAVandReflection.Reflection.Person.getName(),
* public java.lang.String cn.JAVandReflection.Reflection.Person.getId(),
* public void cn.JAVandReflection.Reflection.Person.setName(java.lang.String),
* public void cn.JAVandReflection.Reflection.Person.setSex(char),
* public char cn.JAVandReflection.Reflection.Person.getSex(),
* public int cn.JAVandReflection.Reflection.Person.getAge(),
* public final void java.lang.Object.wait() throws java.lang.InterruptedException,
* public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException,
* public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException,
* public boolean java.lang.Object.equals(java.lang.Object),
* public native int java.lang.Object.hashCode(),
* public final native java.lang.Class java.lang.Object.getClass(),
* public final native void java.lang.Object.notify(),
* public final native void java.lang.Object.notifyAll()]
* 第二个输出 应为man里面除了构造器,就创建了一个main方法用于测试
* [public static void cn.JAVandReflection.Reflection.Man.main(java.lang.String[])]
*/
// 获取单个方法 捕捉异常:NoSuchMethodException
// 注意: 只能获取public修饰的方法
// 参数列表: 第一个:获取方法的名字, 后面的参数依次是该方法对应参数的Class对象,例如,setName(String str)
// 那么第二个参数就是Strin.class
Method setName = c3.getMethod("setName", String.class);
// 获取构造器
Constructor[] constructors = c3.getConstructors();
Constructor[] constructors2 = c3.getDeclaredConstructors();
// 或者获取指定构造器
Constructor constructor = c3.getDeclaredConstructor(String.class, char.class, String.class, int.class);
System.out.println(Arrays.toString(constructors));
System.out.println(Arrays.toString(constructors2));
System.out.println(constructor);
/**
* 输出:
* [public cn.JAVandReflection.Reflection.Person(),
* public cn.JAVandReflection.Reflection.Person(java.lang.String,char,java.lang.String,int)]
* -----------------------------------------------------------------------------------------------
* [public cn.JAVandReflection.Reflection.Person(),
* public cn.JAVandReflection.Reflection.Person(java.lang.String,char,java.lang.String,int)]
* -----------------------------------------------------------------------------------------------
* public cn.JAVandReflection.Reflection.Person(java.lang.String,char,java.lang.String,int)
* 经比对:完全一致:
*/
/*
上面是获取信息,下面利用反射来创建和使用对象
*/
// 获取对象,注意:直接获取到的是Object的对象,需要向下转型
// 注意: 需要调用无参构造器,如果没有无参构造器就会抛异常,InstantiationException
// 如果类构造器的权限是私有的,也不能通过这种方式获取对象
Object person2 = c3.newInstance();
// 通过获取到的构造器进行初始化 constructor 上面获取到的构造器
Person o = (Person)constructor.newInstance("李白", 'M', "002", 15);
// 通过反射调用方法
// 调用方法前需要先创建对象,因为对象才能调用方法
// 通过反射获取方法
// 比如通过反射获取toString方法
Method toString = c3.getDeclaredMethod("toString");
// 使用invoke使用方法:invoke激活的意思
// 使用方法.invoke()参数的第一个是通过反射创建的对象,后面的参数是方法的参数列表,如果没有传入null
System.out.println(toString.invoke(o, null));
/**
* Person{name='李白', sex=M} 执行结果就和正常调用一样,说明处理正常
*/
/**
* !!!!!!!!!!! 注意:无论是获取私有方法还是私有属性,如果直接获取获取不到抛出异常
* 就用 .setAccessible(true)方法关闭安全检测,这不仅能加括执行速度,还可以获取private修饰的东西
*/
// 操作属性
// 通过上面创建的对象 操作属性
// 属性通过反射获取
Field name2 = c3.getDeclaredField("name");
name2.setAccessible(true);
// 通过set修改
name2.set(o, "张飞");
// 通过get查看
System.out.println(name2.get(o));
// 为了验证,我们再次使用toString方法查看
System.out.println(toString.invoke(o, null));
/**
* Person{name='李白', sex=M}
* 张飞
* Person{name='张飞', sex=M}
* 可以看到,对象确实被修改了
*/
/**
* 反射操作泛型:
* java采用泛型擦除机制来引入泛型,java中的泛型仅仅是给编译器javac使用的,确保数据的安全性和免去强制类型转换的问题
* 但是一旦编译完成,所有和泛型相关的类型都会被擦除
* 为了通过反射操作泛型,java新增了ParameterizedType : 表示参数化类型
* 1、 getGenericParameterType
* 2 遍历上面获取到Type 判断每一项 instanceof ParameterizedType
* --- 此处理解先查看Type类型,然后是参数化类型 等
*
*/
}
}
Person 类
package cn.JAVandReflection.Reflection;
public class Person {
public String name;
public char sex;
private String id;
private int age;
public Person() {
}
public Person(String name, char sex, String id, int age) {
this.name = name;
this.sex = sex;
this.id = id;
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", sex=" + sex +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public char getSex() {
return sex;
}
public void setSex(char sex) {
this.sex = sex;
}
public String getId() {
return id;
}
private void setId(String id) {
this.id = id;
}
public int getAge() {
return age;
}
private void setAge(int age) {
this.age = age;
}
protected void eat() {
System.out.println("人要吃饭饭!!!");
}
}
Man
package cn.JAVandReflection.Reflection;
public class Man extends Person{
public Man() {
super.sex = 'M';
}
public Man(String name, String id, int age) {
super(name, 'M', id, age);
}
public static void main(String[] args) {
Man man = new Man("li", "001", 45);
System.out.println(man);
}
}