1 反射的概念
(1)JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。 (2)要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象。 (3)Java属于先编译再运行的语言,程序中对象的类型在编译期就确定下来了,而当程序在运行时可能需要动态加载某些类,这些类因为之前用不到,所以没有被加载到JVM。通过反射,可以在运行时动态地创建对象并调用其属性,不需要提前在编译期知道运行的对象是谁。
2 反射的原理
下图是类的正常加载过程、反射原理与class对象: Class对象的由来是将.class文件读入内存,并为之创建一个Class对象。
3 反射的优缺点:
1、优点:在运行时获得类的各种内容,进行反编译,对于Java这种先编译再运行的语言,能够让我们很方便的创建灵活的代码,这些代码可以在运行时装配,无需在组件之间进行源代码的链接,更加容易实现面向对象。 ?
2、缺点: (1)反射会消耗一定的系统资源 ,因此,如果不需要动态地创建一个对象,那么就不需要用反射; (2)反射调用方法时可以忽略权限检查 ,因此可能会破坏封装性而导致安全问题。
4 反射的用途:
1、反编译:.class–>.java ?
2、通过反射机制访问java对象的属性 ,方法 ,构造方法 等 ?
3、当我们在使用IDE,比如Ecplise时,当我们输入一个对象或者类,并想调用他的属性和方法是,一按点号,编译器就会自动列出他的属性或者方法,这里就是用到反射。 ?
4、反射最重要的用途就是开发各种通用框架 。比如很多框架(Spring)都是配置化的(比如通过XML文件配置Bean),为了保证框架的通用性,他们可能需要根据配置文件加载不同的类或者对象,调用不同的方法,这个时候就必须使用到反射了,运行时动态加载需要的加载的对象。
5 反射的使用
5.1 获取Class对象的三种方式
(1)Object–>getClass() (2)任何数据类型(包括基本的数据类型)都有一个“静态”的class属性 (3)通过Class类的静态方法:forName(String className)(最常用 )
5.1.1 Object.getClass()
Object类中的getClass方法、因为所有类都继承Object类。从而调用Object类来获取。
5.1.2 获取Class对象的三种方式代码实现
package com.pikaqiu.fs;
import com.pikaqiu.entity.User;
public class FanShe {
public static void main(String[] args) {
User user = new User();
Class userClass1 = user.getClass();
System.out.println(userClass1);
Class userClass2 = User.class;
System.out.println(userClass2);
System.out.println(userClass1 == userClass2);
try {
Class userClass3 = Class.forName("com.pikaqiu.entity.User");
System.out.println(userClass3);
System.out.println(userClass1 == userClass3);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
注意:在运行期间,一个类,只有一个Class对象产生。
三种方式常用第三种,第一种对象都有了还要反射干什么。第二种需要导入类的包,依赖太强,不导包就抛编译错误。一般都第三种,一个字符串可以传入也可写在配置文件中等多种方法。
5.2 通过反射获取构造方法
5.2.1 批量的方法
- public Constructor[] getConstructors():所有"公有的"构造方法
- public Constructor[] getDeclaredConstructors():获取所有的构造方法(包括私有、受保护、默认、公有)
5.2.2 获取单个的方法,并调用
- public Constructor getConstructor(Class… parameterTypes):获取单个的"公有的"构造方法:
- public Constructor getDeclaredConstructor(Class… parameterTypes):获取"某个构造方法"可以是私有的,或受保护、默认、公有;
5.2.3 调用构造方法
Constructor–>newInstance(Object… initargs)
5.2.4 代码实现
User类:
package com.pikaqiu.entity;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import javax.naming.Name;
@Component(value = "user")
public class User {
public String name;
public int age;
User(String name){
System.out.println("(默认)的构造方法 s = " + name);
}
public User(){
System.out.println("调用了公有、无参构造方法执行了。。。");
}
public User(char name){
System.out.println("姓名:" + name);
}
public User(String name ,int age){
System.out.println("姓名:"+name+"年龄:"+ age);
}
protected User(boolean n){
System.out.println("受保护的构造方法 n = " + n);
}
private User(int age){
System.out.println("私有的构造方法 年龄:"+ age);
}
}
构造函数调用:
package com.pikaqiu.fs;
import jdk.nashorn.internal.runtime.logging.DebugLogger;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.net.SocketTimeoutException;
import java.util.zip.CheckedOutputStream;
public class GetConstructor {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class name = Class.forName("com.pikaqiu.entity.User");
System.out.println("**********************所有公有构造方法*********************************");
Constructor[] constructors = name.getConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
System.out.println("************所有的构造方法(包括:私有、受保护、默认、公有)***************");
Constructor[] declaredConstructors = name.getDeclaredConstructors();
for (Constructor declaredConstructor : declaredConstructors) {
System.out.println(declaredConstructor);
}
System.out.println("*****************获取公有、无参的构造方法*******************************");
Constructor constructor = name.getConstructor(null);
System.out.println(constructor);
System.out.println("******************获取私有构造方法,并调用*******************************");
Constructor declaredConstructor = name.getDeclaredConstructor(int.class);
System.out.println(declaredConstructor);
declaredConstructor.setAccessible(true);
Object o = declaredConstructor.newInstance(33);
System.out.println(o);
}
}
运行结果:
**********************所有公有构造方法*********************************
public com.pikaqiu.entity.User(java.lang.String,int)
public com.pikaqiu.entity.User()
public com.pikaqiu.entity.User(char)
************所有的构造方法(包括:私有、受保护、默认、公有)***************
private com.pikaqiu.entity.User(int)
protected com.pikaqiu.entity.User(boolean)
public com.pikaqiu.entity.User(java.lang.String,int)
com.pikaqiu.entity.User(java.lang.String)
public com.pikaqiu.entity.User()
public com.pikaqiu.entity.User(char)
*****************获取公有、无参的构造方法*******************************
public com.pikaqiu.entity.User()
******************获取私有构造方法,并调用*******************************
private com.pikaqiu.entity.User(int)
私有的构造方法 年龄:33
com.pikaqiu.entity.User@5e481248
5.3 获取成员变量并调用
5.3.1 批量的方法
- Field[] getFields():获取所有的"公有字段"
- Field[] getDeclaredFields():获取所有字段,包括:私有、受保护、默认、公有;
5.3.2 获取单个的方法
- public Field getField(String fieldName):获取某个"公有的"字段;
- public Field getDeclaredField(String fieldName):获取某个字段(可以是私有的)
5.3.3 设置字段的值
Field --> public void set(Object obj,Object value):
- 参数说明:
- obj:要设置的字段所在的对象;
- value:要为字段设置的值;
5.3.4 代码实现
User类
package com.pikaqiu.entity;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import javax.naming.Name;
@Component(value = "user")
public class User {
public String name;
protected int age;
char sex;
private String phone;
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
", sex=" + sex +
", phone='" + phone + '\'' +
'}';
}
}
成员变量调用
package com.pikaqiu.fs;
import com.pikaqiu.entity.User;
import jdk.nashorn.internal.runtime.Source;
import jdk.nashorn.internal.runtime.logging.DebugLogger;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.SocketTimeoutException;
import java.util.zip.CheckedOutputStream;
public class GetConstructor {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
Class name = Class.forName("com.pikaqiu.entity.User");
System.out.println("************获取所有公有的字段********************");
Field[] fields = name.getFields();
for (Field field : fields) {
System.out.println(field);
}
System.out.println("************获取所有的字段(包括私有、受保护、默认的)********************");
Field[] declaredFields = name.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
}
System.out.println("*************获取公有字段**并调用***********************************");
Field name1 = name.getField("name");
System.out.println(name1);
Object o = name.getConstructor().newInstance();
name1.set(o, "haha");
User o1 = (User) o;
System.out.println("name:" + o1.name);
System.out.println("**************获取私有字段****并调用********************************");
Field phone = name.getDeclaredField("phone");
System.out.println(phone);
phone.setAccessible(true);
phone.set(o,"2341241234");
System.out.println(o1);
}
}
结果:
************获取所有公有的字段********************
public java.lang.String com.pikaqiu.entity.User.name
************获取所有的字段(包括私有、受保护、默认的)********************
public java.lang.String com.pikaqiu.entity.User.name
protected int com.pikaqiu.entity.User.age
char com.pikaqiu.entity.User.sex
private java.lang.String com.pikaqiu.entity.User.phone
*************获取公有字段**并调用***********************************
public java.lang.String com.pikaqiu.entity.User.name
调用了公有、无参构造方法执行了。。。
name:haha
**************获取私有字段****并调用********************************
private java.lang.String com.pikaqiu.entity.User.phone
User{name='haha', age=0, sex= , phone='2341241234'}
5.4 获取成员方法并调用
5.4.1 批量的方法
- public Method[] getMethods():获取所有"公有方法";(包含了父类的方法也包含Object类)
- public Method[] getDeclaredMethods():获取所有的成员方法,包括私有的(不包括继承的)
5.4.2 获取单个的:
-
public Method getMethod(String name,Class<?>… parameterTypes):
- 参数:
- name : 方法名;
- Class … : 形参的Class类型对象
-
public Method getDeclaredMethod(String name,Class<?>… parameterTypes)
5.4.3 调用方法:
- Method --> public Object invoke(Object obj,Object… args):
参数说明:
- obj : 要调用方法的对象;
- args:调用方式时所传递的实参;
5.4.4 代码实现
User类
package com.pikaqiu.entity;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import javax.naming.Name;
@Component(value = "user")
public class User {
public void show1(String s){
System.out.println("调用了:公有的,String参数的show1(): s = " + s);
}
protected void show2(){
System.out.println("调用了:受保护的,无参的show2()");
}
void show3(){
System.out.println("调用了:默认的,无参的show3()");
}
private String show4(int age){
System.out.println("调用了,私有的,并且有返回值的,int参数的show4(): age = " + age);
return "abcd";
}
}
获取成员方法
package com.pikaqiu.fs;
import com.pikaqiu.entity.User;
import jdk.nashorn.internal.runtime.Source;
import jdk.nashorn.internal.runtime.logging.DebugLogger;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.SocketTimeoutException;
import java.util.zip.CheckedOutputStream;
public class GetConstructor {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
Class name = Class.forName("com.pikaqiu.entity.User");
System.out.println("***************获取所有的”公有“方法*******************");
name.getMethods();
Method[] methodArray = name.getMethods();
for(Method m : methodArray){
System.out.println(m);
}
System.out.println("***************获取所有的方法,包括私有的*******************");
methodArray = name.getDeclaredMethods();
for(Method m : methodArray){
System.out.println(m);
}
System.out.println("***************获取公有的show1()方法*******************");
Method m = name.getMethod("show1", String.class);
System.out.println(m);
Object obj = name.getConstructor().newInstance();
m.invoke(obj, "刘德华");
System.out.println("***************获取私有的show4()方法******************");
m = name.getDeclaredMethod("show4", int.class);
System.out.println(m);
m.setAccessible(true);
Object result = m.invoke(obj, 20);
System.out.println("返回值:" + result);
}
}
结果
***************获取所有的”公有“方法*******************
public java.lang.String com.pikaqiu.entity.User.toString()
public void com.pikaqiu.entity.User.setName(java.lang.String)
public void com.pikaqiu.entity.User.show1(java.lang.String)
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
***************获取所有的方法,包括私有的*******************
public java.lang.String com.pikaqiu.entity.User.toString()
public void com.pikaqiu.entity.User.setName(java.lang.String)
private java.lang.String com.pikaqiu.entity.User.show4(int)
void com.pikaqiu.entity.User.show3()
public void com.pikaqiu.entity.User.show1(java.lang.String)
protected void com.pikaqiu.entity.User.show2()
***************获取公有的show1()方法*******************
public void com.pikaqiu.entity.User.show1(java.lang.String)
调用了公有、无参构造方法执行了。。。
调用了:公有的,String参数的show1(): s = 刘德华
***************获取私有的show4()方法******************
private java.lang.String com.pikaqiu.entity.User.show4(int)
调用了,私有的,并且有返回值的,int参数的show4(): age = 20
返回值:abcd
5.5 反射的应用
5.5.1 泛型擦除
将已存在的ArrayList集合中添加一个字符串数据
package com.pikaqiu.fs;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
public class FanShenTest {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(new Integer("234"));
list.add(new Integer(5));
System.out.println(list);
Class aClass = Class.forName("java.util.ArrayList");
Method add = aClass.getMethod("add", Object.class);
add.invoke(list, "haha");
System.out.println(list);
}
}
5.5.2 反射配置文件
- 通过反射配置文件,运行配置文件中指定类的对应方法
读取properties.txt文件中的数据,通过反射技术,来完成User对象的创建 properties.txt文件内容如下:
className=com.pikaqiu.entity.User
methodName=method
- User对象
package com.pikaqiu.entity;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import javax.naming.Name;
@Component(value = "user")
public class User {
public String name;
public int age;
private String address;
public User(String name, int age, String address){
this.name = name;
this.age = age;
this.address = address;
System.out.println("带有String, int, String的构造方法");
}
private void method(){
System.out.println("私有方法");
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + ", address=" + address+ "]";
}
}
- 读取配置文件,调用指定类中的对应方法
package com.pikaqiu.fs;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;
public class FanSheTest2 {
public static void main(String[] args) throws IOException, ClassNotFoundException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchMethodException {
Properties properties = new Properties();
String name = "F:\\java基础学习\\复习testcode\\SpringReview\\src\\main\\resources\\properties.txt";
properties.load(new FileInputStream(name));
String className = properties.getProperty("className");
System.out.println(className);
Class aClass = Class.forName(className);
Constructor constructor = aClass.getConstructor(String.class, int.class, String.class);
Object o = constructor.newInstance("小明", 20, "中国");
System.out.println(o);
String methodName = properties.getProperty("methodName");
Method m5 = aClass.getDeclaredMethod(methodName, null);
m5.setAccessible(true);
m5.invoke(o, null);
}
}
- 结果
com.pikaqiu.entity.User
带有String, int, String的构造方法
Person [name=小明, age=20, address=中国]
私有方法
6 知识点总结
6.1 如何获取.Class文件对象
-
通过Object类 getClass()方法获取 Class对象 -
通过类名.class 方式 获取 Class对象 -
通过反射的方式, Class.forName(String classname) 获取Class对象 public static Class<?> forName(String className)throws ClassNotFoundException
返回与带有给定字符串名的类或接口相关联的Class 对象
6.2 通过反射, 获取类中的构造方法,并完成对象的创建
-
获取指定的构造方法 ?public Constructor<T> getConstructor(Class<?>... parameterTypes)
获取指定的public修饰的构造方法
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
获取指定的构造方法,包含私有的
-
获取所有的构造方法 public Constructor<?>[] getConstructors() 获取所有的public 修饰的构造方法
public Constructor<?>[] getDeclaredConstructors() 获取所有的构造方法,包含私有的
?
6.3 通过反射, 获取类中的构造方法,并完成对象的创建
步骤:
-
获取字节码文件对象 -
通过字节码文件对象 ,获取到指定的构造方法 getConstructor(参数);
-
通过构造方法,创建对象 public T newInstance(Object... initargs)
6.4 私有构造方法,创建对象
-
获取字节码文件对象 -
通过字节码文件对象 ,获取到指定的构造方法 getDeclaredConstructor (参数);
-
暴力访问 con.setAccessible(true);
-
通过构造方法,创建对象 public T newInstance(Object... initargs)
6.5 通过反射,获取Class文件中的方法
-
获取指定的方法 ?public Method getMethod(String name, Class<?>... parameterTypes)
获取指定的public方法
public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
获取指定的任意方法,包含私有的
-
获取所有的方法 public Method[] getMethods() 获取本类与父类中所有public 修饰的方法
public Method[] getDeclaredMethods()获取本类中所有的方法,包含私有的
6.6 通过反射,调用方法
步骤:
-
获取Class对象 -
获取构造方法,创建对象 -
获取指定的public方法 -
执行方法 public Object invoke(Object obj, Object... args)
6.6 私有方法的调用:
-
获取Class对象 -
获取构造方法,创建对象 -
获取指定的private方法 -
开启暴力访问 m5.setAccessible(true);
-
执行方法 public Object invoke(Object obj, Object... args)
6.7 通过反射,获取成员变量(字段)
-
获取指定的成员变量 public Field getField(String name) 获取public修饰的成员变量
public Field getDeclaredField(String name) 获取任意的成员变量,包含私有
-
获取所有的成员变量 public Field[] getFields() 获取所有public修饰的成员变量
public Field[] getDeclaredFields() 获取司所有的成员变量,包含私有
6.8 通过反射,获取成员变量,并赋值使用
步骤:
-
获取字节码文件对象 -
获取构造方法,创建对象 -
获取指定的成员变量 -
对成员变量赋值\获取值操作 public void set(Object obj, Object value) 赋值
public Object get(Object obj) 获取值
6.9 私有成员变量的使用
步骤:
-
获取字节码文件对象 -
获取构造方法,创建对象 -
获取指定的成员变量 -
开启暴力访问 phone.setAccessible(true);
-
对成员变量赋值\获取值操作 public void set(Object obj, Object value) 赋值
public Object get(Object obj) 获取值
参考文献:
- https://blog.csdn.net/a745233700/article/details/82893076
- https://blog.csdn.net/sinat_38259539/article/details/71799078?spm=1001.2101.3001.6650.12&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ELandingCtr%7ERate-12.queryctrv4&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ELandingCtr%7ERate-12.queryctrv4&utm_relevant_index=18
?
|