认识发射
反射指的是对象的反向处理操作,根据对象倒推类的组成。既然是反向处理,我们先来观察一下"正"的操作。在默认情况下,必须要先导入一个包,而后才能产生类的实例化对象
范例:观察正常处理
import java.util.Date;
public class Test {
public static void main(String[] args) throws Exception{
Date date = new Date();
}
}
以上是我们正常的关于对象的处理流程:根据**包名.类名找到类
** 。所谓的"反"指的是根据对象来取得对象的来源信息,而这个"反"的操作核心的处理就在于Object类的一个方法: 取得Class对象
:
* @return The {@code Class} object that represents the runtime
* class of this object.
* @jls 15.8.2 Class Literals
*/
public final native Class<?> getClass();
在反射的世界里面,看重的不再是一个对象,而是对象身后的组成(类、构造、普通、成员等)。
class对象的三种实例化方式
Class类是描述整个类的概念,也是整个反射的操作源头,在使用Class类的时候需要关注的依然是这个类的对象。 任何一个类的class对象由JVM加载类后产生(该对象在JVM中全局唯一),用户只能调用指定方法来取得该对象。
而这个类的对象的产生模式一共有三种:
- 任何类的实例化对象可以通过调用Object类提供的
getClass()
取得该类的Class对象 - “
类名称.Class
”可以直接根据某个具体类来取得其Class对象 - 调用Class类的静态方法
:public static Class<?> Class.forName(String className) throws ClassNotFoundException
传入类的全名称来取得其Class对象
import java.util.Date;
public class Test {
public static void main(String[] args) {
Date date = new Date();
// 通过类对象.getClass()
System.out.println(date.getClass());
// 通过类名称.class
System.out.println(Date.class);
// 通过调用Class类提供的静态方法forName(String className)
try {
System.out.println(Class.forName("java.util.Date"));
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
在以上给出的三个方法中我们可以发现,除了第一种方法会产生Date类的实例化对象之外,其他的两种都不会产生 Date类的实例化对象。
于是取得了Class类对象有一个最直接的好处:可以通过反射实例化对象,在Class类中定义有如下方法:
public T newInstance()
throws InstantiationException, IllegalAccessException
范例:反射实例化对象
import java.util.Date;
public class Test {
public static void main(String[] args) throws Exception{
// 取得类的实例化对象
Class<Date> cls = (Class<Date>)Class.forName("java.util.Date");
// 通过反射取得Date类实例化对象
Date date = cls.newInstance(); // 实例化对象,等价于 new java.util.Date()
System.out.println(date);
}
}
现在发现除了关键字new之外,对于对象的实例化模式有了第二种做法,通过反射进行。
反射与工厂设计模式
工厂设计模式曾经给过原则:如果是自己编写的接口,要想取得本接口的实例化对象,最好使用工厂类来设计。但是也需要知道传统工厂设计所带来的问题。
传统工厂类:https://blog.csdn.net/sifanchao/article/details/83420681
传统工厂类在实际开发之中根本用不到。(问题就在于new)。每增加一个接口的子类就需要修改工厂类。
如果要想解决关键字new带来的问题,最好的做法就是通过反射来完成处理,因为Class类可以使用newInstance()
实例化对象,同时Class.forName()
能够接收类名称。
interface IFruit{
void eat();
}
class Apple implements IFruit {
@Override
public void eat() {
System.out.println("吃苹果");
}
}
class Orange implements IFruit{
@Override
public void eat() {
System.out.println("吃橘子");
}
}
class FruitFactory {
public FruitFactory() {}
public static IFruit getInstance(String className) {
IFruit fruit = null;
try {
fruit = (IFruit) Class.forName(className).newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return fruit;
}
}
public class Test1 {
public static void main(String[] args) throws Exception{
IFruit fruit = FruitFactory.getInstance("www.bit.MyTest.Apple");
fruit.eat();
}
}
引入反射后,每当新增接口子类,无需去修改工厂类代码就可以很方便的进行接口子类扩容。
反射与类操作
利用反射可以做出一个对象具备的所有操作行为,最为关键的是这一切的操作都可以基于Object进行。
取得父类信息
在java中任何的程序类都一定会有父类,在Class类中就可以通过如下方法来取得父类或者实现的父接口:
- 取得类的包名称
public Package getPackage() {
return Package.getPackage(this);
}
- 取得父类的Class对象
public native Class<? super T> getSuperclass();
- 取得实现的父接口
public Class<?>[] getInterfaces()
范例:取得包名称、父类、父接口
interface IFruit{}
interface IMessage{}
class Person{}
class Student extends Person implements IFruit,IMessage{}
public class Test1 {
public static void main(String[] args) throws Exception{
Class<?> cls = Student.class; // 取得Class类对象
// 取得Package名称
System.out.println(cls.getPackage().getName());
// 取得父类名称
System.out.println(cls.getSuperclass().getName());
// 取得实现的父接口对象
Class<?>[] iclass = cls.getInterfaces();
for (Class<?> classes: iclass){
System.out.println(classes.getName());
}
}
}
Files\Java\jdk1.8.0_181\jre\lib\rt.jar;E:\Java\code\out\production\code" www.bit.MyTest.Test1
www.bit.MyTest
www.bit.MyTest.Person
www.bit.MyTest.IFruit
www.bit.MyTest.IMessage
反射调用构造—Constructor(描述类中构造方法)
- 取得类中指定
public权限
参数类型的构造
@CallerSensitive
public Constructor<T> getConstructor(Class<?>... parameterTypes)
throws NoSuchMethodException, SecurityException {
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
return getConstructor0(parameterTypes, Member.PUBLIC);
}
- 取得类中指定参数(
包含private权限
)类型的构造
@CallerSensitive
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
throws NoSuchMethodException, SecurityException {
checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
return getConstructor0(parameterTypes, Member.DECLARED);
}
- 取得类中
public权限
的所有参数类型构造
@CallerSensitive
public Constructor<?>[] getConstructors() throws SecurityException {
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
return copyConstructors(privateGetDeclaredConstructors(true));
}
- 取得类中所有参数(
包含private权限
)类型构造
@CallerSensitive
public Constructor<?>[] getDeclaredConstructors() throws SecurityException {
checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
return copyConstructors(privateGetDeclaredConstructors(false));
}
范例:取得类中构造信息
import java.lang.reflect.Constructor;
class Person {
public Person(){}
protected Person(String name){}
private Person(String name,int age){}
public Person(String name,int age,String id){}
}
public class Test1 {
public static void main(String[] args) throws Exception{
Class<?> cls = Person.class;
// 取得类中指定`public权限`参数类型的构造
Constructor<?> constructor1 = cls.getConstructor(String.class.getClasses());
System.out.println(constructor1);
System.out.println("------------------------------------");
// 取得类中`public权限`的所有参数类型构造
Constructor<?>[] constructor2 = cls.getConstructors();
for (Constructor<?> constructor : constructor2){
System.out.println(constructor);
}
System.out.println("------------------------------------");
// 取得类中指定参数(`包含private权限`)类型的构造
Constructor<?> constructor3 = cls.getDeclaredConstructor(String.class,int.class);
System.out.println(constructor3);
System.out.println("------------------------------------");
// 取得类中所有参数(`包含private权限`)类型构造
Constructor<?>[] constructors = cls.getDeclaredConstructors();
for (Constructor<?> constructor : constructors){
System.out.println(constructor);
}
}
}
Files\Java\jdk1.8.0_181\jre\lib\rt.jar;E:\Java\code\out\production\code" www.bit.MyTest.Test1
public www.bit.MyTest.Person()
------------------------------------
public www.bit.MyTest.Person(java.lang.String,int,java.lang.String)
public www.bit.MyTest.Person()
------------------------------------
private www.bit.MyTest.Person(java.lang.String,int)
------------------------------------
public www.bit.MyTest.Person(java.lang.String,int,java.lang.String)
private www.bit.MyTest.Person(java.lang.String,int)
protected www.bit.MyTest.Person(java.lang.String)
public www.bit.MyTest.Person()
Process finished with exit code 0
- Constructor类提供了实例化对象的方法
@CallerSensitive
public T newInstance()
throws InstantiationException, IllegalAccessException
在定义简单java类的时候一定要保留有一个无参构造
范例:观察Class实例化对象的问题
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 Test1 {
public static void main(String[] args) throws Exception{
Class<?> cls = Person.class;
System.out.println(cls.newInstance());
}
}
Exception in thread "main" java.lang.InstantiationException: www.bit.MyTest.Person
at java.lang.Class.newInstance(Class.java:427)
at www.bit.MyTest.Test1.main(Test1.java:150)
Caused by: java.lang.NoSuchMethodException: www.bit.MyTest.Person.<init>()
at java.lang.Class.getConstructor0(Class.java:3082)
at java.lang.Class.newInstance(Class.java:412)
... 1 more
Process finished with exit code 1
Class类通过反射实例化类对象的时候,只能够调用类中的无参构造。如果现在类中没有无参构造则无法使用Class 类调用,只能够通过明确的构造调用实例化处理。
范例:通过Constructor类实例化对象
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 Test1 {
public static void main(String[] args) throws Exception{
Class<?> cls = Person.class;
// 取得指定参数类型的构造方法对象
System.out.println(cls.getConstructor(String.class,int.class).newInstance("改革开放",40));
}
}
反射调用普通方法
- 取得类中指定
public权限
参数类型的普通方法
@CallerSensitive
public Method getMethod(String name, Class<?>... parameterTypes)
throws NoSuchMethodException, SecurityException {
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
Method method = getMethod0(name, parameterTypes, true);
if (method == null) {
throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes));
}
return method;
}
- 取得类中指定参数(
包含private权限
)类型的普通方法
@CallerSensitive
public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
throws NoSuchMethodException, SecurityException {
checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
Method method = searchMethods(privateGetDeclaredMethods(false), name, parameterTypes);
if (method == null) {
throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes));
}
return method;
}
- 取得类中
public权限
的所有参数类型普通方法
@CallerSensitive
public Method[] getMethods() throws SecurityException {
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
return copyMethods(privateGetPublicMethods());
}
- 取得类中所有参数(
包含private权限
)类型普通方法
@CallerSensitive
public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
throws NoSuchMethodException, SecurityException {
checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
Method method = searchMethods(privateGetDeclaredMethods(false), name, parameterTypes);
if (method == null) {
throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes));
}
return method;
}
范例:取得一个类中的全部普通方法
import java.lang.reflect.Method;
class Person {
private String name;
private int age;
public Person(){}
public Person(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 Test {
public static void main(String[] args) throws Exception{
Class<?> cls = Person.class;
Method[] methods = cls.getDeclaredMethods();
for (Method method : methods){
System.out.println(method);
}
}
}
Files\Java\jdk1.8.0_181\jre\lib\rt.jar;E:\Java\code\out\production\code" www.bit.MyTest.Test1
public java.lang.String www.bit.MyTest.Person.toString()
public java.lang.String www.bit.MyTest.Person.getName()
public void www.bit.MyTest.Person.setName(java.lang.String)
public void www.bit.MyTest.Person.setAge(int)
public int www.bit.MyTest.Person.getAge()
Process finished with exit code 0
上面程序编写的简单java类中的getter、setter方法采用的都是明确的对象调用。
而现在有了反射机制处理之后,即使你没有明确的Person类型对象(依然需要实例化对象,Object对象描述,所有 的普通方法必须在有实例化对象之后才可以进行调用),就可以通过反射调用。
- Method类中有提供调用类中普通方法的API
import java.lang.reflect.Method;
class Person {
private String name;
private int age;
public Person() {}
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;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class Test1 {
public static void main(String[] args) throws Exception{
// 1.拿到Person的Class对象
Class<?> cls = Person.class;
// 2.创建Person实例化对象
Person person = (Person) cls.newInstance();
// 3.拿到setName的Method对象
Method setNameMethod = cls.getMethod("setName", String.class);
// 4.通过invoke进行调用,相当于Person对象.setName("改革开发")
setNameMethod.invoke(person,"改革开发");
Method getNameMethod = cls.getMethod("getName");
// 相当于Person对象.getName()
System.out.println(getNameMethod.invoke(person));
}
}
此类操作的好处是:不再局限于某一具体类型的对象,而是可以通过Object类型进行所有类的方法调用。
反射调用类中属性
在之前已经成功的实现了类的构造调用、方法调用,除了这两种模式之外还有类中属性调用。
前提:类中的所有属性一定在类对象实例化之后才会进行空间分配,所以此时如果要想调用类的属性,必须保证有实例化对象。通过反射的newInstance()
可以直接取得实例化对象(Object类型)
- 取得类中指定
public权限
参数类型的属性
@CallerSensitive
public Field getField(String name)
throws NoSuchFieldException, SecurityException {
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
Field field = getField0(name);
if (field == null) {
throw new NoSuchFieldException(name);
}
return field;
}
- 取得类中指定参数(
包含private权限
)类型的属性
@CallerSensitive
public Field getDeclaredField(String name)
throws NoSuchFieldException, SecurityException {
checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
Field field = searchFields(privateGetDeclaredFields(false), name);
if (field == null) {
throw new NoSuchFieldException(name);
}
return field;
}
- 取得类中
public权限
的所有参数类型属性
@CallerSensitive
public Field[] getFields() throws SecurityException {
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
return copyFields(privateGetPublicFields(null));
}
- 取得类中所有参数(
包含private权限
)类型属性
@CallerSensitive
public Field[] getDeclaredFields() throws SecurityException {
checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
return copyFields(privateGetDeclaredFields(false));
}
范例:取得类中全部属性
import java.lang.reflect.Field;
class Person {
public String name;
public int age;
protected String id;
}
class Student extends Person{
private String school;
}
public class Test1 {
public static void main(String[] args) throws Exception{
Class<?> cls = Class.forName("www.bit.MyTest.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);
}
}
}
}
Files\Java\jdk1.8.0_181\jre\lib\rt.jar;E:\Java\code\out\production\code" www.bit.MyTest.Test1
public java.lang.String www.bit.MyTest.Person.name
public int www.bit.MyTest.Person.age
-----------------------
private java.lang.String www.bit.MyTest.Student.school
Process finished with exit code 0
因为在实际开发之中,属性基本上都会进行封装处理,所以没有必要去关注父类中的属性。也就是说以后所取得的属性都以本类属性为主。
而后就需要关注属性的核心描述类:java.lang.reflect.Field,在这个类之中有两个重要方法:
- 设置属性内容 :
public void set(Object obj, Object value) throws IllegalArgumentException, IllegalAccessException
- 取得属性内容 :
public Object get(Object obj) throws IllegalArgumentException, IllegalAccessException
- 取得属性的类型
public Class<?> getType()
- 动态设置封装
Constructor、Method、Field类都是AccessibleObject
子类。
AccessibleObject提供动态设置封装方法(在本次JVM进行中有效且只能通过反射调用)
public void setAccessible(boolean flag) throws SecurityException {
SecurityManager sm = System.getSecurityManager();
if (sm != null) sm.checkPermission(ACCESS_PERMISSION);
setAccessible0(this, flag);
}
setAccessible(true)
取消了Java的权限控制检查(注意不是改变方法或字段的访问权限),调用了类的private属性,并且能够修改private成员变量的值
import java.lang.reflect.Field;
class Person {
public Person(){}
public String name;
private int age;
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class Test1 {
public static void main(String[] args) throws Exception{
Class<?> cls = Person.class;
Person person = (Person) cls.getConstructor().newInstance();
Field nameField = cls.getField("name");
nameField.set(cls,"刘德华");
System.out.println(nameField.get(cls));
System.out.println(nameField.getType().getName());
Field ageField = cls.getDeclaredField("age");
ageField.setAccessible(true);
ageField.set(cls,45);
System.out.println(ageField.get(cls));
System.out.println(ageField.getType().getName());
}
}