java反射深入剖析(推荐)_java

本篇文章依旧采用小例子来说明,因为我始终觉的,案例驱动是最好的,要不然只看理论的话,看了也不懂,不过建议大家在看完文章之后,在回过头去看看理论,会有更好的理解。

下面开始正文。

【案例1】通过一个对象获得完整的包名和类名

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

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

【案例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实例化其他类的对象

通过无参构造实例化对象

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

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

所以大家以后再编写使用Class实例化其他类的对象的时候,一定要自己定义无参的构造函数
 
【案例】通过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]

【案例】

返回一个类实现的接口:

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的代码)

【案例】:取得其他类中的父类

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

【案例】:获得其他类中的全部构造函数

这个例子需要在程序开头添加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)

但是细心的读者会发现,上面的构造函数没有public 或者private这一类的修饰符

下面这个例子我们就来获取修饰符

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){}

有时候一个方法可能还有异常,呵呵。下面看看:

class hello{
  public static void main(String[] args) {
    Class<?> demo=null;
    try{
      demo=Class.forName("Reflect.Person");
    }catch (Exception e) {
      e.printStackTrace();
    }
    Method method[]=demo.getMethods();
    for(int i=0;i<method.length;++i){
      Class<?> returnType=method[i].getReturnType();
      Class<?> para[]=method[i].getParameterTypes();
      int temp=method[i].getModifiers();
      System.out.print(Modifier.toString(temp)+" ");
      System.out.print(returnType.getName()+" ");
      System.out.print(method[i].getName()+" ");
      System.out.print("(");
      for(int j=0;j<para.length;++j){
        System.out.print(para[j].getName()+" "+"arg"+j);
        if(j<para.length-1){
          System.out.print(",");
        }
      }
      Class<?> exce[]=method[i].getExceptionTypes();
      if(exce.length>0){
        System.out.print(") throws ");
        for(int k=0;k<exce.length;++k){
          System.out.print(exce[k].getName()+" ");
          if(k<exce.length-1){
            System.out.print(",");
          }
        }
      }else{
        System.out.print(")");
      }
      System.out.println();
    }
  }
}

【运行结果】:

【案例】接下来让我们取得其他类的全部属性吧,最后我讲这些整理在一起,也就是通过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;

【案例】其实还可以通过反射调用其他类中的方法:

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

【案例】调用其他类的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

【运行结果】:

【案例】通过反射操作属性

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

【案例】通过反射取得并修改数组的信息:

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

【案例】通过反射修改数组大小

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

动态代理

【案例】首先来看看如何获得类加载器:

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对象时就会被卸载,结束类的生命周期

将反射用于工厂模式

先来看看,如果不用反射的时候,的工厂模式吧:

/**
 * @author Rollen-Holt 设计模式之 工厂模式
 */

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

}

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

现在我们看看利用反射机制:

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

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

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

下面我们来看看: 结合属性文件的工厂模式

首先创建一个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

以上这篇java反射深入剖析(推荐)就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持。

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索java反射
java反射机制深入、java反射书籍推荐、深入剖析android系统、深入剖析tomcat、深入剖析nginx,以便于您获取更多的相关知识。

时间: 2024-11-14 11:59:24

java反射深入剖析(推荐)_java的相关文章

Java反射机制剖析:定义和API

1.什么是Java反射机制 Java的反射机制是在程序运行时,能够完全知道任何一个类,及其它的属性和方法,并且能够任意调用一个对象的属性和方法.这种运行时的动态获取就是Java的反射机制.其实这也是Java是动态语言的一个象征. 用一句话来概括反射就是加载一个运行时才知道的类以及它的完整内部结构. 2.为什么要有Java反射机制 我们为什么要用Java的反射机制呢? 我认为有两种: 第一种:反射的目的就是为了扩展未知的应用.比如你写了一个程序,这个程序定义了一些接口,只要实现了这些接口的dll都

Java反射机制剖析:简单谈谈动态代理

通过<Java反射机制剖析:定义和API>和<Java反射机制剖析:功能以及举例>的学习,已经对反射有了一定的了解,这一篇通过动态代理的例子来进一步学习反射机制. 1.代理模式 代理模式就是为其他对象提供一种代理来控制对这个对象的访问.其实代理模式是在访问的对象时引入一定程度的间接性,这种间接性可以附加多种用途. 它的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息.过滤消息.把消息转发给委托类,以及事后处理消息等.代理类与委托类之间通常会存在关联关系,一个代理类

java反射机制剖析(一)—简介

    由之前动态代理的学习再次接触到反射这个知识点,第二次接触了所以做了一些稍微深入的了解.那么,对于反射这部分的内容我打算分三篇博客来总结.本篇博客先对反射做一个大概的了解,包括反射有关的RTTI.定义的理解以及涉及到的其他知识的简介. 回顾     java之前我接触反射这个知识,是在大话设计中的抽象工厂模式里,通过反射+配置文件来优化抽象工厂提高其应对需求变更的灵活性.当时对于反射的认知仅仅是它是一种技术,一种实例化对象的技术,一种实例化对象不依赖于写死的代码的技术.简单的说就是,它是一

Java反射机制详解_java

本文较为详细的分析了Java反射机制.分享给大家供大家参考,具体如下: 一.预先需要掌握的知识(java虚拟机) java虚拟机的方法区: java虚拟机有一个运行时数据区,这个数据区又被分为方法区,堆区和栈区,我们这里需要了解的主要是方法区.方法区的主要作用是存储被装载的类 的类型信息,当java虚拟机装载某个类型的时候,需要类装载器定位相应的class文件,然后将其读入到java虚拟机中,紧接着虚拟机提取class 中的类型信息,将这些信息存储到方法区中.这些信息主要包括: 1.这个类型的全

JAVA反射机制实例教程_java

本文以实例形式详细讲述了Java的反射机制,是Java程序设计中重要的技巧.分享给大家供大家参考.具体分析如下: 首先,Reflection是Java 程序开发语言的特征之一,它允许运行中的 Java 程序对自身进行检查,或者说"自审",并能直接操作程序的内部属性.例如,使用它能获得 Java 类中各成员的名称并显示出来. Java 的这一能力在实际应用中也许用得不是很多,但是在其它的程序设计语言中根本就不存在这一特性.例如,Pascal.C 或者 C++ 中就没有办法在程序中获得函数

配置pom.xml用maven打包java工程的方法(推荐)_java

最近由于项目需要,研究了一下maven的打包,项目要做到 1,生成3个目录/lib,/conf,/bin目录 2,把所有的jar目录编译.拷贝到/lib目录(包括maven的jar包和lib目录下的jar,以及编译的jar包) 3,把所有的启动脚本从工程根目录拷贝到/bin目录 4,把所有的配置文件从src/main/resources拷贝到/conf  下面是配置的pom.xml,我把相关的配置都加了注释,一看就能明白,把build节点拷贝到你们的项目中,就基本可以用了:) <project

java反射应用详细介绍_Java编程

本篇文章依旧采用小例子来说明,因为我始终觉的,案例驱动是最好的,要不然只看理论的话,看了也不懂,不过建议大家在看完文章之后,在回过头去看看理论,会有更好的理解. 下面开始正文. [案例1]通过一个对象获得完整的包名和类名 复制代码 代码如下: package Reflect; /** * 通过一个对象获得完整的包名和类名 * */ class Demo{ //other codes... } class hello{ public static void main(String[] args)

java编程常用技术(推荐)_java

一:将String字符串放在最前面 防止发生NullPointerException异常,我们通常把String字符串放在equals方法的左边来比较,这样可以有效的避免 空指针异常的发生. 第一种情况,如果variable为Null,则会发生空指针异常情况:第二种情况即使variable为Null,也不会发生空指针异常,而且不会丢失任何数据.所以建议 大家编程时把直接量放在左边.  二:不要相信早期的JDK API 在早期编程中,JDK API还不是很成熟,例如下面代码块: File file

java反射使用示例分享_java

复制代码 代码如下: public class ReflexTest {     public static void main(String[] args)      throws ClassNotFoundException, NoSuchMethodException, SecurityException,     IllegalAccessException, IllegalArgumentException, InvocationTargetException,      Instan