java反射机制

目录

反射的基石-> class类

java程序中各个Java类属于同一类事物,描述这类事物的Java类名就是Class

dog -> animal

java类 -> Class

animal代表动物,他的实例对象就是‘来福’,‘旺财’这一个个具体的狗,Class代表Java类,它的各个实例对象则对应着各个类在内存中的字节码,例如,animal的字节码,ArrayList的字节码等,

一个类被夹在器加载到内存中,占用一片存储空间,这个空间里面的内容就是类的字节码,不同的类的字节码是不同的,所以他们在内存中的内容是不同的,这一个个的空间可分别用一个个的对象来表示,这些对象显然具有相同的类型

反射是什么

反射允许静态语言在运行时检查、修改程序的结构与行为。
在静态语言中,使用一个变量时,必须知道它的类型。在Java中,变量的类型信息在编译时都保存到了class文件中,这样在运行时才能保证准确无误;换句话说,程序在运行时的行为都是固定的。如果想在运行时改变,就需要反射这东西了。
即:Java的反射机制的做用是在项目运行当中动态的获取相应的类,类属性,类方法

为什么用反射

首先我们先明确两个概念,静态编译和动态编译。

  • 静态编译:在编译时确定类型,绑定对象,即通过。
  • 动态编译:运行时确定类型,绑定对象。动态编译最大限度发挥了java的灵活性,体现了多态的应用,有以降低类之间的藕合性。 我们可以明确的看出动态编译的好处,而反射就是运用了动态编译创建对象。

那么我们再来看看实际中反射又有什么好处那?
往往对比能更加直观的向我们展示两者的不同。
例子参考自此博客

interface fruit{  
    public abstract void eat();  
}  

class Apple implements fruit{  
    public void eat(){  
        System.out.println("Apple");  
    }  
}  

class Orange implements fruit{  
    public void eat(){  
        System.out.println("Orange");  
    }  
}  

// 构造工厂类  
// 也就是说以后如果我们在添加其他的实例的时候只需要修改工厂类就行了  
class Factory{  
    public static fruit getInstance(String fruitName){  
        fruit f=null;  
        if("Apple".equals(fruitName)){  
            f=new Apple();  
        }  
        if("Orange".equals(fruitName)){  
            f=new Orange();  
        }  
        return f;  
    }  
}  
class hello{  
    public static void main(String[] a){  
        fruit f=Factory.getInstance("Orange");  
        f.eat();  
    }  

}  

可以发现,每当我们要添加一种新的水果的时候,我们将不得不改变Factory中的源码,而往往改变原有正确代码是一种十分危险的行为。而且随着水果种类的增加,你会发现你的factory类会越来越臃肿,

不得不说这是一种十分–的做法。(初学者可能会问,我们为什么不直接在main方法中new水果那,我们可能会需要getInstance方法做一些别的事情。。。所以不直接new);

而反射无疑是一种聪明的办法,看代码。

interface fruit{  
    public abstract void eat();  
}  

class Apple implements fruit{  
    public void eat(){  
        System.out.println("Apple");  
    }  
}  

class Orange implements fruit{  
    public void eat(){  
        System.out.println("Orange");  
    }  
}  

class Factory{  
    public static fruit getInstance(String ClassName){  
        fruit f=null;  
        try{  
            f=(fruit)Class.forName(ClassName).newInstance();  
        }catch (Exception e) {  
            e.printStackTrace();  
        }  
        return f;  
    }  
}  
class hello{  
    public static void main(String[] a){  
        fruit f=Factory.getInstance("Reflect.Apple");  
        if(f!=null){  
            f.eat();  
        }  
    }  
}  

在出现新品种水果的时候,你完全不用去修改原有代码。

  从上面的案例中,我们可以清楚的体会到反射的优越性。

  那么有的人又会问,这个例子能完全明白,但是如果放到实际的编程,应用中,我们又会在什么情况下用到反射那?

  举一个看到过的例子,在实际开发中,我们需要把一个包中的class new出来,但是这个包中的类总是需要变动,那么怎么办,难道总是修改main方法中xxx=new xxx()吗。这样无疑是麻烦的。而运用反射。我们可以相应的增加一个配置文件,在里面记录包中所有的类名,包中类增加时就加一个类名,删除时就删除一个类名。让main方法去读取这个配置文件中的类名,通过反射获得实例,完全不用我们去修改main方法中的代码。

  反射还有什么用那?他甚至可以修改其他类中的私有属性。android开发中,我们需要改变一个私有标志位的时候,android源码并没有提供set方法,我们又不能改变源码,怎么办,反射可以完美解决这个问题。

  说了这么多,那么我们的开发中,为什么不全部都用反射那?一个原因,开销,它的开销是什么昂贵的,随意尽量在最需要的地方使用反射。

常用的反射方法

package Test.reflex;

import Test.Girl;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
 * @program: aaa
 * @description: 反射的基石->Class类
 * @author: Mr.Wang
 * @create: 2017-11-12 16:09
 **/
public class ReflectTestClass {
    public static void main(String[] args) throws ClassNotFoundException {
        /* 三种获取类字节码的方法
            x.getClass()
            x.class
            Class.forName("")
         */
        //如果加载过则返回字节码,,虚拟机没加载过,加载类,获取字节码
        Class<?> name = Class.forName("java.lang.Thread");
        System.out.println(name);
        Class<Thread> threadClass = Thread.class;
        System.out.println(threadClass);

        Constructor();

        try {
            getFieldInClass();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

        try {
            replaceFiled();
        } catch (Exception e) {
            e.printStackTrace();
        }

        try {
            getMethod();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //反射构造方法
    public static void Constructor() {
        Constructor<String> constructor = null;
        String abc = null;
        try {
            constructor = String.class.getConstructor(StringBuffer.class);
            abc = constructor.newInstance(new StringBuffer("abc"));
        } catch (Exception e) {
            System.out.println("1");
        }
        System.out.println(abc);
    }

    //反射获取成员属性
    public static void getFieldInClass() throws IllegalAccessException {

        Girl girl = new Girl(1, "kk", "k");
        Field name = null;
        try {
//            name = Girl.class.getField("name");   //获取非私有的成员属性
            name = Girl.class.getDeclaredField("name");
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }

        //name属性是私有的,通过**暴力反射**获取
        name.setAccessible(true);

        System.out.println(name.get(girl));
    }


    //这个功能可以用在评论结论接口里面去掉非法字符哈    事实上肯定不会用反射了,哈哈哈
    //反射替换成员属性
    public static void replaceFiled() throws IllegalAccessException {
        Girl girl = new Girl();
        System.out.println(girl.toString() + "这是反射替换前");
        Field[] fields = Girl.class.getFields();
        for (Field field : fields) {
            //因为是同一个字节码,所以这里可以用==而不是equals
            if (field.getType() == String.class) {
                String oldValue = (String)field.get(girl);
                String newValue = oldValue.replace("a", "");
                field.set(girl,newValue);
            }
        }
        System.out.println(girl.toString());
    }

    //反射获取方法
    public static void getMethod() throws Exception{
        Girl girl = new Girl();
        Method getString = girl.getClass().getMethod("getString", null);
        System.out.println(getString.invoke(girl,null));
    }
}

//反射就是将java类中的各种成分映射成相应的java类

//construct  构造方法
//field      类成员
//method     类方法

实体类

package Test;

public class Girl {
    //年龄
    private Integer age;
    //name
    private String name;
    //cup
    private String cupSize;

    public String a = "aabb";
    public String b = "bbaa";
    public String c = "cccc";

    public Girl(){
        super();
    }


    public String getString() {
        return "我返回一个字符串了";
    }

    public Girl(Integer age, String name, String cupSize) {
        this.age = age;
        this.name = name;
        this.cupSize = cupSize;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getCupSize() {
        return cupSize;
    }

    public void setCupSize(String cupSize) {
        this.cupSize = cupSize;
    }

    @Override
    public String toString() {
        return "Girl{" +
                "age=" + age +
                ", name='" + name + '\'' +
                ", cupSize='" + cupSize + '\'' +
                ", a='" + a + '\'' +
                ", b='" + b + '\'' +
                ", c='" + c + '\'' +
                '}';
    }
}

其他常用方法
Class c=Class.forName(“className”);注明:className必须为全名,也就是得包含包名,比如,cn.netjava.pojo.UserInfo;
Object obj=c.newInstance();//创建对象的实例
OK,有了对象就什么都好办了,想要什么信息就有什么信息了。
获得构造函数的方法
Constructor getConstructor(Class[] params)//根据指定参数获得public构造器

Constructor[] getConstructors()//获得public的所有构造器

Constructor getDeclaredConstructor(Class[] params)//根据指定参数获得public和非public的构造器

Constructor[] getDeclaredConstructors()//获得public的所有构造器
获得类方法的方法
Method getMethod(String name, Class[] params),根据方法名,参数类型获得方法
想·
Method[] getMethods()//获得所有的public方法

Method getDeclaredMethod(String name, Class[] params)//根据方法名和参数类型,获得public和非public的方法

Method[] getDeclaredMethods()//获得所以的public和非public方法
获得类中属性的方法
Field getField(String name)//根据变量名得到相应的public变量

Field[] getFields()//获得类中所以public的方法

Field getDeclaredField(String name)//根据方法名获得public和非public变量

Field[] getDeclaredFields()//获得类中所有的public和非public方法

  看到这些方法,你就可以明白,反射是多么的强大了,当你正确使用这些方法的时候,基本上是掌握了反射的技巧。

需要注意的

劲酒虽好,可不要贪杯哟!
反射虽棒,可不要滥用喔!

反射是很耗资源了,详细的就不多说了,你看他弄那么多类出来,搞那么多class都知道需要消耗更多的内存等资源。

参考资料
参考资料
感谢前辈们无私奉献的创作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值