Java进阶之反射机制

反射机制

2018年4月21日星期六

 

1.    理解反射做用户

2.    利用反射来调用类的结构

具体内容

利用对象找到对象的出处,Object类里面提供有一个方法:

       取得Class对象 :publicfinal <?> getClass()

示例:

        import java.util.Date;

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

发现调用了getClass()方法后的输出就输出了类的完整名称,等于是找到了对象的出处。

 

Class类对象实例化

Java.lang.Class 是一个类,这个类是反射的操作的对象,这个类有三种实例化方式

1.调用Object中的getClass方法;

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

2.使用:类.class取得

示例:

import java.util.Date;

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

之前是在产生了类的实例化对象之后取得的Class类对象,但此时并没有实例化对象的产生。

3.    调用Class类提供的一个方法

实例化Class类对象 public static Class <?> forName(String name,boolean initialize,ClassLoader loader)throws ClassNotFoundException
 
public class Reflect {
    public static void main(String[] args) throws Exception {
        Class<?> cls = Class.forName("java.util.Date");
        System.out.println(cls);
    }
}

此时可以不使用import语句导入一个明确的类,而类名称是采用字符串的方式进行描述的。

 

反射实例化对象

       一般情况的用new去实例化一个对象,如果有了Class类对象,那么就可以做到利用反射来实现对象实例化操作。

        实例化对象方法:public T newInstance()throws InstantiationException,IllegalAccessException

 

package com.company.javabase.relfect;

 

class Book {

   public Book() {

      System.out.println("**********无参构造方法*******");

   }

 

   @Override

   public String toString() {

      return"这是一本书";

   }

}

 

publicclass Relfect {

   publicstaticvoid main(String[] argsthrows Exception {

   Class<?> class1 = Class.forName("com.company.javabase.relfect.Book");

      Object object = class1.newInstance(); // 相当于使用new调用无参构造实例化对象

      Book book = (Book) object;

      System.out.println(book);

      System.out.println(object);

   }

}

       有了反射之后,以后进行实例化操作的时候可以用new和反射来操作了

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

工厂模式示例:

package com.company.javaadvance;

interface Fruit{
    public void eat();
}

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

class Factory{
    public static Fruit getInstance (String classname) {
        if ("apple".equals(classname)) {
            return new Apple();
        }
        return null;
    }
}

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

如果此时要添加一个新的Fruit接口子类,那么久表示程序要修改工厂类。

每增加一个类就要去修改工厂类,那么如果随时都可能增加子类呢。

因为现在工厂类中的对象都是通过关键字new直接实例化,而new就成了所有问题的关键点,要想解决这一问题,就只能依靠反射来解决。

package com.company.javabase.relfect;

 

interface Fruit{

    publicvoid eat();

}

 

class Apple implements Fruit{

    @Override

    publicvoid eat() {

        System.out.println("吃苹果!");

    }

}

 

 

class Orange implements Fruit{

    @Override

    publicvoid eat() {

        System.out.println("吃橘子!");

    }

}

 

class Factory{

    publicstatic Fruit getInstance (String classname) {

       Fruit f = null;

       try {

            f = (Fruit)Class.forName(classname).newInstance();

         } catch (Exception e) {

            e.printStackTrace();

         }

       returnf;

    }

}

 

publicclass Relfect {

    publicstaticvoid main(String[] argsthrows Exception {

        Fruit fruit = Factory.getInstance("com.company.javabase.relfect.Orange");

        fruit.eat();

    }

}

       此时的程序就会变得耦合度很低,而且可扩展性很强。

使用反射调用构造

       示例:当前Book类里没有无参构造方法

package com.company.javabase.po;

 

publicclass Book {

   private String title;

   privatedoubleprice;

 

   public Book(String titledoubleprice) {

      this.title = title;

      this.price = price;

   }

 

   @Override

   public String toString() {

      return"图书名称:" + this.title + "\t图书价格" + this.price;

   }

}

 

publicclass Relfect {

publicstaticvoid main(String[] argsthrows Exception {

   Class<?> class1 = Class.forName("com.company.javabase.po.Book");

   Object object = class1.newInstance(); // 相当于使用new调用无参构造实例化对象

   System.out.println(object);

}

}

Exception in thread "main" java.lang.InstantiationException: com.company.javabase.po.Book

   at java.lang.Class.newInstance(Class.java:427)

   at com.company.javabase.relfect.Relfect.main(Relfect.java:58)

Caused by: java.lang.NoSuchMethodException: com.company.javabase.po.Book.<init>()

   at java.lang.Class.getConstructor0(Class.java:3082)

   at java.lang.Class.newInstance(Class.java:412)

   ... 1 more

因为Book类里面没有无参构造方法,所以程序无法进行对象的实例化。在这中情况下,只能够明确的调用有参构造方法

在Class类里面有明确的方法取得构造:

Constructor<T>

getConstructor(<?>... parameterTypes)

返回一个 Constructor对象,该对象反映 Constructor对象表示的类的指定的公共 函数。 

Constructor<?>[]

getConstructors()

返回包含一个数 Constructor对象反射由此表示的类的所有公共构造 对象

 

以上两个方法返回的都是java.lang.reflect.Constructor类的对象。

 在这个类中我们有一个明确传递有参构造内容对象的实例化方法。

publicclass Relfect {

publicstaticvoid main(String[] argsthrows Exception {

   Class<?> class1 = Class.forName("com.company.javabase.po.Book");

   Constructor<?> con = class1.getConstructor(String.class,double.class);

   Object object = con.newInstance("java开发",99.99);

   System.out.println(object);

   }

}
简单Java类的开发之中不管提供有多少个构造方法,请至少保留无参构造。

反射调用方法

       类中的普通方法只有在一个类产生实例化对象之后才可以调用。并且实例化的方式有三种。

示例:定义一个类

package com.company.javabase.po;

 

publicclass Book {

   private String title;

  

   publicvoid setTitle(String title) {

      this.title = title;

   }

   public String getTitle() {

      returntitle;

   }

}

       这个类有无参构造方法,所以实例化对象的时候可以直接利用Class类中提供的newInstance()方法完成。

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

取的一个类中的全部方法:public Method[] getMethods()throws SecurityException

取得一个类中的指定的方法

public Method getMethod(Stringname,Class<?>... parameterTypes)throwsNoSuchMethodException,SecurityException

以上两个操作返回的都是java.lang.reflect.Method的对象

在这个类里面有一个方法叫调用方法:public Object invoke(Objectobj,Object... args)throwsIllegalAccessException,IllegalArgumentException,InvocationTargetException

示例:通过反射调用方法

import java.lang.reflect.Method;

 

publicclass Relfect {

publicstaticvoid main(String[] argsthrows Exception {

   String filedName = "title";   //要操作的成员

   Class<?> class1 = Class.forName("com.company.javabase.po.Book");

   Object object = class1.newInstance();  //必须给出实例化对象

   Method setMethod = class1.getMethod("set"+initcap(filedName), String.class);

   Method getMethod = class1.getMethod("get"+initcap(filedName));

   setMethod.invoke(object"java开发");   //等价于:Book类对象.setTitle("java开发")

   System.out.println(getMethod.invoke(object));

   }

publicstatic String initcap(String str){

   returnstr.substring(0,1).toUpperCase() + str.substring(1);

}

}

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

 

反射调用成员

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

1、        取得全部成员

public Field[]getDeclaredFields()

throws SecurityException

2、        取得指定成员

public Field getDeclaredField(String name)

 throwsNoSuchFieldException,SecurityException

返回类型是 java.lang.reflect.Field类,在这个类里面有两个方法分别是:

取得属性内容

public Object get(Object obj)

throwsIllegalArgumentException,IllegalAccessException

设置属性内容

public void set(Object obj,Object value)

throws IllegalArgumentException,IllegalAccessException

 

示例:提供有如下类

package com.company.javabase.po;

publicclass Book {

   private String title;

}

       这个类里面只定义了一个私有属性,一般无法被外部所使用

示例:反射调用

java.lang.reflect.AccessibleObject 类里面(JDK1.8之后修改)

       Executable:下面继承了 Constructor 、 Method

       Field:

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

public void setAccessible(boolean flag)

throws SecurityException

设置是否封装

publicclass Relfect {

   publicstaticvoid main(String[] argsthrows Exception{

      Class<?> class1 = Class.forName("com.company.javabase.po.Book");

      Object object = class1.newInstance();  //必须给出实例化对象

      Field titileField = class1.getDeclaredField("title");

      titileField.setAccessible(true);     //取消封装

      titileField.set(object"java开发"); //想到与:Book类对象.title = "java开发"

      System.out.println(titileField.get(object));

   }

}

 

       构造方法和普通方法也是可以取消封装的,只不过很少去用

猜你喜欢

转载自blog.csdn.net/qq_42056595/article/details/80792822
今日推荐