java反射机制的浅显理解

1.反射:
反射:解析类,通过字节码得到实例对象的过程
Object obj = new Student();
若程序运行时接收到外部传入的一个对象,该对象的编译类型是 Object,但程序又需要调用该对象运行类型的方法:
》若编译和运行类型都知道,使用 instanceof 判断后,强转。
. 》编译时根本无法预知该对象属于什么类,程序只能依靠运行时信息来发现对象的真实信息,这时就必须使用反射了。
.》要是想得到对象真正的类型,就得使用反射。

2.反射机制
反射机制指的是程序在运行时能够获取自身的信息。在 java 中,只要给定类的名字,那么就可以通过反射机制来获得类的所有信息。
反射机制的优点与缺点:
静态编译:在编译时确定类型,绑定对象,即通过。
动态编译:运行时确定类型,绑定对象。动态编译最大限度发挥了 java 的灵活性,体现了多态的应用,有以降低类之间的藕合性。
反射机制的优点:可以实现动态创建对象和编译,体现出很大的灵活性,特别是在 J2EE 的开发。
缺点:1.违背了封装的原则,打破了泛型的类型检测。
2.对性能有影响。使用反射基本上是一种解释操作,我们可以告诉 JVM,我们希望做什么并且它满足我们的要求。这 类操作总是慢于只直接执行相同的操作。
Class–代表类的类—字节码
Field—代表属性的类
Method—代表方法的类
Constructor—代表构造方法的类
Package—代表包的类
Annotation—代表注解的类

3.获取 Class 对象
每个类被加载后,系统会为该类生成对应的 Class 对象,通过 Class 对象可以访问到 JVM 中的这个类, 3 种方式:
1、调用某个类的 class 属性获取 Class 对象,如 Date.class 会返回 Date 类对应的 Class
对象(其实就是得到一个类的一份字节码文件);
2、使用 Class 类的 forName(String className)静态方法,className 表示全限定名;如
String 的全限定名:java.lang.String;
3、调用某个对象的 getClass()方法。该方法属于 Object 类;
Class<?> clz = new Date().getClass();
Eg:
package junereflect624;
public class ClassDemo1 {
public static void main(String[] args) throws Exception {
//获得Class对象的方法(三种)//
一:调用属性
Class c = String.class;
System.out.println©;// 打印结果: class java.lang.String String.class就表示JVM中一份表示String类的字节码
Class c2 = String.class;
System.out.println(c == c2);//true都是String类的字节码
一个类在虚拟机中只有一份字节码;

二:使用forName()方法

//Class cla = Class.forName("String");//ERROR, Class<String> cla =
(Class<String>)Class.forName("java.lang.String");// 必须用上全限定名,否则报错
System.out.println(c ==cla);//true

三:利用对象调用Object的getClass方法;

Class c3 = new String().getClass();
System.out.println(c == c3);//ture

4.利用 Class 获取类的属性信息

 package junereflect1;
import java.lang.reflect.Modifier;
class A {
}
interface B{
}
interface C{
}
public class BaseDemo3 extends A implements B,C{
//内部类
public class C{}
public interface D{}
public static void main(String[] args) {
//类可以,接口也可以
Class<BaseDemo3> c = BaseDemo3.class; System.out.println(c);//class junereflect1.BaseDemo3
//得到包名
System.out.println(c.getPackage());//package junereflect1
//得到全限定名
System.out.println(c.getName());//junereflect1.BaseDemo3
//得到类的简称
System.out.println(c.getSimpleName());//BaseDemo3
//得到父类
/**
* Class<? super T> getSuperclass() 此处super表示下限返回表示此 Class 所表示的实体(类、接口、基本类型或 void)的超类的 Class。 */
System.out.println(c.getSuperclass().getSimpleName()); //A,
先获取父类,再获取父类的简称
//得到接口
System.out.println(c.getInterfaces());//[Ljava.lang.Class;@1b60280
Class[] arr = c.getInterfaces();
for (Class cla : arr) {
System.out.println(cla);//interface	junereflect624.B
interface junereflect624.C
}

//获得public修饰的类
/**
* Class<?>[] getClasses()
返回一个包含某些 Class 对象的数组,这些对象表示属于此 Class 对象所表示的类的成员的所有公共类和接口。 (如果内部类前面没有加上public的话那么得不到!)
*/
Class[] cl = c.getClasses();
System.out.println(cl.length);//在内部类没有加上public修饰的时候长度为0,加上就是2(获取的是公共的)
for (Class class1 : cl) {
System.out.println(class1);
}
//获得修饰符
int i = c.getModifiers();
System.out.println(i);//常量值1表示public
System.out.println(Modifier.toString(i));//直接打印出public
}
}

5.Class 中得到构造方法 Constructor、方法 Method、字段 Field
Constructor 类用于描述类中的构造方法:
Constructor getConstructor(Class<?>… parameterTypes)
返回该 Class 对象表示类的指定的 public 构造方法;
Constructor<?>[] getConstructors()
返回该 Class 对象表示类的所有 public 构造方法;
Constructor getDeclaredConstructor(Class<?>… parameterTypes)
返回该 Class 对象表示类的指定的构造方法,和访问权限无关;
Constructor<?>[] getDeclaredConstructors()
返回该 Class 对象表示类的所有构造方法,和访问权限无关;
Method 类用于描述类中的方法:
Method getMethod(String name, Class<?> … parameterTypes)
返回该 Class 对象表示类和其父类的指定的 public 方法; Method[] getMethods():
返回该 Class 对象表示类和其父类的所有 public 方法;
Method getDeclaredMethod(String name, Class<?>… parameterTypes)
返回该 Class 对象表示类的指定的方法。和访问权限无关,但不包括继承的方法; Method[] getDeclaredMethods(): 获得类所 有的方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。

6.反射和泛型–获取泛型信息
通过指定对应的 Class 对象,程序可以获得该类里面所有的 Field,不管该 Field 使用 private 方法 public。获得 Field 对象后都可以使用 getType()来获取其类型。
Class<?> type = f.getType();//获得字段的类型但此方法只对普通 Field 有效,若该 Field 有泛型修饰,则不能准确得到该 Field 的泛型参数,如
Map<String,Integer>;
为了获得指定 Field 的泛型类型,我们采用:
Type gType = f.getGenericType();得到泛型类型然后将 Type 对象强转为 ParameterizedType,其表示增加泛型后的类型 Type getRawType()//返回被泛型限制的类型;
Type[] getActualTypeArguments()//返回泛型参数类型;
利用反射来获取泛型的类型(泛型信息)步骤:
1.获取当前类
2.获取目标字段
3.获取包含泛型类型的类型 getGenericType()
4.强转至子类 ParameterizedType 因为 Type 没有任何对应的方法
5.获得泛型真正的类型 getActualTypeArguments()
eg:

package junereflect624;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type;
import java.util.HashMap; import java.util.Map;
public class GetGenericTypeDemo14 {
Map<String,Integer> map = new HashMap<String,Integer>();
public static void main(String[] args) throws Exception { Class c = GetGenericTypeDemo14.class;
Field f = c.getDeclaredField("map"); System.out.println(f); System.out.println(f.getName());//map
// Class<?> getType()	返回一个 Class 对象,它标识了此 Field 对象所表示字段的声明类型。

    Class cl = f.getType();
System.out.println("获得其类型:"+cl);
//获得其类型:interface java.util.Map
/**
*Type getGenericType() 返回一个 Type 对象,它表示此 Field 对象所表示字段的声明类型。
*Type是Class的接口;
*/
Type t = f.getGenericType();//包含泛型的类型
System.out.println(t); //java.util.Map<java.lang.String, java.lang.Integer>
/** * Type这个类里面没有任何的方法,所以需要调用子类的方法,那么大的类型
转到小的类型,需要强转!
*/
ParameterizedType pt = (ParameterizedType)t;//强转到其子类
/**
*	Type[] getActualTypeArguments()
返回表示此类型实际类型参数的 Type对象的数组。
Type getOwnerType()
返回 Type 对象,表示此类型是其成员之一的类型。
Type getRawType()
返回 Type 对象,表示声明此类型的类或接口。
*/
t = pt.getRawType();//类型的类或接口
System.out.println(t);
Type[] ts = pt.getActualTypeArguments(); for (Type type : ts) {
System.out.println(type);
/**
* class java.lang.String class java.lang.Integer
*/
}
}
}
打印:
java.util.Map junereflect624.GetGenericTypeDemo14.map
map
获得其类型:interface java.util.Map
java.util.Map<java.lang.String, java.lang.Integer>
interface java.util.Map
class java.lang.String
class java.lang.Integer

7.使用反射操作字段
Field 提供两组方法操作字段:
xxx getXxx(Object obj):获取 obj 对象该 Field 的字段值,此处的 xxx 表示 8 个基本数据类型。若该字段的类型是引用数据类型则使用,Object get(Object obj); void setXxx(Object obj,xxx val):将 obj 对象的该 Field 字段设置成 val 值,此处的 xxx 表示 8
个基本数据类型。若该字段的类型是引用数据类型则使用,void set(Object obj, Object value);
package junereflect624;
//获取字符,并且赋值,然后再取出来(对应的去查看api,比如这个是Field,别的比如Constructor,Method)
步骤:
1.获取类
2.获取字段
3.赋值(set(c.newInstance(),””));{如果为私有的话设置可接受}

import java.lang.reflect.Field;
class Cat{
private String name;
public int age; private String color;
}
public class FieldDemo12 {
public static void main(String[] args) throws Exception { Class<Cat> clz = Cat.class;
Field[] f = clz.getDeclaredFields();
for (Field field : f) { System.out.println(field);
}
Field fi = clz.getDeclaredField("name");
System.out.println(fi);
System.out.println(fi.getName());//name
//核心开始
/**
*	void set(Object obj, Object value)
将指定对象变量上此 Field 对象表示的字段设置为指定的新值。
*/
Cat c = clz.newInstance();
fi.setAccessible(true);
fi.set(c, "刘昭");//赋值成功
Object o = fi.get(c);
System.out.println(o);//取出成功
fi = clz.getDeclaredField("age");
fi.setAccessible(true);
fi.set(c, 21);
int i = fi.getInt(c);//左边的接受类型已经写成了int,右边的返回类型就也必须是int
System.out.println(i);//获取成功
}
}
打印
private java.lang.String junereflect624.Cat.name public int junereflect624.Cat.age
private java.lang.String junereflect624.Cat.color private java.lang.String junereflect624.Cat.name 

8.调用方法和参数
每个 Method 的对象对应一个具体的底层方法。获得 Method 对象后,程序可以使用 Method 里面的 invoke 方法来执行该底层方法。Object invoke(Object obj,Object … args):obj 表示调用底层方法的对象,后面的 args 表示传递的实际参数。
如果底层方法是静态的,那么可以忽略指定的 obj 参数。该参数可以为 null,想想为什么?如果底层方法所需的形参个数为 0,则所提供的 args 数组长度可以为 0 或 null。不写,null,或 new Object[]{},若底层方法返回的是数组类型,invoke 方法返回的不是底层方法的值,而是底层方法的返回类型;

package junereflect624;
import java.lang.reflect.Method;
class Dept{ public String show(String name){//用反射的方法来调用正常的方法
return name+",您好!";
}
private void privateshow(){//用反射来实现对私有化方法的调用
System.out.println("privateshow");
}
public static void staticshow(){ System.out.println("staticshow");
}
}
public class InvokeDemo9 {
public static void main(String[] args) throws Exception {
/*  传统方式:
String name = new Dept().show("刘昭");
System.out.println(name);*/
/**
* Method getMethod(String name, Class<?>... parameterTypes)
返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。 name - 方法名 parameterTypes - 参数列表
*/
//想要通过反射来调用Dept中的方法
Class<Dept> c = Dept.class;
Method m = c.getMethod("show", String.class);
Object o = m.invoke(c.newInstance(), "刘昭");
System.out.println(o);
//私有化的方法
m = c.getDeclaredMethod("privateshow");//无参方法
m.setAccessible(true);
o = m.invoke(c.newInstance());
//静态方法的调用
m = c.getMethod("staticshow");
m.invoke(null);//staticshow为静态方法,不需创建对象,所以这里会是null
}
}

package junereflect624;
//可变参数的方法调用
import java.lang.reflect.Method;
class VaryMethod{
public static void show(int ...args){
System.out.println("基本数据类型传递过来了!");
}
/*public static void show(int[] args){//这是一样的
}*/
public static void show(String ...args){
System.out.println("引用数据类型传递过来了!");
}
}
public class InvokeVaryDemo10 {
public static void main(String[] args) throws Exception{ Class<VaryMethod> c = VaryMethod.class;
Method m = c.getMethod("show",int[].class); m.invoke(null,new int[]{1,2,3});
m = c.getMethod("show",String[].class); //m.invoke(null,new String[]{"A","B","C"});//ERROR
m.invoke(null,(Object)new  String[]{"A","B","C"});//YES, 强转为Object类型
m.invoke(null,new Object[]{new String[]{"A","B","C"}});// 推荐写法
}
}

总结:查字典是一个程序员很好用的技能,很多时候都记不住接口和方法的使用和调用查API也是对其加深理解。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值