反射的概述(Java Reflection)
Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像以面镜子,透过镜子看到类的结构,所以我们形象的称之为:反射。
正常方式:引入需要的“包类”名称->通过new实例化->取得实例化对象
反射方式:实例化对象->getClass()方法->得到完整的“包类”名称
动态语言vs静态语言
1.动态语言
是一类在运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化。通俗点说就是在运行时代码可以根据某些条件改变自身结构。
主要动态语言:Object-c、C#、JavaScript、PHP、Python、Erlang
2.静态语言
与动态语言相对应,运行时结构不可变的语言就是静态语言如java、c、c++
Java不是动态语言,但Java可以称之为“准动态语言”,即Java有一定的动态性,我们可以利用反射机制、字节码操作获得类似动态语言的特性。Java的动态性让编程的适合更加灵活!
反射机制的研究及应用
Java反射机制提供的功能:
在运行时判断任意一个对象所属的类
在运行是构造任意一个类的对象
在运行时判断任意一个类所具有的成员变量和方法
在运行时获取泛型的信息
在运行时调用任意一个对象的成员变量和方法
在运行时处理注解
生成动态代理
反射相关的主要API
java.lang.Class:代表一个类
java.lang.reflect.Method:代表类的方法
java.lang.reflect.Field:代表类的成员变量
java.lang.reflect.Constructor:代表类的构造器
//实例:反射之后,对于Person的操作
@Test
public void test2() throws Exception{
Class clazz = Person.class;
//1.通过反射,创建Person类的对象
Constructor cons = clazz.getConstructor(String.class,int.class);
Object obj = cons.newInstance("Tom",12);
Person p = (Pserson)obj;
System.out.println(obj.toString());
//2.通过反射,调用对象指定的属性、方法
//调用属性
Field age = clazz.getDeclaredField("age");
age.set(p,10);
System.out.println(p.toString());
//调用方法
Method show = clazz.getDeclaredMethod("show");
show.invoke(p);
//通过反射,可以调用Person类的私有结构的。比如:私有的构造器、方法、属性
Constructor cons1 = clazz.getDeclaredConstructor(String.class);
cons1.setAccessiable(true);
Person p1 = (Person)cons1.newInstance("Jerry");
System.out.println(p1);
Field name = clazz.getDeclaredField("name");
name.setAccessiable(true);
name.set(p1,"Hanmeimei");
//调用私有方法
Method showNation = clazz.getDeclaredMethod("showNation",String.class);
showNation.setAccessible(true);
String nation = (String)showNation.invoke(p1,"中国");//相当于P1.showNation("中国")
System.out.println(nation);
}
反射机制与面向对象中的封装性是否是矛盾的,如何看待两个技术。
不矛盾。反射的特性:动态性。反射表现为是否去掉,封装性表现为是否可掉
关于Java.lang.Class的理解:
1.类加载过程:
程序经过javac.exe命令以后,会生成一个活多个字节码文件(.class)结尾,接着我们使用java.exe命令对某个字节码文件进行解释运行。相当于将某个字节码文件加载到内存中。此过程就称为类的加载。加载内存中的类,就称为运行时类,此运行时类,就作为Class的一个实例
2.换句话说,Class的实例就对应着一个运行时类。
3.加载到内存中的运行时类,会缓存一定的时间,在此时间之内,我们可以通过不同的方式来获取此运行时类。
//如何获取Class实例的方式:(前三种方式需要掌握)
//方式一:调用运行时类的属性:.class
Class<Person> clazz1 = Person.class;
System.out.println(clazz1);
//方式二:通过运行时类的对象,调用getClass()
Person p1 = new Person();
Class clazz2 = p1.getClass();
//方式三:调用Class的静态方法:forName(String classPath)
Class clazz3 = Class.forName("com.java.Person");
System.out.println(clazz3);
System.out.println(clazz1==clazz2);//true
System.out.println(clazz1==clazz3);//true
//方式四:使用类的加载器:Classloader
ClassLoader classloader = 当前类.class.getClassLoader();
Class clazz4 = classLoader.loadClass("com.java.Person");
System.out.println(clazz4);
System.out.println(clazz1==clazz4);
类加载器的作用:
类加载的作用:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后在队中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口
类缓存:标准的JavaSE类加载器可以按要求查找类,但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间。不过JVM垃圾回收机制可以回收这些Class对象。
Properties pros = new Properties();
//读取配置文件方式一:当前module下。
//FileInputStream fis = new FileInputStream("jdbc.properties");
//pros.load(fis);
//读取配置方式二:使用ClassLoader 当前module的src下
ClassLoader classLoader= 当前类.class.getClassLoder();
InputStream is = classLoader.getResourceAsStream("jdbc.properties");
String user = pros.getProperty("user");
System.out.println("user="+user);
创建运行时类的对象
@Test
public void test1(){
Class clazz = Person.classs;
//newInstance():调用此方法,创建对应的运行时类的对象。内部调用了运行时类的空参构造器
//要想此方法正常的创建运行时类的对象 要求:
//1.运行时类必须提供空参的构造器
//2.空参的构造器的访问权限得狗。通常为public
//在javabean中要求提供一个public的空参构造器。原因:
//1.便于通过反射,创建运行时类的对象
//2.便于子类继承此运行时类时,默认调用super()时,保证此类有此构造器
Pserson obj = (Pserson)clazz.newInstance();
System.out.print(obj);
}
@Test
public void test2(){
int num = new Random().nextInt(3);//0,1,2
switch(num){
case 0:
classPath="java.util.Date";
break;
case 1:
classPath="java.lang.Object"
break;
class 2:
classPath="com.java.Person";
break;
}
Object obj = getInstance(classPath);
}
//创建一个指定类的对象
//classPath:指定类的全类名
public Object getInstance(String classPath)throws Exception(){
Class clazz = Class.froName(classPath);
return clazz.newInstance();
}
获取当前运行时类的属性结构
@Test
public void test1(){
Class clazz = Person.class();
//获取属性结构
//getFields():获取当前运行时类及其父类中声明为public访问权限的属性
Field[] fields = clazz.getFields();
for(Filed f : fields){
System.out.println(f);
}
//getDeclaredFields():获取当前运行时类中声明的所有属性。(不包含父类中声明的属性)
Field[] declaredFields = clazz.getDeclaredFields();
for(Field f :declaredFields){
System.out.println(f);
}
}
//权限修饰符 数据类型 变量名
@Test void test2(){
Class clazz = Person.class;
Field[] declaredFields=clazz.getDeclaredFields();
for(Field f : declaredFields){
//1.权限修饰符
int modifier = f.getModifiers();
//将权限修饰符站视出来
System.out.print(Modifier.toString(modifier)+"\t");
//2.数据类型
Class type = f.getType();
System.out.print(type.getName()+"\t");
//3.变量名
String fName=f.getName();
System.out.print(fName);
}
}
获取运行时类的方法结构
Class clazz = Person.class();
//getMethods():获取当前运行时类及其所有父类中生米给你为public 权限的方法
Method[] methods = clazz.getMethods();
for(Method m : methods){
System.out.println(m);
}
//getDeclaredMethods():获取当前运行时类中声明的所有方法(不包含父类的方法)
Method[] declaredMethods = clazz.getDeclaredMethods();
for(Method m : declaredMethods){
System.out.println(m);
}
//@Xxx
//权限修饰符 返回值类型 方法名(参数类型1 形参名1,...)throws XxxException{}
//1.获取方法声明的注解
Class clazz = Person.class;
Method[] declaredMethods = clazz.getDeclaredMethods();
for(Method m : declaredMethods){
//1.获取方法声明的注解
Annotation[] annos = m.getAnnotations();
for(Annotation a: annos){
System.out.println(a);
}
//2.权限修饰符
System.out.print(Modifier.toString(m.getModifiers())+"\t");
//3.返回值类型
System.out.print(m.getReturnType().getName()+"\t");
//4.方法名
System.out.print(m.getName());
System.out.print("(");
//5.形参列表
Class[] parameterTypes = m.getParamterTypes();
if(!(parameterTypes==null && parameterTypes.length==0)){
for(int i=0;i<paramterTypes.length;i++){
if(i==parameterTypes.length-1){
System.out.print(parameterTypes[i].getName()+" args_"+i);
break;
}else{
System.out.print(parameterTypes[i].getName()+" args_"+i+",");
}
}
}
System.out.print(")");
//6.抛出的异常
Class[] exceptionTypes = m.getExceptionTypes();
if(exceptionTypes.length>0){
System.out.print("throws ");
for(int i=0;i<exceptionTypes.lengvth;i++){
if(i==exceptionTypes.length-1){
System.out.println(exceptionTypes[i].getName());
break();
}else{
System.out.println(exceptionTypes[i].getName()+",");
}
}
}
System.out.println();
}
获取其他
//获取构造器
@Test
public void test1(){
Class clazz = Person.class();
//getConstructors():获取当前运行时类中声明为public的构造器
Constructor[] constructors = clazz.getConstuctors();
for(Constructor c : constructors){
System.out.println(c);
}
System.out.println();
//getDeclaredConstructors():获取当前运行时类中声明的所有的构造器
Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
for(Constructor c: declaredConstructors){
System.out.println(c);
}
//获取运行时父类
Class superclass= clazz.getSuperclass();
System.out.println(superclass);
//获取运行时类带泛型的父类的泛型
Type genericSuperclass = clazz.getGenericSuperclass();
ParameterizedType paramType = (ParameterizedType)genericSuperclass;
//获取泛型类型
Type[] actulaTypeArguments = paramType.getActualTypeArguments();
System.out.println(genericSuperclass[0].getTypeName());
//获取运行时类实现的接口
Class[] interfaces = clazz.getInterfaces();
for(Class c : interfaces){
System.out.println(c);
}
System.out.println();
//获取运行时父类实现的接口
Class[] interfaces1 = clazz.getSuperclass().getInterfaces();
for(Class c: interfaces){
System.out.println(c);
}
//获取运行时类所在的包
Package pack = clazz.getPackage();
System.out.println(pack);
//或许运行时类所声明的注解
Annotation[] annotations = clazz.getAnnotations();
for(Annotation annos:annotations){
System.out.println(annos);
}
}
调用运行时类的指定结构:属性、方法、构造器
@Test
public void testField()throws Exception{
Class clazz = Person.class;
//创建运行时类的对象
Person p = (Person) clazz.newInstance();
//获取指定属性:要求运行时中类中的属性声明为public
//通常不用此方法
Field id = clazz.getField("id");
//设置当前属性的值
//ser():参数1:指明设置那个对象的属性 参数2:将此属性值设置为多少
id.set(p,1001);
//获取当前属性的值
//get():参数1:Huoqu那个对线的当前属性值
int pId=(int)id.get(p);
}
@Test
public void testField1() throws Exception{
Class clazz = Person.class;
//创建运行时类的对线
Person p = (Person)clazz.newInstance();
//getDeclaredField(Streingf filedName):获取运行时类中指定变量名的属性
Filed name = clazz.getDeclaredField("name");
//2.保证当前属性是可以访问的
name.setAccessiable(true);
name.set(p,"Tom");
System.out.println(name.get(p));
}
@Test
public void testMethod() throws Exception{
//如何获取运行中指定的方法 --需要掌握
Class clazz = Person.class;
//创建运行时类的对象
Person p = (Person)clazz.newInstance();
//1.获取指定的某个方法,getDeclaredMethod():参数1:指明获取的方法的名称 参数2:指明获取的方法的形参列表
Method show = clazz.getDeclaredMethod("show",String.class);
show.setAccessiable(true);
//invoke():参数1:方法的调用者,参数2:给方法形参赋值的实参,invoke()的返回值几位对应类中调用的方法的返回值
Object returnValue =show.invoke(p,"CHN");
System.out.println(returnValue);
//如何调用静态方法
Method showDesc = clazz.getDeclaredMethod("showDesc");
showDesc.setAccessible(true);
//如果调用的运行时类中没有返回值,此时invoke()返回null
Object returnVal = showDesc.invoke(Person.class);
//Object returnVal = showDesc.invoke(null);
System.out.println(retrurn Val);//null
}
//如何调用运行时类中的指定的构造器
@Test
public void testConstructor()throws Exception{
Class clazz = Person.class;
//1.获取指定的构造器
//getDeclaredConstructor():参数:指明构造器的参数列表
Constructor constructor = clazz.getDeclaredConstructor(String.class);
//2.保证此构造器是可访问的
constructor.setAccessible(true);
//3.调用此构造器创建运行时类的对象
Person per = (Person)constructor.newInstance("Tom");
System.out.println(per);
}
标签:反射,System,clazz,Person,时类,Class,out
From: https://www.cnblogs.com/rhy2103/p/17433254.html