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戳这里(我的博客导航)
- 【反射】JAVA反射机制
- 【Java】JAVA反射机制
- Java 反射机制[Field反射]
- Java 反射机制[Method反射]
- Java反射机制笔记-反射机制
- java的反射机制
- Java的反射机制
- java反射机制详解!
- Java反射机制
- Java的反射机制
- java 反射机制--侯捷
- java反射机制
- java反射机制
- [候捷]Java反射机制
- java 反射机制
- java 反射机制初探
- 关于Java反射机制
- java反射机制
- [CodeForces
- Ctrl+滚轮 禁止页面缩放
- Java比较器
- Linux 入门 -- root boot
- Java正则表达式
- Java反射机制
- Android WebView用法
- 计蒜客ACM ICPC 2017 Warmup Contest 9--B题-Battle Simulation
- Gradle2.0用户指南翻译——第八章.依赖管理基础
- STL模板库的学习之集合(set)
- 深度学习与神经网络习题(1)
- git命令之merge
- 第七周 项目 3
- 小技巧:把风扇的扇叶取下来