Java反射机制 详解
来源:互联网 发布:蒙古舞演出服淘宝 编辑:程序博客网 时间:2024/06/02 02:18
一、什么是反射机制
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
“程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言”。Java不是动态语言。但是Java有着一个非常突出的动态相关机制:Reflection,用在Java身上指的是我们可以于运行时加载、探知、使用编译期间完全未知的classes。换句话说,Java程序可以加载一个运行时才得知名称的class,获悉其完整构造(但不包括methods定义),并生成其对象实体、或对其fields设值、或唤起其methods。
二、初识Java反射机制
反射之中包含了一个“反”的概念,要解释反射就必须先从“正”开始解释,一般而言,一定是先有类再产生实例化对象。如下:
package com.wz.reflectdemo;import java.util.Date;//先有类public class ReflectTest { public static void main(String[] args) { Date date = new Date();//再产对象 System.out.println(date); }}
而所谓的“反”,是通过对象找到类。在Object类里面提供有一个方法,
取得class对象:
public final Class<?> getClass()
注:反射之中的所有泛型都定义为?,返回值都是Object。
实例如下:
package com.wz.reflectdemo;import java.util.Date;//先有类public class ReflectTest { public static void main(String[] args) { Date date = new Date();//再产对象 System.out.println(date.getClass()); }}
执行结果:
class java.util.Date
我们发现,调用getClass()后,得到了类的完整名称。也就找到了对象的出处。
三、Class类对象实例化
java.lang.Class是一个类,它和一般类一样继承自Objec。这个类是反射操作的源头,即所有的反射都要从此类开始进行,这个类有三种实例化方式:
方式一:调用Object类的getClass()方法(很少用到):
package com.wz.reflectdemo;import java.util.Date;public class ReflectTest { public static void main(String[] args) { Date date = new Date(); Class<?> cls = date.getClass(); System.out.println(cls); }}
运行结果:
class java.util.Date
方式二:使用“类.class”取得:
package com.wz.reflectdemo;import java.util.Date;public class ReflectTest { public static void main(String[] args) { //Date date = new Date(); Class<?> cls = Date.class; System.out.println(cls); }}
运行结果:
class java.util.Date
注意:先前取得Class类对象之前需要实例化,但此时并没有进行实例化。
方式三:调用Class类提供的一个方法:
public static Class<?> forName(String className) throws ClassNotFoundException
实例如下:
package com.wz.reflectdemo;//import java.util.Date;public class ReflectTest { public static void main(String[] args) throws Exception { Class<?> cls = Class.forName("java.util.Date"); System.out.println(cls); }}
运行结果:
class java.util.Date
此时,无需使用import语句导入一个明确的类,而类名称是采用字符串的形式进行描述的。
四、反射实例化对象
一般情况下,对象的实例化操作需要依靠构造方法和关键字new完成。可是有了Class类对象之后,可以利用反射来实现对象的实例化。
通过反射实例化对象:
public T newInstance() throws InstantiationException, IllegalAccessException
实例:
package com.wz.reflectdemo;class Book{ public Book(){ System.out.println("Book类的无参构造方法"); } @Override public String toString() { return "This a book !"; }}public class TestDemo { public static void main(String[] args) throws Exception { Class<?> cls = Class.forName("com.wz.reflectdemo.Book"); Object obj = cls.newInstance();//相当于使用new调用无参构造实例化对象 Book book = (Book)obj; System.out.println(book); }}
运行结果:
Book类的无参构造方法This a book !
如上,有了反射之后,进行实例化的操作不再只有依靠关键字new来完成了。但这个操作要比之前使用的new复杂一些,并且并不表示用new来进行实例化被完全取代了。为什么呢?
对于程序的开发一直强调:尽量减少耦合。而减少耦合的最好做法是使用接口,但是就算使用了接口也逃不出关键字new,所以实际上new是造成耦合的关键元凶。
先看一个简单的工厂设计模式:
package com.wz.reflectdemo;interface Fruit{ public void eat();}class Apple implements Fruit{ @Override public void eat(){ System.out.println("eat apple"); }}class Factory{ public static Fruit getInstance(String className){ if("apple".equals(className)){ return new Apple(); } return null; }}public class FactoryDemo{ public static void main(String[] args){ Fruit f = Factory.getInstance("apple"); f.eat(); }}
运行结果:
eat apple
以上是一个简单的工厂设计模式,但是在这个工厂设计模式之中有一个问题:如果增加了Fruit接口子类,那么就需要修改工厂类。
增加了Fruit接口子类Orange :
class Orange implements Fruit { public void eat() { System.out.println("eat orange"); };}
需要修改工厂类:
class Factory{ public static Fruit getInstance(String className){ if("apple".equals(className)){ return new Apple(); }else if("orange".equals(className)){ return new Orange(); } return null; }}
问题来了,每增加一个接口子类,就需要去修改工厂类,那么若随时可能增加多个子类呢?那么就要一直对工厂类进行修改!
根本原因:工厂类中的对象都是通过关键字new直接实例化的。那么如果说现在不使用关键字new了,变为了反射机制呢?
反射机制实例化对象的时候实际上只需要“包.类”就可以,于是根据此操作,修改工厂设计模式如下:
package com.wz.reflectdemo;interface Fruit{ public void eat();}class Apple implements Fruit{ @Override public void eat(){ System.out.println("eat apple"); }}class Orange implements Fruit{ @Override public void eat(){ System.out.println("eat orange"); }}class Factory{ public static Fruit getInstance(String className){ /*if("apple".equals(className)){ return new Apple(); }else if("orange".equals(className)){ return new Orange(); } return null;*/ Fruit f = null; try { f = (Fruit)Class.forName(className).newInstance(); } catch (Exception e) { e.printStackTrace(); } return f; }}public class FactoryDemo{ public static void main(String[] args){ /*Fruit f = Factory.getInstance("apple"); f.eat(); Fruit f1 = Factory.getInstance("orange"); f1.eat();*/ Fruit f1= Factory.getInstance("com.wz.reflectdemo.Apple"); f1.eat(); Fruit f2 = Factory.getInstance("com.wz.reflectdemo.Orange"); f2.eat(); }}
运行结果:
eat appleeat orange
这个时候即使增加了接口的子类,工厂类照样可以完成对象的实例化操作,这个才是真正的工厂类,可以应对于所有的变化。这就完成了解耦合的目的,而且扩展性非常强。
五、反射调用构造方法
之前我们通过反射实例化对象都是这么写的:
Class<?> cls = Class.forName(“*****className*****”);Object obj = cls.newInstance();
这只能调用默认的无参构造方法,那么,问题来了:若类中不提供无参构造方法呢?怎么解决?
看一个范例:
先写一个Book类:
package com.wz.reflectdemo;public 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 "图书名称:"+this.title + " ,价格:"+this.price; }}
然后实例化对象:
package com.wz.reflectdemo;public class ReflectTest { public static void main(String[] args) throws Exception { Class<?> cls = Class.forName("com.wz.reflectdemo.Book"); Object obj = cls.newInstance();//相当于使用new调用无参构造实例化对象 Book book = (Book)obj; System.out.println(book); }}
执行结果:
Exception in thread "main" java.lang.InstantiationException: com.wz.reflectdemo.Book at java.lang.Class.newInstance(Unknown Source) at com.wz.reflectdemo.ReflectTest.main(ReflectTest.java:8)Caused by: java.lang.NoSuchMethodException: com.wz.reflectdemo.Book.<init>() at java.lang.Class.getConstructor0(Unknown Source) ... 2 more
由此可见,由于此时Book类没有提供无参构造方法,而cls.newInstance()的时候又调用了无参构造方法,所以无法进行对象实例化。那么,怎么解决?只能明确的调用有参构造方法。
在Class类里面,提供了方法来取得构造:
(1)取得全部构造:
public Constructor<?>[] getConstructors() throws SecurityException
(2)取得一个指定参数顺序的构造:
public Constructor<T> getConstructor(Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException
以上两个方法返回的都是”java.lang.reflect.Constructor”类的对象。在这个类中提供有一个明确传递有参构造内容的实例化对象方法:
public T newInstance(Object... initargs) throws InstantiationException, IllegalAccessException,IllegalArgumentException, InvocationTargetException
改写上面范例的实例化对象方法,明确调用有参构造方法:
package com.wz.reflectdemo;import java.lang.reflect.Constructor;public class ReflectTest { public static void main(String[] args) throws Exception { Class<?> cls = Class.forName("com.wz.reflectdemo.Book"); /*Object obj = cls.newInstance();//相当于使用new调用无参构造实例化对象 Book book = (Book)obj; System.out.println(book);*/ Constructor<?> con = cls.getConstructor(String.class,double.class); Object obj = con.newInstance("Java开发",79.8);//实例化对象 System.out.println(obj); }}
执行结果:
图书名称:Java开发 ,价格:79.8
很明显,调用无参构造方法实例化对象要比调用有参构造的更加简单、方便。so,简单的Java开发中不过提供有多少个构造方法,请至少保留有无参构造。
六、反射调用普通方法
我们都知道:类中的普通方法只有在一个类产生实例化对象之后才可以调用。
先看一个例子。我们先定义一个类:
package com.wz.reflectdemo;public class Book { private String title; public String getTitle() { return title; } public void setTitle(String title) { this.title = title; }}
这个类有无参构造方法,所有实例化对象的时候可以直接利用Class类提供的newInstance()方法。
在Class类里面提供以下取得类在Method的操作:
(1)取得一个类中的全部方法:
public Method[] getMethods() throws SecurityException
(2)取得指定方法:
public Method getMethod(String name, Class<?>... parameterTypes) throwsNoSuchMethodException, SecurityException
以上的方法返回的都是”java.lang.reflect.Method”类的对象,在这个类中有一个调用方法:
public Object invoke(Object obj, Object... args) throws IllegalAccessException,IllegalArgumentException, InvocationTargetException
我们接着上面的例子来看反射调用方法:
package com.wz.reflectdemo;import java.lang.reflect.Constructor;import java.lang.reflect.Method;public class ReflectTest { public static void main(String[] args) throws Exception { Class<?> cls = Class.forName("com.wz.reflectdemo.Book"); Object obj = cls.newInstance(); Method setMet = cls.getMethod("setTitle", String.class); setMet.invoke(obj, "Java开发");//等价于Book类的setTitle("Java开发") Method getMet = cls.getMethod("getTitle"); System.out.println(getMet.invoke(obj));//等价于Book类的getTitle() }}
执行结果:
Java开发
此时,我们完全看不见具体的操作类型,也就是说,利用反射可以实现任意类的指定方法的调用。
七、反射调用成员
我们都知道,类中的属性一定要在本类实例化对象之后才可以分配内存空间。在Class类里面提供有取得成员的方法:
(1)取得全部成员:
public Field[] getDeclaredFields() throws SecurityException
(2)取得指定成员:
public Field getDeclaredField(String name) throws NoSuchFieldException, SecurityException
这两个方法的返回值类型是”java.lang.reflect.Field”类的对象。在这个类里面有两个重要的方法:
(1)取得属性内容(类似于:对象.属性):
public Object get(Object obj)throws IllegalArgumentException, IllegalAccessException
(2)设置属性内容(类似于:对象.属性=内容):
public void set(Object obj, Object value)throws IllegalArgumentException, IllegalAccessException
接着看一个例子:
先定义一个类:
package com.wz.reflectdemo;public class Book { private String title;}
这个类只定义了一个私有属性,按照之前的做法,它一定无法被外部所使用。但是,可以反射调用:
package com.wz.reflectdemo;import java.lang.reflect.Field;public class ReflectTest { public static void main(String[] args) throws Exception { Class<?> cls = Class.forName("com.wz.reflectdemo.Book"); Object obj = cls.newInstance(); Field titleField = cls.getDeclaredField("title"); titleField.setAccessible(true);//解除封装 titleField.set(obj, "Java开发");//相当于Book类对象.title = "Java开发" System.out.println(titleField.get(obj));//相当于Book类对象的.title }}
执行结果:
Java开发
注:构造方法和普通方法一样可以解除封装,只是很少这么去做。而对于属性的访问还是建议使用setter和getter方法完成。
- java反射机制详解!
- java 反射机制详解
- JAVA反射机制详解
- java反射机制详解
- Java反射机制详解
- Java反射机制详解
- java反射机制详解
- Java反射机制详解
- Java反射机制详解
- JAVA反射机制详解
- java反射机制详解
- Java反射机制详解
- Java反射机制详解
- java反射机制详解
- Java反射机制详解
- java反射机制详解
- Java反射机制详解
- Java反射机制详解
- 编程练习,寻找字符串中的子串
- Codeforces Round #375 (Div. 2)
- hdoj-5003-Osu!
- php
- LeetCode 67. Add Binary
- Java反射机制 详解
- 总结3
- Linux 下关于文本文件操作使用的案例
- 如何获取Spinner里item的值
- codeforces-723【B细节】
- 1087. All Roads Lead to Rome (30)
- Linux - C数据库编程(预习内容六)
- C++ STL容器解读
- 半个双引号引发的血案!