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

来源:互联网 发布:java 圆周率算法 编辑:程序博客网 时间:2024/04/28 05:29

------- 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培训、期待与您交流! ----------

0 0