java中的反射机制
1、反射机制
---1.1概述
---1.2获取Class的三种方式
---1.3通过反射实例化对象
---1.4通过读属性文件实例化对象
---1.5Class.forName()会导致类加载
2、关于路径
---2.1类路径下的绝对路径(通用路径)
---2.2以流的形式返回路径
---2.3资源绑定器
3、类加载器
4、Field
---4.1获取Field
---4.2反编译Field
---4.3通过反射机制访问对象属性
5、Method
---5.1可变长度参数
---5.2反射Method
---5.3反编译Method
6、反射机制调用方法
7、Constructor
---7.1反编译类的Constructor(构造方法)
---7.2反射机制调用构造方法
8、获取父类和父接口
反射机制
概述
1、反射机制有什么用?
通过java语言中的反射机制可以操作字节码文件,有点类似于黑客。(可以读和修改字节码文件)通过反射机制可以操作代码片段。(class文件)
2、反射机制的相关类在哪个包下?
java.lang.reflect.*;
3、反射机制相关的类有哪些?
java.lang.Class<T>
:整个字节码文件,一个类型(一个类)
java.lang.reflect.Method
:代表字节码中的方法字节码(类中的方法)
java.lang.reflect.Constructor<T>
:代表字节码中的构造方法字节码(类中的构造方法)
java.lang.reflect.Field
:代表字节码中的属性字节码(成员变量)
获取Class的三种方式
- 第一种:
Class c = Class.forName("完整英名带包含");
这是静态方法。
方法的参数是一个字符串。
字符串需要的是一个完整类名。
完整类名必须带有包名。 java.lang包也不能省略。 - 第二种:
Class c = 对象.getClass();
java中任何一个对象都有getClass()方法 - 第三种:
Class c = 任何类型.class;
java语言中任何一种类型,包括基本数据类型,它都有.class属性。 - 代码示例
import java.util.Date;
public class Demo{
public static void main(String[] args){
/*第一种*/
Class c1 = null;
Class c2 = null;
Class c3 = null;
try {
//c1代表String.class文件或String类型
c1 = Class.forName("java.lang.String");
//c2代表Integer类型
c2 = Class.forName("java.lang.Integer");
//c3代表Date类型
c3 = Class.forName("java.util.Date");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
/*第二种*/
//java中任何一个对象都有getClass()方法
String s = "abc";
//x代表String.class字节码文件,x代表String类型。
Class x = s.getClass();
//判断内存地址
System.out.println(c1 == x);
/*输出:true,
字节码文件装载到JVM中的时候只装载一份*/
Date date = new Date();
Class y = date.getClass();
System.out.println(y == c3);
//输出:true
/*第三种*/
/*java语言中任何一种类型,
包括基本数据类型,它都有.class属性。*/
Class q = String.class;
Class w = int.class;
Class e = Date.class;
System.out.println(x == q);
//输出:true
System.out.println(y == e);
//输出:true
}
}
通过反射实例化对象
- 概述
1、通过反射机制,获取Class,通过Class的newInstance()方法来实例化对象。(更加灵活)
2、注意:newInstance()方法内部实际上调用了无参数构造方法,必须保证无参构造存在才可以。 - 代码示例
public class DemoTest{
public DemoTest() {
System.out.println("无参构造");
}
/**只定义有参构造(没有无参构造)*/
public DemoTest(String s) {
}
/*只定义有参构造后出现异常:
java.lang.InstantiationException: DemoTest*/
}
public class Demo{
public static void main(String[] args){
try {
Class c = Class.forName("DemoTest");
/*这个方法会调用DemoTest这个类的无参数构造方法,
完成对象的创建。*/
Object obj = c.newInstance();
System.out.println(obj);
} catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) {
e.printStackTrace();
}
}
}
输出:
通过读属性文件实例化对象
- 通过反射实例化对象的优点
1、java代码写一遍,在不改变java源代码的基础之上,可以做到不同对象的实例化。
2、通过修改配置文件,配置文件修改之后,可以创建出不同的实例对象,非常灵活。(符合OCP开闭原则:对扩展开放,对修改关闭。) - 代码示例
配置文件:
public class DemoTest{
public DemoTest() {
System.out.println("无参构造");
}
/**只定义有参构造(没有无参构造)*/
public DemoTest(String s) {
}
/*只定义有参构造后出现异常:
java.lang.InstantiationException: DemoTest*/
}
import java.io.FileReader;
import java.util.Properties;
/**验证反射机制的灵活性*/
public class Demo{
public static void main(String[] args) throws Exception{
//通过IO流读取classinfo.properties文件
FileReader reader = new FileReader("classinfo.properties");
//创建属性对象Map
Properties pro = new Properties();
//加载
pro.load(reader);
//关闭流
reader.close();
//通过value获取key
String className = pro.getProperty("className");
/*System.out.println(className);
//输出:DemoTest*/
//通过反射实例化对象
Class c = Class.forName(className);
Object obj = c.newInstance();
System.out.println(obj);
//例2
String date = pro.getProperty("date");
Class cc = Class.forName(date);
Object obj1 = cc.newInstance();
System.out.println(obj1);
}
}
输出:
Class.forName()会导致类加载
- 概述
1、静态代码块在类加载时只执行一次
2、只让静态代码块执行可以使用forName()方法。 - 代码示例
public class Demo{
public static void main(String[] args){
try {
Class.forName("Myclass");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
class Myclass{
static {
System.out.println("静态代码块");
}
}
输出:
关于路径
类路径下的绝对路径(通用路径)
Class.forName("Myclass");
像这种路径的缺点是:
移植性差,在IDEA中默认的当前路径是project的根。如果不是IDEA,换到了其它位置,可能当前路径就不是project的根了,路径就会无效。- 一种比较通用的路径
即使代码换位置了,这样编写仍然是通用的。(适用于任何操作系统)
注意:使用以下通用方式的前提是:这个文件必须在类路径下。(放在src下的都是类路径下,src是类的根路径。) - 代码示例
public class Demo{
public static void main(String[] args){
/*
Thread.currentThread():当前线程对象。
getContextClassLoader():是线程对象的方法,
可以获取到当前线程的类加载器对象。
getResource():(获取资源)这是类加载器对象的方法,
当前线程的类加载器默认从类的根路径下加载资源。
*/
String path = Thread.currentThread()
.getContextClassLoader()
.getResource("classinfo.properties")
.getPath();
System.out.println(path);
}
}
输出:
这里返回的是class文件所在的目录。所以不能获取java文件的路径,只能把.java改为.class:
getResource("Demo.class")
输出:
以流的形式返回路径
- 代码示例
import java.io.InputStream;
import java.util.Properties;
public class Demo{
public static void main(String[] args) throws Exception{
InputStream path = Thread.currentThread()
.getContextClassLoader()
.getResourceAsStream("classinfo.properties");
Properties pro = new Properties();
pro.load(path);
path.close();
String className = pro.getProperty("className");
System.out.println(className);
}
}
输出:
资源绑定器
- 概述
1、使用资源绑定器便于获取属性配置文件中的内容。
2、只能绑定xxx.properties文件,但是路径后面的扩展名不能写。(.properties不能写)
3、使用的时候,属性配置文件必须放到类路径下。 - 代码示例
import java.util.ResourceBundle;
public class Demo{
public static void main(String[] args) throws Exception{
ResourceBundle bundle = ResourceBundle.getBundle("classinfo");
String date = bundle.getString("date");
System.out.println(date);
}
}
输出:
类加载器
- 概述
1、JDK中自带类加载器,什么是类加载器?
专门负责加载类的命令/工具(ClassLoader)
2、JDK中自带了3个类加载器:
启动类加载器
扩展类加载器
应用类加载器
3、假设有这样一段代码:
String s = "abc"
代码在开始执行之前,会将所需要类全部加载到JVM当中。通过类加载器加载,看到以上代码类加载器会找String.class文件,找到就加载。
4、那么是怎么进行加载的呢?
首先通过“启动类加载器"加载。(启动类加载器专门加载:rt.jar),rt.jar中都是JDK最核心类库。
5、如果通过“启动类加载器"加载不到的时候,会通过"扩展类加载器"加载。(扩展类加载器专门加载:ext*.jar)
6、如果”扩展类加载器"没有加载到,那么会通过“应用类加载器”加载(应用类加载器专门加载:classpath(环境变量里的)中的类。) - 双亲委派机制
1、启动类加载器(父加载器)
扩展类加载器(母加载器)
2、java中为了保证类加载的安全,使用了双亲委派机制。
优先从启动类加载器中加载,这个称为“父”。"父”无法加载到,再从扩展类加载器中加载,这个称为“母"。如果都加载不到才会考虑从应用类加载器中加载,直到加载到为止。
Field
获取Field
- 概述
Field翻译为字段,其实就是属性/成员。 - 与Field有关的方法
1、Field[] getFields()
(java.lang.Class)
返回包含一个数组 Field对象反射由此表示的类或接口的所有可访问的公共字段类对象。
2、Field[] getDeclaredFields()
(java.lang.Class)
返回的数组Field对象反映此表示的类或接口声明的所有字段类对象。
3、String getName()
(java.lang.reflect.Field)
返回由此Field对象表示的字段的名称。
4、String getSimpleName()
(java.lang.Class)
返回源代码中给出的基础类的简单名称。
5、Class<?> getType()
(java.lang.reflect.Field)
返回一个类对象标识了此表示的字段的声明类型Field对象。
6、int getModifiers()
(java.lang.reflect.Field)
返回由该Field对象表示的字段的Java语言修饰符,作为整数。
7、static String toString(int mod)
(java.lang.reflect.Modifier)
返回描述指定修饰符中的访问修饰符标志的字符串。 - 代码示例
public class Demo {
public int no;
private String name;
protected int age;
boolean sex;
public static final double PI = 3.1415926;
}
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
/**反射Demo类中的所有Field*/
public class DemoTest {
public static void main(String[] args) throws Exception {
//获取整个类
Class demoClass = Class.forName("Demo");
//获取所有的public修饰的Field
Field[] fs = demoClass.getFields();
System.out.println("fields数组中元素个数为:"
+ fs.length + "\n"
+ "元素是:"+fs[0]+"\n"
+ "获取元素名字为:"+fs[0].getName());
System.out.println("=========");
//获取所有的Field
Field[] fields = demoClass.getDeclaredFields();
//遍历
for (Field field:fields) {
//获取属性的修饰符列表
int gm = field.getModifiers();
String modifier = Modifier.toString(gm);
System.out.println(modifier);
//获取属性的类型
Class fieldType = field.getType();
System.out.println(fieldType.getSimpleName());
//获取属性的名字
System.out.println(field.getName());
}
}
}
输出:
反编译Field
- 代码示例
public class Demo {
public int no;
private String name;
protected int age;
boolean sex;
public static final double PI = 3.1415926;
}
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
/**反编译Field*/
public class DemoTest {
public static void main(String[] args) throws Exception {
StringBuffer s = new StringBuffer();
//Demo可以换为其它类
Class demoClass = Class.forName("Demo");
s.append(Modifier.toString(demoClass.getModifiers())
+ " class " + demoClass.getSimpleName()
+ "{\n");
Field[] fields = demoClass.getDeclaredFields();
for (Field field:fields) {
s.append("\t");
s.append(Modifier.toString(field.getModifiers()));
s.append(" ");
s.append(field.getType().getSimpleName());
s.append(" ");
s.append(field.getName());
s.append(";\n");
}
s.append("}");
System.out.println(s);
}
}
输出:
通过反射机制访问对象属性
- 访问对象属性的一些方法
1、void set(Object obj, Object value)
(java.lang.reflect.Field)
将指定对象参数上的此 Field对象表示的字段设置为指定的新值。
2、Object get(Object obj)
(java.lang.reflect.Field)
返回该所表示的字段的值Field,指定的对象上。
3、void setAccessible(boolean flag)
(java.lang.reflect.AccessibleObject)
将此对象的accessible标志设置为指示的布尔值。 - 代码示例
public class Demo {
public int no;
private String name;
protected int age;
boolean sex;
public static final double PI = 3.1415926;
}
import java.lang.reflect.Field;
/**通过反射机制访问对象属性
*给属性赋值set
*获取属性的值get*/
public class DemoTest {
public static void main(String[] args) throws Exception {
//Demo可以换为其它类
Class demoClass = Class.forName("Demo");
Object obj = demoClass.newInstance();
//获取no属性(根据属性的名称来获取Field)
Field noField = demoClass.getDeclaredField("no");
//给obj对象(Demo对象)的no属性赋值
noField.set(obj,11);
//获取对象属性的值
System.out.println(noField.get(obj));
/*给私有属性赋值 */
Field nameField = demoClass.getDeclaredField("name");
//打破封装
nameField.setAccessible(true);
//给name属性赋值
nameField.set(obj,"zhangsan");
System.out.println(nameField.get(obj));
}
}
输出:
Method
可变长度参数
- 概述
1、int... args
这就是可变长度参数
语法是:类型...(注意:一定是3个点。)
2、可变长度参数要求的参数个数是:0~N个。
3、可变长度参数在参数列表中必须在最后一个位置上,而且可变长度参数只能有1个。 - 代码示例
public class Demo {
public static void main(String[] args) {
m();
m(10,20);
m2("zhangsan", "lisi");
m3("wangwu", "zhaoliu");
}
public static void m(int... args){
System.out.println("m方法执行");
}
public static void m2(String... args){
System.out.println("m2方法执行");
}
public static void m3(String... args){
for (int i=0; i < args.length; i++){
System.out.println(args[i]);
}
}
}
输出:
反射Method
- 关于反射Method的一些方法
1、Method[] getDeclaredMethods()
(java.lang.Class)
返回包含一个数组方法对象反射的类或接口的所有声明的方法,通过此表示类对象,包括公共,保护,默认(包)访问和私有方法,但不包括继承的方法。
2、int getModifiers()
(java.lang.reflect.Method)
返回由该对象表示的可执行文件的Java语言modifiers 。
3、Class<?> getReturnType()
(java.lang.reflect.Method)
返回一个 类对象,它表示由该表示的方法的正式返回类型 方法对象。
4、Class<?>[] getParameterTypes()
(java.lang.reflect.Method)
返回一个 类对象的数组, 类以声明顺序表示由该对象表示的可执行文件的形式参数类型。 - 代码示例
public class Demo {
public boolean login(String name,String password){
if ("admin".equals(name)&&"123".equals(password)){
return true;
}
return false;
}
public void logout() {
System.out.println("已退出!");
}
}
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
/**反射Method*/
public class DemoTest {
public static void main(String[] args) throws Exception{
//Demo可以换为其它类
Class demoClass = Class.forName("Demo");
//获取所有的Method
Method[] methods = demoClass.getDeclaredMethods();
for (Method method:methods) {
//获取修饰符列表
System.out.println(Modifier.toString(method.getModifiers()));
//获取方法的返回值类型
System.out.println(method.getReturnType().getSimpleName());
//获取方法名
System.out.println(method.getName());
//方法的修饰符列表(一个方法的参数可能有多个)
Class[] parameterTypes = method.getParameterTypes();
for (Class parameterType:parameterTypes) {
System.out.println(parameterType.getSimpleName());
}
}
}
}
输出:
反编译Method
- 相关方法
1、char charAt(int index)
返回char在指定索引在这个序列值。(根据索引返回对应的字符)
2、static String valueOf(char c)
返回char参数的字符串(将char类型转换为String类型)。
3、StringBuffer deleteCharAt(int index)
删除char在这个序列中的指定位置。(删除指定位置的字符) - 代码示例
public class Demo {
public boolean login(String name,String password){
if ("admin".equals(name)&&"123".equals(password)){
return true;
}
return false;
}
public void logout() {
System.out.println("已退出!");
}
}
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
/**反编译Method*/
public class DemoTest {
public static void main(String[] args) throws Exception{
StringBuffer s = new StringBuffer();
//Demo可以换为其它类
Class demoClass = Class.forName("Demo");
s.append(Modifier.toString(demoClass.getModifiers())
+ " class "
+ demoClass.getSimpleName()
+ " {\n");
//获取所有的Method
Method[] methods = demoClass.getDeclaredMethods();
for (Method method:methods) {
s.append("\t");
s.append(Modifier.toString(method.getModifiers()));
s.append(" ");
s.append(method.getReturnType().getSimpleName());
s.append(" ");
s.append(method.getName());
s.append("(");
//参数列表
Class[] parameterTypes = method.getParameterTypes();
for (Class parameterType:parameterTypes) {
s.append(parameterType.getSimpleName());
s.append(",");
}
if (",".equals(String.valueOf(s.charAt(s.length()-1)))){
s.deleteCharAt(s.length()-1);
}
s.append("){}\n");
}
s.append("}");
System.out.println(s);
}
}
输出:
反射机制调用方法
- 调用方法四要素:对象、方法名、实参列表、返回值。
- 代码示例
public class Demo {
public boolean login(String name,String password){
if ("admin".equals(name)&&"123".equals(password)){
return true;
}
return false;
}
public void logout() {
System.out.println("已退出!");
}
}
import java.lang.reflect.Method;
public class DemoTest {
public static void main(String[] args) throws Exception{
Class demoClass = Class.forName("Demo");
//通过反射机制创建对象
Object obj = demoClass.newInstance();
//获取Method
Method loginMethod = demoClass.getDeclaredMethod("login",
String.class, String.class);
/*四要素:
* loginMethod方法
* obj对象
* "admin", "123"实参
* retValue返回值
* */
Object retValue = loginMethod.invoke(obj, "admin", "123");
System.out.println(retValue);
}
}
输出:
Constructor
反编译类的Constructor(构造方法)
- 相关方法
Constructor<?>[] getConstructors()
返回包含一个数组Constructor对象反射由此表示的类的所有公共构造类对象。 - 代码示例
public class Demo {
int no;
String name;
public Demo() {
}
public Demo(int no) {
this.no = no;
}
public Demo(int no, String name) {
this.no = no;
this.name = name;
}
}
import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
public class DemoTest {
public static void main(String[] args) throws Exception{
StringBuilder s = new StringBuilder();
Class demoClass = Class.forName("Demo");
s.append(Modifier.toString(demoClass.getModifiers()));
s.append(" class ");
s.append(demoClass.getSimpleName());
s.append("{\n");
/*拼接构造方法*/
Constructor[] constructors = demoClass.getConstructors();
for (Constructor constructor:constructors) {
s.append("\t");
s.append(Modifier.toString(demoClass.getModifiers()));
s.append(" ");
s.append(demoClass.getSimpleName());
s.append("(");
//拼接参数
Class[] parameterTypes = constructor.getParameterTypes();
for (Class parameterType:parameterTypes) {
s.append(parameterType.getSimpleName());
s.append(",");
}
if (parameterTypes.length > 0){
s.deleteCharAt(s.length()-1);
}
s.append(") {}\n");
}
s.append("}");
System.out.println(s);
}
}
输出:
反射机制调用构造方法
- 代码示例
public class Demo {
int no;
String name;
public Demo() {
}
public Demo(int no) {
this.no = no;
}
public Demo(int no, String name) {
this.no = no;
this.name = name;
}
@Override
public String toString() {
return "Demo{" +
"no=" + no +
", name='" + name + '\'' +
'}';
}
}
import java.lang.reflect.Constructor;
public class DemoTest {
public static void main(String[] args) throws Exception{
Class demoClass = Class.forName("Demo");
/*调用无参1*/
Object obj = demoClass.newInstance();
System.out.println(obj);
/*调用无参2*/
Constructor cons0 = demoClass.getDeclaredConstructor();
Object obj1 = cons0.newInstance();
System.out.println(obj1);
/*调用有参*/
//第一步:获取有参数的构造方法
Constructor cons = demoClass.getDeclaredConstructor(int.class, String.class);
//第二步:调用构造方法new对象
Object newObj = cons.newInstance(11,"Thompson");
System.out.println(newObj);
}
}
输出:
获取父类和父接口
- 相关方法
1、Class<? super T> getSuperclass()
返回 类表示此所表示的实体(类,接口,基本类型或void)的父类 。
2、Class<?>[] getInterfaces()
确定由该对象表示的类或接口实现的接口。 - 代码示例
public class DemoTest {
public static void main(String[] args) throws Exception{
//举例:String
Class stringClass = Class.forName("java.lang.String");
//获取String的父类
Class superClass = stringClass.getSuperclass();
System.out.println("父类:"+superClass.getSimpleName());
//获取String类实现的所有接口
Class[] interfaces = stringClass.getInterfaces();
for (Class in:interfaces) {
System.out.println("实现的接口有:"+in);
}
}
}
输出: