黑马程序员——高新技术--反射

时间:2023-02-17 22:39:24

——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——-

反射:

jdk1.2开始的新特性

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

动态获取类中信息,就是java反射。可以理解为对类的解剖。也可以理解为把Java类中的各个成分反射成相应的Java类

根据“万物皆对象”的原则,每个class都是一个实例对象(为Class类的实例对象),基本数据类型也是一个对象(为基本数据类型类),每个方法也是一个对象(Method类),成员变量(Field类)、包(Package类)、构造方法(Constructor类)等等也都是对象。

Class:
java类是用于描述一类事物的共性,那么各个java类当然也有共性,也可以用一个类来描述java类的共性,这个类就是Class类(注意c为大写)。

字节码:
类要编译成class存放在硬盘中,这些class文件实际上是一堆二进制代码,要把这些二进制代码加载进内存里面,才可以用它创建一个个对象。这些二进制代码就称为字节码。

每个类都有一份字节码,用于描述字节码的类就是Class类。通过使用该类的字节码创建Class类对象,我们可以得到该java类的各种信息,类的名字、类的访问属性、类的所属包名等等,可以获取字节码文件中的所有内容,那么反射就是依靠Class类完成的。想要对一个类文件进行解剖,只要获取到该类的字节码文件对象即可。

如果想要对指定名称的字节码文件进行加载并获取其中的内容并调用,这时就使用到了反射技术。

获取字节码文件对象有3种方式:

public class Person
{
private int age;
private String name;

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

System.out.println("Person param run..." + this.name + ":" + this.age);
}

public Person(){
super();
System.out.println("person run");
}

public void show(){
System.out.println(name + "...show run..." + age);
}

private void privateMethod(){
System.out.println("method run");
}

public void paramMethod(String str,int num){
System.out.println("paramMethod run..." + str + ":" + num);
}

public static void staticMethod(){
System.out.println("static method run...");
}
}

//要想要对字节码文件进行解剖,必须要有字节码文件对象。
public class ReflectDemo
{
public static void main(String[] args) throws ClassNotFoundException {
getClassObject_1();
System.out.println("--------------------");
getClassObject_2();
System.out.println("--------------------");
getClassObject_3();
}

/*
* 获取字节码对象的方式:
* 方式一:Object类中的getClass()方法的。
* 想要用这种方式,必须要明确具体的类,并创建对象。
* 麻烦。
*/

public static void getClassObject_1(){

Person p = new Person();

//需要有具体的对象
Class clazz = p.getClass();

Person p1 = new Person();
Class clazz1 = p1.getClass();

System.out.println(clazz == clazz1); //结果为true
}

/*
* 方式二:任何数据类型都具备一个静态的属性.class来获取其对应的Class对象。
* 相对简单,但是还是要明确用到类中的静态成员。
* 还是不够扩展。
*/

public static void getClassObject_2(){

Class clazz = Person.class;
Class clazz1 = Person.class;

System.out.println(clazz == clazz1); //结果为true
}

/*
* 方式三:只要通过给定的类的字符串名称就可以获取该类,更为扩展。
* 可以用Class类中的方法完成。
* 该方法就是forName。
* 这种方法只要有名称即可,更为方便,扩展性更强。
*/

public static void getClassObject_3() throws ClassNotFoundException {

//可以把类的字符串名称写到配置文件中,然后读取出来。
//如果有打包的话,要写完整的名称
String className = "Person";
Class clazz = Class.forName(className);

System.out.println(clazz);
}
}

获取Class中的构造函数:

import java.lang.reflect.*;

public class ReflectDemo
{

public static void main(String[] args) throws Exception {
createNewObject_1();
System.out.println("--------------------");
createNewObject_2();
}

public static void createNewObject_1() throws ClassNotFoundException,InstantiationException,IllegalAccessException {
//早期:new时候,先根据被new的类的名称找寻该类的字节码文件,加载进内存,并创建该字节码文件对象,并接着创建该字节文件的对应的Person对象。
//Person p = new Person();

//现在:
String name = "Person";
//找寻该文件类文件,并加载进内存,并产生Class对象。
Class clazz = Class.forName(name);
//如何产生该类的对象呢?
//调用Person的空参构造函数
Object obj = clazz.newInstance();
}

public static void createNewObject_2() throws ClassNotFoundException,InstantiationException,NoSuchMethodException,IllegalAccessException,InvocationTargetException {

//Person p = new Person("小强",39);

/*
* 当获取指定名称对应类中的所体现的对象时。
* 而该对象初始化不使用空参数构造函数该怎么办呢?
* 既然是通过指定的构造函数进行对象的初始化。
* 所以应该先获取到该构造函数,通过字节码文件对象即可完成。
* 该方法是:getConstructor(parameterTypes);
*/


String name = "cn.itcast.bean.Person";
//找寻该名称类文件,并加载进内存,并产生Class对象。
Class clazz = Class.forName(name);
//获取到了指定的构造函数对象
Constructor constructor = clazz.getConstructor(int.class,String.class);
//通过该构造器对象的newInstance方法进行对象的初始化。
Object obj = constructor.newInstance(38,"小明");
}
}

获取Class的字段(即属性):

import java.lang.reflect.*;

public class ReflectDemo
{
public static void main(String[] args) throws Exception {
getFieldDemo();
}

public static void getFieldDemo() throws Exception {

Class clazz = Class.forName("cn.itcast.bean.Person");

//getField只能获取所有可访问公共字段,private获取不到。
//Field field = clazz.getField("age");

//getDeclaredField可以获取到公共字段,也可以获取到私有字段。
Field field = clazz.getDeclaredField("age");

//对私有字段的访问取消权限检查,暴力访问。
field.setAccessible(true);

Object obj = clazz.newInstance();

//为对象的属性赋值
field.set(obj,89);

//获取某对象的某属性值
Object o = field.get(obj);

System.out.println(field);
}
}

获取Class中的方法:

import java.lang.reflect.*;

public class ReflectDemo
{

public static void main(String[] args) throws Exception {
getMethodDemo_1();
System.out.println("---------------------------");
getMethodDemo_2();
System.out.println("---------------------------");
getMethodDemo_3();
}

/*
* 获取指定Class中的公共函数。
*/

public static void getMethodDemo_1() throws Exception {

Class clazz = Class.forName("cn.itcast.bean.Person");

//获取的都是公有的方法
Method[] methods = clazz.getMethods();

//只获取本类中所有方法,包括私有。
methods = clazz.getDeclaredMethods();

for(Method method : methods){
System.out.println(method);
}
}

public static void getMethodDemo_2() throws Exception {

Class clazz = Class.forName("cn.itcast.bean.Person");

//获取空参数一般方法
Method method = clazz.getMethod("show",null);

Object obj = clazz.newInstance();

Constructor constructor = clazz.getConstructor(int.class,String.class);
obj = constructor.newInstance(37,"小明");

//调用空参数方法
method.invoke(obj,null);
}

public static void getMethodDemo_3() throws Exception {

Class clazz = Class.forName("cn.itcast.bean.Person");

//获取带有参数的方法
Method method = clazz.getMethod("paramMethod",String.class,int.class);

Object obj = clazz.newInstance();

Constructor constructor = clazz.getConstructor();
obj = constructor.newInstance();

//调用带有参数的方法
method.invoke(obj,"小强",89);
}
}

反射在框架中的应用:

软件框架是面向领域(如ERP、计算领域等)的、可复用的“半成品”软件,它实现了该领域的共性部分,并提供了一些定义良好的可变点以保证灵活性和可扩展性。也就是说软件框架是领域分析结果的软件化,是领域内最终应用的模板。

如果用电脑来描述的话,框架就相当于主板,框架已经把计算机的成分定义完成了,而我们要做的只是专心于计算机各种模块(类),将他应用到主板上(框架)供其使用,就可以完成一部电脑(软件)。

但是,很明显,框架是先开发出来的,在我们还不知道会有什么类的情况下,怎么让框架去调用后写出来的类呢?这时候就要用到反射。可以用Class对象先写好代码,之后再动态地定义该对象指向的字节码。

反射的好处:

其实就一句话:高类聚低耦合。把代码动态化,提高了代码的灵活度减少了代码量!

反射的缺点:

代码灵活度增大了,自然会带来效率的问题,因此,大量使用反射会带来效率低的问题!