Java中反射机制很重要,Java的动态语言就是靠反射机制实现的,反射技术也是程序员走的更远必不可少的一个技能。一般情况下我们都是通过类来创建对象,如果要求通过一个对象找到一个类,就需要用到反射机制,各种开源框架的编写更是离不开反射的机制。
一、了解反射机制
一般我们写代码,按照如下的步骤使用一个类:
1、使用关键字 import 导入类所在的包;
2、使用类名称或者接口名称定义对象;
3、通过关键字 new 进行类对象实例化;
4、使用【对象.属性】进行类中属性的调用;
5、使用【对象.方法】进行类中方法的调用;
而 Java 中的反射过程就是指:在程序的运行过程中,把 Java 类中的各种元素映射成对应的类,并能动态的执行这些类的方法。
Java 反射机制里重要的几个类:
类:java.lang.Class
属性:java.lang.reflect.Field
构造方法:java.lang.reflect.Constructor
方法:java.lang.reflect.Method
用反射可以轻松的获取一个类中的以上元素,并可以动态地对其进行调用,在不知道别人写的代码的时候,可以通过配置来调用别人的代码,很多开源框架都离不开反射机制,反射的魅力也正如此。
二、认识 Class 类
1、了解 Object 类:
我们知道如果一个类没有明确的声明集成自哪个父类时,那么它默认继承 Object 类,Object 类是所有类的父类。
Object 类中的 getClass()方法的源码:
public final native Class<?> getClass();
getClass()方法返回的类型是一个 Class 类,Class 类就是 Java 反射的源头。以上的源码用到了 native 关键字和泛型,使用 native 关键字说明这个方法是原生函数,也就是这个方法是用 C/C++ 语言实现的,由 Java 通过 JNI 去调用的本地接口;如果不让程序出现警告信息,可以在使用时指定其操作的泛型具体类型,或者直接用“?”来取代。
2、获取 Class 类的 4 种实例化方式:
1、通过类本身的 class 属性实例化【类.class】;
2、通过 Object 类的 getClass()方法实例化【对象.getClass()】;
3、通过【Class.forName()方法】实例化,这种方式最常用,体现出了反射的动态性;
4、通过类的加载器获取;
/**
* 如何获取Class类的实例的4种方式
*/
@Test
public void TestClass() throws Exception {
// 1、调用运行时类本身的.class属性
Class<?> clazz = User.class;
System.out.println(clazz);
// 2、通过运行时类的对象获取
User p = new User();
Class<?> clazz1 = p.getClass();
System.out.println(clazz1);
// 3、通过Class的静态方法获取,体现了反射的动态性,
// 传入className就能创建Class类的实例
String className = "com.jpm.reflection.User";
Class<?> clazz3 = Class.forName(className);
System.out.println(clazz3);
// 4、通过类的加载器获取
ClassLoader classLoader = this.getClass().getClassLoader();
Class clazz4 = classLoader.loadClass(className);
System.out.println(clazz4);
}
/**
* User类,用于演示反射,为了不影响体验,代码见文章末尾
*/
运行结果:
class com.jpm.reflection.User
class com.jpm.reflection.User
class com.jpm.reflection.User
class com.jpm.reflection.User
三、类的加载器 ClassLoader
1、了解 Class 类的 3 种加载器:
1、系统类加载器(AppClassLoader):加载自定义类和普通 jar 包;
2、扩展类加载器(ExtClassLoader):加载 jre/lib/ext 目录下的类;
3、启动类加载器(BootStrapClassLoader):加载 jdk 的核心类库,启动类加载器 Java 代码获取不到;
/**
* 类的加载器测试
*/
@Test
public void testClassLoader() throws Exception {
ClassLoader loader1 = this.getClass().getClassLoader();
// 获取系统类加载器:sun.misc.Launcher$AppClassLoader@7852e922
System.out.println(loader1);
ClassLoader loader2 = loader1.getParent();
// 获取系统类加载器的父类,扩展类加载器:sun.misc.Launcher$ExtClassLoader@330bedb4
System.out.println(loader2);
ClassLoader loader3 = loader2.getParent();
// 获取扩展类的加载器的父类为null,说明引导类加载器无法在代码中获取
System.out.println(loader3);
Class clazz1 = User.class;
ClassLoader loader4 = clazz1.getClassLoader();
// 普通类用系统类加载器加载:sun.misc.Launcher$AppClassLoader@7852e922
System.out.println(loader4);
Class clazz2 = Class.forName("java.lang.String");
ClassLoader loader5 = clazz2.getClassLoader();
// String类是jdk的核心类库,用引导类加载器加载,返回null,说明类加载器无法在代码中获取
System.out.println(loader5);
}
运行结果:
sun.misc.Launcher$AppClassLoader@7852e922
sun.misc.Launcher$ExtClassLoader@330bedb4
null
sun.misc.Launcher$AppClassLoader@7852e922
null
2、类加载器的应用案例
1、获取 Java 包里的配置文件
@Test
public void test() throws IOException {
// 类加载器的应用,获取java包里的配置文件,但是不能获取工程目录下的文件
ClassLoader loader = this.getClass().getClassLoader();
InputStream is = loader.getResourceAsStream("configs//jdbc.properties");
Properties properties = new Properties();
properties.load(is);
is.close();
String user = properties.getProperty("user");
System.out.println(user);
String pwd = properties.getProperty("pwd");
System.out.println(pwd);
// 文件名不一定是properties后缀,也可以是txt等
InputStream is1 = loader.getResourceAsStream("configs//jdbc.txt");
Properties properties1 = new Properties();
properties1.load(is1);
is1.close();
String user1 = properties1.getProperty("user");
System.out.println(user1);
String pwd1 = properties1.getProperty("pwd");
System.out.println(pwd1);
}
配置文件内容:
运行结果:
properties
jdbc.properties
txt
jdbc.txt
2、获取工程下的配置文件
上面的类加载器获取 java 包里的配置文件,但是不能获取工程目录下的文件,要想获取工程目录下的文件,可以使用如下的代码:
@Test
public void test2() throws IOException {
// 工程下的文件获取,文件名不一定是properties后缀,也可以是txt等
InputStream is = new BufferedInputStream(new FileInputStream(new File("jdbc2.properties")));
Properties pro = new Properties();
pro.load(is);
is.close();
System.out.println(pro.getProperty("user"));
System.out.println(pro.getProperty("pwd"));
}
运行结果:
root
root123
四、Java 反射初体验
下面一个例子用来演示如何通过 Java 的反射机制,动态创建 User 类对象,并调用 User 类对象的属性赋值以及调用它的方法。
/**
* 反射的初步体验
*/
@Test
public void TestReflection() throws Exception {
Class<?> clazz = Class.forName("com.jpm.reflection.User");
// 创建运行时类User的对象
Object user = clazz.newInstance();
System.out.println(user);
// 通过反射调用运行时类的指定public属性
Field f1 = clazz.getField("name");
f1.set(user, "JPM");
System.out.println(user);
// 通过反射调用运行时类的指定private属性
Field f2 = clazz.getDeclaredField("age");
f2.setAccessible(true); // 为私有属性赋值
f2.set(user, 18);
System.out.println(user);
// 通过反射调用运行时类的指定方法
Method m1 = clazz.getMethod("show");
m1.invoke(user);
Method m2 = clazz.getMethod("display", String.class);
m2.invoke(user, "BJ");
}
/**
* User类,用于演示反射
*/
public class User {
public String name;
private int age;
// 创建类时尽量写一个空参的构造器
public User() {
super();
}
public User(String name) {
super();
this.name = name;
}
public User(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User [name=" + name + ", age=" + age + "]";
}
public void show() {
System.out.println("show method run!");
}
public void display(String address) {
System.out.println("display method run,address=" + address);
}
}
运行结果:
User [name=null, age=0]
User [name=JPM, age=0]
User [name=JPM, age=18]
show method run!
display method run,address=BJ
本文主要介绍了反射的概念、反射的源头 Class 类、类的 3 种加载器、类加载器ClassLoader 的应用场景、用Java 反射获取用户自定义类User,初步体验了 Java 反射机制的魅力。
后面我会用 2 篇文章来说明 Java 反射机制的深入体验,反射的高级应用动态代理的实现,敬请期待......