获取类对象的方式
1、Class.forName(全限定类名)
2、通过实例的getClass方法
3、类的.class属性来获取。
注:类对象是一个单例对象。
class Cat {
private String name;
public void eat(String food) {
System.out.println(name + " 正在吃 " + food);
}
public Cat(String name) {
this.name = name;
}
}
public class TestReflect {
public static void main(String[] args) throws ClassNotFoundException {
// 获取类对象, 获取到 Cat 类的说明书.
// 第一种获取方式是最灵活的. 写代码的时候根本就不需要知道类名. 在实际运行时再获取类名.
// 第二种和第三种方式都是需要在写代码的时候就要知道类名.
// 1. 直接通过全限定类名类获取.
Class catClass = Class.forName("Cat");
// 2. 通过类的实例来获取
Cat cat = new Cat("咪咪");
Class catClass2 = cat.getClass();
// 3. 通过类来直接获取
Class catClass3 = Cat.class;
System.out.println(catClass == catClass2);
System.out.println(catClass == catClass3);
}
}
反射
1、定义
Java的反射(reflection)机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性,既然能拿到那么,我们就可以修改部分类型信息;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射(reflection)机制。
2、用途
1、在日常的第三方应用开发过程中,经常会遇到某个类的某个成员变量、方法或是属性是私有的或是只对系统应用开放,这时候就可以利用Java的反射机制通过反射来获取所需的私有成员或是方法 。
2、反射最重要的用途就是开发各种通用框架,比如在spring中,我们将所有的类Bean交给spring容器管理,无论是XML配置Bean还是注解配置,当我们从容器中获取Bean来依赖注入时,容器会读取配置,而配置中给的就是类的信息,spring根据这些信息,需要创建那些Bean,spring就动态的创建这些类。
3、反射基本信息
Java程序中许多对象在运行时会出现两种类型:运行时类型(RTTI)和编译时类型,例如Person p = new Student();这句代码中p在编译时类型为Person,运行时类型为Student。程序需要在运行时发现对象和类的真实信息。而通过使用反射程序就能判断出该对象和类属于哪些类。
4、反射的使用
class Cat {
private String name = "小猫";
public Cat() {
}
public Cat(String name) {
this.name = name;
}
public void eat(String food) {
System.out.println(name + " 正在吃 " + food);
}
public void eat(String food1, String food2) {
System.out.println(name + " 正在吃 " + food1 + ", " + food2);
}
}
借助反射来实例化对象
// 通过反射来实例化对象
public static void testInstance() throws ClassNotFoundException, IllegalAccessException, InstantiationException {
Class catClass = Class.forName("Cat");
// Cat cat = new Cat();
Cat cat = (Cat) catClass.newInstance();
}
在这里不使用new关键字也能创建出对象!
借助反射来修改/获取属性
public static void testField() throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
// 1. 先获取到类对象.
Class catClass = Class.forName("Cat");
// 2. 借助类对象, 获取到指定的 Field 对象
// 现在这一步获取到的 field 对象相当于从一个大图纸中获取了一个局部的图纸.
Field field = catClass.getDeclaredField("name");
field.setAccessible(true); // 专门处理 private 成员的方式. 破门而入.
// 3. 根据图纸来修改/获取对象的相关字段
Cat cat = new Cat();
// 可以通过 get 方法获取对应属性
// 也可以通过 set 方法来修改属性
field.set(cat, "咪咪");
String name = (String) field.get(cat);
System.out.println(name);
}
借助反射来获取/调用方法
public static void testMethod() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
// 1. 先获取到类对象.
Class catClass = Class.forName("java16_0521.Cat");
// 2. 根据类对象, 根据名字获取到指定的 Method 对象
// getMethod 从第二个参数开始, 其实是用来描述当前 eat 对应的方法应该是哪个版本(当出现 eat 被重载的时候, 能够借助参数列表的类型区分出来).
Method method = catClass.getMethod("eat", String.class);// 获取到一个参数的版本的 eat
method.setAccessible(true);
// Method method = catClass.getMethod("eat", String.class, String.class);// 获取到两个参数的版本的 eat
// 3. 借助 Method 对象来调用指定的方法(对于非静态方法, 需要指定实例来调用).
Cat cat = new Cat();
method.invoke(cat, "鱼");
}
调用构造方法(也是在创建实例)
public static void testConstructor() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
// 1. 获取类对象
Class catClass = Class.forName("java16_0521.Cat");
// 2. 借助类对象获取 Constructor 对象
// 下面的操作意思是获取到参数为一个 String 的构造方法.
Constructor constructor = catClass.getConstructor(String.class);
constructor.setAccessible(true);
// 3. 根据 Constructor 实例化对象
Cat cat = (Cat) constructor.newInstance("小黑");
cat.eat("猫粮");
}
5、反射的优缺点
优点:
-
对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法
-
增加程序的灵活性和扩展性,降低耦合性,提高自适应能力
-
反射已经运用在了很多流行框架如:Struts、Hibernate、Spring 等等。
缺点: -
使用反射会有效率问题。会导致程序效率降低。具体参考这里:http://www.imooc.com/article/293679
-
反射技术绕过了源代码的技术,因而会带来维护问题。反射代码比相应的直接代码更复杂 。
主要问题在于Java的反射API设计不够简洁
注:反射不是一种“常规的编程手段”,不到特殊时刻是不建议使用的。