java的反射机制

所谓的"反"就是指可以利用对象找到对象的出处,在Object类里面提供有一个方法:

  • 取得Class对象:public final Class<?> getClass();

 

Class类对象实例化:

java.lang.Class是一个类, 这个类是反射操作的源头,即:所有的反射都要从此类开始进行,而最关键的是这个类有三种实例化方式

  •  调用Object类中的getClass()方法: Class<?> clazz = new Date().getClass();

  •  调用"类.class" : Class<?> clazz = Date.class;

  •  调用类Class提供的一个方法: Class<?> clazz = Class.forName("java.util.Date");

 

反射实例化对象:

先有Class对象

  •  实例化对象方法: public T newInstance()

class Book{
    public Book() {
        System.out.println("********** Book类的无参构造方法 **********");
    }

    @Override
    public String toString() {
        return "这是一本书";
    }
}

public class TestClazz {

    public static void main(String[] args)throws Exception {
        Class<?> clazz = Class.forName("com.feng.Clazz.Book");
        Object o = clazz.newInstance();
    }
}

  在任务的开发中, new 是造成耦合的最大元凶,一切的耦合都起源于new

  工厂设计模式:每增加一个类就要去修改工厂类,如果随时都可能增加子类?

interface Fruit{
    public void eat();
}
class Apple implements Fruit{
    public void eat(){
        System.out.println("吃苹果。。。");
    }
}

class Orange implements Fruit{
    public void eat(){
        System.out.println("吃橘子。。。");
    }
}
class Factory{
    public static Fruit getInstance(String name){
        if ("apple".equals(name)){
            return new Apple();
        } else if ("orange".equals(name)){
            return new Orange();
        }
        return null;
    }
}

public class TestFactoryPattern {

    public static void main(String[] args) {
        Fruit fruit = Factory.getInstance("apple");
        fruit.eat();

    }
}

  解决办法: 做反射

interface Fruit{
    public void eat();
}
class Apple implements Fruit{
    public void eat(){
        System.out.println("吃苹果。。。");
    }
}

class Orange implements Fruit{
    public void eat(){
        System.out.println("吃橘子。。。");
    }
}
class Factory{
    public static Fruit getInstance(String name) throws Exception{
        Fruit f = (Fruit)Class.forName(name).newInstance();
        return f;
    }
}

public class TestFactoryPattern {
  
    public static void main(String[] args) throws Exception {
        Fruit fruit = Factory.getInstance("com.feng.factory.Orange");//全类名
        fruit.eat();
    }
}

  

使用反射调用构造:

在之前所编写的代码中, 实际上发现都默认使用了类中的无参构造方法,如果类中不提供无参的构造方法呢?

类中不存在无参构造

class Book{

    private String title;
    private double price;

    public Book(String title, double price) {
        this.title = title;
        this.price = price;
    }

    @Override
    public String toString() {
        return "Book{" +
                "title='" + title + '\'' +
                ", price=" + price +
                '}';
    }
}

  由于类中不存在无参构造方法,下面的程序是错误的

public class TestClazz {

    public static void main(String[] args)throws Exception {

        Class<?> clazz = Class.forName("com.feng.Clazz.Book");
        Object o = clazz.newInstance(); //相当于使用new调用无参构造实例化对象

        Book book = (Book) o;
        System.out.println(book);
    }
}
Exception in thread "main" java.lang.InstantiationException: com.feng.Clazz.Book
	at java.lang.Class.newInstance(Class.java:427)
	at com.feng.Clazz.TestClazz.main(TestClazz.java:33)
Caused by: java.lang.NoSuchMethodException: com.feng.Clazz.Book.<init>()
	at java.lang.Class.getConstructor0(Class.java:3082)

  所以在这种情况下,只能够调用有参构造方法:

    •  在Class类中, 有一种方法可以取得全部构造: public Constructor<T>[] getConstructors()

    •  在Class类中, 有一种方法可以取得指定参数(参数为类型)顺序的构造: public Constructor<T> getConstructor(Class<?> ..parameterTypes)

  以上两个方法返回的都是“java.lang.reflect.Constructor”类的对象,在这个类中提供有一个明确传递有参构造内容的实例化对象方法:

  •  public T newInstance(Object...initargs) // 参数是具体的入参

class Book{

    private String title;
    private double price;

    public Book(String title, double price) {
        this.title = title;
        this.price = price;
    }

    @Override
    public String toString() {
        return "Book{" +
                "title='" + title + '\'' +
                ", price=" + price +
                '}';
    }
}

public class TestClazz {

    public static void main(String[] args)throws Exception {

        Class<?> clazz = Class.forName("com.feng.Clazz.Book");
        Constructor<?> constructor = clazz.getConstructor(String.class, double.class);
        Object b = constructor.newInstance("java开发", 20.2);
        System.out.println(b);
    }
}

  

反射调用方法:

方法的调用: 一个类产生实例化对象之后才可以调用。实例化对象的三种方式:

在Class类里面提供有以下取得类中Method的操作:

  • 取得一个类中的全部方法: public Method[] getMethods();

  • 取得一个类中的指定方法: public Method getMethod(Sting methodName, Class<?> ....parameterType) // parameterType: 参数类型

  以上的两个操作返回的是Method类的对象, 在这个类里面,重点关注一个方法“

  •  public Object invoke(Object obj, Object...args)

    obj: 实例化对象

    args: 参数内容

利用反射调用方法:

class Book{

    private String title;

    public Book() {
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }
}

public class TestClazz {

    public static void main(String[] args)throws Exception {
     //要操作的成员 String fieldName = "title"; Class<?> clazz = Class.forName("com.feng.Clazz.Book");
     //必须给出实例化对象  Object obj = clazz.newInstance(); Method setMethod = clazz.getMethod("set" + initCap(fieldName), String.class); Method getMethod = clazz.getMethod("get" + initCap(fieldName)); //等价于:Book类对象.setTitle("Java开发") setMethod.invoke(obj, "Java开发"); System.out.println(getMethod.invoke(obj)); } public static String initCap(String str){ return str.substring(0,1).toUpperCase() + str.substring(1); } }

  此时完全看不见具体的操作类型,也就是利用反射可以实现任意类的指定方法的调用

 

反射调用成员:

  类中的属性一定要在本类实例化对象之后才可以分配内存空间。在Class类里面提供取得成员的方法

  • 取得全部员: public  Field[]  getDeclaredFields();

  • 取得指定成员: public Field  getDeclaredField(String name); 

    参数name : 成员的名称

 返回的类型是Field类, 在这个类里面有两个重要的方法:

  • 取得属性内容: public Object get(Object obj);

    参数obj: 实例化对象

  • 设置属性内容: public void set(Object obj,  Object value);

    参数obj: 实例化对象

    参数value: 参数内容

 在AccessibleObject类下面:(jdk1.8之后修改)

  • Executable: 下面继承了Constructor、 Method

  • Field : 

 在这个类里面提供有一个方法:

  • 设置是否封装: public void setAccessible(boolean flag)

 

反射调用属性:

class Book{
    private String title;
}

public class TestClazz {

    public static void main(String[] args)throws Exception {

        Class<?> clazz = Class.forName("com.feng.Clazz.Book");
        Object obj = clazz.newInstance();//必须给出实例化对象
        Field field = clazz.getDeclaredField("title");
        //封装取消应该设置在赋值之前
        field.setAccessible(true);
        field.set(obj, "Java开发");

        System.out.println(field.get(obj));
    }
}    

  构造方法和普通方法也同样可以取消封装,只不过很少这样去做,而且对于属性的访问还是建议使用setting和getting方法完成;

 

总结:

1. 实例化对象的方式又增加了一种反射

2. 反射调用类结构只是开始

 

posted @ 2022-04-10 22:39  IT6889  阅读(30)  评论(0编辑  收藏  举报