反射的基本概念
如果正常的情况下,如果使用一个类,则必须按照如下的步骤操作:
- 使用import 导入类所在的包;(类:java.lang.Class)
- 明确的使用类名称或借口名称定义对象;
- 通过关键字new进行类对象实例化;(构造方法:java.lang.reflect.Constructor);
- 产生对象可以使用“对象.属性”进行类中属性的调用(属性:java.lang.reflect.Field);
- 通过“对象.方法()”调用类中方法(方法:java.lang.reflect.Method);
反射过程不需要有明确类型的对象,所有的对象使用Object表示
1. 可以直接使用Object与反射机制的混合调用类中的方法。
Object类中的所有方法以及每一个方法使用上的注意事项:
- 对象克隆:protected Object clone() throws CloneNotSupportedException 创建并返回此对象的副本。 “复制”的精确含义可能取决于对象的类。
- 为什么克隆方法返回的是Object? 答:因为克隆方法可能针对所有类对象使用,为了统一参数用Object
- 克隆对象所在的类一定要实现java.lang.Cloneable接口而子类只需要继续调用Object 的克隆方法就可以成功实现克隆操作;
- 对象输出:public String toString() 返回对象的字符串表示形式。
- 直接输出对象时会默认调用toString()方法
- 原因:由于平时我们会直接System.out.println();来直接输出对象,那么我们打开源码看一下为什么会默认调用toString()方法
在printStream类中找到print输出方法 如下
public void print(Object obj) {write(String.valueOf(obj));}
可以看到他在输出的时候调用了String的valueOf方法,下面打开valueOf方法源码
public static String valueOf(Object obj) { return (obj == null) ? "null" : obj.toString(); }
从中我们可以看到如果传入的对象不为null的话就会自动的调用toString()方法然后返回
- 对象比较:public boolean equals(Object obj) 指示一些其他对象是否等于此。
- 有哪些时候会隐式调用此方法??
- 当我们保存Set集合时,会依靠hashCode()和equals()判断对象是否重复;
- 取得对象的hash码:public boolean equals(Object obj) 指示一些其他对象是否等于此。
- 可以理解为每一个对象的唯一编码,比较时会先判断编码是否相同,然后再调用equals方法判断是否相同
- 取得Class类对象:public final Class<?> getClass() 返回此Object的运行时类。
- 通过一个已经实例化好的对象进行对象的反射操作;
- 线程等待:public final void wait() throws InterruptedException 导致当前线程等到另一个线程调用该对象的notify()方法或notifyAll()方法。 换句话说,这个方法的行为就好像简单地执行调用wait(0) 。
- 执行到此代码时线程要等待执行,直到执行notify()或者notifyAll()方法来唤醒线程;
- 一个线程唤醒:public final void notify() 唤醒正在等待对象监视器的单个线程。
- 全部线程唤醒:public final void notifyAll() 唤醒正在等待对象监视器的所有线程。
- 垃圾回收前释放:protected void finalize() throws Throwable 已过时。 定稿机制本质上是有问题的。 定稿可能导致性能问题,死锁和挂起。
- 当使用gc回收无用的垃圾空间时默认调用;
Class类
class类是整个反射的操作源头,而类的定义如下:
public final class Class<T> extends Object implements
Serializable, GenericDeclaration, Type, AnnotatedElement
如果想要使用Class类进行操作,那么必须首先产生Class类的实例化对象,而有三种方法可以去的Claas类的实例化对象
- Object类提供了一个返回Class类对象的方法:public final Class<?> getClass();
- 利用“类.class”取得,日后建的最多的就是在Hibernate上;
- 利用Class类的static方法取得,public static Class<?> forName(String className) throws ClassNotFoundException 返回与给定字符串名称的类或接口相关联的Class对象。
如果是程序设计人员,使用最多的方法一定是forName()方法,但是如果是使用者会使用“类.class”。工厂设计模式最好利用反射机制来解决耦合问题。
利用反射实例化对象
Class类如果使用了forName()方法之后,就可以使用Class类定义的newInstance()方法默认去调用类之中的无参构造器进行操作
public T newInstance() throws InstantiationException, IllegalAccessException,此泛型使用不到
代码演示: 在这里是不能接受的了这个返回值的,
这里就是解决上面错误的代码实现: 从中我们就可以看到我们不一定非要使用new实例化对象,只要我们有一个类的完整名称也可以实例化对象
public class R { public static void main(String[] args) throws Exception { Class<?> cls = Class.forName("test.Student"); //jdk 1.9 开始直接使用的newInstance()方法已经过时,可以使用下面的方式来调用newInstance()方法 //相当于关键字new实例化对象。等价于 Object newInstance = new Student();Object newInstance = cls.getDeclaredConstructor().newInstance(); } } class Student{ public Student() { System.out.println("构造方法Student"); } }
执行结果:构造方法Student
但是如果使用反射实例化对象,必须要求类中存在有无参构造方法,因为newInstance()方法只能找到无参。如果没有无参构造函数如下:
Exception in thread "main" java.lang.NoSuchMethodException:
如果想找到无参构造怎么办?操作构造方法
操作构造方法:
为了解决NoSuchMethodException错误,这个时候这能取得类之中的构造方法,传递所需要的参数后才能使用。
在Class类里面定义了可以取得一个类中的构造方法的操作:
- public Constructor<T> getConstructor(Class<?>... parameterTypes) (重点使用)
throws NoSuchMethodException, SecurityException
返回一个Constructor对象,及时取得类中制定参数的构造,该对象反映由此Class对象表示的类的指定公共构造函数。
- public Class<?>[] getDeclaredClasses() throws SecurityException
返回一个Class对象的数组,就是全部构造,反映了所有被声明为由这个Class对象表示的类的成员的类和接口。
代码演示:取得String中的全部构造方法。
import java.lang.reflect.Constructor; public class GetStringConstructor { public static void main(String[] args) throws Exception { Class<?> forName = Class.forName("java.lang.String"); Constructor<?>[] constructors = forName.getConstructors();//得到所有构造 for (int i = 0; i < constructors.length; i++) { System.out.println(constructors[i]); } } }
执行结果:
public java.lang.String(byte[]) public java.lang.String(byte[],int,int) public java.lang.String(byte[],java.nio.charset.Charset) public java.lang.String(byte[],java.lang.String) throws java.io.UnsupportedEncodingException public java.lang.String(byte[],int,int,java.nio.charset.Charset) public java.lang.String(java.lang.StringBuilder) public java.lang.String(java.lang.StringBuffer) public java.lang.String(char[],int,int) public java.lang.String(char[]) public java.lang.String(java.lang.String) public java.lang.String() public java.lang.String(byte[],int,int,java.lang.String) throws java.io.UnsupportedEncodingException public java.lang.String(byte[],int) public java.lang.String(byte[],int,int,int) public java.lang.String(int[],int,int