反射-reflect
动态的获取和访问类中的属性(Field),方法(Method),构造方法(Constructor).
Class类-实际上是获取任意类型(也包括基本类型)对应的字节码文件,他是反射技术的源头,可以解析出类中的各个成员。
获取Class对象的三种方法
- 对象名.getClass()---建议在对象已创建好的情况下使用
- 类名.Class---建议在已知类名的情况下使用。可以使用int.class,String.class表示获取方法中的参数类型。
- Class.forName("包名.类名")---前两个表示要解析的类对应的class文件已经加载到内存中了,这个方法表示类的class文件还没有被加载。
Class类的作用
一个Class类用来表示类类型,类应该有属性,那么Class类应该有getField()方法,但是一个类可以有多个属性,那么Class类有的就不只是getField(),还应该有getFields(),返回值类型是Field[] 类型。相同的Class类应该也有getMethods()和getConstructors()方法,用来返回这个类所有的方法和所有构造器的定义。
Class:类的反射对象
Field:属性(字段)反射对象
Method:方法反射对象
Constructor:构造器反射对象
Class,Field,Method,Constructor统称为反射对象。
导致一个类被加载的可能有
- 使用一个类的静态方法
- 使用一个类的静态属性
- 创建这个类的对象
- 使用Class.forName()方法加载类
- 反序列化一个类的对象(对象序列化是把对象永久性的存储到流中(就是存储到本地),反序列化就是从流中读取该对象,这时候会加载类)
- 加载一个类的子类时,也会加载其父类
- 加载一个类时也会加载和这个类相关的其他类
方法总结
Class类的方法(空格前的表示该方法的返回类型,空格后表示方法名)
总结:带"s"的都是所有的,带"Declared"的都是任意权限的但不包含父类中的属性或方法的
getModifiers(),Modifier.toString()和getName()方法在Class,Constructor,Method和Field中都有,作用都是一样的。
- String getName():返回类名,格式是包名.类名,在方法中表示方法名,在构造器中表示构造器名,字段中表示字段名。
- int getModifiers():获取类的所有权限修饰符的Code形式,可以通过Modifier.toString(Code)来获取权限修饰符名。在Constructor中代表构造器的修饰符,在Method中代表方法的修饰符,在Field中代表字段的修饰符。返回此类或接口以整数编码的 Java 语言修饰符。修饰符由 Java 虚拟机的
public
、protected
、private
、final
、static
、abstract
和interface
对应的常量组成;它们应当使用Modifier
类的方法来解码。- Modifier.toString(Code):获取权限修饰符的名字,可以一步到位写成Modifier.toString(obj.getModifiers())
- String getSimpleName():返回简单类名,不包含包名,数组类型使用它比较方便。
- T newInstance():使用本类无参构造器来常见本类对象,Class类创建的对象只能是无参的,Constructor创建的对象可以是有参的
- Constructor getConstructor(Class... parameterTypes):获取有参数的公共权限(public)的构造器的反射对象,括号内为可变参数,可以为空也可以写多个参数类型,要写的话就写String.class,int.class之类的
- Constructor[] getConstructors():获取所有公共权限的构造器对象,返回的是一个构造器数组
- Constructor getDeclaredConstructor(Class... parameterTypes):获取指定参数类型的所有权限(public,private,protected,默认)的构造器对象
- Constructor[] getDeclaredConstructors():获取任意权限所有的构造器对象
- Field getField(String name):获取指定名字的公有权限属性的对象,包含父类中声明的公有属性
- Field[] getFields():获取所有的公有权限的属性对象,包括父类中声明的公有属性,返回的是字段(属性)数组
- Field getDeclaredField(String name):获取指定名字的任意权限属性的对象,不包括父类中声明的任何属性
- Field[] getDeclaredFields():获取所有的任意权限属性的对象,不包括父类中声明的任何属性
- Method getMethod(String name, Class... parameterTypes):通过方法名和方法形参(参数类型)获取指定的公有方法对象,包含父类中声明的公有方法,name是该方法的方法名,Class...parameterTypes是可变参数,可以写多个形参,如果没有形参则可以省略不写
- Method[] getMethods():获取的公有的本类中所有的方法,包含父类中的公有方法
- Method getDeclaredMethod(String name, Class... parameterTypes):通过方法名和形参获取指定的任意权限的方法对象,不包含父类中的任何方法。
- Method[] getDeclaredMethods():获取任意权限的本类中所有的方法,不包含父类中的任何方法
- Class getSuperClass():获取父类,返回值为Class,Object.class.getSuperClass()返回null
Constructor中的方法
- String getName():获取构造器名,显示的是包名.类名,和Class对象显示的信息一致,因为构造器名肯定是和类名一致的,Class类中是获取类名,Method中是获取方法名,Filed中是获取属性名
- int getModifiers():获取构造器的所有修饰符的Code信息
- String Modifier.toString(Code):获取构造器修饰符名的字符串形式
- Class getDeclaringClass():获取构造器所属的类型,即这个构造器是在哪个类中的。
- Class[] getParameterTypes():获取构造器的所有参数的类型
- Class getExceptionTypes():获取构造器上声明的所有异常类型
- T newInstance(Object... initargs):通过指定构造器创建对象,括号内不写东西即创建无参构造器对象,对象内写对应的参数值即创建有参构造函数对象。如Person p=con.newInstance(); Object obj=con.newInstance("张三",20);
Method中的方法
- String getName():获取方法名,Class类中是获取类名,Method中是获取方法名,Filed中是获取属性名
- int getModifiers():获取方法的所有修饰符的Code信息
- String Modifier.toString(Code):获取方法修饰符名的字符串形式
- Class getDeclaringClass():获取方法所属的类型,即这个方法是在哪个类中的。
- Class[] getParameterTypes():获取方法中的所有参数的类型
- Class getReturnType():获取方法的返回值类型,调用getName方法就可以得到字符串
- Class getExceptionTypes():获取方法上声明的所有异常类型
- Object invoke(Object obj,Object...args):(invoke,调用)通过方法反射对象调用方法,如果当前方法是实例方法,那么当前对象就是obj,如果当前方法是static方法,静态方法不涉及对象,那么可以给obj传递null或者直接不写。args表示方法的参数。 该方法配合getMethod使用,先用getMethod()获取到方法对象Method me,然后me在调用invoke()方法,obj写一开始你想要操作的那个对象,即使用对象.getClass()方法获取到Class对象的那个对象,Object...args可变参数,写这个对象代表的方法中是否需要传参进去,不需要的话就可以不写,但是一定要确定传参的类型,否则可能出现NoSuchMethodException异常。
Field方法
- String getName():获取字段名,Class类中是获取类名,Method中是获取方法名,Filed中是获取属性名
- int getModifiers():获取字段的所有修饰符的Code信息
- String Modifier.toString(Code):获取字段修饰符名的字符串形式
- Class getDeclaringClass():获取字段所属的类型,即这个字段是在哪个类中的。
- Class getType():获取当前属性的类型名,直接输出返回的是class +这个类型所在的包 +类型名
- Object get(Object obj):获取Object对象当前的属性值
- setAccessible(true):打破字段的私有化访问权限,然后就可以给属性赋值了
- void set(Object obj,Objecct value):设置Object对象当前的属性值为value
- XXX getXXX(Object obj):如果当前属性为基本类型,可以使用getXXX()系列方法获取到基本类型属性值。如果当前属性为int类型,那么可以使用getInt(Object obj)方法获取到obj对象的当前属性值。
- void setXXX(Object obj,XXX value):如果当前属性为基本类型,可以使用setXXX()方法设置基本类型属性值。如果当前属性为int类型,那么可以使用setInt(Object obj,int value)方法设置obj对象的当前属性值为value。
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.concurrent.SynchronousQueue;
public class Demo1 {
public static void main(String[] args)throws Exception {
@SuppressWarnings("unchecked")
//1.对象名.getClass()
Class<Person> c1=(Class<Person>)new Person().getClass();
System.out.println(c1.getName());//Demo1.Person,返回类名
System.out.println(c1.getSimpleName());//Person
//2.类名.class
Class c2=Person.class;
System.out.println(c2);//class Demo1.Person,打印的是class对象,该对象重写了toString方法
//3.Class.forName("包名.类名");
Class c3=Class.forName("Demo1.Person");
System.out.println(c3);
Object p=c2.newInstance();//创建实例对象,Class类中只能创建无参的对象
// System.out.println(p);//null...0下面重写了toString方法,不然的话应该是地址值
Constructor[] cons=c1.getConstructors();//获取所有public权限的构造方法
Constructor con2=c1.getConstructor();
Constructor con=c1.getConstructor(String.class,int.class);//创建公共的指定构造方法的对象
Constructor con1=c1.getDeclaredConstructor(String.class);//decalred可以访问private的构造方法
for (int i = 0; i < cons.length; i++) {
int mod=cons[i].getModifiers();//获取构造器的权限修饰符代码
System.out.println(mod);//1,public的代码
String au=Modifier.toString(mod);//public---
System.out.println(au+"---");//获取权限修饰符字符串形式
String name=cons[i].getName();//Demo1.Person
System.out.println(name+"+++");//获取构造方法名--包名.类名
Class[] a=cons[i].getParameterTypes();//获取参数类型
System.out.println(a);//[Ljava.lang.Class;@1db9742
for (int j = 0; j < a.length; j++) {
String b=a[j].getTypeName();//java.lang.String,int,获取参数类型名,因为String他本身也是一个类,所以会显示包名.类名
System.out.println(b+"~~~");
}
}
Object obj=con2.newInstance();//创建无参构造函数的对象
Object obj1=con.newInstance("张三",20);//创建有参构造函数,只有Constructor可以做到,Class对象中的方法不行
System.out.println(obj);
System.out.println(obj1);
///////////////////字段/////////////////////
Field[] f1=c1.getFields();//获取所有公共字段
Field f2=c1.getField("name");//获取指定属性的属性对象
Field f4=c1.getDeclaredField("age");
System.out.println(f2.getType().getName());
Field[] f3=c1.getDeclaredFields();//获取所有字段,包括private
Object o=f2.get(obj1);//只有字段对象才能调用字段的方法
System.out.println(o+"!!!");//张三
f4.setAccessible(true);//私有化的属性必须打破私有化权限才可以访问和修改,否则报错IllegalAccessException
Object o2=f4.get(obj1);//获取obj1的age属性的对象
System.out.println(o2);//20
f4.set(obj1, 199);//打破私有化界限后改写obj1的age属性为199
System.out.println(obj1);//199
int ageobj1=f4.getInt(obj1);//如果属性是基本类型可以用这种方法获取和改写
System.out.println(ageobj1);//199
f4.setInt(obj1, 19);//如果属性是基本类型可以用这种方法进行改写
System.out.println(obj1);//张三...19
Method m=c2.getMethod("test",String.class,int.class);//String name,class...para
System.out.println(m);//public void Demo1.Person.test()想要输出完整的方法名格式,分段获取,打印,如下
int mod=m.getModifiers();
String modi=Modifier.toString(mod);//public
String re=m.getReturnType().getName();//void
String name=m.getName();//test
Class[] ty= m.getParameterTypes();//参数类型
System.out.print(modi+" "+re+" "+name+"(");
for (int i = 0; i < ty.length; i++) {
if (i==1) {
System.out.print(ty[i].getName()+"){}");
}else
System.out.print(ty[i].getName()+",");
}
}
}
class Person{
public String name;
private int age;
public Person(){}
public Person(String name,int age){
this.name=name;
this.age=age;
}
private Person(String name){}
@Override
public String toString() {
return name+"..."+age;
}
public void test(String name,int age){
System.out.println("test");
}
}
class student{}
向限定泛型数据类型为Integer的集合中添加字符串元素
这种方式称之为泛型的擦除技术,因为泛型是在编译时就检查错误的,如果写add字符串进去,编译时就会报错,程序不能运行,但是反射是运行时进行操作的,有效的避免了泛型的检查,就可以成功的添加字符串数据进集合了。
import java.lang.reflect.Method;
import java.util.ArrayList;
//向给定元素类型的集合中添加不同类型元素
public class Demo3 {
public static void main(String[] args)throws Exception {
ArrayList<Integer>list=new ArrayList<>();
list.add(123);
System.out.println(list+"----");
Class li=list.getClass();//创建集合的Class对象
/*获取向集合中添加元素的add方法,注意数据类型是Object的,因为ArrayList本身就是可以接纳任何类型的数据,
泛型只是帮助他限定元素类型的,所以这里要写他定义时候的元素类型,否则就会报错,找不到指定方法*/
Method me=li.getMethod("add", Object.class);
/*invoke表示执行此方法,谁调用它它就执行谁,执行后我们要往list集合中添加元素,所以这里的对象我们写list,后面的
数据就写我们要向list中添加的元素即可,这样就完成了向已经限定数据类型的集合中添加别的类型元素的要求*/
me.invoke(list, "abc");
System.out.println(list);
}
}