Java反射机制

来源:互联网 发布:232数据盒rs-100 编辑:程序博客网 时间:2024/06/16 09:21

导语

反射机制是Java程序开发语言的重要特征之一。

主要内容

  • 认识反射
  • 理解反射作用
  • 利用反射来调用类的结构

具体内容

认识反射

反射的话先通过“反”来理解,既然有“反”就有“正”,在正常情况下,一定是先有类而后再产生对象
所谓的“反”就是指可以利用对象找到对象的出处,在Object类里面提供有一个方法:

取得Class对象:public final Class

import java.util.Date;  // 先有类public class TestDemo {    public static void main(String args[]) {        Date date = new Date(); // 产生对象        System.out.println(date.getClass());    }}

输出结果

class java.util.Date

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

Class类对象实例化

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

  • 第一种:调用Object类中的getClass()方法。
import java.util.Date;public class TestDemo {    public static void main(String args[]) {        Date date = new Date();        Class<?> cls = date.getClass();        System.out.println(cls);    }}
  • 第二种:使用“类.class”取得。
import java.util.Date;public class TestDemo {    public static void main(String args[]) {        Class<?> cls = Date.class;        System.out.println(cls);    }}

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

  • 第三种:调用Class类提供的一个方法。实例化Class对象:public static Class
public class TestDemo {    public static void main(String args[]) {        Class<?> cls = Class.forName("java.util.Date);        System.out.println(cls);    }}

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

反射实例化对象

当拿到一个类的时候,肯定要直接使用关键字new进行对象的实例化操作,这属于习惯 性的做法,但是如果有了Class类对象,那么就可以做到,利用反射来实现对象实例化操作:
- 实例化对象方法:public T newInstance() throws throws InstantiationException, IllegalAccessException。

范例:利用反射实例化对象

package cn.cc.demo;class Book {    public Book() {        System.out.println("Book类的无参构造方法");    }    @Override    public String toString() {        return "这是一本书!";    }}public class TestDemo {    public static void main(String args[]) {        Class<?> cls = Class.forName("cn.cc.demo.Book");        Object obj = cls.newInstance();  // 相当于使用new调用无参构造实例化        Book book = (Book) obj;        System.out.println(book);    }}

输出结果

Book类的无参构造方法这是一本书!

有了反射之后,以后进行对象实例化的操作不再只是单独的依靠关键字new完成了,反射也同样可以完成,但是这并不表示new就被完全取代了。

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

范例:观察工厂设计模式

package cn.cc.test;interface Fruit {    public void eat();}public class Apple implements Fruit {    @Override    public void eat() {        System.out.println("吃苹果!");    }}public class Factory {    public static Fruit getInstance(String className) {        if("apple".equals(className)) {            return new Apple();        }        return null;    }}public class TestFactory {    public static void main(String args[]) {        Fruit f = Factory.getInstance("apple");        f.eat();    }}

如果此时增加了Fruit接口子类,那么就表示程序要修改工厂类。

package cn.cc.test;interface Fruit {    public void eat();}public class Apple implements Fruit {    @Override    public void eat() {        System.out.println("吃苹果!");    }}public class Orange implements Fruit {    @Override    public void eat() {        System.out.println("吃橘子!");    }}public class Factory {    public static Fruit getInstance(String className) {        if("apple".equals(className)) {            return new Apple();        } else if() {            return new Orange();        } else {            return null;        }    }}public class TestFactory {    public static void main(String args[]) {        Fruit f = Factory.getInstance("apple");        f.eat();    }}

每增加一个类就要去修改工厂类,那么如果随时都可能增加子类呢?工厂类要一直被进行修改。
因为现在工厂类中的对象都是通过关键字new直接实例化的,而new就成了所有问题的关键点。要想解决这一问题,就只能够依靠反射来完成。

package cn.cc.test;interface Fruit {    public void eat();}public class Apple implements Fruit {    @Override    public void eat() {        System.out.println("吃苹果!");    }}public class Orange implements Fruit {    @Override    public void eat() {        System.out.println("吃橘子!");    }}public class Factory {    public static Fruit getInstance(String className) {        Fruit f = null;        try {            f = (Fruit) Class.forName(className);        } catch(Exception e) {}        return f;    }}public class TestFactory {    public static void main(String args[]) {        Fruit f = Factory.getInstance("cn.cc.test.Apple");        f.eat();    }}

输出结果

吃苹果!

此时的程序就真正完成了解耦合的目的,而且可扩展性非常的强。

使用反射调用构造

在之前 所编写的代码实际上发现都默认使用了类之中的无参构造方法,可是类中还有可能不提供无参构造。

范例:观察代码问题
定义Book类:

package cn.cc.po;public class Book {    private String title;    private double price;    public Book(String title, double price) {        this.title = title;        this.price = price;    }    @Overide    public String toString() {        return "书名:" + this.title + "  价格:" + this.price;    }}

定义TestDemo类:

package cn.cc.demo;public class TestDemo {    public static void main(String args[]) {        Class<?> cls = Class.forName("cn.cc.po.Book");        Object obj = cls.newInstance();  // 相当于使用new调用无参构造实例化        System.out.println(obj);    }}

由于此时Book类没有提供无参构造方法,所以代码会出错。

Exception in thread "main" java.lang.InstantiationException: cn.cc.po.Book    at java.lang.Class.newInstance(Class.java:427)    at cn.cc.demo.TestDemo.main(TestDemo.java:5)Caused by: java.lang.NoSuchMethodException: cn.cc.po.Book.<init>()    at java.lang.Class.getConstructor0(Class.java:3082)...

以上所出现的错误指的就是因为当前Book类里面并没有无参构造方法,所以程序无法进行对象的实例化。在这种情况下,只能够明确的调用有参构造方法。
在Class类里面提供有一个方法能够取得构造:
- 取得全部构造:public Constructor

package cn.cc.demo;public class TestDemo {    public static void main(String args[]) {        Class<?> cls = Class.forName("cn.cc.po.Book");        // public Book(String title, double price),找到类型        Constructor<?> con = cls.getConstructor(String.class, double.class);        Object obj = con.newInstance("Java开发", 79.8);  // 实例化对象        System.out.println(obj);    }}

输出结果

书名:Java开发  价格:79.8

简单Java类的开发之中不管提供有多少个构造方法,请至少保留有无参构造(为反射实例化对象的时候方便)。

反射调用方法

类中的普通方法只有在一个类产生实例化对象之后才可以调用,并且实例化对象的方式有三种:
- new实例化对象。
- 克隆对象。
- 通过反射实例化对象。

在Class类里面提供有以下取得类中Method(方法)的操作:
- 取得一个类中的全部方法:public Method[] getMethods() throws SecurityException。
- 取得指定方法:public Method getMethod(String name, Class

package cn.cc.po;public class Book {    private String title;    public void setTitle(String title) {        this.title = title;    }    public String getTitle() {        return title;    }}

定义TestDemo类:

package cn.cc.demo;public class TestDemo {    public static void main(String args[]) {        String fieldName = "title";  // 要操作的成员        Class<?> cls = Class.forName("cn.cc.po.Book");        Object obj = cls.newInstance();  // 必须给出实例化对象        Method setMet = cls.getMethod("set" + initcap(fieldName), String.class);        Method getMet = cls.getMethod("get" + initcap(fieldName);        setMet.invoke(obj, "Java开发");  // 等价于:Book类对象.setTitle("Java开发");        System.out.println(getMet.invoke(obj));  // 等价于:Book类对象.getTitle();    }    public static String initcap(String str) {        return str.substring(0, 1).toUpperCase() + str.substring(1);    }}

输出结果

Java开发

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

反射调用成员

类中的属性一定要在本类 实例化对象产生之后才可以分配内存空间。在Class类里面提供有取得成员的方法:
- 取得全部成员:public Field[] getDeclaredFields() throws SecurityException。
- 取得指定成员:public Field getDeclaredField(String name) throws NoSuchFieldException, SecurityException。

返回的类型是java.lang.reflect.Field类,在这个类里面有两个重要的方法:
- 取得属性内容:public native Object get(Object object)
throws IllegalAccessException, IllegalArgumentException。
- 设置属性内容:public native void set(Object object, Object value)
throws IllegalAccessException, IllegalArgumentException。

一般情况下,类中的私有属性,按原始的做法,此时它一定无法被外部所使用。
在java.lang.reflect.AccessibleObjject类下面(JDK1.8之后修改):
- Executable:下面继续继承了Constructor、Method。
- Field。

在这个类中提供了一个方法:public void setAccessible(boolean flag) throws SecurityException,设置是否封装。

范例:使用反射调用成员
定义一个Book类。

package cn.cc.po;public class Book {    private String title;}

定义TestDemo类:

package cn.cc.demo;public class TestDemo {    public static void main(String args[]) {        String fieldName = "title";  // 要操作的成员        Class<?> cls = Class.forName("cn.cc.po.Book");        Object obj = cls.newInstance();  // 必须给出实例化对象        Field titleField = cls.getDeclaredField(fieldName);        titleField.setAccessible(true);  // 封装取消了,否则不能调用private属性        titleField.set(obj, "Java开发");  // 等价于:Book类对象.title = "Java开发";        System.out.println(titleField.get(obj));  // 等价于:Book类对象.title;    }}

输出结果

Java开发

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

总结

  • 实例化对象的方式又增加了一种反射。
  • 对于简单Java类的定义应该更加清晰了。
  • 反射调用类结构只是一个开始。

进入我的CSDN戳这里(我的博客导航)

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 档案和派遣证不在同一个地方怎么办 档案缺招工表和派遣证怎么办退休 开发商对接的银行利息比较高怎么办 工作未满一年离职转正定级怎么办 被网络骗贷翻到几十万怎么办 面试通过后迟迟不通知入职怎么办 厂里捡到饭卡花了里面的钱怎么办 上班用自己手机打卡没电怎么办 逸尚考勤机显示超出考勤记录怎么办 下雨穿套裙工装上班好冷怎么办 蚂蚁借呗放款中不到账怎么办 护士电子化注册没有激活码怎么办? 地铁站务员入职体检身高差点怎么办 车子钥匙锁在车里了怎么办 打错账户了怎么办5天了 下岗失业人员在就业后档案怎么办 皮秒祛斑后没有敷面膜怎么办 硕士毕业工资好低不想工作怎么办 车卖了etc忘拿了怎么办 成熟卵泡打破卵针也不破怎么办? 深圳孩子户口挂别人名下上学怎么办 襄阳东风合运花园房贷怎么办下来 猫在外面躲起来找不到了怎么办 本科三批取消以后三本学校怎么办 机票名字多了个字母安检怎么办 出隧道口限速40超速了怎么办 社保交了五年后断交以后该怎么办 躺椅折叠椅坐的地方坏了怎么办 苹果手机锁频密码忘了怎么办 客户要货公司不给进怎么办 高铁23:00买的票怎么办 带电子手刹的车刹车失灵怎么办 下坡刹车失灵遇行人不看车怎么办 跟大车后面看不见红灯闯了怎么办 跟着大货车后面闯了红灯怎么办 如果用工单位不发放加班工资怎么办 事故家属要司机向医院多交钱怎么办 在大学里和室友关系弄僵怎么办 房产证办出来房产局不给证怎么办 单位全员竞聘老员工没岗位怎么办 快件被快递公司签收了没给送怎么办