【java基础 七】反射机制

时间:2023-02-16 14:12:29

转自http://blog.csdn.net/nalanmingdian/article/details/77718120

基本概念

反射机制的定义

所谓的反射机制,即在运行状态中,对于任意一个文件,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性。这种动态获取信息以及动态调用对象的方法的功能就称为Java语言的反射机制。

反射机制的作用

1,反编译:.class–>.java
2 , 通过反射机制访问java对象的属性,方法,构造方法等;

反射机制提供的类

java.lang.Class;
java.lang.reflect.Constructor
java.lang.reflect.Field;
java.lang.reflect.Method;
java.lang.reflect.Modifier;

首先,我们知道,反射机制是要对字节码文件进行剖析的,那么就必须要有字节码文件对象。而字节码文件是以.class为扩展名的,因此,Java提供的反射类中就有java.lang.Class。

其次,我们知道,反射机制是对Java对象的属性,方法,构造方法等进行访问。 那么就有相应的反射类,如对属性的访问java.lang.reflect.Filed对方法的访问java.lang.reflect.Method,对构造器的访问java.lang.reflect.Constructor。

具体功能实现

待反射的类文件如下:


package com;

public class Person {
String name;
private int age;
public int money;
public Person() {
System.out.println("无参构造器");
}

public Person(String name, int age) {
System.out.println("有参构造器");
this.name = name;
this.age = age;
}


@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}

关于Class

特性

1、Class是一个类,一个描述类的类(也就是描述类本身),封装了描述方法的Method,描述字段的Filed,描述构造器的Constructor等属性
2、对象照镜子后(反射)可以得到的信息:某个类的数据成员名、方法和构造器、某个类到底实现了哪些接口。
3、对于每个类而言,JRE 都为其保留一个不变的 Class 类型的对象。 一个 Class 对象包含了特定某个类的有关信息。
4、Class 对象只能由系统建立对象
5、一个类在 JVM 中只会有一个Class实例

获取一个Class对象

  public static void main(String[] args) throws ClassNotFoundException {
Class clazz = null;

//1 直接通过类名.Class的方式得到
clazz = Person.class;
System.out.println("通过类名: " + clazz);

//2 通过对象的getClass()方法获取,这个使用的少(一般是传的是Object,不知道是什么类型的时候才用)
Object obj = new Person();
clazz = obj.getClass();
System.out.println("通过getClass(): " + clazz);

//3 通过全类名获取,用的比较多,但可能抛出ClassNotFoundException异常
clazz = Class.forName("com.Person");
System.out.println("通过全类名获取: " + clazz);
}

测试结果:

通过类名: class com.Person
无参构造器
通过getClass(): class com.Person
通过全类名获取: class com.Person

利用newInstance创建对象


/**
* Class类的newInstance()方法,创建类的一个对象。
*/

@Test
public void testNewInstance()
throws ClassNotFoundException, IllegalAccessException, InstantiationException {

Class clazz = Class.forName("com.java.reflection.Person");

//使用Class类的newInstance()方法创建类的一个对象
//实际调用的类的那个 无参数的构造器(这就是为什么写的类的时候,要写一个无参数的构造器,就是给反射用的)
//一般的,一个类若声明了带参数的构造器,也要声明一个无参数的构造器
Object obj = clazz.newInstance();
System.out.println(obj);
}

注意,抛出了三个异常。
当想对对象初始化不使用空参数构造时,应先获得该构造函数,然后再进行初始化。如下所示,构造含有两个参数的初始化。

public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {
Class clazz = Class.forName("com.Person");

//使用Class类的newInstance()方法创建类的一个对象
//实际调用的类的那个 无参数的构造器(这就是为什么写的类的时候,要写一个无参数的构造器,就是给反射用的)
//一般的,一个类若声明了带参数的构造器,也要声明一个无参数的构造器

Constructor cs = clazz.getConstructor(String.class,int.class);
Object obj = cs.newInstance("tml",12);
System.out.println(obj);
}

测试结果

有参构造器
Person{name='tml', age=12}

获取字段

public static void main(String[] args) throws Exception {
Class clazz=Class.forName("com.Person");
//访问本类中所有字段
Field[] fs=clazz.getDeclaredFields();
for (int i = 0; i < fs.length; i++) {
System.out.println("访问本类中所有字段---fs-->"+fs[i]);
}
//访问本类中的公有字段
Field[] fields=clazz.getFields();
for (int i = 0; i < fields.length; i++) {
System.out.println("访问本类中的公有字段---fields-->"+fields[i]);
}
System.out.println("==================================================================");
//getField访问某个字段,该字段必须公有
Field field=clazz.getField("money");
System.out.println("访问指定字段,该字段必须公有---field-->"+field);
//getDeclaredField访问本类中某个字段,可私有
Field field2=clazz.getDeclaredField("name");
System.out.println("访问指定字段,该字段可私有---declaredField-->"+field2);
//getField访问字段为私有,运行时异常:NoSuchFieldException
// Field field1=clazz.getField("id");
}

运行结果

访问本类中所有字段---fs-->private java.lang.String com.Person.name
访问本类中所有字段---fs-->private int com.Person.age
访问本类中所有字段---fs-->public int com.Person.money
访问本类中的公有字段---fields-->public int com.Person.money
==================================================================
访问指定字段,该字段必须公有---field-->public int com.Person.money
访问指定字段,该字段可私有---declaredField-->private java.lang.String com.Person.name

获取方法

获取方法的方式

同获取字段类似,获取方法有getMethod和getDeclaredMethod,其中前者是获取的都是公有的,后者获取的都是本类的,包括私有

 public static void main(String[] args) throws Exception {
Class clazz=Class.forName("com.Person");
Method[] methods=clazz.getMethods(); //所有公有方法
for (int i = 0; i < methods.length; i++) {
System.out.println(methods[i]);
}
System.out.println("=======================================================");
Method[] methods2=clazz.getDeclaredMethods();//本类的全部方法
for (int i = 0; i < methods2.length; i++) {
System.out.println(methods2[i]);
}
}

运行结果

public static void com.Person.main(java.lang.String[]) throws java.lang.Exception
public java.lang.String com.Person.toString()
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
=======================================================
public static void com.Person.main(java.lang.String[]) throws java.lang.Exception
public java.lang.String com.Person.toString()
private void com.Person.tml()

获取指定方法,执行方法

  public static void main(String[] args) throws Exception {
Class clazz=Class.forName("com.Person");
//有参的方法获取
Method method=clazz.getMethod("tml", int.class);
Object obj=clazz.newInstance();
method.invoke(obj, 21);
//无参的方法获取
Method method1=clazz.getMethod("tml", null);
Object object=clazz.newInstance();
method1.invoke(object, null);
//利用构造方法初始化然后来获取无参方法
Constructor cs=clazz.getConstructor(String.class,int.class);
Object obj2=cs.newInstance("xiaoming",23);
Method method2=clazz.getMethod("getName",null); //运行前已赋值
method2.invoke(obj2, null);
}

运行结果

无参构造器
我是一个有参tml3
无参构造器
我是一个无参tml
有参构造器
xiaoming:23

【java基础 七】反射机制

总结

以上就是基于反射来动态获取某个类中的信息或对象信息以及对类和对象属性和方法等的调用。我们就是通过这些方法来进行反编译的。

尽管反射非常强大,但也不能滥用。如果一个功能可以不用反射完成,那么最好就不用。在我们使用反射技术时,下面几条内容应该牢记于心:

性能第一

反射包括了一些动态类型,所以JVM无法对这些代码进行优化。因此,反射操作的效率要比那些非反射操作低得多。我们应该避免在经常被 执行的代码或对性能要求很高的程序中使用反射。

安全限制

使用反射技术要求程序必须在一个没有安全限制的环境中运行。如果一个程序必须在有安全限制的环境中运行,如Applet,那么这就是个问题了。

内部暴露

由于反射允许代码执行一些在正常情况下不被允许的操作(比如访问私有的属性和方法),所以使用反射可能会导致意料之外的副作用--代码有功能上的错误,降低可移植性。反射代码破坏了抽象性,因此当平台发生改变的时候,代码的行为就有可能也随着变化。