反射知识点

被面试官问spring框架版本,java发射原理,C++中有反射吗?于是,今天我自己整理整理反射的笔记。

概述

1.java.lang.class

Class 类的实例表示正在运行的 Java 应用程序中的类和接口。
枚举是一种类,注释是一种接口。
每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象。


基本的 Java 类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void 也表示为 Class 对象。

反射

Person p = new Student();
若程序运行时接收到外部传入的一个对象,该对象的编译类型是Object,但程序又需要调用该对象运行类型的方法:
	1.若编译和运行类型都知道,使用instanceof判断后,强转。
	2.编译时根本无法预知该对象的类属于哪些类,程序只能依靠运行时信息来发现对象和类的真实信息,这是反射就必须使用了。
	3.要是想通过对象来找其对应的类名,就得使用反射。

1. 获得Class对象

三种方式

如何得到各个字节码对应的实例对象?

每个类被加载后,系统会为该类生成对应的Class对象,通过Class对象可以访问到JVM中的这个类,3种方式:

  1. 使用Class类的Class.forName(String n)静态方法,n表示类全名;包名.子包名…类名

  2. 调用某个类的class属性获取Class对象,如Date.class会返回Date类对应的Class对象(其实就是得到一个类的一份字节码文件);

  3. 调用某个对象的getClass()方法。该方法属于Object类;Class clz = new Date().getClass();

说明:第一二种方式是根据类,forName可能会抛出ClassNotFoundException异常

获得运行对象的3中方式,最多的是Class.forName()


这里写图片描述

这里写图片描述
这里写图片描述

Class类中的toString( )方法:

这里写图片描述

接口和基本数据类型直接打印名称,其他类型,前边加一个class

说明:Void类可以和包装类类比来记忆。Void.TYPE == void.class; //true

包装类的TYPE属性表示基本数据类型的字节码,所以上边是相等的。

数组只要类型和维数一样就表示一份字节码,与数组中的内容无关


2. 获得class对象后,从class中获取各种信息

注:通过子类可以找到父类,不能通过父类找子类

(1)通过class对象获得信息(基本信息)

package test;

import java.lang.reflect.Modifier;

public class BaseClassDemo extends Super implements IU,IM{
    public String name;
    public class Inner1{}
    public interface Inner2{}

    public static void main(String[] args) throws Exception{

        //三种方式
        Class<BaseClassDemo> clz = BaseClassDemo.class;
        //Class<?> clz = Class.forName("test.BaseClassDemo");
        //Class<? extends BaseClassDemo> clz = new BaseClassDemo().getClass();
        //Class<IM> clz = IM.class;

        //得到BaseClassDemo的包
        System.out.println(clz.getPackage());//package test

        //得到全限类名
        System.out.println(clz.getName());//test.BaseClassDemo
        
        //得到类的简称
        System.out.println(clz.getSimpleName());//BaseClassDemo

        //得到类的直接父类
        System.out.println(clz.getSuperclass());//class test.Super
        
        //得到类的接口
        Class<?>[] ins = clz.getInterfaces();
        for (Class<?> c : ins) {
            System.out.println(c);
            //interface test.IU
            //interface test.IM
        }

        //获得类public修饰的的内部类/接口
        ins = clz.getClasses();
        System.out.println("长度= " + ins.length);
        for (Class<?> c : ins) {
            System.out.println(c);
            //interface test.BaseClassDemo$Inner2
            //class test.BaseClassDemo$Inner1
        }
        //获得类的修饰符
        int mod = clz.getModifiers();
        System.out.println(mod);//1   表示public
        System.out.println(Modifier.toString(mod));//public

    }
}

(2)通过class对象获得构造器(4个方法)

  1. Constructor<>[] getConstructors( ) 得到类的所有public公共构造方法

  2. Constructor<> getConstructor(Class<> paraType) 得到public对应参方法

  3. Constructors<>[] getDeclaredConstructors( ) 所有的构造方法,与权限无关

  4. Constructors<> getDeclaredConstructors(Class<> paraType) 所有的构造方法,与权限无关

package com.linger.svm;
import java.lang.reflect.Constructor;

class Employee{
	private String name;
	private int age;

	private  Employee() {
	}
	
	Employee(String name) {
	}
	public Employee(String name,Integer age) {
	}	
}
public class T {
	public static void main(String[] args) throws Exception {
		
				
		Class<Employee> clz = Employee.class;
		
		//得到类的所有public公共构造方法
		
		Constructor<Employee>[] cs = (Constructor<Employee>[]) clz.getConstructors();
		
		for (Constructor<Employee> c : cs) {
			System.out.println(c);  
			//私有不能获得public com.linger.svm.Employee(java.lang.String,java.lang.Integer)
		}
		
		//得到public指定的构造器
		//Employee(String name,int age) 
		//Constructor<Employee> con=clz.getConstructor(String.class,int.class);
		//Employee(String name,Integer age) 
		Constructor<Employee> con=clz.getConstructor(String.class,Integer.class);
		System.out.println(">>  "+con);
		//>>  public com.linger.svm.Employee(java.lang.String,java.lang.Integer)
		
		//===========================================
		
		/**
		 * 带declared:访问不受访问权限控制:私有也可获得
		 *  Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 
          	返回一个 Constructor 对象,该对象反映此 Class 对象所表示的类或接口的指定构造方法。 
 			Constructor<?>[] getDeclaredConstructors() 
		 */
		
		cs = (Constructor<Employee>[]) clz.getDeclaredConstructors();
		for (Constructor<Employee> c : cs) {
			System.out.println("-->"+c);
			/**-->private com.linger.svm.Employee()
			   -->com.linger.svm.Employee(java.lang.String)
			   -->public com.linger.svm.Employee(java.lang.String,java.lang.Integer)
			 */
		}
		
		con = clz.getDeclaredConstructor();//无参
		System.out.println(con); //private com.linger.svm.Employee()
	}
}


(3)通过class对象获得方法(4个方法)

1.Method getMethod(String name, Class<> paraType ) 获得该类对应参的public方法
2.Method[] getMethods( ) 获得该类及父类(就这一个可以获得父类) 所有public方法

3.Method getDeclaredMethod(String name,Class<> paraType) 获得该类对应参方法,无权限限制
4.Method[] getDeclaredMethods( ) 获得该类 所有方法,无权限限制

	Method m = clz.getMethod("main", String[].class);
	System.out.println(m);
	
	//public static void com.linger.svm.Test.main(java.lang.String[]) throws java.lang.Exception

	m = clz.getMethod("toString");
	System.out.println(m);//public java.lang.String java.lang.Object.toString()

(4)通过class对象获得字段(4个方法)

1.Field[] getFields(); //获得所有的public 字段,包括继承
2.Field getField(String name); //获取对应name的public字段,包括继承

3.Field[] getDeclaredFields(); //获得该类所有,无关权限
4.Field getField(String name); //获得该类对应字段, 无关权限

package com.linger.svm;
import java.lang.reflect.Field;

class A{
	
	protected String name;
	private int	 age;
	
	public char c;
}
public class Test extends A {
	private String hahha;
	public boolean s;
	
	public static void main(String[] args) throws Exception {
		
		Class<Test> clz = Test.class;
		
		//获得所有的public 字段,包括继承
		
		Field[] fs = clz.getFields();
		for (Field field : fs) {
			System.out.println("1-->  "+field);
		}
		
		//指定的一个 public的,包括继承
		Field f = clz.getField("c");
		System.out.println("2--> "+f);
		
		//得到所有的字段,只能获取当期类里面的,和访问权限无关
		
		fs = clz.getDeclaredFields();
		
		for (Field field : fs) {
			System.out.println("3--> "+field);
		}
		
		//获得当前类指定一个字段,和访问权限无关 
		
		f = clz.getDeclaredField("hahha");
		System.out.println(f);
		
	}
}
//结果
1-->  public boolean com.linger.svm.Test.s
1-->  public char com.linger.svm.A.c
2--> public char com.linger.svm.A.c
3--> private java.lang.String com.linger.svm.Test.hahha
3--> public boolean com.linger.svm.Test.s
private java.lang.String com.linger.svm.Test.hahha

(5)通过class对象获得注解Annotation

@Inherited//可继承的
@Deprecated

Class<AnonationDemo> clz = AnonationDemo.class;
		
//所有的标签,但是是RUNTIME类型,可以获取继承过来的
Annotation[] as = clz.getAnnotations();

2. 知识2 使用反射生成并操作对象

(1)反射创建对象的两种方式


获得class对象clz
---->调用newInstance( )方法

注:实际上是利用默认无参构造器来创建实例的

import javax.swing.JFrame;

public class Test{
	public static void main(String[] args) throws Exception {
		
		//传统new 方式
		JFrame jf = new JFrame();
		jf.setVisible(true);   //出现对话框
		
		//使用反射来做:方式一默认构造器
		
		//得到JFrame字节码,只有forName()那里要全限定名,前边都行
		Class<javax.swing.JFrame> clz = (Class<JFrame>) Class.forName("javax.swing.JFrame");
		//创建对象
		JFrame jf2  = clz.newInstance();
		jf2.setVisible(true);		
		
	}
}


获得class对象clz
---->调用getConstructor(Class<>)得到构造器
---->通过构造器调用newInstance( )方法

//反射方式2
		Constructor<javax.swing.JFrame> cons = (Constructor<JFrame>)    Class.forName("javax.swing.JFrame").getConstructor(String.class);
		//创建对象
		JFrame jf3  = cons.newInstance("测试窗口"); //参数和获得的构造器必须对应
		jf3.setVisible(true);	

这里写图片描述

说明:如果构造器是私有的,通过getDeclaredConstrctor可以获取,是否可以创建对象呢?
A: 虽然可以获取,但不能直接创建对象。还要设置可创建属性。

cons.setAccessible(true);

补充:读取配置文件中的字符串创建对象
文件:resources/obj.properties
内容:JFrame=javax.swing.JFrame

import java.util.Properties;
import javax.swing.JFrame;

public class Test{
	public static void main(String[] args) throws Exception {		
		new Test().getInstance();
	}
	
	public void getInstance() {
		InputStream in = this.getClass().getClassLoader().getResourceAsStream("obj.properties");
		
		Properties p = new Properties();
		
		try {
			p.load(in);
			String val = p.getProperty("JFrame");
			System.out.println(val);
			
			//使用反射创建对象
			
			Class<JFrame> clz = (Class<JFrame>) Class.forName(val);
			
			JFrame j = clz.newInstance();
			j.setVisible(true);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

(2)调用方式

通过class对象获得method方法上面已经说了4种方法。获得method对象后,就可以调用它对应的方法。method里包含一个invoke( )方法,如下:

Object invoke(Object obj,Object...args)

/*说明:obj - 从中调用底层方法的对象(必须要写,静态方法可以写null)
       args - 用于方法调用的参数 
	   返回:
			使用参数 args 在 obj 上指派该对象所表示方法的结果 
*/

对于私有方法、public方法、static方法做一下测试。

class Dept{
	
	public String[] publicMethod(String name){
		//return name +" 恭喜发财";
		return new String[]{name};
	}
	private void privateMethod(){
		System.out.println("show");
	}
	public static void staticMethod(){
		System.out.println("staticMethod");
	}
	
}

测试:

public static void main(String[] args) throws Exception {

		//使用反射来调用Dept里的法		
		Class<Dept> clz   = Dept.class;
		
		//public void publicMethod(String name)
		
		//先得到这个需要被调用的方法
		Method m = clz.getMethod("publicMethod", String.class);
		
		/**
		 *  Object invoke(Object obj, Object... args)   对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。 
		 *  参数:
				obj - 从中调用底层方法的对象
				args - 用于方法调用的参数 
				返回:
				使用参数 args 在 obj 上指派该对象所表示方法的结果 

		 */
		System.out.println(m);//public java.lang.String[] Dept.publicMethod(java.lang.String)
		
		//Object ret = m.invoke(clz.newInstance(), "will");//YES
		Object ret = m.invoke(clz.newInstance(), new Object[]{"Will"});	
		
		//===================
		
		//private void privateMethod()		
		m = clz.getDeclaredMethod("privateMethod");		
		//调用之前设置可访问的
		m.setAccessible(true);
		m.invoke(clz.newInstance());
		
		//	public static void staticMethod()
		
		/**
		 * 如果底层方法是静态的,那么可以忽略指定的 obj 参数。该参数可以为 null。 

  		如果底层方法所需的形参数为 0,则所提供的 args 数组长度可以为 0 或 null或不写

		 * 
		 */
		m = clz.getMethod("staticMethod");
		//m.invoke(null);//YES
		//m.invoke(null, null);//YES
		m.invoke(null,new Object[]{});//YES
	}

说明:第二个参数为可变个数参数(和数组一样),特别注意写法。如下:

public class InvokeMethodByVarArgsDemo {
	public static void show(int ...is){
		System.out.println("基本类型执行过来");
	}

	public static void show(String ...sArr){
		System.out.println("引用类型执行过来");
	}
	public static void main(String[] args) throws Exception {
		Class<InvokeMethodByVarArgsDemo> clz  =InvokeMethodByVarArgsDemo.class;
		
		//调用public void show(int ...is)
		
		Method m  = clz.getMethod("show", int[].class);
		
		//m.invoke(null,1,2,3);//ERROR
		m.invoke(null,new int[]{1,2,3});//YES
		m.invoke(null,(Object)new int[]{1,2,3});//YES
		m.invoke(null,new Object[]{new int[]{1,2,3}});//YES
		
		//public static void show(String ...sArr)
		
		m = clz.getMethod("show", String[].class);
		
		//m.invoke(null, "A","B","C");//ERROR
		//m.invoke(null, new String[]{"A","B","C"});//ERROR
		//m.invoke(null, (Object)new String[]{"A","B","C"});//装包  YES
		m.invoke(null, new Object[]{new String[]{"A","B","C"}});//装包  YES
		
		/**
		 * public Object invoke(Object obj,Object... args)
		 * 
		 * new Object[]{传递的实际参数};通用做法;
		 */
	}
}

**第二个参数通用做法 **
**new Object[]{传递的实际参数}; **

如果参数是泛型T,getMethod的时候用T的上限字节码(下限),没有就用Object字节码

(3)访问和设置字段

getXxx( ) , setXxx( ) ,引用类型去掉XXX即可

		Cat c = clz.newInstance();
        
        f = clz.getDeclaredField("age");		
		f.setAccessible(true);
		f.setInt(c, 16);  //
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值