zoukankan      html  css  js  c++  java
  • java反射机制

        反射,reflection,听其名就像照镜子一样,可以看见自己也可以看见别人的每一部分。在java语言中这是一个很重要的特性。下面是来自sun公司官网关于反射的介绍: 

    Reflection is a feature in the Java programming language. It allows an executing Java program to examine or "introspect" upon itself, and manipulate internal properties of the program. For example, it's possible for a Java class to obtain the names of all its members and display them.

    The ability to examine and manipulate a Java class from within itself may not sound like very much, but in other programming languages this feature simply doesn't exist. For example, there is no way in a Pascal, C, or C++ program to obtain information about the functions defined within that program.

    One tangible use of reflection is in JavaBeans, where software components can be manipulated visually via a builder tool. The tool uses reflection to obtain the properties of Java components (classes) as they are dynamically loaded.

     

          那么解释一下就是,反射是java语言的一个特性,它允程序在运行时(注意不是编译的时候)来进行自我检查并且对内部的成员进行操作。例如它允许一个 java的类获取他所有的成员变量和方法并且显示出来。这个能特定我们不常看到,但是在其他的比如C或者C++语言中很不就存在这个特性。一个常见的例子 是在JavaBean中,一些组件可以通过一个构造器来操作。这个构造器就是用的反射在动态加载的时候来获取的java中类的属性的。

     

           反射的前传:类类型   Class Class                                                                                                                                     

         

       java中有一个类很特殊,就是Class类,很多朋友在写程序的时候有用 过比如Apple.class来查看类型信息,大家就可以把它理解为封装了类的信息,很多解释说Class类没有构造器,其实是有的,只不过它的构造方法 是private的(构造函数还有private的??有,这样是为了禁止开发者去自己创建Class类的实例)。我们可以看一下JDK中源码:

          

      注释很明确的告诉了我们,这个类是有JVM来创建的,所以我们就不用麻烦了。如果我们拿到一个类的类型信息,就可以利用反射获取其各种成员以及 方法了。(注:Class 从JDK1.5版本后就开始更多为泛型服务了)那么我们怎么拿到一个类型的信息呢?假设我们有一个Role类:

    复制代码
     1 package yui;
    2
    3 /**
    4 * A base class having some attributes and methods
    5 * @author Octobershiner
    6 * @since 2012 3 17
    7 *
    8 * */
    10 public class Role {
    11
    12 private String name;
    13 private String type;
    14
    15 // Constructors
    16 public Role(){
    17 System.out.println("Constructor Role() is invoking");
    18 }
    19 //私有构造器
    20 private Role(String name){
    21 this.name = name;
    22 System.out.println("Constructor Role(String name) is invoking.");
    23 }
    24
    25 //get and set method
    26
    27 public String getName() {
    28 return name;
    29 }
    30 public void setName(String name) {
    31 this.name = name;
    32 }
    33 public String getType() {
    34 return type;
    35 }
    36 public void setType(String type) {
    37 this.type = type;
    38 }
    39
    40 //override the toString method to show the class
    41 @Override
    42 public String toString(){
    43 return "This is a role called "+this.name;
    44 }
    45
    46 }
    复制代码

      在没有对象实例的时候,主要有两种办法。

            //获得类类型的两种方式
    Class cls1 = Role.class;
    Class cls2 = Class.forName("yui.Role");

      注意第二种方式中,forName中的参数一定是完整的类名(包名+类名),并且这个方法需要捕获异常。现在得到cls1就可以创建一个Role类的实例了,利用Class的newInstance方法相当于调用类的默认的构造器

            Object o = cls1.newInstance(); //创建一个实例
    //Object o1 = new Role(); //与上面的方法等价

      这样就创建了一个对象,缺点是我们只能利用默认构造函数,因为Class的newInstance是不接受参数的,后面会讲到可接受参数的newInstance,第二,如果类的构造函数是private的,比如Class,我们仍旧不能实例化其对象。

       获 取类的构造器                                                                                                                                                                       

         

      首先介绍一下Constructor类,这个类用来封装反射得到的构造器,Class有四个方法来获得Constructor对象

    • public Constructor<?>[] getConstructors()      返回类中所有的public构造器集合,默认构造器的下标为0
    • public Constructor<T> getConstructor(Class<?>... parameterTypes)   返回指定public构造器,参数为构造器参数类型集合
    • public Constructor<?>[] getDeclaredConstructors()  返回类中所有的构造器,包括私有
    • public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 返回任意指定的构造器
        从名字来看,还是很好懂的,带上Declared的都是获得所有的构造方法,包括私有,哈,这下我们就可以调用原本不允许调用的私有构造器了,看代码
     
    复制代码
     1         /**
    2 * 获取构造方法Constructor
    3 * getConstructor() only for public
    4 * getDeclaredConstructor() global access all
    5 *
    6 * */
    7
    8 //指定参数列表获取特定的方法
    9 Constructor con = cls1.getDeclaredConstructor(new Class[]{String.class});
    10 con.setAccessible(true); //设置可访问的权限
    11 Object obj = con.newInstance(new Object[]{"liyang"});
    12 System.out.println(obj); //打印一下这个对象的信息
    13
    14 //获取所有的构造方法集合
    15 Constructor con1[] = cls1.getDeclaredConstructors();
    16 con1[1].setAccessible(true);
    17 Object obj1 = con1[1].newInstance(new Object[]{"tom"});
    18 System.out.println(obj1);
    复制代码
      
      解释一下:第一个是获得一个指定的方法,我们指定了参数是一个String类型,第 二段我们获得了所有的构造方法集合,并选取了其中一个创建了新的对象。注意这里Constructor的newInstance方法就可以设置参数了,与 文章前面的同样的方法形成了对比。
      注意,以上的四个方法全部需要抛出异常,当我们获得私有的方法的时候,要用setAccessible设置一下可访问的权限,例子中没有演示获取共有方法,那个比较简单,就不做介绍了,其实掌握了上面两个,其他就好理解了。
     
    获 取类的成员变量                                                                                                                                                                    
         
       了解了构造器,其实你可以猜到成员变量的获取方法了,成员变量用Field类进行封装。
           主要的方法非常的类似:
    • public Field getDeclaredField(String name)  获取任意指定名字的成员
    • public Field[] getDeclaredFields()             获取所有的成员变量
    • public Field getField(String name)           获取任意public成员变量
    • public Field[] getFields()                          获取所有的public成员变量
        可以看出这些方法都是异曲同工的,好了直接看一下例子吧
     
    复制代码
    1         /**
    2 * 获取成员变量Field
    3 * getField()
    4 * getDeclaredField()
    5 * */
    6 Field mem = cls1.getDeclaredField("name");
    7 mem.setAccessible(true);
    8 System.out.println("we get form field :"+mem.get(obj));
    9
    复制代码
      
      这是在访问私有变量,什么私有变量也可以访问??是的。。。。
         
          获 取类的方法                                                                                                                                                                      
         
       我觉得你已经可以帮我写这一段了,封装类的方法的类是Method.获取method也有四个方法,猜到了没??
    • public Method[] getMethods()    获取所有的共有方法的集合
    • public Method getMethod(String name,Class<?>... parameterTypes) 获取指定公有方法 参数1:方法名 参数2:参数类型集合  
    • public Method[] getDeclaredMethods()  获取所有的方法
    • public Method getDeclaredMethod(String name,Class<?>... parameterTypes) 获取任意指定方法
           看下面的例子吧
       
    复制代码
    1         /**
    2 * 调用类的方法 Method
    3 * getMethod()
    4 * getDeclaredMethod()
    5 *
    6 * */
    7 Method f = cls1.getMethod("getName", null);
    8 Object name = f.invoke(obj, null);
    9 System.out.println("we invoke method : "+ name);
    复制代码
             
      这个很简单吧,无参的时候我们只要传null就行了。
     

    java中有内存泄露吗?

    会。java导致内存泄露的原因很明确:长生命周期的对象持有短生命周期对象的引用就很可能发生内存泄露,尽管短生命周期对象已经不再需要,但是因为长生命周期对象持有它的引用而导致不能被回收,这就是java中内存泄露的发生场景。

        1.集合类,集合类仅仅有添加元素的方法,而没有相应的删除机制,导致内存被占用。这一点其实也不明确,这个集合类如果仅仅是局部变量,根本不会造成内存泄露,在方法栈退出后就没有引用了会被jvm正常回收。而如果这个集合类是全局性的变量(比如类中的静态属性,全局性的map等即有静态引用或final一直指向它),那么没有相应的删除机制,很可能导致集合所占用的内存只增不减,因此提供这样的删除机制或者定期清除策略非常必要。

       2.单例模式。不正确使用单例模式是引起内存泄露的一个常见问题,单例对象在被初始化后将在JVM的整个生命周期中存在(以静态变量的方式),如果单例对象持有外部对象的引用,那么这个外部对象将不能被jvm正常回收,导致内存泄露,考虑下面的例子:

      class A{

      public A(){

        B.getInstance().setA(this);

      }

      ....

      }

      //B类采用单例模式

      class B{

      private A a;

      private static B instance=new B();

      public B(){}

      public static B getInstance(){

      return instance;

      }

      public void setA(A a){

      this.a=a;

      }

      //getter...

      }

      显然B采用singleton模式,他持有一个A对象的引用,而这个A类的对象将不能被回收。想象下如果A是个比较大的对象或者集合类型会发生什么情况。

       上面所讲的这些也启发我们如何去查找内存泄露问题,在代码复审的时候关注长生命周期对象:全局性的集合、单例模式的使用、类的static变量等等。在Java的实现过程中,也要考虑其对象释放,最好的方法是在不使用某对象时,显式地将此对象赋空。最好遵循谁创建谁释放的原则。
     
    长声明周期对象拥有短声明周期的引用的话,那么短声明周期的内存将不能被释放。
     
    Java中的垃圾回收机制-------------------------------------------------------
    1、  引用计数法(Reference Counting Collector)

    引用计数法是唯一没有使用根集的垃圾回收的法,该算法使用引用计数器来区分存活对象和不再使用的对象。一般来说,堆中的每个对象对应一个引用计数 器。当每一次创建一个对象并赋给一个变量时,引用计数器置为1。当对象被赋给任意变量时,引用计数器每次加1当对象出了作用域后(该对象丢弃不再使用), 引用计数器减1,一旦引用计数器为0,对象就满足了垃圾收集的条件。

    基于引用计数器的垃圾收集器运行较快,不会长时间中断程序执行,适宜地必须 实时运行的程序。但引用计数器增加了程序执行的开销,因为每次对象赋给新的变量,计数器加1,而每次现有对象出了作用域生,计数器减1。

    2、tracing算法(Tracing Collector)

    tracing算法是为了解决引用计数法的问题而提出,它使用了根集的概念。基于tracing算法的垃圾收集器从根集开始扫描,识别出哪些对象 可达,哪些对象不可达,并用某种方式标记可达对象,例如对每个可达对象设置一个或多个位。在扫描识别过程中,基于tracing算法的垃圾收集也称为标记 和清除(mark-and-sweep)垃圾收集器.

    3、compacting算法(Compacting Collector)

    为了解决堆碎片问题,基于tracing的垃圾回收吸收了Compacting算法的思想,在清除的过程中,算法将所有的对象移到堆的一端,堆的 另一端就变成了一个相邻的空闲内存区,收集器会对它移动的所有对象的所有引用进行更新,使得这些引用在新的位置能识别原来 的对象。在基于Compacting算法的收集器的实现中,一般增加句柄和句柄表。  

    4、copying算法(Coping Collector)

    该算法的提出是为了克服句柄的开销和解决堆碎片的垃圾回收。它开始时把堆分成 一个对象 面和多个空闲面, 程序从对象面为对象分配空间,当对象满了,基于coping算法的垃圾 收集就从根集中扫描活动对象,并将每个 活动对象复制到空闲面(使得活动对象所占的内存之间没有空闲洞),这样空闲面变成了对象面,原来的对象面变成了空闲面,程序会在新的对象面中分配内存。
     
    每次把内存分成两个区域,每次只是用一个区域,对一个区域进行回收的时候,我们把当前活动的对象移动到另一个区域中。
    优点:比较高效,简单
    缺点:每次都浪费一半的内存空间


    一种典型的基于coping算法的垃圾回收是stop-and-copy算法,它将堆分成对象面和空闲区域面,在对象面与空闲区域面的切换过程中,程序暂停执行。

    5、generation算法(Generational Collector)
      stop-and-copy垃圾收集器的一个缺陷是收集器必须复制所有的活动对象,这增加了程序等待时间,这是coping算法低效的原因。 在程序设计中有这样的规律:多数对象存在的时间比较短,少数的存在时间比较长。因此,generation算法将堆分成两个或多个,每个子堆作为对象的一 代(generation)。由于多数对象存在的时间比较短,随着程序丢弃不使用的对象,垃圾收集器将从最年轻的子堆中收集这些对象。在分代式的垃圾收集 器运行后,上次运行存活下来的对象移到下一最高代的子堆中,由于老一代的子堆不会经常被回收,因而节省了时间。

    6、adaptive算法(Adaptive Collector)

    在特定的情况下,一些垃圾收集算法会优于其它算法。基于Adaptive算法的垃圾收集器就是监控当前堆的使用情况,并将选择适当算法的垃圾收集器。
     
     
    finalize方法,在对象被释放前会被调用,所以可以在此方法中定义一些释放资源的操作。对象可能不被垃圾回收,所以此方法也就不会得到调用。
    transient 和 volatile都是java的关键字,transient是在序列化时这些变量的值不会被保存
    java序列化用什么用----用于对象的传输
    java为什么要引入内部类还要匿名类:
      一种代码隐藏机制,并且它了解外部类,可以与之通信,写出来的代码更加的清晰和明朗。
     
    1)transient和volatile是java关键字吗?(瞬联)
    2)抽象类和接口有什么区别?(瞬联)
    3)能说一下java的反射机制吗?(瞬联)
    4)在java中怎样实现多线程?(瞬联)
    5)你用过哪种设计模式?(瞬联,IBM,aspenTech)
    6)请说一下MVC架构(瞬联,IBM,aspenTech)
    7)如果类a继承类b,实现接口c,而类b和接口c中定义了同名变量,请问会出现什么问题?(瞬联)
    8)请说一下java中为什么要引入内部类?还有匿名内部类?(瞬联,IBM)
    9)请说一下final,finally和finalize的区别?(瞬联)
    10)请说一下HTTP请示的基本过程(IBM)
    11)java中存在内存泄漏问题吗?请举例说明?(IBM)
    12)请说一下java中的内存回收机制所采用的算法(IBM,瞬联)
    13)请说一下System.gc()函数的作用。什么什么时候可以调用垃圾回收器?(瞬联)
    14)你做过的项目中采用了什么安全认证机制?(IBM)
    15)Math.round()什么作用?
  • 相关阅读:
    11
    关于一些问题的解决办法[记录]TF400017
    jdbc一次性采集mysql和oracle的海量数据,5000W+为例
    java 读取配置文件类
    Apache Thrift
    Runtime.getRuntime().exec 类 防止阻塞
    angularjs select
    angularjs
    简单的搭mysql开发环境
    运用java反射
  • 原文地址:https://www.cnblogs.com/zhanglanyun/p/2670998.html
Copyright © 2011-2022 走看看