(转)java反射基础和使用(Class 、Constructor、Method、Field)

转载自:https://blog.csdn.net/guijun6/article/details/80302517

反射

反射指的是对象的反向处理操作,根据对象取得对象的来源信息,在反射的世界里面,看重的不再是一个对象,而是对象身后的组成(类、构造、普通、成员)。

Class类是描述整个类的概念,也是整个反射的操作源头,在使用Class类的时候需要关注的依然是这个类的对象。
而这个类的对象的产生模式一共有三种:

①、任何类的实例化对象可以通过Object类中的getClass()方法取得Class类对象。
②、"类.class":直接根据某个具体的类来取得Class类的实例化对象。
③、使用Class类提供的方法:public static Class<?> forName(String className) throwsClassNotFoundException

例:使用getClass()方法

import java.util.Date;
public class Test {
    public static void main(String[] args) {
    Date date = new Date() ;
    System.out.println(date.getClass());
    }
}

例:使用某个具体的类.class

public class Test {
    public static void main(String[] args) {
        Class<?> cls = Date.class;
        System.out.println(cls);
    }
}

例:使用Class.froName()方法

public class Test {
    public static void main(String[] args) throws ClassNotFoundException {
        Class<?> cls = Class.forName("java.util.Date") ;
        System.out.println(cls);
    }
}

在以上给出的三个方法中我们可以发现,除了第一种方法会产生Date类的实例化对象之外,其他的两种都不会产生Date类的实例化对象。于是取得了Class类对象有一个最直接的好处:可以通过反射实例化对象,在Class类中定义有如下方法:

public T newInstance()throws InstantiationException, IllegalAccessException

例:通过反射实例化对象

public class Test {
    public static void main(String[] args) throws ClassNotFoundException,InstantiationException,IllegalAccessException {
        Class<?> cls = Class.forName("java.util.Date") ;
        Object obj = cls.newInstance() ; // 实例化对象,等价于 new java.util.Date() ;
        System.out.println(obj);
    }
}

这里举一个传统工厂模式与改用反射后的工厂模式进行对比

例:传统工厂类

interface IFruit {
    public void eat() ;
}
class Apple implements IFruit {
    @Override
    public void eat() {
        System.out.println("[Apple] 吃苹果 ");
    }
}
class FruitFactory {
    private FruitFactory() {}
    public static IFruit getInstance(String className) {
        if ("apple".equals(className)) {
            return new Apple() ;
        }
        return null ;
    }
}
public class Test {
    public static void main(String[] args) {
        IFruit fruit = FruitFactory.getInstance("apple") ;
        fruit.eat() ;
    }
}

以上传统工厂类问题就在于new。每增加一个接口的子类就需要修改工厂类。
例:传统工厂类增加接口子类

class Orange implements IFruit {
    @Override
    public void eat() {
        System.out.println("[Orange] 吃橘子 ");
    }
}
class FruitFactory {
    private FruitFactory() {}
    public static IFruit getInstance(String className) {
        if ("apple".equals(className)) {
            return new Apple() ;
        }else if ("orange".equals(className)) {
            return new Orange() ;
        }
        return null ;
    }
}

如果要想解决关键字new带来的问题,最好的做法就是通过反射来完成处理,因为Class类可以使用newInstance()实例化对象,同时Class.forName()能够接收类名称。

例:利用反射修改后的工厂模式

package www.bit.java.testthread;
interface IFruit {
    public void eat() ;
}
class Apple implements IFruit {
    @Override
    public void eat() {
        System.out.println("[Apple] 吃苹果 ");
    }
}
class Orange implements IFruit {
    @Override
    public void eat() {
        System.out.println("[Orange] 吃橘子 ");
    }
}
class FruitFactory {
    private FruitFactory() {}
    public static IFruit getInstance(String className) {
        IFruit fruit = null ;
        try {
            fruit = (IFruit) Class.forName(className).newInstance() ;
        } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return fruit ;
    }
}
public class Test {
    public static void main(String[] args) {
        IFruit fruit = FruitFactory.getInstance("www.bit.java.testthread.Orange") ;
        fruit.eat() ;
    }
}

引入反射后,每当新增接口子类,无需去修改工厂类代码就可以很方便的进行接口子类扩容。以上这种工厂类代码我们称之为简单工厂模式。

反射类操作(四大核心类)

利用反射可以做出一个对象具备的所有操作行为,最为关键的是这一切的操作都可以基于Object进行。
这里写图片描述

这里写图片描述

取得父类信息
在java中任何的程序类都一定会有父类,在Class类中就可以通过如下方法来取得父类或者实现的父接口:

取得类的包名称: public Package getPackage()

例:取得包名称

package www.bit.java.testthread;
interface IFruit {}

interface IMessage{}

class CLS implements IFruit,IMessage{}

public class Test {
    public static void main(String[] args) {
        Class<?> cls = CLS.class ; // 取得Class类对象
        System.out.println(cls.getPackage().getName());
    }
}
取得父类的Class对象: public native Class<? super T> getSuperclass();
取得实现的父接口:public Class<?>[] getInterfaces();

 
 
  • 1
  • 2

例:取得父类(实现的父接口)

package www.bit.java.testthread;
interface IFruit {}

interface IMessage{}

class CLS implements IFruit,IMessage{}

public class Test {
    public static void main(String[] args) {
        Class<?> cls = CLS.class ; // 取得Class类对象
        // 取得Package名称
        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());
        }
    }
}

通过反射可以取得类结构上的所有关键信息。

反射调用构造
个类中可以存在多个构造方法,如果要想取得类中构造的调用,就可以使用Class类中提供的两个方法:

1、取得指定参数类型的构造:
public Constructor<T> getConstructor(Class<?>... parameterTypes)throws NoSuchMethodException, SecurityException
2、取得类中的所有构造:
public Constructor<?>[] getConstructors() throws SecurityException

以上两个方法返回的类型都是java.lang.reflect.Constructor类的实例化对象,这个类之中大家只需要关注一个方法:

public T newInstance(Object ... initargs)throws InstantiationException, IllegalAccessException,IllegalArgumentException, InvocationTargetException

例:取得类中所有构造信息

package www.bit.java.testthread;
import java.lang.reflect.Constructor;
class Person {
    public Person() {}
    public Person(String name) {}
    public Person(String name,int age) {}
}
public class Test {
    public static void main(String[] args) {
        Class<?> cls = Person.class ;
        // 取得类中的全部构造
        Constructor<?>[] constructors = cls.getConstructors() ;
        for (Constructor<?> constructor : constructors) {
            System.out.println(constructor);
        }
    }
}

以上的操作是直接利用了Constructor类中的toString()方法取得了构造方法的完整信息(包含方法权限,参数列表),而如果你只使用了getName()方法,只会返回构造方法的包名.类名。

Class类通过反射实例化类对象的时候,只能够调用类中的无参构造。如果现在类中没有无参构造则无法使用Class类调用,只能够通过明确的构造调用实例化处理。因此,在定义简单java类的时候一定要保留有一个无参构造。
package www.bit.java.testthread;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
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 class Test {
    public static void main(String[] args) throws InstantiationException,IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException,InvocationTargetException {
        Class<?> cls = Person.class ;
        //直接用Class实例化对象只能调用无参构造
        System.out.println(cls.newInstance());
        // 取得指定参数类型的构造方法对象
        Constructor<?> cons = cls.getConstructor(String.class,int.class) ;
        System.out.println(cons.newInstance("yuisama",29));
    }
}

* 反射调用普通方法(核心)*
类中普通方法的反射调用你在开发之中一定会使用到,并且使用好了可以节省大量的重复编码。在Class类中有如下两种取得类中普通方法的函数:

1、取得全部普通方法:
public Method[] getMethods() throws SecurityException
2、取得指定普通方法:
public Method getMethod(String name, Class<?>... parameterTypes)

以上两个方法范辉的类型是java.lang.reflect.Method类的对象,在此类中提供有一个调用方法的支持:

调用:
public Object invoke(Object obj, Object... args)throws IllegalAccessException,IllegalArgumentException,InvocationTargetException

例:取得一个类中的全部普通构造方法

package www.bit.java.testthread;
import java.lang.reflect.InvocationTargetException;
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.getMethods() ;
        for (Method method : methods) {
            System.out.println(method);
        }
    }
}

之前程序编写的简单java类中的getter、setter方法采用的都是明确的对象调用。
而现在有了反射机制处理之后,即使你没有明确的Person类型对象(依然需要实例化对象,Object对象描述,所有的普通方法必须在有实例化对象之后才可以进行调用)。就可以通过反射调用。

例:通过反射调用setter、getter方法

package www.bit.java.testthread;
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 = Class.forName("www.bit.java.testthread.Person") ;
        // 任何时候调用类中的普通方法都必须有实例化对象
        Object obj = cls.newInstance() ;
        // 取得setName这个方法的实例化对象,设置方法名称与参数类型
        Method setMethod = cls.getMethod("setName", String.class) ;
        // 随后需要通过Method类对象调用指定的方法,调用方法需要有实例化对象
        // 同时传入参数
        setMethod.invoke(obj, "yuisama") ; // 相当于Person对象.setName("yuisama") ;
        Method getMethod = cls.getMethod("getName") ;
        Object result = getMethod.invoke(obj) ; // 相当于Person对象.getName() ;
        System.out.println(result) ;
    }
}

此类操作的好处是:不再局限于某一具体类型的对象,而是可以通过Object类型进行所有类的方法调用。

反射调用类中属性
前提:类中的所有属性一定在类对象实例化之后才会进行空间分配,所以此时如果要想调用类的属性,必须保证有实例化对象。通过反射的newInstance()可以直接取得实例化对象(Object类型)

在Class类中提供有两组取得属性的方法:

1.第一组(父类中)-取得类中全部属性: public Field[] getFields() throws SecurityException
2.第一组(父类中)-取得类中指定名称属性: public Field getField(String name) throws NoSuchFieldException, SecurityException
3.第二组(本类中)-取得类中全部属性: public Field[] getDeclaredFields() throws SecurityException
4.第二组(本类中)-取得类中指定名称属性:public Method getDeclaredMethod(String name, Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException

 
 
  • 1
  • 2
  • 3
  • 4

例:取得类中全部属性

package www.bit.java.testthread;
import java.lang.reflect.Field;
class Person {
    public String name ;
    public int age ;
}
class Student extends Person {
    private String school ;
}
public class Test {
    public static void main(String[] args) throws Exception {
        Class<?> cls = Class.forName("www.bit.java.testthread.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,在这个类之中有两个重要方法:

1.设置属性内容 : public void set(Object obj, Object value) throws IllegalArgumentException,IllegalAccessException
2.取得属性内容 : public Object get(Object obj) throws IllegalArgumentException,IllegalAccessException

例:通过反射操作属性

package www.bit.java.testthread;
import java.lang.reflect.Field;
class Person {
    private String name ;
}
public class Test {
    public static void main(String[] args) throws Exception {
        Class<?> cls = Class.forName("www.bit.java.testthread.Person") ;
        // 实例化本类对象
        Object obj = cls.newInstance() ;
        // 操作name属性
        Field nameField = cls.getDeclaredField("name") ;
        nameField.set(obj, "yuisama") ; // 相当于对象.name = "yuisama"
        System.out.println(nameField.get(obj)); // 取得属性
    }
}

来看Java反射继承结构
这里写图片描述

在AccessibleObject类中提供有一个方法:

动态设置封装:
public void setAccessible(boolean flag) throws SecurityException

例:动态设置封装:

package www.bit.java.testthread;
import java.lang.reflect.Field;
class Person {
    private String name ;
}
public class Test {
    public static void main(String[] args) throws Exception {
        Class<?> cls = Class.forName("www.bit.java.testthread.Person") ;
        // 实例化本类对象
        Object obj = cls.newInstance() ;
        // 操作name属性
        Field nameField = cls.getDeclaredField("name") ;
        // 取消封装
        nameField.setAccessible(true) ;
        // ----------------------------
        nameField.set(obj, "yuisama") ; // 相当于对象.name = "yuisama"
        System.out.println(nameField.get(obj)); // 取得属性
    }
}

在Field类之中有一个特别有用的方法:

取得属性类型:
public Class<?> getType()

例:取得属性类型

package www.bit.java.testthread;
import java.lang.reflect.Field;
class Person {
    private String name ;
}
public class Test {
    public static void main(String[] args) throws Exception {
        Class<?> cls = Class.forName("www.bit.java.testthread.Person") ;
        // 实例化本类对象
        Object obj = cls.newInstance() ;
        // 操作name属性
        Field nameField = cls.getDeclaredField("name") ;
        // 包.类
        System.out.println(nameField.getType().getName()) ;
        // 类名称
        System.out.println(nameField.getType().getSimpleName()) ;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值