反射及作用
Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期间借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
-
反射(Reflect)是在运行时动态访问类与对象的技术,即把创建对象的时机从程序编译时延迟到程序运行时。
-
反射是JDK1.2版本后的高级特性隶属于java.lang.reflect
-
大多数Java框架都基于反射实现参数配置、动态注入等特性
Class类及对象实例化
反射核心类
◆Constructor构造方法类
◆Class类
◆Method方法类
◆Field成员变量类
Class类
-
Class是JVM中代表”类和接口”的类
-
Class对象具体包含了某个特定类的结构信息。
-
通过Class对象可获取对应类的构造方法/方法/成员变量
1.关于java. lang. Class类的理解 1.类的加载过程: 程序经过javac.exe命令以后,会生成一个或多个字 节码文件(.cLass结尾)。接着我们使用java. exe命令对某个字节码文件进行解释运行。相当于将某个字节码文件加载到内存中。此过程就称为类的加载。加载到内存中的类,我们就称为运行时类,此运行时类,就作为Class的一个实例。
2.换句话说,Class的实例就对应着一个运行时类(例如Person类)。
3.加载到内存中的运行时类,会缓存一 定的时间。在此时间之内,我们可以通过不同的方式来获取此运行时类。
类可以造对象,类本身还是另外一个类的对象。例如有“概念”,“手机”这两个词,“手机”需要用一些描述性语句去解释,这些描述性语句就相当于”手机“的”概念“了,但是”概念”本身也是一个词,”概念”这个词也得需要一些描述性语句(“概念”)去解释。
Class核心方法
其中Class.forName()的参数是具体类的完整路径字符串,用来获取指定类的Class对象。
获取Class实例的4种方式
-
前提:若已知具体的类,通过类的class属性获取,该方法最为安全可靠, 程序性能最高
实例:Class clazz = String.class;
-
前提:已知某个类的实例,调用该实例的getClass()方法获取Class对象
实例:Class clazz = “com.zqf.Person”.getClass();
-
前提:已知一个类的全类名,且该类在类路径下,可通过Class类的静态方 法forName()获取,可能抛出ClassNotFoundException
实例:Class clazz = Class.forName(“java.lang.String”);
-
其他方式
ClassLoader cl = this.getClass().getClassLoader(); Class clazz4 = cl.loadClass(“类的全类名”);
//方式一:调用运行时类的属性:.class
Class clazz1 = Person.class;
System.out.println(clazz1);
//方式二:通过运行时类的对象,调用getCLass()
Person p1 = new Person();
Class clazz2 = p1.getClass();
System.out.println(c1azz2);
//方式三:调用Class 的静态方法: forName(String classPath)
Class clazz3 = Class.forName("com.zqf.java.Person");
System. out. println(clazz3);
//方式四:使用类的加载器: ClassLoader
ClassLoader classLoader = ReflectionTest.class.getClassLoader();
Class clazz4 = classLoader.1oadClass("com.zqf.java.Person");
System.out.println(clazz4);
案例:
package com.imooc.reflect.entity;
/**
* 员工实体类
*/
public class Employee {
//类的初始化是经过静态块来完成的
static {
System.out.println("Employee类已被加载到jvm,并已初始化");
}
private Integer eno;
public String ename;
private Float salary;
private String dname;
public Employee(){
System.out.println("Employee默认构造方法已被执行");
}
public Employee(Integer eno,String ename,Float salary , String dname){
this.eno = eno;
this.ename = ename;
this.salary = salary;
this.dname = dname;
System.out.println("Employee带参构造方法已被执行");
}
public Integer getEno() {
return eno;
}
public void setEno(Integer eno) {
this.eno = eno;
}
public String getEname() {
return ename;
}
public void setEname(String ename) {
this.ename = ename;
}
public Float getSalary() {
return salary;
}
public void setSalary(Float salary) {
this.salary = salary;
}
public String getDname() {
return dname;
}
public void setDname(String dname) {
this.dname = dname;
}
@Override
public String toString() {
return "Employee{" +
"eno=" + eno +
", ename='" + ename + '\'' +
", salary=" + salary +
", dname='" + dname + '\'' +
'}';
}
public Employee updateSalary(Float val){
this.salary = this.salary + val;
System.out.println(this.ename + "调薪至" + this.salary + "元");
return this;
}
}
Sample:
package com.imooc.reflect;
import com.imooc.reflect.entity.Employee;
public class ClassSample {
public static void main(String[] args) {
try {
//Class.forName()方法将指定的类加载到jvm,并返回对应Class对象
Class employeeClass = Class.forName("com.imooc.reflect.entity.Employee");
//employeeClass包含了Employee中所描述的所有静态成员以及方法和构造函数
System.out.println("Employee已被加载到jvm");
//newInstance通过默认构造方法(无参)创建新的对象
//newInstance()默认返回Object类型,需要进行强制转换
Employee emp = (Employee)employeeClass.newInstance();
System.out.println(emp);
} catch (ClassNotFoundException e) {
//类名与类路径书写错误是抛出"类无法找到"异常
e.printStackTrace();
} catch (IllegalAccessException e) {
//非法访问异常,当在作用域外访问对象方法或成员变量时抛出
e.printStackTrace();
} catch (InstantiationException e) {
//对象无法被实例化,抛出"实例化异常"
e.printStackTrace();
}
}
}
通过加载Employee得到类对象之后,利用newInstance()方法调用默认构造方法创建对应的对象。
Constructor构造方法对象
Constructor类
-
Constructor类是对Java类中的构造方法的抽象
-
Contructor对象包含了具体类的某个具体构造方法的声明
-
通过Constructor对象调用带参构造方法创建对象
Constructor类核心方法
通过Constructor对象我们可以得到具体构造方法,他有几个参数,参数的类型是什么。同时也可以通过Constructor对象调用带参构造方法创建新的对象。
package com.imooc.reflect;
import com.imooc.reflect.entity.Employee;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/**
* 利用带参构造方法创建对象
*/
public class ConstructorSample {
public static void main(String[] args) {
try {
//获得对应的类对象 使用Class.forName对指定的类对象进行加载
Class employeeClass = Class.forName("com.imooc.reflect.entity.Employee");
//获取指定格式的带参构造方法
Constructor constructor = employeeClass.getConstructor(new Class[]{
Integer.class,String.class,Float.class,String.class
});
Employee employee = (Employee) constructor.newInstance(new Object[]{
100,"李磊",3000f,"研发部"
});
System.out.println(employee);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
//没有找到与之对应格式的方法
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
//当被调用的方法的内部抛出了异常而没有被捕获时
e.printStackTrace();
}
}
}
通过向getConstructor中传入一个Class类型的数组,在数组中声明参数的类型和数量,即可获取指定格式的带参构造方法。
在调用newInstance方法创建对象的时候,要传入实际的数据,传入一个Object类型的数组,并进行初始化操作。
Method方法对象
Method方法类
-
Method对象指代某个类中的方法的描述
-
Method对象使用classObj.getMethod()方法获取
-
通过Method对象调用指定对象的对应方法
Class employeeClass = Class.forName("com.imooc.reflect.entity.Employee");
Constructor constructor = employeeClass.getConstructor(new Class[]{
Integer.class,String.class,Float.class,String.class
});
Employee employee = (Employee)constructor.newInstance(new Object[]{
100,"李磊",3000f,"研发部"
});
Method updateSalaryMethod = employeeClass.getMethod("updateSalary" , new Class[]{
Float.class
});
Employee employee1 = (Employee)updateSalaryMethod.invoke(employee,new Object[]{1000f});
System.out.println(employee1);
在getMethod方法里,单有一个方法名称是无法精准定位的,因为在JAVA里可以通过重载,也就是一个方法通过不同的参数可以处理不同的事情,只有方法名是无法确定需要调用哪个方法的,所以在getMethod方法里还需要传入一个参数,代表参数类型是哪些,这里需要传入一个类型数组new Class[],传入我们要调用的方法的参数,得到的返回值是Method的方法对象。
得到方法对象之后就可以对他进行invoke调用,Invoke有两个参数,第一个参数是要执行哪一个对象,第二个参数则要传入具体的参数数值。
Field成员变量对象
Field成员变量类
-
Field对应某个具体类中的成员变量的声明
-
Field对象使用classObj.getField()方法获取
-
通过Field对象可为某对象成员变量赋值/取值
Field类核心方法
Class employeeClass = Class.forName("com.imooc.reflect.entity.Employee");
Constructor constructor = employeeClass.getConstructor(new Class[]{
Integer.class,String.class,Float.class,String.class
});
Employee employee = (Employee) constructor.newInstance(new Object[]{
100,"李磊",3000f,"研发部"
});
Field enameField = employeeClass.getField("ename");
enameField.set(employee,"李雷");
String ename = (String)enameField.get(employee);
System.out.println("ename:" + ename);
getField方法只有一个参数,即成员变量的名称,返回一个Field对象。
获取成员变量值:
所得到的Field对象有一个get的方法, 要获取哪个对象的成员变量值,就将这个对象作为get方法的参数传入进去,其返回值是Object类型,需要进行强制转换。
设置成员变量值:
与获取成员变量值类似,都是基于Field成员变量实现,只不过是使用set方法,它包含两个参数,第一个是要为哪个成员变量进行赋值,第二个是要为赋值的参数,且参数类型要与成员变量的声明类型一致。
getDeclared系列方法说明
-
getDeclaredConstructor(s)|Method(s)|Field(s)获取对应对象,可以访问私有对象。
-
getConstructor(s)|Method(s)|Field(s)只能获取public对象
-
访问非作用域内构造方法、方法、成员变量,会抛出无法访问的异常。
加s是获取所有声明的构造方法,不加s是获取单个的构造方法,Method和Field同理。
Class employeeClass = Class.forName("com.imooc.reflect.entity.Employee");
Constructor constructor = employeeClass.getConstructor(new Class[]{
Integer.class, String.class, Float.class, String.class
});
Employee employee = (Employee) constructor.newInstance(new Object[]{
100, "李磊", 3000f, "研发部"
});
//获取当前类所有成员变量
Field[] fields = employeeClass.getDeclaredFields();
for(Field field : fields){
// System.out.println(field.getName());
if(field.getModifiers() == 1){ //pubilc修饰
Object val = field.get(employee);
System.out.println(field.getName() + ":" + val);
else if(field.getModifiers() == 2){ //private修饰
//根据成员变量名获取getXxx方法名,例如 ename --> getEname()
String methodName = "get" + field.getName().substring(0,1).toUpperCase()
+ field.getName().substring(1);
//getXxx方法一定是public修饰的,所以使用getMethod方法没有问题
Method getMethod = employeeClass.getMethod(methodName);
//getXxx()方法是没有参数的,故只需要传入employee
Object ret = getMethod.invoke(employee);
System.out.println(field.getName() + ":" + ret);
}
field.getModifiers()方法返回值是一个整数,如果返回1则说明当前成员变量是public修饰,如果返回2则说明当前成员变量是private修饰。
在Object ret = getMethod.invoke(employee);不需要进行强制类型转换,因为成员变量的类型是不同的。
案例:
I18N接口:
public interface I18N {
public String say();
}
Zhcn:
public class Zhcn implements I18N {
@Override
public String say() {
return "生命不息奋斗不止";
}
}
Application:
import java.io.FileInputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.Properties;
public class Application {
public static void say(){
Properties properties = new Properties();
String configPath = Application.class.getResource("/config.properties").getPath();
try {
configPath = new URLDecoder().decode(configPath,"UTF-8");
properties.load(new FileInputStream(configPath));
String language = properties.getProperty("language");
// System.out.println(language);
//创建对象这里 需要动态创建
I18N i18n = (I18N)Class.forName(language).newInstance();
System.out.println(i18n.say());
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Application.say();
}
}
config-properties:
language=com.imooc.i18n.Zhcn
以后如果需要修改成英文,需要创建一个En类,实现I18N接口并重写say方法,并且在配置文件中将Zhcn更改为En即可。在Application中
I18N i18n = (I18N)Class.forName(language).newInstance();
利用反射机制可动态地创建对象。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/99479.html