反射

本文深入探讨Java反射机制,从基础知识开始,详细解释Class类操作、反射调用方法、获取完整类结构等核心概念,并通过实例展示反射在实际开发中的应用,最后讨论反射的进一步应用和安全性考虑。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

------- android培训java培训、期待与您交流! ----------  


第一节:需要掌握的知识点

1.       了解反射的作用

2.       掌握Class类的操作及使用

3.       可以通过反射取得一个类的实例化对象。

4.       可以通过反射取得类的结构,可以通过反射进行属性方法的调用。

5.       通过反射调用类中方法的原理.

6.       工厂设计的改进,重点是掌握其思想.

第二节:反射概述以及反射的具体方法

1.       认识反射:

l 如果要想使用一个类,则必须找到这个类,之后通过此类产生实例化对象,必须先有类,后有对象,如果现在要想通过一个对象找到所在类?

2.       在Object类中有一个方法:

l public  final Class <?>  getClass()

3.       认识Class类:在反射机制中Class类是一个操作的源头,所有的反射操作从此类展开,但是如果要想实例化此类对象可以通过以下三种途径完成。

l 通过Object类中的getClass方法: public  final Class<?> getClass();

l 通过类.class的形式:Person.class

l 通过类的静态方法:

public  static calss<?>  forName(StringclassName) throws ClassNotFoundException

4.       范例:验证 实例化对象.getClass()

publicclass ClassInstance1 {

     publicstaticvoid main(String[] args) {

              Personp = new Person();//实例化对象

              Class<?>c = p.getClass();//class对象实例化

              System.out.println(c.getName());

     }

}

5.       范例:验证 .class

public class ClassInstance2 {

     public static void main(String[] args) {

              Class<?> c= Person.class;

              System.out.println(c.getName());

     }

}

6.       范例:验证 class.forName();

public class ClassInstance3 {

     public static void main(String[] args)throws ClassNotFoundException {

              Class<?> c= Class.forName("Person");

              System.out.println(c.getName());

     }

}

                              虽然有三种方法,但是最常用的是forName()方法,其次就是.class的形式.

第三节:通过Class类实例化对象

1.       在Class的使用中实例化对象是最常用的一种操作,而且所有的框架各个程序的实现原理都是依靠Class类完成的.

2.       实例化无参构造的类(只有一个无参构造函数):

l 根据包.类名称实例化Class对象

l 通过Class类中的以下方法:public  T newInstance()

public class ClassInstance4 {

public static void main(String[] args)throws Exception {

Person p = null;

Class<?> c = null;

c = Class.forName("Person");

p = (Person)c.newInstance();

System.out.println(p);

}

}

3.       实例化没有无参构造的类(调用指定构造):

l 如果要想得到一个类的构造方法,可以使用:

public  Constructor<?>[]  getConstructors()

l 示例:

import java.lang.reflect.Constructor;

public class ClassInstance4 {

         public static void main(String[] args)throws Exception {

                   Person p = null;

                   Class<?> c = null;

                   c = Class.forName("Person");//得到Person

                   Constructor<?> cs[] = c.getConstructors();//得到全部的构造函数    

                   p = (Person)cs[0].newInstance("张三",12);

                   System.out.println(p);

         }

}

 

l 注意:getConstructors()方法只能得到Person类的权限为public的构造方法.

l 为了开发的简便,一定要在类中定义无参的构造方法.

第四节:通过Class类取得完整结构(了解)

1.       通过Class类中的很多方法,可以轻易的取得一个类中定义的全部的构造方法,普通方法,常量,变量等等.

现在假设有如下的类:

package org.lxh.ClassInstance2;

interface Info{

         public static final String AUTHOR ="小七";

         public String getInfo();

         public String say(String name,String content);

         public void sayHello();

}

public class Person implements Info{

         private String name;

         private int age;

         public Person(){}

         public Person(String name){

                   this.name = name;

         }

         public Person(String name,int age) {

                   super();

                   this.name = name;

                   this.age = age;

         }

         public String getName() {

                   return name;

         }

         public void setName(String name) {

                   this.name = name;

         }

         public int getAge() {

                   return age;

         }

         public void setAge(int age) {

                   this.age = age;

         }

         public String toString() {

                   return "姓名:"+this.name+"年龄:"+this.age;

         }

         public String getInfo() {

                   return "Hello World";

         }

         public String say(String name, String content) {

                   return name +"说了" + content;

         }

 

         public void sayHello() {

                   System.out.println("Hello --> "+AUTHOR);    

         }

}

 

2.       取得继承的父类:

l 通过publicClass<? super T> getSuperclass()

package org.lxh.ClassInstance2;

public class getSuperClass {

         public static void main(String[] args)throws ClassNotFoundException {

                   Class<?> c = Class.forName("org.lxh.ClassInstance2.Person");

                   Class<?> sc = c.getSuperclass();

                   System.out.println(sc.getName());

         }

}

 

此时Person类没有继承任何父类就是Object类.

3.       取得实现的全部接口:

l 通过publicClass<?>[] getInterfaces()

package org.lxh.ClassInstance2;

public class getInterfaces {

         public static void main(String[] args)throws ClassNotFoundException {

                   Class<?> c = Class.forName("org.lxh.ClassInstance2.Person");

                   Class<?> inters[] = c.getInterfaces();

                   for(Class<?> inter : inters ){

                            System.out.println(inter.getName());

             }

}

}

 

4.       取得一个类的全部构造方法:

l 之前已经实现了取得全部的构造方法:

package org.lxh.ClassInstance2;

import java.lang.reflect.Constructor;

public class getConstrutors {

         public static void main(String[] args)throws ClassNotFoundException {

                   Class<?> c = Class.forName("org.lxh.ClassInstance2.Person");

                   Constructor<?> cons[] = c.getConstructors();

                   for(Constructor<?> con : cons){

                            System.out.println(con.getName());

                   }

         }

}

5.         package org.lxh.ClassInstance2;

6.         import java.lang.reflect.Constructor;

7.         public class getConstrutors {

8.            public static void main(String[] args)throws ClassNotFoundException {

9.                      Class<?> c = Class.forName("org.lxh.ClassInstance2.Person");

10.                  Constructor<?> cons[] = c.getConstructors();

11.                  for(Constructor<?> con : cons){

12.                           System.out.println(con.getName());

13.                  }

14.        }

15.    }

l 以上确实得到了全部的构造方法,但是有弊病,此时是通过toString方法自动输出的,如果现在直接通过getName方法取得,则发现只能取得构造方法的名字,而访问的修饰符和参数全部不见.所以,如果想要拼凑出完成的构造方法,还需要一下几个方法的支持.

l 得到访问修饰符:publicint getModifers()

l 得到全部参数: TypeVariable<Class<T>>[]getParameterTypes();

package org.lxh.ClassInstance2;

import java.lang.reflect.Constructor;

public class getConstrutors {

         public static void main(String[] args)throws ClassNotFoundException {

                   Class<?> c = Class.forName("org.lxh.ClassInstance2.Person");

                   Constructor<?> cons[] = c.getConstructors();

                   for(Constructor<?> con : cons){

                            //得到修饰符

                            int mod = con.getModifiers();

                            //得到全部的参数

                            Class<?> params[] = con.getParameterTypes();

                            System.out.print(mod+" ");

                            System.out.print(con.getName()+"( ");

                            for(int x=0; x<params.length;x++){

                                     System.out.print(params[x].getName());

                                     if(x<params.length-1)

                                               System.out.print(",  "+params[x].getName());

                            }

                            System.out.println(")");

                   }

         }

}

 

此时的输出结果如下:

1 org.lxh.ClassInstance2.Person( )

1 org.lxh.ClassInstance2.Person( java.lang.String,java.lang.String, int)

1 org.lxh.ClassInstance2.Person( java.lang.String)

从此结果中可以发现,访问修饰符以数字表示,在实际开发中,访问权限都是以数字形式表现出来的,这样程序比较好写.而且对于public  static final是三个值相加的结果,所以如果要想正确的还原一个方法的修饰符,则必须使用一个Modifer的类进行还原.

5.       取得一个类的全部方法:

l  根据: Method[] getMethods()

l 范例:得到Person类的全部方法,(其中包含从Object继承的方法)

package org.lxh.ClassInstance2;

import java.lang.reflect.Method;

public class getMethods {

 

         public static void main(String[] args)throws ClassNotFoundException {

                   Class<?> c = Class.forName("org.lxh.ClassInstance2.Person");

                   //取得全部的方法

                   Method ms[] = c.getMethods();

                   for(Method m : ms){

                            System.out.println(m);

                   }

}

 

l  取得一个返回值类型: Class<?> getReturnType()

l  取得方法的全部参数: Class<?>[] getParameterTypes()

l  取得方法的抛出异常:Class<?>[] getExceptionTypes()

 

package org.lxh.ClassInstance2;

import java.lang.reflect.Method;

import java.lang.reflect.Modifier;

public class getMethods2 {

         public static void main(String[] args)throws ClassNotFoundException {

                   Class<?> c = Class.forName("org.lxh.ClassInstance2.Person");

                   //取得全部的方法

                   Method ms[] = c.getMethods();

                   for(Method m : ms){

                            //取得访问权限

                            String mod = Modifier.toString(m.getModifiers());

                            //取得方法名称

                            String metName = m.getName();

                            //取得返回值类型

                            Class<?> ret = m.getReturnType();

                            //取得全部的异常

                            Class<?> exc[] = m.getExceptionTypes();

                            //取得全部参数

                            Class<?> params[] = m.getParameterTypes();

                            System.out.print(mod+" ");

                            System.out.print(ret+" ");

                            System.out.print(metName);

                            System.out.print("( ");

                            for(int x=0; x<params.length;x++){

                                     System.out.print(params[x].getName());

                                     if(x<params.length-1)

                                               System.out.print(", "+params[x].getName());

                            }

                            System.out.print(" ) ");

                            if(exc.length>0){

                                     System.out.print("throws ");

                                    

                                     for(int x=0; x<exc.length;x++){

                                               System.out.print(exc[x].getName());

                                               if(x<exc.length-1)

                                                        System.out.print(", "+exc[x].getName());

                                     }

                            }       

                           

                            System.out.println();                

                   }

         }

}

4.          

 

6.       取得一个类的全部属性:

l  得到继承而来的公共属性: Field[] getFields()

l  得到自定义的属性: Field[] getDeclaredFields()

package org.lxh.ClassInstance2;

 

import java.lang.reflect.Field;

import java.lang.reflect.Modifier;

 

public class getFields {

         public static void main(String[] args)throws ClassNotFoundException {

                   Class<?> c = Class.forName("org.lxh.ClassInstance2.Person");

                   {

                            Field fields[] = c.getFields();

                            for(Field field : fields){

                                     String mod = Modifier.toString(field.getModifiers());

                                     Class<?> type = field.getType();                        

                                     System.out.print(mod+" ");

                                     System.out.print(type.getName()+" ");

                                     System.out.println(field.getName()+" ");

                  

                            }

                   }

                   {

                            Field fields[] = c.getDeclaredFields();

                            for(Field field : fields){

                                     String mod = Modifier.toString(field.getModifiers());

                                     Class<?> type = field.getType();                        

                                     System.out.print(mod+" ");

                                     System.out.print(type.getName()+" ");

                                     System.out.println(field.getName()+" ");

                  

                            }

                   }

 

         }

 

}

                                                                 以上就是开发工具的核心原理,但是此种操作在实际开发中很少使用,只做了解.

 

第五节:反射的进一步应用(理解)

                              在正常情况下士通过对象.方法()的形式调用类中的指定方法,那么实际上可以通过反射机制完成类中方法的调用.

                               在Class类中使用以下的方法取得要调用方法的Method对象:

l  Method getMethod(String name,Class<?>... parameterTypes)

l  之所以要传可变参数,主要是一个方法在使用的时候可能存在多个调用参数,所以在此处必须指定好参数的类型.

l  取得Method对象以后通过如下方法进行调用:

l  Object invoke(Object obj, Object... args)

package org.lxh.ClassInstance3;

import java.lang.reflect.*;

public class InvokeMethod {

         public static void main(String[] args)throws Exception{

                   Class<?> c = Class.forName("org.lxh.ClassInstance2.Person");

                   //调用Person类中sayHello方法

                   Method m = c.getMethod("sayHello");

                   //invoke方法中必须有实例化的对象

                   m.invoke(c.newInstance());             

         }

}

以上的调用只是调用了类中没有参数,没有返回值的方法,下面看一下调用Person类中的getInfo()方法,此方法存在返回值.

package org.lxh.ClassInstance3;

import java.lang.reflect.*;

public class InvokeMethod2 {

         public static void main(String[] args)throws Exception{

                   Class<?> c = Class.forName("org.lxh.ClassInstance2.Person");

                   //调用Person类中getInfo方法

                   Method m = c.getMethod("getInfo");

                   //invoke方法中必须有实例化的对象

                   String str = (String)m.invoke(c.newInstance());               

                   System.out.print(str);

         }

}

继续使用以上操作,调用Person类中的say方法,此方法有返回值,有参数类型.

package org.lxh.ClassInstance3;

import java.lang.reflect.*;

public class InvokeMethod3 {

         public static void main(String[] args)throws Exception{

                   Class<?> c = Class.forName("org.lxh.ClassInstance2.Person");

                   //调用Person类中getInfo方法

                   Method m = c.getMethod("say",String.class,String.class);

                   //invoke方法中必须有实例化的对象

                   String str = (String)m.invoke(c.newInstance(),"7",",要百折不挠");         

                   System.out.print(str);

         }

}

 

第六节:通过反射调用类中的getter和sertter

 

1.       类中在定义的时候就已经明确的声明了,必须存在getter和sertter方法,之所以这样定义主要的目的是为了反射的操作.

package org.lxh.ClassInstance3;

import java.lang.reflect.*;

public class InvokeMethod3 {

         public static void main(String[] args)throws Exception{

                   Class<?> c = Class.forName("org.lxh.ClassInstance2.Person");

                   Object o = c.newInstance();             

                   set("name","7",o,String.class);

                   set("age",30,o,int.class);

                   String name = (String)get("name",o);

                   int age = (Integer)get("age",o);

                   System.out.println(name+" "+age);

         }

         /**

          * @param name属性名称

          * @param value属性值

          * @param obj要操作的对象

          * @throws NoSuchMethodException

          * @throws SecurityException

          */

         public static void set(String name, Object value, Object obj,Class<?> type)

                            throws Exception {

                   // 属性名称首字母应该大写

                   Method met = obj.getClass().getMethod("set" +initStr(name),

                                     type);

                   // 调用方法

                   met.invoke(obj, value);

         }

         public static Object get(String name,Object obj) throws Exception{

                   Method met = obj.getClass().getMethod("get"+initStr(name));

                   Object value = met.invoke(obj);

                   return value;

         }

         public static String initStr(String name) {

                   StringBuilder sb = new StringBuilder();

                   sb.append(name.substring(0, 1).toUpperCase()).append(name.substring(1));

                   return sb.toString();

         }

}

                                                 以后的学习中会经常看见这种类似的操作原理出现,本身在实际开发中开发者一般不会去直接编写此代码.

第七节:通过反射直接操作属性

1.       在反射机制中,不光可以操作方法,还可以直接操作类中的属性.

package org.lxh.ClassInstance3;

import java.lang.reflect.*;

public class InvokeMethod4 {

         public static void main(String[] args)throws Exception{

                   Class<?> c = Class.forName("org.lxh.ClassInstance2.Person");

                   Object obj = c.newInstance();

                   Field nameField = c.getDeclaredField("name");

                   nameField.setAccessible(true);

                   nameField.set(obj, "7");

                   System.out.println(nameField.get(obj));                  

         }

}

从以上程序中可以发现,属性也是可以通过代码设置访问权限的,但是以上的操作基本上是不建议的,以为直接操作属性不安全.

第八节:反射的进一步应用

反射的应用必须结合到一个完整的设计模式之中.例如工厂设计模式.

package org.factoryDemo1;

interface Fruit{

         public void eat();

}

class Apple implements Fruit{

                   public void eat(){

                            System.out.println("吃苹果");

                   }       

}

class Orange implements Fruit{

         public void eat() {

                   System.out.println("吃橘子");       

         }       

}

class Factory{

         public static Fruit getInstance(String className){

                   Fruit f = null;

                   if(className.equals("apple")){

                            f = new Apple();

                   }

                   if(className.equals("orange")){

                            f = new Orange();

                   }

                   return f;

         }

}

public class FactoryDemo1 {

         public static void main(String[] args) {  

                   Fruit f = Factory.getInstance("apple");

                   f.eat();

         }

}

                  以上程序实现了工厂操作,但是否存在问题?所有的问题集中在工厂操作中,因为每次只要一增加子类,则必须修改工厂,那么这个时候就可以更具反射机完成.

通过Class类修改:

package org.factoryDemo1;

interface Fruit{

         public void eat();

}

class Apple implements Fruit{

                   public void eat(){

                            System.out.println("吃苹果");

                   }       

}

class Orange implements Fruit{

         public void eat() {

                   System.out.println("吃橘子");       

         }       

}

class Factory{

         public static Fruit getInstance(String className){

                   Fruit f = null;

                   try {

                            f = (Fruit) Class.forName(className).newInstance();

                   } catch (Exception e) {}

                   return f;

         }

}

public class FactoryDemo1 {

         public static void main(String[] args) {  

                   Fruit f = Factory.getInstance("org.factoryDemo1.Apple");

                   f.eat();

         }

}

                                    在以上的操作中工厂类是完全不用修改的,但是也存在问题.每次在操作的时候后都必须输入很长的”包.类”名称,所以使用时很不方便.

                   最好的做法是通过一个别名表示完整的”包.类”名称,而且在类增加的时候别名也可以自动增加,所以如果要想完成这样的操作,则可以使用属性类配置.

package org.factoryDemo2;

import java.io.*;

import java.util.*;

interface Fruit{

         public void eat();

}

class Apple implements Fruit{

                   public void eat(){

                            System.out.println("吃苹果");

                   }       

}

class Orange implements Fruit{

         public void eat() {

                   System.out.println("吃橘子");       

         }       

}

class PropertiesOperate{//属性操作类

         private Propertiespro= null;

         private File  f = new File("D:"+File.separator+"Fruit.Properties");

         public PropertiesOperate(){

                   this.pro =new Properties();

                   if(f.exists()){//文件存在

                            try {

                                     //读取

                                     pro.loadFromXML(new FileInputStream(f));

                            } catch (Exception e) {

                                     e.printStackTrace();

                            }

                   }else{

                            this.save();

                   }       

         }

         private void save(){

                   this.pro.setProperty("apple","org.factoryDemo2.Apple");

                   this.pro.setProperty("orange","org.factoryDemo2.Orange");

                   try {

                            this.pro.storeToXML(new FileOutputStream(this.f),"Fruit");

                   } catch (Exception e) {              

                            e.printStackTrace();

                   }

         }

         public Properties getProperties(){

                   returnthis.pro;

         }

}

class Factory{

         public static Fruit getInstance(String className){

                   Fruit f = null;

                   try {

                            f = (Fruit) Class.forName(className).newInstance();

                   } catch (Exception e) {}

                   return f;

         }

}

public class FactoryDemo1 {

         public static void main(String[] args) {

                   Properties pro = new PropertiesOperate().getProperties();

                   Fruit f = Factory.getInstance(pro.getProperty("apple"));                

                   f.eat();

         }

}

                   从以上的操作代码中可以发现,程序通过一个配置文件,可以控制程序的执行,也就是达到了配置文件与程序相分离的目的.

                   那么这种设计思想一直到今天还在使用者,包括各个开发框架:Struts,Spring,Hibernate,而最新的程序设计理论,是将注释写入到代码之中,让注释起到程序控制的作用,那么要想实现此操作,则要使用Annotation.

 

------- android培训java培训、期待与您交流! ----------

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值