目录
1、认识反射
反射的话先通过“反”来理解,既然有“反”就一定有“正”,在正常情况下,一定是先有类而后再产生对象。
public class Demo {
public static void main(String[] args) {
Date date=new Date();
System.out.println(date);
}
}
所谓的“反”就是指可以利用对象找到对象的出处,在Object类里面提供有一个方法:
- 取得Class对象:public final Class<?> getClass。
public class Demo {
public static void main(String[] args) {
Date date=new Date();
System.out.println(date.getClass());
}
}
输出:
class java.util.Date
发现调用了getClass()方法后的输出就输出了类的完整名称,等于是找到了对象的出处。
2、Class 类实例化
java.lang.Class 是一个类,这个类是反射操作的源头,即:所有的反射都要从此类开始进行。
三种获得Class的方式:
- 第一种:调用Object 类中的getClass方法。
import java.util.Date;
public class Demo {
public static void main(String[] args) {
Date date=new Date();
Class<?> cls=date.getClass();
System.out.println(cls);
}
}
- 第二种:使用“类.class” 取得,Hibernate、MyBatis、Spring使用的此种方式。
public class Demo {
public static void main(String[] args) {
Class<?> cls=Date.class;
System.out.println(cls);
}
}
之前在产生了类的实例化对象之后取得Class类对象,但是此时并没有实例化对象。
- 第三种:调用Class类提供的一个方法:
|-实例化Class类对象:public static Class<?> forName(String className) throws ClassNotFoundException
public class Demo {
public static void main(String[] args) throws ClassNotFoundException {
Class<?> cls=Class.forName("java.util.Date");
System.out.println(cls);
}
}
此时可以不使用import 语句导入一个明确的类,而类名称是采用字符串的形式进行描述的。
3、反射实例化对象
拿到一个类的时候,肯定要直接使用关键字new 进行对象的实例化操作,这属于习惯性的做法,但是如果有了Class类对象
那么就可以做到,利用反射实现对象实例化操作。
- 实例化对象方法 public T newInstance() throws InstantiationException,IllegalAccessException
利用反射实例化对象
public class Book {
public Book(){
System.out.println("******** BOOK类的无参构造方法 *********");
}
@Override
public String toString() {
return "这是一本书";
}
}
public class Demo {
public static void main(String[] args) throws ClassNotFoundException {
Class<?> cls=Class.forName("com.man.java8.reflect.Book");
try {
//相当于调用无参构造实例
Book book=(Book)cls.newInstance();
System.out.println(book);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
有了反射之后,以后进行对象实例化的操作不再只是单独的依靠关键字new完成了,反射并不表示new就被完全取代了。
在任何的开发中,new是造成耦合的最大元凶,一切的耦合都起源于new。
package com.man.java8.reflect;
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 TestFactory {
public static void main(String[] args) {
Fruit f=Factory.getInstance("apple");
f.eat();
}
}
如果此时增加了Fruit接口子类,程序要修改工厂类
class Orange implements Fruit{
@Override
public void eat() {
System.out.println("**** 吃橘子*****");
}
}
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就成了所有问题的关键点。要解决这一问题就只能够依靠反射完成。
class Factory{
public static Fruit getInstance(String className){
Fruit f=null;
try {
f = (Fruit) Class.forName(className).newInstance();
}catch (Exception e){
e.printStackTrace();
}
return f;
}
}
public class TestFactory {
public static void main(String[] args) {
Fruit f=Factory.getInstance("com.man.java8.reflect.Apple");
f.eat();
}
}
此时的程序就真正完成了解耦合的目的,而且可扩展性非常的强。
4、使用反射调用构造
package com.man.java8.reflect;
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 "这是一本书";
}
}
public class Demo {
public static void main(String[] args) throws ClassNotFoundException {
Class<?> cls=Class.forName("com.man.java8.reflect.Book");
try {
Book book=(Book)cls.newInstance();
System.out.println(book);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
调用cls.newInstance(); 报错
java.lang.InstantiationException: com.man.java8.reflect.Book
at java.lang.Class.newInstance(Class.java:427)
at com.man.java8.reflect.Demo.main(Demo.java:9)
Caused by: java.lang.NoSuchMethodException: com.man.java8.reflect.Book.<init>()
at java.lang.Class.getConstructor0(Class.java:3082)
at java.lang.Class.newInstance(Class.java:412)
... 1 more
出现以上错误,当前Book类没有无参构造方法,所以程序无法进行对象实例化;
这种情况下,只能调用有参构造方法;
在Class类里面提供有一个方法可以取得构造。
- 取得全部构造:public Constructor<?>[] getConstructors() throws SecurityException
- 取得一个构造: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.man.java8.reflect;
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 "Book{" +
"title='" + title + '\'' +
", price=" + price +
'}';
}
}
package com.man.java8.reflect;
import java.lang.reflect.Constructor;
public class Demo {
public static void main(String[] args) throws Exception {
Class<?> cls=Class.forName("com.man.java8.reflect.Book");
Constructor<?> con=cls.getConstructor(String.class,double.class);
Object obj=con.newInstance("hibernate实战",78.8);
System.out.println(obj);
}
}
输出结果:
Book{title='hibernate实战', price=78.8}
5、反射调用方法
普通方法只有一个类产生实例化对象之后,才可以调用,并且实例化对象的方式有三种:
- new
- 克隆
- 反射
在Class类里面提供有以下取得类中Method的操作:
- 取得类中全部方法:public Method[] getMethods() throws SecurityException
- 取得类中一个方法:public Method getMethod(String name, Class<?>... parameterTypes) throws NoSuchMethodException,SecurityException
以上的两个操作返回的是java.lang.reflect.Method 类的对象,在里面重点关注:
- 调用方法:public Object invoke(Object obj,Object... args)
throws IllegalAccessException, IllegalArgumentException, InvocationTargetException
例:
package com.man.java8.reflect;
public class Book {
private String title;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
package com.man.java8.reflect;
import java.lang.reflect.Method;
public class Demo {
public static void main(String[] args) throws Exception {
Class<?> cls=Class.forName("com.man.java8.reflect.Book");
//这个类有无参构造方法,所以实例化对象的时候可以直接用Class类中提供的newInstance完成。
Object obj=cls.newInstance();//必须给出实例化对象
Method setMet=cls.getMethod("setTitle", String.class);
setMet.invoke(obj,"java开发");
Method getMet=cls.getMethod("getTitle");
System.out.println(getMet.invoke(obj));
}
}
6、反射调用成员变量
类中的属性一定要在本类实例化对象产生之后才可以分配内存空间。在Class里面有取得成员的方法。
- 取得全部成员:public Field[] getDeclaredFields() throws SecurityException
- 取得指定成员:public Field getDeclaredField(String name) throws NoSuchFieldException,SecurityException
返回的类型是java.lang.reflect.Field类,在这个类里面的两个重要方法
- 取得属性内容:public Object get(Object obj) throws IllegalArgumentException, IllegalAccessException
- 设置属性内容:public void set(Object obj,Object value)throws IllegalArgumentException,IllegalAccessException
package com.man.java8.reflect;
public class Book {
private String title;
}
package com.man.java8.reflect;
import java.lang.reflect.Field;
public class Demo {
public static void main(String[] args) throws Exception {
Class<?> cls=Class.forName("com.man.java8.reflect.Book");
//这个类有无参构造方法,所以实例化对象的时候可以直接用Class类中提供的newInstance完成。
Object obj=cls.newInstance();//必须给出实例化对象
Field f=cls.getDeclaredField("title");
f.setAccessible(true);//取消封装了
f.set(obj,"java开发");
System.out.println(f.get(obj));
}
}
构造方法与普通方法也同样可以取消封装,只不过是很少这样去做,对属性不要直接访问,推荐通过getter和setter去访问。