黑马程序员——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培训、期待与您交流!
----------
相关文章推荐
- 黑马程序员Java培训、Android培训_语言基础
- 黑马程序员——java语言基础——常见小实例(For语句)
- 黑马程序员-Java语言基础 第4天
- 黑马程序员-Java语言基础– 多线程 第12天
- 黑马程序员-Java语言基础–面向对象 第8天
- 黑马程序员_Java基础增强_jdk1.5新特性(2) 反射(1)
- 黑马程序员——java语言基础——总结一下学习过的类
- 黑马程序员-Java语言基础–面向对象 第5天
- 黑马程序员-Java语言基础 第2天
- 黑马程序员-Java语言基础– 多线程 第11天
- 黑马程序员——java语言基础知识回顾(一)
- 黑马程序员——java语言基础——静态static知识小结
- 黑马程序员——java语言基础知识回顾(三)
- 黑马程序员-Java语言基础 –面向对象 第6天
- 黑马程序员Java培训、Android培训_Java 学习过程记录_语言基础2
- 黑马程序员——java语言基础知识回顾(二)
- 黑马程序员——java语言基础知识回顾(四)
- 黑马程序员——java语言基础——面向对象
- 黑马程序员-Java语言基础–面向对象 第9天
- 黑马程序员-Java语言基础 –面向对象 第7天