1、初始反射
反射是一切EE框架的基础,它指的是对象的反向处理,即根据对象倒推类的组成。
反射核心的处理就在于Object类的一个方法:取得Class对象
public final native Class<?> getClass();
Class类描述的就是各个类的组成(构造方法、普通方法、普通属性等)。
2、Class类的三种实例化方式
任何一个类的Class对象由JVM加载类后产生(该对象在JVM中全局唯一),用户只能调用指定方法来取得该对象。
Date date = new Date();
1)任何类的对象可以通过调用object类提供的getClass()取得该类的Class对象
date.getClass()
2)类名称.class可以直接根据某个具体类来取得其class对象
Date.class
3)调用Class类的静态方法Class.forName(String className)传入类的全名称来取得其Class对象
java.util.Date
public class Reflect {
public static void main(String[] args) throws ClassNotFoundException {
Class<?> cls = Class.forName("java.util.Date");
System.out.println(cls.getName());
}
}
取得一个类的Class对象后,可以通过反射来实例化对象:
在Class类中有如下方法:
public T newInstance() throws InstantiationException,IllegalAccessException
注意:此方法只能调用类中无参构造且无参构造必须是public权限
public class Reflect {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
Class<?> cls = Class.forName("java.util.Date");
Object obj = cls.newInstance(); //实例化对象,等价于new java.util.Date()
System.out.println(obj);
}
}
实例化对象有两种方式:
1)使用new关键字
2)使用反射
3、反射与类操作
3.1 反射取得父类、父接口信息
- 取得类的包名称
public Package getPackage()
- 取得父类的Class对象
public native Class<? super T> getSuperclass()
- 取得实现的父接口
public Class<?>[] getInterfaces()
取得包名称:
interface IFruit {
}
interface IMessage {
}
class CLS implements IFruit, IMessage {
}
public class Reflect {
public static void main(String[] args) {
Class<?> cls = CLS.class; //取得class对象
System.out.println(cls.getPackage().getName());
}
}
取得父类(实现的父接口):
interface IFruit {
}
interface IMessage {
}
class CLS implements IFruit, IMessage {
}
public class Reflect {
public static void main(String[] args) {
Class<?> cls = CLS.class; //取得class对象
System.out.println(cls.getPackage().getName());
System.out.println(cls.getSuperclass().getName());
Class<?>[] iClass = cls.getInterfaces();
for (Class<?> class1 : iClass) {
System.out.println(class1.getName());
}
}
}
3.2 调用类中构造方法——Constructor(描述类中构造方法)
- 取得指定参数类型的构造:
public Constructor<T> getConstructor(Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException
——只能取得类中public权限的构造方法
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException
——可以取得类中全部构造方法,包含私有构造 - 取得类中的所有构造:
public Constructor<?>[] getConstructors() throws SecurityException
public Constructor<?>[] getDeclaredConstructors() throws SecurityException
class Person {
public Person() {
}
public Person(String name) {
}
public Person(String name, int age) {
}
}
public class ReflectConstructor {
public static void main(String[] args) {
Class<?> cls = Person.class;
Constructor<?>[] constructors = cls.getConstructors();
for (Constructor<?> constructor : constructors) {
System.out.println(constructor);
}
}
}
Constructor类提供了实例化对象的方法:
public T newInstance(Object ... initargs)
:与Class类不同的是,Constructor类中的newInstance方法可以调用类中其它有参构造
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person [name=" + name + ",age=" + age + "]";
}
}
public class ReflectConstructor {
public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
Class<?> cls = Person.class;
//取得指定参数类型的构造方法对象
Constructor<?> cons = cls.getConstructor(String.class, int.class);
System.out.println(cons.newInstance("Nanfeng", 21));
}
}
3.3 反射调用类中普通方法(***)
——Method(描述类中普通方法)
-
取得类中指定名称的普通方法
public Method getMethod(String name, Class<?>... parameterTypes)
public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
-
取得类中全部普通方法
public Method[] getMethods() throws SecurityException
——取得本类以及父类中所有public方法
public Method[] getDeclaredMethods() throws SecurityException
——取得本类中全部普通方法,包含私有方法
class Person1 {
private String name;
private int age;
public Person1() {
}
public Person1(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person [name=" + name + ",age=" + age + "]";
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
public class ReflectPuTong {
public static void main(String[] args) {
Class<?> cls = Person.class;
Method[] methods = cls.getMethods();
for (Method method : methods) {
System.out.println(method);
}
}
}
Method类中提供调用类中普通方法的API:
public Object invoke(Object obj,Object ... args)
通过反射调用setter、getter方法:
class Person1 {
private String name;
private int age;
public Person1() {
}
public Person1(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person [name=" + name + ",age=" + age + "]";
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
public class ReflectPuTong {
public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
//1.拿到PersonClass对象
Class<?> cls = Person1.class;
//2.创建Person实例化对象
Person1 per = (Person1) cls.newInstance();
//3.拿到setName的Method对象
Method setMethod = cls.getMethod("setName", String.class);
//4.通过invoke进行调用
setMethod.invoke(per, "Nanfeng");
System.out.println(per);
}
}
3.4 反射调用类中属性——Field
类中所有属性一定在类对象实例化之后才会进行空间分配,所以如果要调用类的属性,必须保证有实例化对象。通过反射的newInstance()可以直接取得实例化对象(Object类型)。
在Class类中提供有两组取得属性的方法:
1)第一组(父类中)
- 取得类中全部属性:
public Field[] getFields() throws SecurityException;
- 取得类中指定名称属性:
public Field getField(String name) throws NoSuchFieldException,SecurityException;
2)第二组(本类中)
- 取得类中全部属性:
public Field[] getDeclaredFields() throws SecurityException;
- 取得类中指定名称属性:
public Method getDeclaredMethod(String name,Class<?> ... parameterTypes) throws NoSuchFieldException,SecurityException;
取得类中全部属性:
class Person2 {
public String name;
public int age;
}
class Student extends Person2 {
private String school;
}
public class ReflectPro {
public static void main(String[] args) throws ClassNotFoundException {
Class<?> cls = Class.forName("com.nanfeng.reflect.Student");
{
//第一组:取得类中全部属性
Field[] fields = cls.getFields();
for (Field field : fields) {
System.out.println(field);
}
}
System.out.println("---------------------------");
{
//第二组:取得类中全部属性
Field[] fields = cls.getDeclaredFields();
for (Field field : fields) {
System.out.println(field);
}
}
}
}
在实际开发中,属性基本上都会进行封装,所以我们取得的属性基本上都是本类属性。
这就需要关注属性的核心描述类:java.lang.reflect.Field
,这个类中有两个重要方法:
- 设置属性内容:
public void set(Object obj,Object value) throws IllegalArgumentException,IllegalAccessException;
- 取得属性内容:
public Object get(Object obj) throws IllegalArgumentException;
通过反射操作属性:
class Person3 {
private String name;
}
public class GetReflectPro {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException {
Class<?> cls = Class.forName("com.nanfeng.reflect.Person3");
//实例化本类对象
Object obj = cls.newInstance();
//操作name属性
Field nameField = cls.getDeclaredField("name");
nameField.set(obj, "Nanfeng"); //等价于:对象.name = "Nanfeng"
System.out.println(nameField.get(obj));
}
}
动态设置封装:
Constructor、Method、Field类都是AccessibleObject子类。
在AccessibleObject类中提供一个方法,可以实现动态设置封装:
public void setAccessible(boolean flag) throws SecurityException;
(在本次JVM进程中有效且只能通过反射调用)
class Person3 {
private String name;
}
public class GetReflectPro {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException {
Class<?> cls = Class.forName("com.nanfeng.reflect.Person3");
//实例化本类对象
Object obj = cls.newInstance();
//操作name属性
Field nameField = cls.getDeclaredField("name");
//取消封装
nameField.setAccessible(true);
//---------------
nameField.set(obj, "Nanfeng"); //等价于:对象.name = "Nanfeng"
System.out.println(nameField.get(obj));
}
}
Field类中还有一个取得属性类型的方法:
public Class<?> getType();
class Person3 {
private String name;
}
public class GetReflectPro {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException {
Class<?> cls = Class.forName("com.nanfeng.reflect.Person3");
//实例化本类对象
Object obj = cls.newInstance();
//操作name属性
Field nameField = cls.getDeclaredField("name");
//包.类
System.out.println(nameField.getType().getName());
//类名称
System.out.println(nameField.getType().getSimpleName());
}
}
4、反射与简单Java类
假设现在有一个简单Java类,按照原始做法使用getter和setter对属性进行操作,如果一个类中存在几十个属性,那么操作就会超级麻烦。
学了反射之后我们就可以使用反射操作VO。
反射与单级VO操作
EMP类:
public class Emp {
private String ename;
private String job;
public String getEname() {
return ename;
}
public void setEname(String ename) {
this.ename = ename;
}
public String getJob() {
return job;
}
public void setJob(String job) {
this.job = job;
}
@Override
public String toString() {
return "Emp{" + "ename='" + ename + '\'' + ",job='" + job + '\'' + '}';
}
}
EmpAction类:
public class EmpAction {
private Emp emp = new Emp();
public void setValue(String value) throws NoSuchMethodException, NoSuchFieldException, IllegalAccessException, InvocationTargetException {
BeanOperation.setBeanValue(this,value);
}
public Emp getEmp(){
return emp;
}
}
BeanOperation类:
/**
* 设置简单Java类
* Author:nanfeng
* Created:2019/8/4
*/
//BeanOperation类设计(公共程序类)
public class BeanOperation {
private BeanOperation(){
}
/**
* 负责设置类中的属性操作
* @param actionObject 表示当前发出设置请求的程序类的当前对象
* @param msg 属性的具体内容,格式为:“属性名称:|内容|属性名称:|内容...”
*/
public static void setBeanValue(Object actionObject,String msg) throws InvocationTargetException, NoSuchMethodException, IllegalAccessException, NoSuchFieldException {
//要想进行内容设置,必须将字符串拆分
//按照竖线拆分,取出所有要设置的内容
String[] result = msg.split("\\|");
//每次执行之后只剩下“属性名称:内容”
for (int i=0;i<result.length;i++){
//需要针对属性名称及内容做进一步拆分
String[] temp = result[i].split(":");
//属性名称“emp.ename”
String attribute = temp[0];
//属性内容
String value = temp[1];
String[] fields = attribute.split("\\.");
//获取当前操作的简单Java类对象(通过反射取得XXAction中的真实对象)
Object currentObject = getObject(actionObject,fields[0]);
//调用简单Java类的setter方法
setObjectValue(currentObject,fields[1],temp[1]);
}
}
/**
* 将给定字符串的首字母大写
* @param str 给定的字符串
* @return 返回首字母大写的字符串
*/
public static String initCap(String str){
return str.substring(0,1).toUpperCase()+str.substring(1);
}
/**
* 负责调用XXXAction类中的getter方法取得简单Java类对象
* @param obj 表示调用方法的所在类对象
* @param attribute 表示属性名称
* @return 调用对象结果
*/
public static Object getObject(Object obj,String attribute) throws NoSuchFieldException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
//调用指定属性的Field对象,目的是取得对象类型,如果没有此属性意味着操作无法继续进行(取得action对象的class对象)
Field field = obj.getClass().getDeclaredField(attribute);
//拼装getEmp方法名
String methodName = "get" + initCap(attribute);
if (field==null){
//第二次尝试从父类中取得该属性
field = obj.getClass().getField(attribute);
}
if (field==null){
//两次都未取得指定属性的对象,表示该对象一定不存在
return null;
}
//取得Method对象
Method method = obj.getClass().getMethod(methodName);
//相当于调用empAction.getEmp();
return method.invoke(obj);
}
/**
* 根据指定的类对象设置指定类中的属性,调用setter方法
* @param obj 属性所在类的实例化对象
* @param attribute 属性名称
* @param value 属性内容
*/
public static void setObjectValue(Object obj,String attribute,String value) throws NoSuchFieldException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Field field = obj.getClass().getDeclaredField(attribute);
//判断属性是否存在
if (field==null){
//第二次尝试从父类中取得该属性
field = obj.getClass().getField(attribute);
}
if (field==null){
//两次都未取得指定属性的对象,表示该对象一定不存在
return;
}
String methodName = "set" + initCap(attribute);
Method setMethod = obj.getClass().getMethod(methodName,field.getType());
setMethod.invoke(obj,value);
}
}
TestDemo类:
public class TestDemo {
public static void main(String[] args) throws InvocationTargetException, NoSuchMethodException, IllegalAccessException, NoSuchFieldException {
String value = "emp.ename:Nanfeng|emp.job:Java Coder";
EmpAction empAction = new EmpAction();
empAction.setValue(value);
System.out.println(empAction.getEmp());
}
}
5、 ClassLoader类加载器
类加载器:
通过一个类的全名称来获取此类的二进制字节流,实现这个操作的代码模块称为类加载器。
5.1 认识ClassLoader
首先通过Class类观察如下方法:
public ClassLoader getClassLoader()
范例:编写一个简单的反射程序,来观察ClassLoader的存在:
public class TestDemo {
public static void main(String[] args) {
Class<?> cls = TestDemo.class;
System.out.println(cls.getClassLoader());
System.out.println(cls.getClassLoader().getParent());
System.out.println(cls.getClassLoader().getParent().getParent());
}
}
5.2 JDK中内置的三大类加载器
Bootstarp(启动类加载器):
- 使用C++实现,是JVM的一部分,其它所有类加载器均使用Java实现。
- 负责将存放于
JAVA_HOME\lib
目录下的能被JVM识别的
类库(rt.jar包存放了Java所有的基础类库,java.lang,java.util)加载到JVM中。 - 启动类加载器无法被Java程序直接引用。
ExtClassLoader(扩展类加载器)
- 使用Java实现,并且可以被Java程序直接引用。
- 加载Java_Home\lib\ext目录下能被识别的类库。
AppClassLoader(应用程序类加载器)
- 负责加载用户路径(classpath)上指定的类库。
- 如果应用程序中没有自定义类加载器,则此加载器就是Java程序中默认的类加载器。
5.3 类加载器双亲委派模型(考点)
——JDK 1.2引入
定义:
JDK内置的三种类加载器与用户自定义类加载器之间的层次关系称为类加载器的双亲委派模型。
要求除了顶层的父类加载器之外,其余的类加载器都应有自己的父类加载器。
执行流程:
如果一个类加载器收到了类加载请求,它首先不会自己去尝试加载此类,而是把类加载请求委托给父类加载器完成,每一个层次类加载器都是如此。只有当父类加载器无法完成加载请求时(在自己搜索范围内没有找到此类),子加载器才会尝试自己去加载。
意义:
双亲委派模型保证Java程序稳定运行。Java中基础类库一定由顶层BootStrap类加载器加载,因此,诸如Object等核心类在各种类加载器环境下都是同一个类。
5.4 自定义类加载器
自定义类加载器直接继承ClassLoader类即可。
class MyClassLoader extends ClassLoader
比较两个类相等的前提: 这两个类必须是由同一个类加载器加载的前提下才有意义。
6、反射与代理设计模式
6.1 基础代理:基于接口
interface ISubject {
void eat();
}
class RealSubject implements ISubject {
@Override
public void eat() {
System.out.println("吃签佰度");
}
}
class ProxySubject implements ISubject {
private ISubject subject;
public ProxySubject(ISubject subject) {
this.subject = subject;
}
@Override
public void eat() {
beforeEat();
this.subject.eat();
afterEat();
}
public void beforeEat() {
System.out.println("坐宝马吃串串");
}
public void afterEat() {
System.out.println("走回来锻炼");
}
}
public class JiChuDaiLi {
public static void main(String[] args) {
ISubject subject = new ProxySubject(new RealSubject());
subject.eat();
}
}
6.2 动态代理:一个代理类代理所有类似接口
要进行动态代理的实现,代理类不再具体实现某一个接口,实现InvocationHandle接口。
interface ISubject {
void eat(int count);
}
class RealSubject implements ISubject {
@Override
public void eat(int count) {
System.out.println("吃" + count + "签佰度");
}
}
class ProxySubject implements InvocationHandler {
private Object realObject;
/**
* 绑定真实主题类
*
* @param realObject
* @return 代理类
*/
public Object bind(Object realObject) {
this.realObject = realObject;
//把代理类和真实类绑定在一起
return Proxy.newProxyInstance(realObject.getClass().getClassLoader(),
realObject.getClass().getInterfaces(),
this);
}
public void before() {
System.out.println("eat before");
}
public void after() {
System.out.println("eat after");
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
this.before();
//调用真实主题类方法
Object result = method.invoke(realObject, args);
this.after();
return result;
}
}
public class DongTaiDaiLi {
public static void main(String[] args) {
ISubject subject = (ISubject) new ProxySubject().bind(new RealSubject());
subject.eat(100);
}
}