当前位置:首页 > 技术博文 > java学习中如何认识反射,业内专家告诉你
java学习中如何认识反射,业内专家告诉你
时间:2018-06-22作者:华清远见

反射的概念

反射的概念是由 Smith 在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力。

换句话说,就是能够得到代码自身的特征。

换句话说,就是把类本身也看成是对象,包括类中的变量名、方法名、内部类、超类、包、修饰符等等,都可以通过代码来得到并被看成是对象。

java为此设计了一些类来方便我们使用反射。这些类并不多,它们分别是:Field、Constructor、Method、Class、Object,下面对这些类做一个简单的说明。

摘抄于其它资料,仅供阅读

Field 类:提供有关类或接口的属性的信息,以及对它的动态访问权限。反射的字段可能是一个类(静态)属性或实例属性,简单的理解可以把它看成一个封装反射类的属性的类。

Constructor 类:提供关于类的单个构造方法的信息以及对它的访问权限。这个类和 Field 类不同,Field 类封装了反射类的属性,而 Constructor 类则封装了反射类的构造方法。

Method 类:提供关于类或接口上某个单独方法的信息。所反映的方法可能是类方法或实例方法(包括抽象方法)。 这个类不难理解,它是用来封装反射类方法的一个类。

Class 类:类的实例表示正在运行的 Java 应用程序中的类和接口。枚举是一种类,注释是一种接口。每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象。

Object 类:每个类都使用 Object 作为超类。所有对象(包括数组)都实现这个类的方法。

获取Class类

有一个类,类名是Class,(首字母大写,不同于关键字class)。任何一个java类都是这个Class类的对象,即“类本身也是对象”的感觉。

一旦我们获取到了一个类的Class实例,那么在此基础上要获取Field、Constructor、Method等等的话也就很容易了(因此java的所有代码都在类中的嘛)。所以首要步骤是获取Class实例。

获取类自身有三种方式:

(1)利用 对象.getClass() 的方式获取该对象的Class实例;

(2)利用 对象.class 的方式来获取Class实例,对于基本数据类型的封装类,还可以采用.TYPE来获取相对应的基本数据类型的Class实例;

(3)使用 Class类的静态方法forName(“全路径名”),用类的名字获取一个Class实例。

示例

class ClassTest {

public static void main(String[] args) throws Exception {

String str1 = "abc";

Class cls1 = str1.getClass();//法一

Class cls2 = String.class;//法二

Class cls3 = Class.forName("java.lang.String");//法三

System.out.println(cls1 == cls2);

System.out.println(cls1 == cls3);

}

}

运行结果为

true

true

解释

1、运行结果为true说明虚拟机为某个类只会产生一份字节码,将来用这份字节码可以产生多个实例对象。

2、也即是说,在运行期间,如果我们要产生某个类的对象,Java虚拟机(JVM)会检查该类型的Class对象是否已被加载。如果没有被加载,JVM会根据类的名称找到.class文件并加载它。一旦某个类型的Class对象已被加载到内存,就可以用它来产生该类型的所有对象。

利用Class实例创建对象

以前我们创建对象都是用“new 类名()”的方式,现在我们先得到构造方法,并用构造方法来创建。现在我们要使用Consturctor(构造器)类:它代表某个类中的一个构造方法。

得到某个类所有的构造方法

Constructor [] constructors = Class.forName("java.lang.String").getConstructors();

得到某一个构造方法

Constructor constructor = Class.forName("java.lang.String").getConstructor(StringBuffer.class);

注:参数是一个Class实例,即去拿匹配这样参数的构造方法。

创建实例对象,用Constructor的newInstance方法

传统方式:String str=new String(new StringBuffer("abc"));

反射方式:String str=(String) constructor.newInstance(new StringBuffer("abc"));

注:newInstance()方法参数可变长,请尝试放多个参数。不合适时,报异常IllegalArgumentException。

上述原理可以下面示例来演练

class Test {

public static void main(String[] args) throws Exception {

Class c = Class.forName("java.lang.String");

Constructor constructor = c.getConstructor(StringBuffer.class);

String str = (String) constructor.newInstance(new StringBuffer("abc"));

System.out.println(str);

}

}

利用Constructor来创建实例与利用Class类来创建实例

class类也有创建实例的方法,下面的例子进行了展示。

此例来源于http://seahb.iteye.com/blog/855107。

import java.lang.reflect.Constructor;

class A {

private A() { // 将private改为public试试

System.out.println("A's constructor A() is called.");

}

private A(int a, int b) {

System.out.println("A's constructor A(a,b) is called.");

System.out.println("a:" + a + " b:" + b);

}

}

class B {

public static void main(String[] args) {

B b = new B();

System.out.println("通过Class.NewInstance()调用私有构造函数:");

b.byClassNewInstance();

System.out.println("通过Constructor.newInstance()调用私有构造函数:");

b.byConstructorNewInstance();

}

/* 法一:通过Class.NewInstance()创建新的类示例 */

private void byClassNewInstance() {

try {

Class c = Class.forName("A");

A a = (A) c.newInstance();//调用无参构造方法。如果方法是私有的,则运行时会异常IllegalAccessException

} catch (Exception e) {

e.printStackTrace();

System.out.println("通过Class.NewInstance()调用构造方法【失败】");

}

}

/*法二:通过Constructor.newInstance()创建新的类示例 */

private void byConstructorNewInstance() {

try {

Class c = Class.forName("A");

Constructor c0 = c.getDeclaredConstructor();/* 调用无参构造方法 */

c0.setAccessible(true); //必须设置一下可见性后就可调用了

A a0 = (A) c0.newInstance();//调用构造方法

System.out.println("成功1");

Constructor c1 = c.getDeclaredConstructor(new Class[] { int.class, int.class });/* 调用带参构造方法 */

c1.setAccessible(true);

//A a1 = (A) c1.newInstance(new Object[] { 5, 6 });//参数是对象数组

A a1 = (A) c1.newInstance(5, 6);//参数可连写,因为newInstance()支持可变参数

//A a1 = (A) c1.newInstance(5, 6,7);//参数若不合适,则就报异常IllegalArgumentException

System.out.println("成功2");

} catch (Exception e) {

e.printStackTrace();

}

}

}

结论

class.newInstance和constructor.newInstance 区别

通过反射创建新的类示例,有两种方式:

Class.newInstance()

Constructor.newInstance()

Class.newInstance() 只能够调用无参的构造函数,即默认的构造函数;

Constructor.newInstance() 可以根据传入的参数,调用任意构造构造函数。

Class.newInstance() 要求被调用的构造函数是可见的,也即必须是public类型的;

Constructor.newInstance() 在特定的情况下,可以调用私有的构造函数。

如果被调用的类的构造函数为默认的构造函数,采用Class.newInstance()则是比较好的选择,一句代码就OK;如果是调用带参构造函数、私有构造函数,就需要采用Constractor.newInstance(),两种情况视使用情况而定。不过Java Totorial中推荐采用Constractor.newInstance()。


发表评论

全国咨询电话:400-611-6270,双休日及节假日请致电值班手机:15010390966

在线咨询: 曹老师QQ(3337544669), 徐老师QQ(1462495461), 刘老师 QQ(3108687497)

企业培训洽谈专线:010-82600901,院校合作洽谈专线:010-82600350,在线咨询:QQ(248856300)

Copyright 2004-2018 华清远见教育集团 版权所有 ,京ICP备16055225号,京公海网安备11010802025203号