一. 反射机制简介

在Java语言里面之所以会有如此众多的开源技术的支撑,很大一部分是来自于Java最大的特征 —— 反射机制。

如果你现在不能够灵活去使用反射机制进行项目的开发和设计,那么可以说你并未接触到Java的精髓所在。

所有的技术实现的目标只有一点: 重用性。

对于反射技术首先考虑的是 “反” 与 “正” 的操作,所谓的 “正” 操作指的是当我们要使用一个类的时候,要先导入程序所在的包,而后根据类,进行对象的实例化,并且依靠对象调用类中的方法。但是如果说 “反” ,根据实例化对象反推出其类型。

如果要想实现反的处理操作,那么首先要采用的就是 Object类 中所提供的的一个新的方法:

  • 获取Class对象信息: public final Class<?>getClass();

范例: 观察Class对象的使用

1
2
3
4
5
6
7
8
package cn.ngp.demo;
import java.util.Date; //1. 导入对象所在的包,类,知道对象的出处了
public class JavaAPIDemo {
public static void main(String[] args) throws Exception{
Date date = new Date() ; // 2. 通过类产生实例化对象
System.out.println(date.getClass()); //3. 根据对象调用类中的方法
}
}

getClass()可以帮助使用者找到对象的根源。

二. 反射机制(Class类对象的三种实例化模式)

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

  • 第一种: 调用Object类中的getClass()方法

    1
    2
    3
    4
    5
    6
    7
    8
    package cn.ngp.demo;
    import java.util.Date;//先有类
    public class TestDemo {
    public static void main(String[] args) throws Exception {
    Date date = new Date();
    System.out.println(date.getClass());
    }
    }
  • 第二种: 使用"类.class"取得

    1
    2
    3
    4
    5
    6
    7
    8
    package cn.ngp.demo;
    import java.util.Date;//先有类
    public class TestDemo {
    public static void main(String[] args) throws Exception {
    Class<?> cls = Date.class;
    System.out.println(cls);
    }
    }

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

  • 第三种: 调用Class类提供的一个方法

    • -| 实例化Class对象: public static Class<?> forName(String name, boolean initialize,ClassLoader loader) throws ClassNotFoundException

      1
      2
      3
      4
      5
      6
      7
      package cn.ngp.demo;
      public class TestDemo {
      public static void main(String[] args) throws Exception {
      Class<?> cls = Class.forName("java.util.Date");
      System.out.println(cls);
      }
      }

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

​ 初期: Class.forName用得最多

三. 反射机制(反射实例化对象)

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

  • 实例化对象方法:public T newInstance() throws InstantiationException, IllegalAccessException
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package cn.ngp.demo;
class Book {
public Book() {
System.out.println("****Book类的无参构造方法****");
}
@Override
public String toString() {
return "这是一本书";
}
}
public class TestDemo {
public static void main(String[] args) throws Exception {
// Book b = new Book();
// System.out.println(b);
Class<?> cls = Class.forName("cn.ngp.demo.Book");
Object obj = cls.newInstance(); //相当于调用了无参构造实例化
Book book = (Book)obj;
System.out.println(book);
}
}

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

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

范例: 观察工厂设计模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package cn.ngp.test;
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借口子类,那么就表示程序要修改工厂类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package cn.ngp.test;
interface Fruit {
public void eat();
}
class Apple implements Fruit {
@Override
public void eat() {
System.out.println("*** 吃苹果 ***");
}
}
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;
}
}
public class TestFactory {
public static void main(String[] args) {
Fruit f1 = Factory.getInstance("apple");
Fruit f2 = Factory.getInstance("orange");
f1.eat();
f2.eat();
}
}

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package cn.ngp.test;
interface Fruit {
public void eat();
}
class Apple implements Fruit {
@Override
public void eat() {
System.out.println("*** 吃苹果 ***");
}
}
class Orange implements Fruit {
@Override
public void eat() {
System.out.println("*** 吃橘子 ***");
}
}
class Factory {
public static Fruit getInstance(String className) {
Fruit f = null;
try {
f = (Fruit) Class.forName(className).newInstance();
} catch (Exception e) {}
return f;
}
}
public class TestFactory {
public static void main(String[] args) {
Fruit f1 = Factory.getInstance("cn.ngp.test.Apple");
f1.eat();
Fruit f2 = Factory.getInstance("cn.ngp.test.Orange");
f2.eat();
}
}

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

四. 反射机制(调用构造方法)

​ 在之前