zoukankan      html  css  js  c++  java
  • Java反射机制及IoC原理

    原文章地址:https://www.cnblogs.com/Eason-S/p/5851078.html

    一. 反射机制概念

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

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

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

    二. 反射机制的作用

    1. 在运行时判断任意一个对象所属的类;
    2. 在运行时获取类的对象;
    3. 在运行时访问java对象的属性,方法,构造方法等。

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

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

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

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

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

    四. 反射机制的示例

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

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

    复制代码
     1 package Reflect;
     2 
     3 class Demo{
     4     //other codes...
     5 }
     6 
     7 class hello{
     8     public static void main(String[] args) {
     9         Demo demo=new Demo();
    10         System.out.println(demo.getClass().getName());
    11     }
    12 }
    13 //【运行结果】:Reflect.Demo
    复制代码

    2.实例化Class类对象

    复制代码
     1 package Reflect;
     2 
     3 class Demo{
     4     //other codes...
     5 }
     6 
     7 class hello{
     8     public static void main(String[] args) {
     9         Class<?> demo1=null;
    10         Class<?> demo2=null;
    11         Class<?> demo3=null;
    12         try{
    13             //一般尽量采用这种形式
    14             demo1=Class.forName("Reflect.Demo");
    15         }catch(Exception e){
    16             e.printStackTrace();
    17         }
    18         demo2=new Demo().getClass();
    19         demo3=Demo.class;
    20 
    21         System.out.println("类名称   "+demo1.getName());
    22         System.out.println("类名称   "+demo2.getName());
    23         System.out.println("类名称   "+demo3.getName());
    24     }
    25 }
    26 //【运行结果】:
    27 //类名称   Reflect.Demo
    28 //类名称   Reflect.Demo
    29 //类名称   Reflect.Demo
    复制代码

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

    复制代码
     1 package Reflect;
     2 
     3 class Person{
     4     public String getName() {
     5         return name;
     6     }
     7     public void setName(String name) {
     8         this.name = name;
     9     }
    10     public int getAge() {
    11         return age;
    12     }
    13     public void setAge(int age) {
    14         this.age = age;
    15     }
    16     @Override
    17     public String toString(){
    18         return "["+this.name+"  "+this.age+"]";
    19     }
    20     private String name;
    21     private int age;
    22 }
    23 
    24 class hello{
    25     public static void main(String[] args) {
    26         Class<?> demo=null;
    27         try{
    28             demo=Class.forName("Reflect.Person");
    29         }catch (Exception e) {
    30             e.printStackTrace();
    31         }
    32         Person per=null;
    33         try {
    34             per=(Person)demo.newInstance();
    35         } catch (InstantiationException e) {
    36             // TODO Auto-generated catch block
    37             e.printStackTrace();
    38         } catch (IllegalAccessException e) {
    39             // TODO Auto-generated catch block
    40             e.printStackTrace();
    41         }
    42         per.setName("Rollen");
    43         per.setAge(20);
    44         System.out.println(per);
    45     }
    46 }
    47 //【运行结果】:
    48 //[Rollen  20]
    复制代码

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

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

    1 public Person(String name, int age) {
    2         this.age=age;
    3         this.name=name;
    4     }

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

    复制代码
    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创建其他类的对象)

    View Code

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

    View Code

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

    6.取得其他类中的父类

    View Code

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

    View Code
    View Code

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

    View Code

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

    View Code

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

    View Code

    11.通过反射操作属性

    View Code

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

    View Code

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

    View Code

    14.动态代理

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

    View Code

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

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

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

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

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

    View Code

    类的生命周期

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

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

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

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

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

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

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

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

    五. IoC原理

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

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

    复制代码
     1 /**
     2  * 工厂模式
     3  */
     4 interface fruit{
     5     public abstract void eat();
     6 }
     7 
     8 class Apple implements fruit{
     9     public void eat(){
    10         System.out.println("Apple");
    11     }
    12 }
    13 
    14 class Orange implements fruit{
    15     public void eat(){
    16         System.out.println("Orange");
    17     }
    18 }
    19 // 构造工厂类
    20 // 也就是说以后如果我们在添加其他的实例的时候只需要修改工厂类就行了
    21 class Factory{
    22     public static fruit getInstance(String fruitName){
    23         fruit f=null;
    24         if("Apple".equals(fruitName)){
    25             f=new Apple();
    26         }
    27         if("Orange".equals(fruitName)){
    28             f=new Orange();
    29         }
    30         return f;
    31     }
    32 }
    33 
    34 class hello{
    35     public static void main(String[] a){
    36         fruit f=Factory.getInstance("Orange");
    37         f.eat();
    38     }
    39 }
    复制代码

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

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

    复制代码
     1 package Reflect;
     2 
     3 interface fruit{
     4     public abstract void eat();
     5 }
     6 
     7 class Apple implements fruit{
     8     public void eat(){
     9         System.out.println("Apple");
    10     }
    11 }
    12 
    13 class Orange implements fruit{
    14     public void eat(){
    15         System.out.println("Orange");
    16     }
    17 }
    18 
    19 class Factory{
    20     public static fruit getInstance(String ClassName){
    21         fruit f=null;
    22         try{
    23             f=(fruit)Class.forName(ClassName).newInstance();
    24         }catch (Exception e) {
    25             e.printStackTrace();
    26         }
    27         return f;
    28     }
    29 }
    30 
    31 class hello{
    32     public static void main(String[] a){
    33         fruit f=Factory.getInstance("Reflect.Apple");
    34         if(f!=null){
    35             f.eat();
    36         }
    37     }
    38 }
    复制代码

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

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

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

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

    1 apple=Reflect.Apple
    2 orange=Reflect.Orange

    然后编写主类代码:

    复制代码
     1 package Reflect;
     2 
     3 import java.io.*;
     4 import java.util.*;
     5 
     6 interface fruit{
     7     public abstract void eat();
     8 }
     9 
    10 class Apple implements fruit{
    11     public void eat(){
    12         System.out.println("Apple");
    13     }
    14 }
    15 
    16 class Orange implements fruit{
    17     public void eat(){
    18         System.out.println("Orange");
    19     }
    20 }
    21 //操作属性文件类
    22 class init{
    23     public static Properties getPro() throws FileNotFoundException, IOException{
    24         Properties pro=new Properties();
    25         File f=new File("fruit.properties");
    26         if(f.exists()){
    27             pro.load(new FileInputStream(f));
    28         }else{
    29             pro.setProperty("apple", "Reflect.Apple");
    30             pro.setProperty("orange", "Reflect.Orange");
    31             pro.store(new FileOutputStream(f), "FRUIT CLASS");
    32         }
    33         return pro;
    34     }
    35 }
    36 
    37 class Factory{
    38     public static fruit getInstance(String ClassName){
    39         fruit f=null;
    40         try{
    41             f=(fruit)Class.forName(ClassName).newInstance();
    42         }catch (Exception e) {
    43             e.printStackTrace();
    44         }
    45         return f;
    46     }
    47 }
    48 
    49 class hello{
    50     public static void main(String[] a) throws FileNotFoundException, IOException{
    51         Properties pro=init.getPro();
    52         fruit f=Factory.getInstance(pro.getProperty("apple"));
    53         if(f!=null){
    54             f.eat();
    55         }
    56     }
    57 }
    58 //【运行结果】:Apple
    复制代码
  • 相关阅读:
    hello , world Tkinter代码描述
    Tkinter 类
    什么是Tkinter?
    99_恢复二叉搜索树
    总结eclipse中常用好用的快捷键或者自定义一下快捷键:
    封装与职责分离的开发思维
    正在学习的路上
    串比较
    坚持的力量 第二十篇
    串连接
  • 原文地址:https://www.cnblogs.com/gyadmin/p/8058488.html
Copyright © 2011-2022 走看看