您的位置:首页 > 职场人生

黑马程序员——Java语言基础——10.反射

2014-12-20 11:27 344 查看



------- android培训java培训、期待与您交流!
----------

本节考点:

一、获取Class对象的三种方式
二、对反射的理解
三、暴力反射
四、使用反射设计程序

1-1 反射概述

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

反射其实就是动态加载一个指定的类,并获取该类中的所有的内容。而且将字节码文件封装成对象,

并将字节码文件中的内容都封装成对象,这样便于操作这些成员。就是把JAVA类中的各种成分反射成为相应的JAVA类

简单说:反射技术可以对一个类进行解剖。

反射的好处:大大的增强了程序的扩展性。

1-1-1 反射的基本步骤

1、获得Class对象,就是获取到指定的名称的字节码文件对象。

2、实例化对象,获得类的属性、方法或构造函数。

3、访问属性、调用方法、调用构造函数创建对象。

1-1-2 获取Class对象的方式

获取这个Class对象,有三种方式:

1:通过每个对象都具备的方法getClass来获取。弊端:必须要创建该类对象,才可以调用getClass方法。

2:每一个数据类型(基本数据类型和引用数据类型)都有一个静态的属性class。弊端:必须要先明确该类。

 前两种方式不利于程序的扩展,因为都需要在程序使用具体的类来完成。

3:使用的Class类中的方法,静态的forName方法。

 指定什么类名,就获取什么类字节码文件对象,这种方式的扩展性最强,只要将类名的字符串传入即可。

注意:字节码文件是唯一的,所以无论怎么获取,都是同一份字节码文件。

示例:

// 1. 根据给定的类名来获得 用于类加载
String classname = "cn.itcast.reflect.Person";// 来自配置文件
Class clazz = Class.forName(classname);//此对象代表Person.class
// 2. 如果拿到了对象,不知道是什么类型 用于获得对象的类型
Object obj = new Person();
Class clazz1 = obj.getClass();// 获得对象具体的类型
// 3. 如果是明确地获得某个类的Class对象 主要用于传参
Class clazz2 = Person.class;

1-1-3 九个预定义Class实例对象

Class 类的实例表示正在运行的 Java 应用程序中的类和接口。枚举是一种类,注释是一种接口。

每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该Class 对象。

基本的 Java 类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void也表示为 Class 对象

1-2 反射的用法

1-2-1 创建对象

获取了字节码文件对象后,最终都需要创建指定类的对象:

创建对象的两种方式(其实就是对象在进行实例化时的初始化方式):

1,调用空参数的构造函数:使用了Class类中的newInstance()方法。

2,调用带参数的构造函数:先要获取指定参数列表的构造函数对象,然后通过该构造函数的对象的newInstance(实际参数) 进行对象的初始化。

综上所述,第二种方式,必须要先明确具体的构造函数的参数类型,不便于扩展。所以一般情况下,被反射的类,内部通常都会提供一个公有的空参数的构造函数。

String name="com.dsa.类名";
//寻找该名称类文件,并加载进内存,并非产生class对象
Class clazz=Class.forName(name);
//产生该类的对象
Object obj=clazz.newInstance();
//得到某一个指定构造方法
Constructor constructor= Class.forName("").getConstructor(String.class);
//创建实例对象
Object obj=constructor.newInstance("abc");

1-2-2 Constructor构造方法

代表某个类中的一个构造方法。

Constructor类的实例对象代表类的一个构造方法。 

反射公共,私有和保护的构造方法: 

反射公共的需要的方法是:getConstructor(); 

反射私有的需要的方法是:getDeclaredConstructor(); 

Constructor对象代表一个构造方法,Constructor对象有的方法:得到构造方法名字,得到所属于的类,产生实例对象。

得到某个类空参数构造方法,例:

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

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

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

得到某一个带参数的构造方法,例:         

Constructor constructor =Class.forName("java.lang.String").getConstructor(StringBuffer.class); 
注意:一个类有多个构造方法,用什么方式可以区分清楚想得到其中的哪个方法呢?根据参数的个数和类型,例如,Class.getMethod(name,Class... args)中的args参数就代表所要获取的那个方法的各个参数的类型的列表。重点:参数类型用什么方式表示?用Class实例对象。

1-2-3 成员变量的反射(Field类) 

Field类代表反射某个类中的一个成员变量。 

问题:得到的Field对象是对应到类上面的成员变量,还是对应到对象上的成员变量?类只有一个,而该类的实例对象有多个,如果是与对象关联,那关联的是哪个对象呢?所以字段fieldAge代表的是Age的定义,而不是具体的Age变量。

(注意访问权限的问题)也就是说,定义的是类对象,而非对象的对象。

当我们需要对其操作的时候,需要确定是那个具体的对象。 

示例:

package com.itheima.study;

public class Person {

<span style="white-space:pre"> </span>public int age;

<span style="white-space:pre"> </span>private int height;

<span style="white-space:pre"> </span>public Person(int age,int height){

<span style="white-space:pre"> </span>this.age=age;

<span style="white-space:pre"> </span>this.height=height;

<span style="white-space:pre"> </span>}

}

package com.itheima.study;

import java.lang.reflect.Field;

public class ReflectTest{

<span style="white-space:pre"> </span>public static void main(String...args) throws Exception{

<span style="white-space:pre"> </span>Person p = new Person(20,30);

<span style="white-space:pre"> </span>Field fieldAge = p.getClass().getField("age");

<span style="white-space:pre"> </span>System.out.println(fieldAge.get(p));

<span style="white-space:pre"> </span>}

}

1-2-4 成员方法的反射(Method类) 

Method类代表某个类中的一个成员方法 

得到类中的某一个方法: 

例子:Method charAt =Class.forName("java.lang.String").getMethod("charAt",int.class); 

调用方法:

通常方式:System.out.println(str.charAt(1));

反射方式:System.out.println(charAt.invoke(str, 1)); 
如果传递给Method对象的invoke()方法的第一个参数为null,这有着什么样的意义呢? 

说明该Method对象对应的是一个静态方法。

反射各种使用示例:

class ReflectDemo
{

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

// 获取类的字节码文件,并创建对象
public static void getMethod_1()throws Exception
{
String className = "Person";

Class clazz = Class.forName(className);//知道了类的名字,通过forName方法获取

Object obj = clazz.newInstance();//创建空参数构造函数的对象,等于new Person()

System.out.println(obj);
}

// 获取构造器对象
public static void getMethod2()throws Exception
{
String className = "Person";
Class clazz = Class.forName(className);
Constructor cons = clazz.getConstructor(String.class,int.class);//获取指定构造函数的构造器

Object obj = cons.newInstance("nishiabi",12);//通过构造器对象中的方法创建指定参数的Person对象,如同new Person(“shacha”,10);

System.out.println(obj);
}

// 暴力访问和获取字段对象
public static void getMethod3()throws Exception
{
String className = "Person";
Class clazz = Class.forName(className);

String fieldName = "age";

Field field = clazz.getDeclaredField(fieldName);//获取字段对象

Object obj = clazz.newInstance();

field.setAccessible(true);//取消权限检查

field.set(obj,20);

System.out.println(obj);

}

// 非静态方法
public static void getMethod4()throws Exception
{
String className = "Person";
Class clazz = Class.forName(className);
Object obj = clazz.newInstance();

Method method = clazz.getMethod("sop1",String.class);//获取方法对象
method.invoke(obj,"nihao");//调用非静态函数的方法,需要传入对象和实参

}

// 使用静态方法
public static void getMethod5()throws Exception
{
String className = "Person";
Class clazz = Class.forName(className);
// Object obj = clazz.newInstance();不用创建对象

String methodName = "sop";
Method method = clazz.getMethod(methodName,null);
method.invoke(null,null);//不用写对象和参数
}
}

1-3 反射的使用

练习一:利用反射的思想设计程序

import java.util.*;
import java.io.*;

class NoteBookRun
{
public static void main(String[] args) throws Exception
{
NoteBook open = new NoteBook();
open.run();

// 这样传统的接口方法太麻烦,需要反复修改代码才能做到程序的拓展,因此重新使用反射的思想来设计程序

//建立配置文件
File configFile = new File("temp\\usb.properties");
if (!configFile.exists())
{
configFile.createNewFile();
}

//读取配置文件
FileReader fr = new FileReader(configFile);

//方法获取其中的信息,建立properties对象
Properties prop = new Properties();
prop.load(fr);

System.out.println(prop);

//因为Properties对象中不存在0角标,而是从1开始,所以为0会出现空指针异常
for (int i=1; i<=prop.size(); i++)
{
String className = prop.getProperty("usb"+i);
Class clazz = Class.forName(className);
USB usb = (USB)clazz.newInstance();

open.useUSB(usb);

}

fr.close();
}
}

class NoteBook
{
public void run()
{
System.out.println("NoteBook run");
}

public void useUSB(USB usb)
{
if(usb != null)
{
usb.open();
usb.close();
}
}

}
//
//
class MouseByUSB implements USB
{
public void open()
{
System.out.println("mouse open");
}

public void close()
{
System.out.println("mouse close");
}

}
class KeyByUSB implements USB
{
public void open()
{
System.out.println("key open");
}

public void close()
{
System.out.println("key close");
}

}

interface USB
{
void open();

void close();
}练习二:定义功能,可同时打印数组和一般数据
import java.lang.reflect.*;

public class PrintArray {
public static void main(String[] args) {
//定义个功能,是数组就打印数组,是基本变量就打印数值
int[] a = {110, 112, 119};
sop(a);
}

private static void sop(Object obj) {
//利用反射来判断,传入的类是什么类型
Class clazz = obj.getClass();

if (clazz.isArray()) {
//是数组,取元素打印
int len = Array.getLength(obj);// 关键还是数组类,为我们提供了方法
for (int i = 0; i < len; i++) {
System.out.print(Array.get(obj, i) + " ");
}
} else {
//不是数组是变量,直接打印
System.out.println(obj);
}
}
}


------- android培训java培训、期待与您交流!
----------

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: