java反射机制

一丶反射的理解。

在正常情况下,我们可以通过一个封装好的类来进行一个对象的实例化操作。

反过来,我们通过实例化好的对象来【取得类的完整的信息】,这就是反射。

通过反射,我们可以取得反向的信息,取得一个完整对象的所属于【包.类】名称。

二丶反射的结果—Class类。

Class类是一切反射的根源,此类的定义如下:

pubilc final class Class<T> extends Object
implements Serializable,GenericDeclaration,Type,AnnotatedElement

此类是一个最终类,是不允许有子类的,而且有泛型,证明要指明泛型。

可以通过以下的三种方法进行实例化操作:

1、Object类中的getClass()方法。

package org.study.RebortChao;

public class ClassDemo{
    public static void main(String[] args){
        String str = "HELLO";
        Class<?> cls = str.getClass();
        System.out.println(cls.getName());
    }
}

2、通过【.class】完成。

package org.study.RebortChao;

public class ClassDemo01{
    public static void main(String[] args){
        Class<?> cls = String.class;
        System.out.println(cls.getName());
    }
}

3、通过Class类内部的forName()方法完成。 重点内容

package org.study.RebortChao;

public class ClassDemo02{
    public static void main(String[] args){
        Class<?> cls = Class.forName("java.lang.String");
        System.out.println(cls.getName());
    }
}

三丶使用Class类进行对象的实例化。

  • Class的最大好处是通过【包.类名称】进行对象的实例化。

1、调用无参构造进行实例化。——这里的类构造器是没有参数的。

正常情况下,必须通过关键字new进行对象的实例化操作。
但是,
现在有了Class类之后,直接通过反射进行对象的实例化操作。

package org.study.RebortChao;

public class ClassObjectDemo01{
    public static void main(String[] args) throws Exception{
        Class<?> cls = Class.forName("类全名");
        类名称 变量名称 = (类名称) cls.newInstance();
        /*set类的一系列操作。*/
        System.out.println(变量名称);
    }
}

这里的【类全名】获取方法:【右击类名称】-->【copy qualified name】

另一种比较方便的形式是(无参构造的最常用形式):

无参构造的最常用形式。

package org.study.RebortChao;

public class ClassObjectDemo02{
    public static void main(String[] args) throws Exception{
        类名称 变量名称 = (类名称) cls.forName("类全名").newInstance();
        /*set类的一系列操作。*/
        System.out.println(变量名称);
    }
}

以上的两种操作方法都是只适用于存在无参构造器的方法。

2、调用含参构造进行实例化。

这个时候必须按照如下步骤完成:

  • ①、还是实例化Class类的对象。

  • ②、通过Class类找到此类中的构造方法。

  • ③、通过构造方法明确的给出需要的参数,并进行对象的实例化操作。

Class通过以下的方法取得一个类中的全部构造:

`java.lang.reflect.Constructor`
  • ①、一个类中存多个构造方法,所以将返回一个Constructor的对象数组。

  • ②、此方法接受的一个可变参数,在调用以上方法的时候,需要指明参数的顺序及个数。

范例:通过Constructor进行对象的实例化操作。

package org.study.RebortChao;

import java.lang.reflect.Constructor;

public class ClassObjectDemo03{
    public static void main(String[] args) throws Exception{
        Class<?> cls = Class.forName("类全名");
        Constructor<?>[] con=cls.getConstructor();
        //得到全部构造。以数组的形式保存。
        类名称 变量名称=(类名称) con[0].newInstance(对参数的操作);
        System.out.println(变量名称);
    }
}

明显的是比第一种的方法复杂,所以最好的建议是【类中保留无参构造方法】。

总结:对象实例化方法。

  • 1、通过关键字new。耦合度加深,中间加入了工厂设计,来解决问题。
  • 2、clone。对象的克隆。
  • 3、通过Class类,进行反射加载实例化。(最方便,通过传入完整的【包。类】就可以。)

  • 4、引用传递也是一种。
    面向对象基础中有引用传递的信息

四丶全面的了解反射。

反射机制的最大好处就是可以通过一个Class类取得一个类中的完整信息。

  • 完整信息包括:【继承的父类】、类中所有的【方法】和【属性】etc...

✎_取得类所在的包:

package org.study.RebortChao;

public class RefleDemo{
    public static void main(String[] args) throws Exception{
        Class<?> cls = Class.forName("类全名");
        package pack = cls.getPackage();//取得包名
        System.out.println(pack);
    }
}

✎_取得类中的全部方法:

package org.study.RebortChao;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

public class RefleDemo01{
    public static void main(String[] args) throws Exception{
        Class<?> cls = Class.forName("类全名");
        Method met[]= cls.getMethod(); //获取全部的方法
        for(int x=0;x<met.length; x++){
            int mod = met[x].getModifiers();//获取方法的修饰符
            System.out.printin(Modifier.toString(mod)+"、");
            /*toString()传入修饰符的数字,并转换为修饰符描述。*/
            String ret=met[x].getReturnType().getName();//得到返回值的类型
            System.out.println(ret+"、");

        Class<?> param[]=met[x].getParameterTypes();//取得全部参数类型
            for(int y=0;y<param.length;y++){
                System.out.println(param[y].getName()+"arg-"+y);
                if(y<param.length-1){
                    System.out.println("、");
                }
            }
        System.out.println(")");

        Class<?> exp[]=met[x].getExceptionTypes();//取得全部的异常
            if(exp.length > 0){
                System.out.println("throws ");
                for(int y=0;y<exp.length;y++){
                    System.out.println(exp[y].getName());
                }
                if(y<exp.length-1){
                    System.out.println("、");
                }
            }
        }
        System.out.println(" ");
        }
    }
}

以上程序中用到许多常用的操作:

  • Method met[]=cls.getMethod();以对象数组的形式返回得到的全部方法。

  • met[x].getModifiers();得到全部的修饰符。

    • 一般讲的修饰符是public、private之类的,它们可以按照一定的顺序相加,所以返回的是【整型】。

    • 【整型的修饰符通过Modifier还原】Modifier.toString(mod)

  • met[x].getReturnType().getName();得到返回值类型。

  • met[x].getParameterTypes();得到全部的参数类型。

  • met[x].getExceptionTypes();得到全部的异常。

  • Modifier.toString(mod)传入修饰符的数字,并转换为修饰符描述。

✎_取得类中的全部属性:

package org.study.RebortChao;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

public class RefleDemo02{
    public static void main(String[] args) throws Exception{
        Class<?> cls = Class.forName("类全名");
        {
            Field field[]=cls.getFields();
            /*取得全部属性,这里的属性是继承来的属性。*/
            for(int x=0; x<field.length; x++){
                int mod=field[x].getModifiers();
                System.out.println(Modifier.toString(mod)+" ");
                //还原修饰符
                System.out.println(
                            field[x].getType().getName()+" ";
                System.out.println(field[x].getName()+" ";             
            }
        }

        {
            Field field[]=cls.getDeclaredFields();
            /*取得全部属性,是本类中的属性*/
            for(int x=0; x<field.length; x++){
                int mod=field[x].getModifiers();
                System.out.println(Modifier.toString(mod)+" ");
                //还原修饰符
                System.out.println(
                            field[x].getType().getName()+" ";
                            /*取得属性的类型的名称*/
                System.out.println(field[x].getName()+" ";
                /*取得属性的名称*/             
            }
        }
    }
}

五丶通过反射操作类。

1、调用类中的方法。
正常的情况下,通过【对象.方法()】进行方法调用的。
那么,通过如下操作使用反射完成同样的操作:

  • getMethod()取得类中到的一个方法。

  • Object invoke()调用类中的方法,传入实例化对象,以及具体的参数内容。

调用类中【没有参数】的方法:

package org.study.RebortChao;

import java.lang.reflect.Method;

public class MethodInvokeDemo{
    public static void main(String[] args) throws Exception{
        Class<?> cls=Class.forName(类名称);
        Method met=cls.getMethod("无参的方法");//调用无参的方法
        met.invoke(cls.newInstance());//调用类中的方法
    }
}
/*由于没有实例化对象,所以此时可以调用,都是通过Object完成的。*/

调用类中【有参数、有返回值】的方法

package org.study.RebortChao;

import java.lang.reflect.Method;

public class MethodInvokeDemo01{
    public static void main(String[] args) throws Exception{
        Class<?> cls=Class.forName("类名称");
        Method met=cls.getMethod("有参有返回
                值的方法",参数类型1.class,参数类型2.class,...);
        /*调用有参有返回值的方法,还需要把传参数类型*/
        Object obj=met.invoke(cls.newInstance()
                        ,传参1,传参2,...);/*调用类中的方法*/
        System.out.println(obj);
    }
}

2、调用setter和getter

我们要求类中的所有属性都要封装,这样就可以使用反射机制进行方法的自动调用,只需要传入属性的名称就可以。

package org.study.RebortChao;

import java.lang.reflect.Method;

public class MethodInvokeDemo02{
    public static void main(String[] args) throws Exception{
        Class<?> cls=Class.forName("类名称");
        Object obj=cls.newInstance();
        setter(obj,"name","张三",String.class);
        setter(obj,"age",20,,int.class);
        getter(obj,"name");
        getter(obj,"age");
    }
    public static void setter(Object inst,String attr,Object value,Class<?> valType) throws Exception{
        Method met=inst.getClass().getMethod("set"+initCap(attr)+valType);
        met.invoke(inst,value);
    }

    public static void getter(Object inst,String attr) throws Exception{
        Method met=inst.getClass().getMethod("get"+initCap(attr));
        Object re=met.invoke(inst);
        System.out.println(re);
    }

    public static String initCap(String att){
        StringBuffer buf = new StringBuffer();
        buf.append(att.substring(0,1).toUpperCase()).append(att.substring(1));
        return buf.toString();
    }
}
/*开源框架中所采用的核心设计思想。这里只需要了解。*/

3、直接调用属性。

用到了java.lang.reflect.Field类

package org.study.RebortChao;

import java.lang.reflect.Field;

public class FieldObjectDemo{
    public static void main(String[] args){
        Class<?> cls = Class.forName("类全名");
            Object obj = cls.newInstance();
            Field name = cls.getDeclaredField("name");
            Field age = cls.getDeclaredField("age");
            name.setAccessible(true);//取消了private
            age.setAccessible(true);//取消了private
            name.set(obj,"张三");
            age.set(obj,20);
            System.out.println(name.get(obj));
            System.out.println(age.get(obj));
    }
}
/*一切都是以Object为操作的标准。*/

六丶在工厂设计中应用反射机制。

1、工厂设计。
90%的情况下,只要程序出现了接口,就必须通过工厂进行对象的【解耦合操作】。

package org.study.RebortChao;
interface IFruit{
    public void eat();
}
class Apple implements IFruit{
    pubnlic void eat(){
        System.out.printin("Eatting Apple !"); 
    }
}
class Orange implements IFruit{
    pubnlic void eat(){
        System.out.printin("Eatting Orange !"); 
    }
}
class Factory{
    public static IFruit getInstance(String className){
         IFruit fruit = null ;
         if("Apple".equal(className)){
             fruit = new Apple(); 
         }
         if("Orange".equal(className)){
             fruit = new orange(); 
         }
         return fruit;
    }
}
public class Factory01{
    public static void main(String[] args){
        IFruit f = Factory.getInstance("Apple");
        //这个的Apple是一个【类名称】。
        f.eat(); 
    }
}
/*基本上完成了一个工厂设计模式。*/

2、通过反射修改工厂。

依照之前的做法,每次扩充子类之后都要修改工厂。

【将反射机制应用在工厂之中】,则在子类扩充的时候就没有必要去修改工厂了。

package org.study.RebortChao;
interface IFruit{
    public void eat();
}
class Apple implements IFruit{
    pubnlic void eat(){
        System.out.printin("Eatting Apple !"); 
    }
}
class Orange implements IFruit{
    pubnlic void eat(){
        System.out.printin("Eatting Orange !"); 
    }
}
class Factory{
    public static IFruit getInstance(String className){
         IFruit fruit = null ;
         try{
             fruit=(IFruit) Class.forName(className).newInstance();
         }catch(InstantiationException e){
             e.printStackTrace();
         }catch(IllegalAccessException e){
             e.printStackTrace();
         }catch(ClassNotFoundException e){
             e.printStackTrace();
         }
         return fruit;
    }
}
public class Factory02{
    public static void main(String[] args){
        IFruit f = Factory.getInstance("org.study.RebortChao.Apple");
        //这里需要传一个完整的【包.类名称】。
        f.eat(); 
    }
}
/*如果程序突然修改了这个类所在的包,则程序就非常麻烦了。
此时可以使用属性文件完成。*/

3、通过属性,进行子类的配置。

package org.study.RebortChao;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.InvalidPropertiesFormatException;
import java.util.Properties;
interface IFruit{
    public void eat();
}
class Apple implements IFruit{
    pubnlic void eat(){
        System.out.printin("Eatting Apple !"); 
    }
}
class Orange implements IFruit{
    pubnlic void eat(){
        System.out.printin("Eatting Orange !"); 
    }
}
class PropertiesOperate{ //属性操作
    private Properties pro = null;
    private PropertiesOperate(){
        this.pro=new Properties();
        File f=new File("存放的磁盘位      置"+File.separator+"fruit.xml");
        if(f.exists()){
             try{
                 this.pro.loadFromXML(new FileInputStream(f));
             }catch(InstantiationException e){
                 e.printStackTrace();
             }catch(IllegalAccessException e){
                 e.printStackTrace();
             }catch(ClassNotFoundException e){
                 e.printStackTrace();
             }
        }else{
            this.pro.setProperty("fruit","org.study.RebortChao.Apple");
            try{
                this.pro.storeToXml(new FileOutputStream(f),"fruit Info");
            }catch(FileNotFoundException e){
                e.printStrackTrace();
            }catch(IOException e){
                e.printStrackTrace();
            }
        }   
    }
    public Properties getPro(){
        return this.pro; 
    }
}
class Factory{
    public static IFruit getInstance(String className){
         IFruit fruit = null ;
         try{
             fruit=(IFruit) Class.forName(className).newInstance();
         }catch(InstantiationException e){
             e.printStackTrace();
         }catch(IllegalAccessException e){
             e.printStackTrace();
         }catch(ClassNotFoundException e){
             e.printStackTrace();
         }
         return fruit;
    }
}
public class Factory03{
    public static void main(String[] args){
        IFruit f = Factory.getInstance(new PropertiesOperate().getPro().getProperty("fruit"));
        f.eat(); 
    }
}
/*现在的程序可以通过配置文件进行控制,实现了配置文件和程序的相互分离的代码*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值