通常情况下,我们做项目时,设计一个类,根据类去创建这个类的对象,根据对象名调用方法、属性和构造方法。
而反射与这种情况不一样。
什么是反射?
(1)Java反射机制的核心是在程序运行时动态加载类并获取类的详细信息,从而操作类或对象的属性和方法。本质是JVM得到class对象之后,再通过class对象进行反编译,从而获取对象的各种信息。
(2)Java属于先编译再运行的语言,程序中对象的类型在编译期就确定下来了,而当程序在运行时可能需要动态加载某些类,这些类因为之前用不到,所以没有被加载到JVM。通过反射,可以在运行时动态地创建对象并调用其属性,不需要提前在编译期知道运行的对象是谁。
反射是框架设计的灵魂。
框架定义:半成品软件,可以在框架的基础上进行软件开发,简化代码,解耦。
下面程序执行过程。
反射的关键是获取Class对象(类的字节码文件)。
获取Class类对象有三种方式。
1.源代码阶段:Class.forName("全类名"):将字节码文件加载到内存,返回该字节码文件的Class类对象
---多用在配置文件,把类的定义放在配置文件里,读取配置文件,加载类。
2.Class类对象阶段:通过类名.class获取Class类对象
---多用于参数传递。
3.运行阶段:通过对象名.getClass()获取,此方法生命在Object类里的方法
---多用于获取字节码文件。
java">package com.hpe.java;
public class TestReflection {
public static void main(String[] args) throws ClassNotFoundException {
//通常的做法
//Person person = new Person();
//person.setName("张三");
//person.setAge(20);
//System.out.println(person);
//person.eat();
//1.源代码阶段:Class.forName("全类名");
Class clazz = Class.forName("com.hpe.java.Person");
System.out.println(clazz);//class com.hpe.java.Person
//2.Class类对象阶段:通过类名.class获取Class类对象
Class<Person> clazz1 = Person.class;
System.out.println(clazz1);//class com.hpe.java.Person
//运行阶段:通过对象名.getClass()获取
Person person = new Person();
Class clazz2 = person.getClass();
System.out.println(clazz2);//class com.hpe.java.Person
//通过==比较三个Class类对象
System.out.println(clazz == clazz1);//true
System.out.println(clazz == clazz2);//true
Class<Student> studentClass = Student.class;
System.out.println(studentClass == clazz);//false
}
}
通过上面三个Class对象的比较,可以知道一个类在 JVM 中只会有一个Class实例,无论使用哪一种方式获取Class类对象,获取到的都是同一个。
那获取到Class对象之后要做什么呢?
Class类对象的功能
1.获取成员变量
2.获取成员方法
3.获取构造方法
4.获取类名
首先来说一下涉及到成员变量的几个方法。
获取成员变量:
1.Field[] getFields():获取所有public修饰的成员方法。
2.Field getField():根据变量名获取public修饰的成员变量。
3.Field[] getDeclareFields():获取所有的成员变量。
4.Field getDeclareField(String name):根据变量名获取成员变量。
对成员变量设置值和获取值
set(Object obj, Object value):设置成员变量的值。
Object get(Object obj):获取成员变量值。
int getModifiers():获取每个属性的访问修饰符
Class getModifiers():获取属性的类型
先写一个Person类。
java">package com.hpe.java;
public class Person {
private String name;
private int age;
public int id;
int a;
protected int b;
private int c;
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;
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person() {
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public void eat() {
System.out.println("人在吃饭");
}
public void eat(String food){
System.out.println("人在吃饭,吃的" + food);
}
private void show() {
System.out.println("show方法");
}
public String sayHello(){
return "Hello";
}
public static void info(){
System.out.println("静态info方法");
}
}
java">package com.hpe.java;
import java.lang.reflect.Field;
public class TestField {
public static void main(String[] args) throws Exception {
//获取Class对象
Class<Person> personClass = Person.class;
//1.Field[] getFields():获取所有public修饰的成员方法
Field[] fields = personClass.getFields();
for(Field field : fields){
System.out.println(field);//public int com.hpe.java.Person.id
}
System.out.println("===================================");
//2.Field getField():根据变量名获取public修饰的成员变量
Field id = personClass.getField("id");
//System.out.println(name);//public int com.hpe.java.Person.id
//Object get(Object obj):获取成员变量值。
//创建personClass对应的运行时对象
//直接通过Class对象创建实例
Person p = personClass.newInstance();
Object o = id.get(p);
System.out.println(o);//0
//设置值
id.set(p, 10);
System.out.println(id.get(p));//10
System.out.println("===================================");
//3.Field[] getDeclareFields():
Field[] dlf = personClass.getDeclaredFields();
for(Field field : dlf){
//1.获取每个属性的访问修饰符
//缺省-0 public-1 private-2 protected-4
int modifiers = field.getModifiers();
System.out.println(modifiers);
//2.获取属性的类型
Class type = field.getType();
String name = type.getName();
System.out.println(name);
//System.out.println(field);
}
Field name = personClass.getDeclaredField("c");
System.out.println(name);//private java.lang.String com.hpe.java.Person.name
//忽略访问权限的安全检查机制
name.setAccessible(true);
Object c = name.get(p);//报错
System.out.println(c);
}
}
注意:
1.getFields和getField只能获取到public修饰的变量;getDeclareField和getDeclareFields能获取所有的变量。
2.getModifiers会返回int类型的数据,缺省-0、public-1、private-2、protected-4。
3.当用get获取private修饰的变量的值的时候,要忽略访问权限的安全检查机制,即需要先执行 属性.setAccessible(true);然后再使用get获取值。
下面看一下获取构造方法。
构造方法的作用就是创建对象。
获取构造方法
1.Constructor[] getConstructors():返回所有的public修饰的构造方法。
2.Constructor getConstructor(参数类型.class,...):根据参数列表获取对应的public修饰的构造方法。
3.Constructor[] getDeclareConstructor(参数类型.class,...):获取所有构造方法。
4.Constructor getDeclareConstructor(参数类型.class,.):根据参数列表获取对应的构造方法。
java">package com.hpe.java;
import java.lang.reflect.Constructor;
public class TestConstructor {
public static void main(String[] args) throws Exception {
//1.获取Class类对象
Class<Person> personClass = Person.class;
//Constructor getConstructor(参数类型.class,...):根据参数列表获取对应的构造方法。
Constructor<Person> c = personClass.getConstructor(String.class, int.class);
System.out.println(c);//public com.hpe.java.Person(java.lang.String,int)
//构造方法的作用就是创建对象
//通过有参构造方法创建对象
Person person = c.newInstance("张三", 18);
System.out.println(person);
//通过无参构造方法创建对象,创建对象的常用方式都是无参构造方法
Constructor<Person> c1 = personClass.getConstructor();
Person person1 = c1.newInstance();
System.out.println(person1);
//简化的方式:用Class类对象的newInstance()创建对象
Person person2 = personClass.newInstance();
System.out.println(person2);
}
}
说明:
1.获取属性、构造函数时,凡是方法里带有Declare的都可以获取所有的。
2.获取构造函数的目的是创建对象,可以通过有参构造函数和无参构造函数创建对象,也可以直接通过Class对象创建对象,通过Class对象创建对象相当于调用了无参构造函数创建对象。
下面说一下获取方法。
获取方法
1.Method[] getMethods():获取所有的public方法,包括父类的。
2.Method getMethod(方法名):返回指定的public方法。
3.Method[] getDeclareMethods():获取所有方法,包含私有的,不包含父类的。
4.Method getDeclareMethod(方法名):返回指定的方法。
java">package com.hpe.java;
public class Person {
private String name;
private int age;
public int id;
int a;
protected int b;
private int c;
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;
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person() {
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public void eat() {
System.out.println("人在吃饭");
}
public void eat(String food){
System.out.println("人在吃饭,吃的" + food);
}
private void show() {
System.out.println("show方法");
}
public String sayHello(){
return "Hello";
}
public static void info(){
System.out.println("静态info方法");
}
}
java">package com.hpe.java;
import java.lang.reflect.Field;
public class TestMethod {
public static void main(String[] args) throws Exception {
Class<Person> personClass = Person.class;
//1.方法[] getMethods():获取所有的public方法
Method[] methods = personClass.getMethods();
for (Method method : methods) {
System.out.println(method);
}
System.out.println("==================================");
//2.方法[] getDeclareMethods():获取所有方法,包含私有的,不包含父类的
Method[] declaredMethods = personClass.getDeclaredMethods();
for (Method method : declaredMethods) {
System.out.println(method);
}
System.out.println("==================================");
//创建运行时对象
Person person = personClass.newInstance();
//操作属性
//获取属性
Field name = personClass.getDeclaredField("name");
Field age = personClass.getDeclaredField("age");
name.setAccessible(true);
age.setAccessible(true);
//设置属性值
name.set(person, "张三");
age.set(person, 18);
Object name1 = name.get(person);
Object age1 = age.get(person);
System.out.println("name: " + name1 + ",age: " + age1);
//3.方法 getMethods(String name,...):返回指定的public方法
Method eat = personClass.getMethod("eat");
//invoke(指定的对象, 形参列表)
eat.invoke(person);//方法属于对象
//调用有返回值的方法
Method sayHello = personClass.getMethod("sayHello");
Object returnValue = sayHello.invoke(person);
System.out.println(returnValue);
//调用静态方法
Method info = personClass.getMethod("info");
//info.invoke(person);
//info.invoke(personClass);
info.invoke(Person.class);
//调用有参数的方法
Method eat1 = personClass.getMethod("eat", String.class);
eat1.invoke(person, "辣子鸡");
}
}
注意:
1.getMethods不仅获取本类的public方法,也能获取到父类的public方法。
2.getDeclareMethods只能获取本类的所有方法。
3.调用有返回值的方法的时候,需要创建Object类型变量来接受返回值。
4.调用静态方法的时候,invoke的参数是本类的Class对象。
5.调用有参数的方法的时候,invoke的第一个参数为对象名,后几个参数为实参。
练习:模拟一个框架,在不改变该类的任何代码的前提下,可以帮我们创建类的对象,并且可以执行该对象的任何方法。
如何实现?
1.配置文件
2.反射
具体步骤。
1.创建配置文件,在配置文件里定义需要创建类的全类名和需要执行的方法或者属性。
2.在java程序中加载配置文件,读配置文件里的相关信息。
3.使用反射技术加载文件到内存中。
4.创建对象。
5.执行方法或者属性。
java">package com.hpe.java;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Properties;
public class TestFrame {
public static void main(String[] args) throws Exception {
//1.创建Properties对象
Properties properties = new Properties();
通过流的方式读取配置文件
//FileInputStream fis = new FileInputStream(new File("proclass.properties"));
将输入流加载到Properties对象中
//properties.load(fis);
//fis.close();
//2.通过类加载器去加载配置文件
//类加载器的作用就是把文件加载到内存中,我们就可以通过类加载器获取所有的字节码文件,
//同样也可以获取项目中的配置文件。
//2.1获取当前类字节码文件的类加载器(所有的字节码文件都是同一个类加载器)
ClassLoader classLoader = TestFrame.class.getClassLoader();
//2.2通过类加载器加载配置文件输入流
InputStream is = classLoader.getResourceAsStream("proclass.properties");
properties.load(is);
//3.获取配置文件里的相关配置
String user = properties.getProperty("user");
String password = properties.getProperty("password");
System.out.println(user + ":" + password);
String className = properties.getProperty("className");
String methodName = properties.getProperty("methodName");
//4.通过反射获取Class对象(把字节码文件加载到内存中)
Class clazz = Class.forName(className);
//5.创建对象
Object o = clazz.newInstance();
//6.获取对象的方法
Method method = clazz.getMethod(methodName);
//7.调用方法
method.invoke(o);
}
}
读取配置文件有两种方式,第一种是流的方式读取配置文件,第二种是通过类加载器读取配置文件。
我们主要来说明一下第二种方式:通过类加载器读取配置文件。
一般来说,Java 虚拟机使用 Java 类的方式如下:Java 源程序(.java 文件)在经过 Java 编译器编译之后就被转换成 Java 字节代码(.class 文件)。类加载器负责读取 Java 字节代码,并转换成 java.lang.Class类的一个实例。每个这样的实例用来表示一个 Java 类。