Java反射机制及IoC原理

Java  2018年1月30日 pm5:32发布2个月前更新 91es.com站长
81 0 0

这篇讲解的不错,摘抄于此。感谢《Java反射机制及IoC原理

一. 反射机制概念

主要是指程序可以访问,检测和修改它本身状态或行为的一种能力,并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义。在java中,只要给定类的名字, 那么就可以通过反射机制来获得类的所有信息。

反射是Java中一种强大的工具,能够使我们很方便的创建灵活的代码,这些代码可以再运行时装配,无需在组件之间进行源代码链接。但是反射使用不当会成本很高!

类中有什么信息,利用反射机制就能可以获得什么信息,不过前提是得知道类的名字。

二. 反射机制的作用

  1. 在运行时判断任意一个对象所属的类;

  2. 在运行时获取类的对象;

  3. 在运行时访问java对象的属性,方法,构造方法等。

三. 反射机制的优点与缺点

首先要搞清楚为什么要用反射机制?直接创建对象不就可以了吗,这就涉及到了动态与静态的概念。 静态编译:在编译时确定类型,绑定对象,即通过。 动态编译:运行时确定类型,绑定对象。动态编译最大限度发挥了java的灵活性,体现了多态的应用,有以降低类之间的藕合性。

反射机制的优点:可以实现动态创建对象和编译,体现出很大的灵活性(特别是在J2EE的开发中它的灵活性就表现的十分明显)。通过反射机制我们可以获得类的各种内容,进行了反编译。对于JAVA这种先编译再运行的语言来说,反射机制可以使代码更加灵活,更加容易实现面向对象。

比如,一个大型的软件,不可能一次就把把它设计的很完美,当这个程序编译后,发布了,当发现需要更新某些功能时,我们不可能要用户把以前的卸载,再重新安装新的版本,假如这样的话,这个软件肯定是没有多少人用的。采用静态的话,需要把整个程序重新编译一次才可以实现功能的更新,而采用反射机制的话,它就可以不用卸载,只需要在运行时才动态的创建和编译,就可以实现该功能。

反射机制的缺点:对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它 满足我们的要求。这类操作总是慢于只直接执行相同的操作。

四. 反射机制的示例

1.通过一个对象获得完整的包名和类名

添加一句:所有类的对象其实都是Class的实例。

package Reflect;

class Demo{
    //other codes...
}

class hello{
    public static void main(String[] args) {
        Demo demo=new Demo();
        System.out.println(demo.getClass().getName());
    }
}
//【运行结果】:Reflect.Demo

2.实例化Class类对象

package Reflect;

class Demo{
    //other codes...
}

class hello{
    public static void main(String[] args) {
        Class<?> demo1=null;
        Class<?> demo2=null;
        Class<?> demo3=null;
        try{
            //一般尽量采用这种形式
            demo1=Class.forName("Reflect.Demo");
        }catch(Exception e){
            e.printStackTrace();
        }
        demo2=new Demo().getClass();
        demo3=Demo.class;

        System.out.println("类名称   "+demo1.getName());
        System.out.println("类名称   "+demo2.getName());
        System.out.println("类名称   "+demo3.getName());
    }
}
//【运行结果】:
//类名称   Reflect.Demo
//类名称   Reflect.Demo
//类名称   Reflect.Demo

3.通过Class实例化其他类的对象

package Reflect;

class Person{
    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;
    }
    @Override
    public String toString(){
        return "["+this.name+"  "+this.age+"]";
    }
    private String name;
    private int age;
}

class hello{
    public static void main(String[] args) {
        Class<?> demo=null;
        try{
            demo=Class.forName("Reflect.Person");
        }catch (Exception e) {
            e.printStackTrace();
        }
        Person per=null;
        try {
            per=(Person)demo.newInstance();
        } catch (InstantiationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        per.setName("Rollen");
        per.setAge(20);
        System.out.println(per);
    }
}
//【运行结果】:
//[Rollen  20]

但是注意一下,当我们把Person中的默认的无参构造函数取消的时候,比如自己定义只定义一个有参数的构造函数之后,会出现错误:

比如定义了一个构造函数:

public Person(String name, int age) {
        this.age=age;
        this.name=name;
    }

然后继续运行上面的程序,会出现:

java.lang.InstantiationException: Reflect.Person
at java.lang.Class.newInstance0(Class.java:340)
at java.lang.Class.newInstance(Class.java:308)
at Reflect.hello.main(hello.java:39)
Exception in thread "main" java.lang.NullPointerException
at Reflect.hello.main(hello.java:47)

所以大家以后再编写使用Class实例化其他类的对象的时候,一定要自己定义无参的构造函数。

4.通过Class调用其他类中的构造函数

(也可以通过这种方式通过Class创建其他类的对象)

package Reflect;

import java.lang.reflect.Constructor;

class Person{
    public Person() {

    }
    public Person(String name){
        this.name=name;
    }
    public Person(int age){
        this.age=age;
    }
    public Person(String name, int age) {
        this.age=age;
        this.name=name;
    }
    public String getName() {
        return name;
    }
    public int getAge() {
        return age;
    }
    @Override
    public String toString(){
        return "["+this.name+"  "+this.age+"]";
    }
    private String name;
    private int age;
}

class hello{
    public static void main(String[] args) {
        Class<?> demo=null;
        try{
            demo=Class.forName("Reflect.Person");
        }catch (Exception e) {
            e.printStackTrace();
        }
        Person per1=null;
        Person per2=null;
        Person per3=null;
        Person per4=null;
        //取得全部的构造函数
        Constructor<?> cons[]=demo.getConstructors();
        try{
            per1=(Person)cons[0].newInstance();
            per2=(Person)cons[1].newInstance("Rollen");
            per3=(Person)cons[2].newInstance(20);
            per4=(Person)cons[3].newInstance("Rollen",20);
        }catch(Exception e){
            e.printStackTrace();
        }
        System.out.println(per1);
        System.out.println(per2);
        System.out.println(per3);
        System.out.println(per4);
    }
}
//【运行结果】:
//[null  0]
//[Rollen  0]
//[null  20]
//[Rollen  20]

5.返回一个类实现的接口

package Reflect;

interface China{
    public static final String name="Rollen";
    public static  int age=20;
    public void sayChina();
    public void sayHello(String name, int age);
}

class Person implements China{
    public Person() {

    }
    public Person(String sex){
        this.sex=sex;
    }
    public String getSex() {
        return sex;
    }
    public void setSex(String sex) {
        this.sex = sex;
    }
    @Override
    public void sayChina(){
        System.out.println("hello ,china");
    }
    @Override
    public void sayHello(String name, int age){
        System.out.println(name+"  "+age);
    }
    private String sex;
}

class hello{
    public static void main(String[] args) {
        Class<?> demo=null;
        try{
            demo=Class.forName("Reflect.Person");
        }catch (Exception e) {
            e.printStackTrace();
        }
        //保存所有的接口
        Class<?> intes[]=demo.getInterfaces();
        for (int i = 0; i < intes.length; i++) {
            System.out.println("实现的接口   "+intes[i].getName());
        }
    }
}
//【运行结果】:
//实现的接口   Reflect.China

(以下几个例子,都会用到这个例子的Person类,所以为节省篇幅,此处不再粘贴Person的代码部分,只粘贴主类hello的代码)

6.取得其他类中的父类

class hello{
    public static void main(String[] args) {
        Class<?> demo=null;
        try{
            demo=Class.forName("Reflect.Person");
        }catch (Exception e) {
            e.printStackTrace();
        }
        //取得父类
        Class<?> temp=demo.getSuperclass();
        System.out.println("继承的父类为:   "+temp.getName());
    }
}
//【运行结果】
//继承的父类为:   java.lang.Object

7.获得其他类中的全部构造函数

//这个例子需要在程序开头添加import java.lang.reflect.*;
class hello{
    public static void main(String[] args) {
        Class<?> demo=null;
        try{
            demo=Class.forName("Reflect.Person");
        }catch (Exception e) {
            e.printStackTrace();
        }
        Constructor<?>cons[]=demo.getConstructors();
        for (int i = 0; i < cons.length; i++) {
            System.out.println("构造方法:  "+cons[i]);
        }
    }
}
//【运行结果】:
//构造方法:  public Reflect.Person()
//构造方法:  public Reflect.Person(java.lang.String)
class hello{
    public static void main(String[] args) {
        Class<?> demo=null;
        try{
            demo=Class.forName("Reflect.Person");
        }catch (Exception e) {
            e.printStackTrace();
        }
        Constructor<?>cons[]=demo.getConstructors();
        for (int i = 0; i < cons.length; i++) {
            Class<?> p[]=cons[i].getParameterTypes();
            System.out.print("构造方法:  ");
            int mo=cons[i].getModifiers();
            System.out.print(Modifier.toString(mo)+" ");
            System.out.print(cons[i].getName());
            System.out.print("(");
            for(int j=0;j<p.length;++j){
                System.out.print(p[j].getName()+" arg"+i);
                if(j<p.length-1){
                    System.out.print(",");
                }
            }
            System.out.println("){}");
        }
    }
}
//【运行结果】:
//构造方法:  public Reflect.Person(){}
//构造方法:  public Reflect.Person(java.lang.String arg1){}

8.取得其他类的全部属性

将这些整理在一起,也就是通过class取得一个类的全部框架

class hello {
    public static void main(String[] args) {
        Class<?> demo = null;
        try {
            demo = Class.forName("Reflect.Person");
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("===============本类属性========================");
        // 取得本类的全部属性
        Field[] field = demo.getDeclaredFields();
        for (int i = 0; i < field.length; i++) {
            // 权限修饰符
            int mo = field[i].getModifiers();
            String priv = Modifier.toString(mo);
            // 属性类型
            Class<?> type = field[i].getType();
            System.out.println(priv + " " + type.getName() + " "
                    + field[i].getName() + ";");
        }
        System.out.println("===============实现的接口或者父类的属性========================");
        // 取得实现的接口或者父类的属性
        Field[] filed1 = demo.getFields();
        for (int j = 0; j < filed1.length; j++) {
            // 权限修饰符
            int mo = filed1[j].getModifiers();
            String priv = Modifier.toString(mo);
            // 属性类型
            Class<?> type = filed1[j].getType();
            System.out.println(priv + " " + type.getName() + " "
                    + filed1[j].getName() + ";");
        }
    }
}
//【运行结果】:
//===============本类属性========================
//private java.lang.String sex;
//===============实现的接口或者父类的属性========================
//public static final java.lang.String name;
//public static final int age;

9.通过反射调用其他类中的方法

class hello {
    public static void main(String[] args) {
        Class<?> demo = null;
        try {
            demo = Class.forName("Reflect.Person");
        } catch (Exception e) {
            e.printStackTrace();
        }
        try{
            //调用Person类中的sayChina方法
            Method method=demo.getMethod("sayChina");
            method.invoke(demo.newInstance());
            //调用Person的sayHello方法
            method=demo.getMethod("sayHello", String.class,int.class);
            method.invoke(demo.newInstance(),"Rollen",20);
        }catch (Exception e) {
            e.printStackTrace();
        }
    }
}
//【运行结果】:
//hello ,china
//Rollen  20

10.调用其他类的set和get方法

class hello {
    public static void main(String[] args) {
        Class<?> demo = null;
        Object obj=null;
        try {
            demo = Class.forName("Reflect.Person");
        } catch (Exception e) {
            e.printStackTrace();
        }
        try{
         obj=demo.newInstance();
        }catch (Exception e) {
            e.printStackTrace();
        }
        setter(obj,"Sex","男",String.class);
        getter(obj,"Sex");
    }

    /**
     * @param obj   操作的对象
     * @param att   操作的属性
     * */
    public static void getter(Object obj, String att) {
        try {
            Method method = obj.getClass().getMethod("get" + att);
            System.out.println(method.invoke(obj));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * @param obj   操作的对象
     * @param att   操作的属性
     * @param value 设置的值
     * @param type  参数的属性
     * */
    public static void setter(Object obj, String att, Object value,
            Class<?> type) {
        try {
            Method method = obj.getClass().getMethod("set" + att, type);
            method.invoke(obj, value);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}// end class
//【运行结果】:
//男

11.通过反射操作属性

class hello {
    public static void main(String[] args) throws Exception {
        Class<?> demo = null;
        Object obj = null;

        demo = Class.forName("Reflect.Person");
        obj = demo.newInstance();

        Field field = demo.getDeclaredField("sex");
        field.setAccessible(true);
        field.set(obj, "男");
        System.out.println(field.get(obj));
    }
}// end class

12.通过反射取得并修改数组的信息

import java.lang.reflect.*;

class hello{
    public static void main(String[] args) {
        int[] temp={1,2,3,4,5};
        Class<?>demo=temp.getClass().getComponentType();
        System.out.println("数组类型: "+demo.getName());
        System.out.println("数组长度  "+Array.getLength(temp));
        System.out.println("数组的第一个元素: "+Array.get(temp, 0));
        Array.set(temp, 0, 100);
        System.out.println("修改之后数组第一个元素为: "+Array.get(temp, 0));
    }
}
//【运行结果】:
//数组类型: int
//数组长度  5
//数组的第一个元素: 1
//修改之后数组第一个元素为: 100

13.通过反射修改数组大小

 class hello{
     public static void main(String[] args) {
         int[] temp={1,2,3,4,5,6,7,8,9};
         int[] newTemp=(int[])arrayInc(temp,15);
         print(newTemp);
         System.out.println("=====================");
         String[] atr={"a","b","c"};
         String[] str1=(String[])arrayInc(atr,8);
         print(str1);
     }
     /**
      * 修改数组大小
      * */
     public static Object arrayInc(Object obj,int len){
         Class<?>arr=obj.getClass().getComponentType();
         Object newArr=Array.newInstance(arr, len);
         int co=Array.getLength(obj);
         System.arraycopy(obj, 0, newArr, 0, co);
         return newArr;
     }
     /**
      * 打印
      * */
     public static void print(Object obj){
         Class<?>c=obj.getClass();
         if(!c.isArray()){
             return;
         }
         System.out.println("数组长度为: "+Array.getLength(obj));
         for (int i = 0; i < Array.getLength(obj); i++) {
             System.out.print(Array.get(obj, i)+" ");
         }
     }
 }
 //【运行结果】:
 //数组长度为: 15
 //1 2 3 4 5 6 7 8 9 0 0 0 0 0 0 =====================
 //数组长度为: 8
 //a b c null null null null null

14.动态代理

首先来看看如何获得类加载器:

 class test{

 }
 class hello{
     public static void main(String[] args) {
         test t=new test();
         System.out.println("类加载器  "+t.getClass().getClassLoader().getClass().getName());
     }
 }
 //【程序输出】:
 //类加载器  sun.misc.Launcher$AppClassLoader

其实在java中有三种类类加载器。

1)Bootstrap ClassLoader 此加载器采用c++编写,一般开发中很少见。

2)Extension ClassLoader 用来进行扩展类的加载,一般对应的是jre\lib\ext目录中的类

3)AppClassLoader 加载classpath指定的类,是最常用的加载器。同时也是java中默认的加载器。

如果想要完成动态代理,首先需要定义一个InvocationHandler接口的子类,已完成代理的具体操作。

 package Reflect;

 import java.lang.reflect.*;

 //定义项目接口
 interface Subject {
     public String say(String name, int age);
 }

 // 定义真实项目
 class RealSubject implements Subject {
     @Override
     public String say(String name, int age) {
         return name + "  " + age;
     }
 }

 class MyInvocationHandler implements InvocationHandler {
     private Object obj = null;

     public Object bind(Object obj) {
         this.obj = obj;
         return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj
                 .getClass().getInterfaces(), this);
     }

     @Override
     public Object invoke(Object proxy, Method method, Object[] args)
             throws Throwable {
         Object temp = method.invoke(this.obj, args);
         return temp;
     }
 }

 class hello {
     public static void main(String[] args) {
         MyInvocationHandler demo = new MyInvocationHandler();
         Subject sub = (Subject) demo.bind(new RealSubject());
         String info = sub.say("Rollen", 20);
         System.out.println(info);
     }
 }
 //【运行结果】:
 //Rollen  20

类的生命周期

在一个类编译完成之后,下一步就需要开始使用类,如果要使用一个类,肯定离不开JVM。在程序执行中JVM通过装载,链接,初始化这3个步骤完成。

类的装载是通过类加载器完成的,加载器将.class文件的二进制文件装入JVM的方法区,并且在堆区创建描述这个类的java.lang.Class对象。用来封装数据。 但是同一个类只会被类装载器装载以前

链接就是把二进制数据组装为可以运行的状态。

链接分为校验,准备,解析这3个阶段:

校验一般用来确认此二进制文件是否适合当前的JVM(版本),

准备就是为静态成员分配内存空间,。并设置默认值

解析指的是转换常量池中的代码作为直接引用的过程,直到所有的符号引用都可以被运行程序使用(建立完整的对应关系)。

完成之后,类型也就完成了初始化,初始化之后类的对象就可以正常使用了,直到一个对象不再使用之后,将被垃圾回收。释放空间。当没有任何引用指向Class对象时就会被卸载,结束类的生命周期。

五. IoC原理

Spring中的IoC的实现原理就是工厂模式加反射机制。

1.我们首先看一下不用反射机制时的工厂模式:

/**
 * 工厂模式
 */
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();
    }
}

当我们在添加一个子类的时候,就需要修改工厂类了。如果我们添加太多的子类的时候,改的就会很多。

2. 利用反射机制的工厂模式:

 package Reflect;

 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();
         }
     }
 }

现在就算我们添加任意多个子类的时候,工厂类就不需要修改。

使用反射机制的工厂模式可以通过反射取得接口的实例,但是需要传入完整的包和类名。而且用户也无法知道一个接口有多少个可以使用的子类,所以我们通过属性文件的形式配置所需要的子类。

3.使用反射机制并结合属性文件的工厂模式(即IoC)

首先创建一个fruit.properties的资源文件:

apple=Reflect.Apple
orange=Reflect.Orange

然后编写主类代码:

 package Reflect;

 import java.io.*;
 import java.util.*;

 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 init{
     public static Properties getPro() throws FileNotFoundException, IOException{
         Properties pro=new Properties();
         File f=new File("fruit.properties");
         if(f.exists()){
             pro.load(new FileInputStream(f));
         }else{
             pro.setProperty("apple", "Reflect.Apple");
             pro.setProperty("orange", "Reflect.Orange");
             pro.store(new FileOutputStream(f), "FRUIT CLASS");
         }
         return pro;
     }
 }

 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) throws FileNotFoundException, IOException{
         Properties pro=init.getPro();
         fruit f=Factory.getInstance(pro.getProperty("apple"));
         if(f!=null){
             f.eat();
         }
     }
 }
 //【运行结果】:Apple

版权声明 1、 本站名称: 91易搜
2、 本站网址: 91es.com3xcn.com
3、 本站内容: 部分来源于网络,仅供学习和参考,若侵权请留言
3、 本站申明: 个人流水账日记,内容并不保证有效

暂无评论

暂无评论...

随机推荐

Android 13之MediaProvider的优化

前言记录一下Android 13的MediaProvider的优化。随着Android发展,Android10 扫描器的的主要功能就放MediaProvider中了,MediaScannner类的作用就淡化了。记录一下,对MediaProvider的优化,方便自己查阅。正文这里只是简单记...

Android触摸事件简单分析

这篇文章以前写的,后来博客清空重新开始了。最近看到,因此摘抄于此,以便查阅。在开始分析之前,大家可以先记几个结论,这样便于理解。  结论1:事件一定是先到达父控件上。  结论2:事件简单来说可以分为三种:Down事件、Move事件、Up事件。PS:(1) 为了让读者看得更清晰,在代码中只打...

老舍:春风

济南与青岛是多么不相同的地方呢!一个设若比作穿肥袖马褂的老先生,那一个便应当是摩登的少女。可是这两处不无相似之点。拿气候说吧,济南的夏天可以热死人,而青岛是有名的避暑所在;冬天,济南也比青岛冷。但是,两地的春秋颇有点相同。济南到春天多风,青岛也是这样;济南的秋天是长而晴美,青岛亦然。对于秋天,我不...

Android启动Activity到副屏

前言随着Android版本的提高,现在Android设备大都存在2个屏幕,也就是主屏和副屏。偶尔需要把指定副屏显示指定的界面,因此有空整理一下相关内容。记录于此,方便自己查阅。正文记录一下常用的几种方式,启动Activity到副屏,也对比一下优缺点。隐藏内容!付费阅读后才能查看!¥2 ...

席慕容:时间

一锅米饭,放到第二天,水气就会干了一些,放到第三天,味道恐怕就有问题了。第四天,我们几乎可以发现,它已经变坏了。再放下去,眼看就要发霉了。是什么原因,使那锅米饭变馊变坏?是时间。可是,在浙江绍兴,年轻的父母生下女儿,他们就会在地窖里,埋下一坛坛米做的酒。十七八年后,女儿长大了,这些酒就成为了嫁...

125啦新版来了,欢迎使用

1、好记性不如烂笔头;2、觉得已经晚了,恰恰是最早的时候;3、鸡蛋从外打破是食物,从内打破是生命 我们新版上线啦(掌声),这次改版很大,放弃之前传统的导航模式,首先采用“网址导航+读书(阅读)+博客”全新的模式,依旧是国内第一个以读书(阅读)为主题的网址导航大全!因为热爱,所以折...