Java反射总结

目录

摘要:        

Class类

Class常用方法 

 调用构造方法

newInstance()方法

Constructor类

获取继承关系 

获取父类的Class

获取 interface

继承关系 

         访问字段

         获取Field 字段

         调用方法

         Method类


摘要:        

Java 反射机制是 Java 语言的一个重要特性。
        Java 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为 Java 语言的反射机制。简单来说,反射机制指的是程序在运行时能够获取自身的信息。在 Java 中,只要给定类的名字,就可以通过反射机制来获得类的所有信息。
        Java 反射机制在服务器程序和中间件程序中得到了广泛运用。在服务器端,往往需要根据客户的请求,动态调用某一个对象的特定方法或者为属性赋值。例如:在主流的 ORM 框架的实现中,运用 Java 反射机制可以读取任意一个 JavaBean 的所有属性,或者给这些属性赋值。
        Java 反射机制主要提供了以下功能,这些功能都位于java.lang.reflect包。
●在运行时判断任意一个对象所属的类。
●在运行时构造任意一个类的对象。
●在运行时判断任意一个类所具有的成员变量和方法。
●在运行时调用任意一个对象的方法。
●生成动态代理。

Class类

        以String类为例,当JVM加载String类时,它首先读取String.class文件到内存,然后,为String类创建一个Class实例并关联起来:

Class cls = new Class(String);

        这个Class实例是JVM内部创建的,如果我们查看JDK源码,可以发现Class类的构造方法是private,只有JVM能创建Class实例,我们自己的Java程序是无法创建Class实例的。 

         由于JVM为每个加载的class创建了对应的Class实例,并在实例中保存了该class的所有信息,包括类名、包名、父类、实现的接口、所有方法、字段(成员变量)等,因此,如果获取了某个Class实例,我们就可以通过这个Class实例获取到该实例对应的class的所有信息。这种通过Class实例获取class信息的方法称为反射(Reflection)。

如何获取一个class的Class实例?有三个方法:

//Class对象的三种创建方式
	public static void main(String[] args) throws ClassNotFoundException {
		Class stringclass1=String.class;
		String s="";
		Class stringclass2=s.getClass();
		Class stringclass3=Class.forName("java.lang.String");
		//方式一:通过类名访问class
		System.out.println(stringclass1.hashCode());
		//方式二:通过实例访问getClass()
		System.out.println(stringclass2.hashCode());
		//方式三:通过Class类的静态方法forName(类名)
		System.out.println(stringclass3.hashCode());
	}

因为Class实例在JVM中是唯一的,所以,上述方法获取的Class实例是同一个实例。可以看他们的hashCode:

结果: 要从Class实例获取获取的基本信息,参考下面的代码:

 

public static void main(String[] args) {
		Class clsss=String.class;
		printClassInfo(clsss);
	}
	public static void printClassInfo(Class cls) {
		System.out.println("类(接口)的名称:"+cls.getSimpleName());
		System.out.println("完全限定名:"+cls.getName());
		System.out.println("类(接口)的类型名称:"+cls.getTypeName());
		//父类
		Class superCls=cls.getSuperclass();
		System.out.println("类的父类:"+superCls.toString());
		
		//实现的接口
		Class[] interfaceCls=cls.getInterfaces();
		System.out.println("当前类实现的接口:");
		for(Class icls:interfaceCls) {
			System.out.println(icls);
		}
		//package包
		Package pck=cls.getPackage();
		if(pck!=null)
		{
			System.out.println("类所在的包:"+pck.getName());
		}
		
		//判断类型
		System.out.println("是否为接口:"+cls.isInterface());
		System.out.println(cls.isArray());
		System.out.println(cls.isEnum());
	}

Class常用方法 

类型

访问方法

返回值类型

说明

包路径

getPackage()

Package 对象

获取该类的存放路径

类名称

getName()

String 对象

获取该类的名称

继承类

getSuperclass()

Class 对象

获取该类继承的类

实现接口

getlnterfaces()

Class 型数组

获取该类实现的所有接口

构造方法

getConstructors()

Constructor 型数组

获取所有权限为 public 的构造方法

getDeclaredContruectors()

Constructor 对象

获取当前对象的所有构造方法

方法

getMethods()

Methods 型数组

获取所有权限为 public 的方法

getDeclaredMethods()

Methods 对象

获取当前对象的所有方法

成员变量

getFields()

Field 型数组

获取所有权限为 public 的成员变量

getDeclareFileds()

Field 对象

获取当前对象的所有成员变量

 调用构造方法

newInstance()方法

如果通过反射来创建新的实例,可以调用Class提供的newInstance()方法:

Person p = Person.class.newInstance();

调用Class.newInstance()的局限是,它只能调用该类的public无参数构造方法。如果构造方法带有参数,或者不是public,就无法直接通过Class.newInstance()来调用。

Constructor类

为了调用任意的构造方法,Java的反射API提供了Constructor对象,它包含一个构造方法的所有信息,可以创建一个实例。Constructor对象是一个构造方法,调用结果总是返回实例:

public static void main(String[] args) throws Exception {
        // 获取构造方法Integer(int):
        Constructor cons1 = Integer.class.getConstructor(int.class);
        
        // 调用构造方法:
        Integer n1 = (Integer) cons1.newInstance(123);
        System.out.println(n1);

        // 获取构造方法Integer(String)
        Constructor cons2 = Integer.class.getConstructor(String.class);
        Integer n2 = (Integer) cons2.newInstance("456");
        System.out.println(n2);
    }

获取继承关系 

获取父类的Class

有了Class实例,我们还可以获取它的父类的Class:

public static void main(String[] args) throws Exception {
        Class i = Integer.class;
        Class n = i.getSuperclass();
        System.out.println(n);
        
        Class o = n.getSuperclass();
        System.out.println(o);
        System.out.println(o.getSuperclass());
    }

获取 interface


        由于一个类可能实现一个或多个接口,通过Class我们就可以查询到实现的接口类型。例如:查询Integer实现的接口: 

    public static void main(String[] args) throws Exception {
        Class s = Integer.class;
        Class[] is = s.getInterfaces();
        for (Class i : is) {
            System.out.println(i);
        }
    }

继承关系 

当我们判断一个实例是否是某个类型时,正常情况下,使用instanceof操作符:

//instanceof运算符:判断引用和类型之间的关系
Object obj=Integer.valueOf(5698);
		System.out.println("是否为Integer?"+(obj instanceof Integer));
		System.out.println("是否为Object?"+(obj instanceof Object));
		System.out.println("是否为Integer?"+(obj instanceof Double));
		System.out.println("是否为Comparable?"+(obj instanceof Comparable));

如果是两个Class实例,要判断一个向上转型是否成立,可以调用isAssignableFrom():

public static void main(String[] args) {
		//Number a=new Integer(565);
		//isAssignableFrom()方法:判断类型和类型之间的关系
		System.out.println("Integer<=Integer?   "+Integer.class.isAssignableFrom(Integer.class));
		System.out.println("Integer<=Double?   "+Integer.class.isAssignableFrom(Double.class));
		System.out.println("Integer<=Number?   "+Integer.class.isAssignableFrom(Number.class));
		System.out.println("Number<=Integer?   "+Integer.class.isAssignableFrom(Integer.class));
		System.out.println("Comparable<=Integer?   "+Comparable.class.isAssignableFrom(Integer.class));

	}

访问字段

获取Field 字段

        对任意的一个Object实例,只要我们获取了它的Class,就可以获取它的一切信息。比如通过一个Class实例获取字段信息。Class类提供了以下几个方法来获取字段:
●Field getField(name):根据字段名获取当前类中某个public的field(包括父类)
●Field getDeclaredField(name):根据字段名获取当前类中定义的某个field(不包括父类)
●Field[] getFields():获取所有public的field(包括父类)
●Field[] getDeclaredFields():获取当前类中定义的所有field(不包括父类)

 

public static void main(String[] args) {
		Class cls=Book.class;
//		Field[] field=cls.getFields();
		Field[] fields=cls.getDeclaredFields();
		for (Field field2 : fields) {
			System.out.println("访问修饰符:"+field2.getModifiers());
			System.out.println("访问修饰符:"+Modifier.toString(field2.getModifiers()));
			System.out.println("成员变量类型:"+field2.getType());
			System.out.println("成员变量名称:"+field2.getName());
			System.out.println();
		}
	}
	
}
class Book{
	public String bookName;
	public int number;
	private String age;
	private String price;
	public String getBookName() {
		return bookName;
	}
	public void setBookName(String bookName) {
		this.bookName = bookName;
	}
	public int getNumber() {
		return number;
	}
	public void setNumber(int number) {
		this.number = number;
	}
	public String getAge() {
		return age;
	}
	public void setAge(String age) {
		this.age = age;
	}
	public String getPrice() {
		return price;
	}
	public void setPrice(String price) {
		this.price = price;
	}
	public int radomncreat() {
		return this.radomncreat(100);
	}
	public int radomncreat(int a) {
		return (int) ((Math.random())*a);
	}
	@Override
	public String toString() {
		return "图书信息:Book [bookName=" + bookName + ", number=" + number + ", age=" + age + ", price=" + price + "]";
	}
	

 使用反射的方式完成成员变量存值


public static void main(String[] args) throws InstantiationException, IllegalAccessException, NoSuchFieldException, SecurityException {
		//运行期
		//使用反射的方式
		Class cls=Book.class;//获取Class类型对象
		Object book1=cls.newInstance();//通过反射创建Book类的对象
		Field field1=cls.getDeclaredField("bookName");//获取指定字段
		field1.set(book1, "西游记");//参数一:目标对象;参数二:存入的值
        System.out.println(book1);
	}

使用反射的方式输出参数对象的所有成员变量和值 

public static void main(String[] args) {
            printInfo(book);
    public static void printInfo(Object obj) throws IllegalArgumentException,             
    IllegalAccessException {
		//输出参数对象的所有成员变量和值
		Class cls=obj.getClass();
		Field[] fields=cls.getDeclaredFields();
		for (Field field : fields) {
			System.out.println("成员变量名称:"+field.getName());
			if(!field.isAccessible()) {
				field.setAccessible(true);
			}
			System.out.println("成员变量内容:"+field.get(obj));
		}
	}
}

调用方法

Method类

我们已经能通过Class实例获取所有Field对象,同样的,可以通过Class实例获取所有方法(Method类型的对象)。Class类提供了以下几个方法来获取Method:
●Method getMethod(name, Class...):获取某个public的Method(包括父类)
●Method getDeclaredMethod(name, Class...):获取当前类的某个Method(不包括父类)
●Method[] getMethods():获取所有public的Method(包括父类)
●Method[] getDeclaredMethods():获取当前类的所有Method(不包括父类)

//反射操作:获取方法
//每个方法都会被封装成一个Method对象
	public static void main(String[] args) {
		Class book =Book.class;
		//获取所有Public方法(包含父类)
//		Method[] methods= book.getMethods();
		Method[] methods=book.getDeclaredMethods();
		for (Method method : methods) {
			System.out.println("方法的名字:"+method.getName());
			System.out.println("方法的返回值类型:"+method.getReturnType());
			System.out.println("方法的访问修饰符:"+Modifier.toString(method.getModifiers()));
			Parameter[] parms=method.getParameters();
			for (Parameter parm : parms) {
				System.out.println(parm.getName());
				System.out.println(parm.getType());
				System.out.println("-----------------------------");
			}
			System.out.println();
		}
	}

调用方法: 

当我们获取到一个Method对象时,就可以对它进行调用。

//反射操作调用方法
	public static void main(String[] args) throws InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {
		//获取Class对象
		Class book=Book.class;
//		按照方法名称和参数类型获取Method方法对象
		Object book1=book.newInstance();
		Method meod1=book.getDeclaredMethod("radomncreat");
		int a1=(int) meod1.invoke(book1);
		System.out.println(a1);
//		按照方法名称和参数类型获取Method方法对象
		Method meod2=book.getDeclaredMethod("radomncreat",int.class);
		int a2=(int) meod2.invoke(book1,10000);
		System.out.println(a2);
	}

 

调用静态方法:

如果获取到的Method表示一个静态方法,调用静态方法时,由于无需指定实例对象,所以invoke方法传入的第一个参数永远为null。我们以Integer.parseInt(String)为例:

	public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
		Class cls=Math.class;
		Method log10=cls.getMethod("log10",double.class);
		
		System.out.println(log10.invoke(null, 324544));
	}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

猿究◎小飞

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值