Java反射机制

1.1反射机制的作用

        通过java 语言中的发射机制可以操作字节码文件。

        通过反射机制可以操作代码片段。(.class文件)。

1.2 反射机制的原理:

        1、首先明确的概念: 一切皆对象----类也是对象.

        2、然后知道类中的内容 :modifier(权限修饰符) constructor(构造方法) field(属性) method(方法).

        3、其次明白加载: 当Animal.class在硬盘中时,是一个文件,当载入到内存中,可以认为是一个对象,是java.lang.class的对象.

1.3反射机制相关类所在包

java.lang.reflect.*;

1.4反射机制相关的重要类

        java.lang.Class 代表整个字节码,代表一个类型(整个类)

        java.lang.reflect.Method 代表字节码中的方法字节码(类中的方法)

        java.lang.reflect.Constructor 代表字节码中的构造方法字节码(类中的构造方法)

        java.lang.reflect.Field 代表字节码中的属性字节码(类中的成员变量:静态变量+实例变量)

1.5获取整个类的字节码的三种方式:

        第一种:Class c = Class.forName("完整包名+类名");

        第二种:Class c = 对象.getClass();

        第三种:Class c=任何类型.class;

public static void main(String[] args) {
		Class c1=null;
		Class c2=null;
		
		try {
			/*
			 方法一:Class.forName():Class类下的forName()方法
			  	1.静态方法
			  	2.方法的参数是一个字符串,字符串需要是一个完整的类名
			  	3.完整的类名必须带有包名。java.lang包也不可以省略
			  	4.返回Class对象
			 */
			//获取整个类的字节码文件
			c1=Class.forName("java.lang.String");//c1代表String.class,或者说明c1代表String类型
			c2=Class.forName("java.util.Date");//c2代表Date类型
			Class c3=Class.forName("java.lang.Integer");//c3代表Integer类型
			System.out.println(c1);
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		// 方法二:java中任何一个对象(Object)都有一个方法:getClass()
		String str="www";
		Class x=str.getClass();//x代表String.class字节码文件,x代表String类型
		System.out.println(c1==x);//true(判断的是对象的内存地址)
		
		Date d=new Date();
		Class dCl=d.getClass();
		System.out.println(c2==dCl);//true(c2和dCl两个变量中保存的内存地址都是一样的,都指向这个方法区中的字节码文件)
		
		// 方法三:java语言中任何一种类型,包括基本数据类型,它都有.class属性
		Class a = String.class;//a代表String类型
		Class b = int.class;//b代表int类型
		Class c = double.class;//c代表double类型
		Class f = Date.class;//f代表Date类型
		System.out.println(x==a);//true
		
	}

注意:字节码文件装载到JVM中时,只装载一份。

1.6 反射机制的应用

        1.通过反射机制获得的Class,可以调用无参数构造方法来实例化对象

        通过Class的newInstance()方法来实例化对象。

// c代表日期Date类型
Class c = Class.forName("java.util.Date");
// 实例化一个Date日期类型的对象
Object obj = c.newInstance();

 注意:newInstance()方法内部实际调用了无参构造,必须确保无参构造存在,否则会出现"实例化"异常。

public class ReflectTest02 {
	public static void main(String[] args) {
		// 使用构造方法创建对象
		User user=new User();
		System.out.println(user);
		// 使用反射机制的方式创建对象
		try {
			// 通过反射机制,获得Class,通过Class来实例化对象
			Class c = Class.forName("com.wanho.po.User");//c代表User类型
			// newInstance()方法调用User类的无参构造,完成对象的创建。
			// 注意:newInstance()方法调用的是无参构造,要确保无参构造的存在。
			Object obj = c.newInstance();
			System.out.println(obj);//com.wanho.po.User@6d06d69c
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InstantiationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

        2. 验证反射机制的灵活性

        java代码写一遍,再不改变java源代码的基础之上,可以做到不同对象实例化。符合OCP开闭原则:对扩展开放,对修改关闭。

public class ReflectTest03 {
	public static void main(String[] args) throws Exception {
// 只能创建一个User类对象
		//User user = new User();
// 通过修改配置文件,创建不同的实例化对象		
		// 通过IO流读取classinfo.properties配置文件
		// 工程下的相对路径/review/classinfo.properties
		FileReader reader = new FileReader("classinfo.properties");
		// 创建属性类对象Map
		Properties pro = new Properties();//key value均是String类型
		// 加载配置文件
		pro.load(reader); 
		// 关闭流
		reader.close();
		
		// 通过key获取value
		String className = pro.getProperty("className");
		System.out.println("完整包名+类名:"+className);
		//通过反射机制实例化对象
		Class c = Class.forName(className);
		Object obj = c.newInstance();
		System.out.println("通过反射实例化的对象:"+obj);
	}
}

        3. 如果只想让一个类的“静态代码块”执行的话,可以使用Class.forName("该类的完整类名称")

        重点:如果你只是希望一个类的静态代码块执行,其他代码一律不执行,可以使用:Class.forName("完整类名"); 这个方法的执行会导致类加载,类加载时,静态代码块执行。

public class ReflectTest04 {
	public static void main(String[] args) {
		try {
			//Class.forName()这个方法的执行会导致:类加载
			Class.forName("com.wanho.reflect.MyClass");
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
public class MyClass {
	//静态代码块在类加载时执行,并且只执行一次
		static {
			System.out.println("MyClass类的静态代码块执行了!!!!");
		}
}

        4.文件路径问题的研究

        以下是获取一个文件的绝对路径的通用方式。前提是:文件需要在类路径下(src下),这种方式才可以使用。

public class AboutPath {
	public static void main(String[] args) throws Exception{
		//这种方式的路径缺点是:移植性差,默认当前路径是project的根
		//代码离开eclipse,到了其他位置,路径不是project的根了,这时这个路径就无效了
		//FileReader reader = new FileReader("classinfo.properties");
		
		//下面是一种比较通用的路径,即使代码换了位置,这样的编写仍然是通用的
		//注意:使用以下通用方式的前提是:这个文件必须在类路径下。
		//类路径下:凡是在src下的都是类路径下。
		//src是类的根路径
		/*
		  	类加载器的种类:启动类加载器、应用类加载器、扩展类加载器
		  	Thread.currentThread() 当前线程对象
		  	getContextClassLoader() 是线程对象的方法,可以获取到当前线程的类加载器对象
		  	getResource() 【获取资源】这是类加载器对象的方法,当前线程的类加载器默认从类的根路径下加载资源
		 */
		//以下获取文件的绝对路径是通用的
		String path = Thread.currentThread().getContextClassLoader().getResource("classinfo2.properties").getPath();
		//采用以上代码可以获得一个文件的绝对路径。
		// /D:/software/eclipse-workspace/review/bin/classinfo2.properties
		System.out.println(path);
		
		// 获取db.properties文件的路径,需要从类的根路径下作为起点开始
		String path2 = Thread.currentThread().getContextClassLoader().getResource("com/wanho/po/db.properties").getPath();
		System.out.println(path2);
	}
}

        5.IoProperties.java

        注意:classinfo2.properties配置文件在类路径(src)下

public class IoPropertiesTest {
	public static void main(String[] args) throws Exception{
		//获取一个文件的绝对路径-----根据相对路径获取绝对路径
//		String path = Thread.currentThread().getContextClassLoader().getResource("classinfo2.properties").getPath();
		//System.out.println(path);
		// 读取配置文件
//		FileReader reader = new FileReader(path);
		InputStream reader = Thread.currentThread().getContextClassLoader().getResourceAsStream("classinfo2.properties");
		Properties pro = new Properties();
		// 加载配置文件
		pro.load(reader);
		reader.close();
		// 根据key获取value
		String className = pro.getProperty("className");
		System.out.println(className);
	}
}

        6. 资源绑定器

        java.util包下提供了一个资源绑定器,便于获取属性配置文件中的内容。

注意:使用以下这种方式的时候,属性配置文件xxx.properties必须放到类路径下。

public class ResourceBundleTest {
	public static void main(String[] args) {
		// 资源绑定器,只能绑定xxx.properties文件。并且这个文件必须在类路径下。文件的扩展名也必须是properties
		// 注意:在写路径的时候,路径后面的扩展名不能写。
//		ResourceBundle bundle = ResourceBundle.getBundle("classinfo2");
		ResourceBundle bundle = ResourceBundle.getBundle("com/wanho/po/db");
		// 根据key获得value
		String className = bundle.getString("className");
		System.out.println(className);
		
	}
}

1.7 反射属性Field(Field类)

1.7.1 获取Field对象

方法含义
getField(String name)获取指定的public修饰的属性对象
getFields()获取类中所有的使用public修饰的属性对象,返回属性数组Field[]
getDeclaredField(String name)获取指定的属性对象
getDeclaredFields()获取所有属性对象,返回属性数组Field[]
getModifiers()获取属性的修饰符列表,返回的修饰符是一个int型数字,每个数字是修饰符的代号
Modifier.toString(int i)将修饰符代号转换成字符串,返回String类型
getType()获取属性的类型,返回Class对象
public class ReflectTest05 {
	public static void main(String[] args) throws Exception{
		// 获取整个类
		Class studentClass = Class.forName("com.wanho.po.Student");
		System.out.println("studentClass:"+studentClass);
		// 获取类名
		String className = studentClass.getName();
		System.out.println("完整类名:"+className);
		
		String simpleName  = studentClass.getSimpleName();
		System.out.println("简单类名:"+simpleName);
		
		System.out.println("================");
		// 获取类中所有的使用public修饰的属性
		Field[] fields = studentClass.getFields();
		System.out.println(fields.length);
		// 取出属性
		Field f = fields[0];
		// 取出属性的名称
		String fieldName = f.getName();
		System.out.println(fieldName);
		
		
		// 获取所有属性
		Field[] fields2 = studentClass.getDeclaredFields();
		System.out.println(fields2.length);
		System.out.println("================");
		// 遍历
		for(Field field:fields2) {
			// private java.lang.String com.wanho.po.Student.name
			System.out.println(field);
			// 获取属性的修饰符列表
			int i = field.getModifiers(); //返回的修饰符是一个数字,每个数字是修饰符的代号!
			System.out.println(i);
			// 将修饰符代号转换成字符串
			String modifierString = Modifier.toString(i);
			System.out.println("属性的修饰符:"+modifierString);
			// 获取属性的类型
			Class fieldType = field.getType();
			//String fName = fieldType.getName(); //java.lang.String
			String fName = fieldType.getSimpleName(); //String
			System.out.println("属性的类型:"+fName);
			// 获取属性的名字
			System.out.println("属性的名字:"+field.getName());
		}
	}
}

1.7.2 反编译Field

public class ReflectTest06 {
	public static void main(String[] args) throws Exception{
		// 创建这个为了拼接字符串
		StringBuilder sBuilder = new StringBuilder();
		// 获取整个类
		Class studentClass = Class.forName("com.wanho.po.Student");
		sBuilder.append(Modifier.toString(studentClass.getModifiers())+" class "+studentClass.getSimpleName()+"{\n");
		// 获取所有属性
		Field[] fields = studentClass.getDeclaredFields();
		for(Field field:fields) {
			sBuilder.append("\t");
			sBuilder.append(Modifier.toString(field.getModifiers()));
			sBuilder.append(" ");
			sBuilder.append(field.getType().getSimpleName());
			sBuilder.append(" ");
			sBuilder.append(field.getName());
			sBuilder.append(";\n");
		}
		sBuilder.append("}");
		System.out.println(sBuilder);
	}
}

1.7.3 通过反射机制访问对象属性(重点)

/**
 * (掌握):通过反射机制访问对象属性
 * 怎么通过反射机制访问一个java对象的属性?
 * 		给属性赋值set
 * 		获取属性的值get
 */
public class ReflectTest07 {
	public static void main(String[] args) throws Exception{
		//不使用反射机制,访问一个对象的属性
		Student student = new Student();
		student.no=1001;//给student对象的no属性赋值1001
						//要素1:Student对象;要素2:no属性;要素3:属性值1001
		// 读属性值
		System.out.println(student.no);
		
		//使用反射机制,怎么去访问一个对象的属性。(set  get)
		// 获取整个类
		Class studentClass = Class.forName("com.wanho.po.Student");
		Object obj = studentClass.newInstance();// Student对象。(底层调用无参构造方法)
		// 获取no属性(根据属性名获得属性)
		Field noField=studentClass.getDeclaredField("no");
		// 给属性赋值
		/*
			虽然使用了反射机制,但是三要素还是缺一不可:
				要素1:obj对象
				要素2:no属性
				要素3:no属性值1002
		 */
		noField.set(obj, 1002);//给obj对象(Student对象)的no属性赋值1002
		// 读取属性的值
		// 两个要素:获取obj对象的no属性的值。
		System.out.println(noField.get(obj));
		
		// 可以访问私有的属性吗?---- 不可以
		Field nameField=studentClass.getDeclaredField("name");
		//System.out.println(nameField);
		// 打破封装(反射机制的缺点:打破封装,可能会给不法分子留下机会!!!)
		// 这样设置完之后,在外部也是可以访问private的。
		nameField.setAccessible(true);
		
		// 给name属性赋值
		nameField.set(obj, "marry");
		// 获取name属性的值
		System.out.println(nameField.get(obj));
	}
}

1.8 反射Method类(重点)

1.8.1 反射Method

方法含义
getMethods()获取所有公共public的方法对象
getMethod(String name, Class... parameterTypes)获取指定的公共public的方法对象
getDeclaredMethods()获取所有Method方法对象,返回Method[]
getDeclaredMethod(String name, Class... parameterTypes)获取指定的方法对象
getParameterTypes()获取参数列表,返回Class[]
public class ReflectTest08 {
	public static void main(String[] args) throws Exception{
		// 获取类
		Class uSClass = Class.forName("com.wanho.service.UserService");
		// 获取所有方法Method
		Method[] methods = uSClass.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());
			}
		}
	}
}

1.8.2 反编译Method

public class ReflectTest09 {
	public static void main(String[] args) throws Exception{
		StringBuilder s = new StringBuilder();
		// 获取类
		Class uSClass = Class.forName("java.lang.String");
		s.append(Modifier.toString(uSClass.getModifiers())+" class "+uSClass.getSimpleName()+"{\n");
		Method[] methods = uSClass.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(",");
			}
			s.deleteCharAt(s.length()-1);
			
			s.append(")");
			
			s.append("{}\n");
		}
		s.append("}");
		System.out.println(s);
	}
}

1.8.3 通过反射机制调用方法(重点)

Method类中的invoke()方法

方法含义
invoke(Object obj, Object ...value)调用该方法
/**
 * 	(重点)通过反射机制怎么调用一个对象的方法
 * 	五个星※※※※※
 * 
 * 	反射机制,让代码很具有通用性,可变化的内容都是写到配置文件当中,
 * 	通过修改配置文件,可以创建不同的对象,调用不同的方法,
 * 	但是java代码不需要做任何改动。这就是反射机制的魅力。
 * @author Lucky
 *
 */
public class ReflectTest10 {
	public static void main(String[] args) throws Exception{
		// 不使用反射机制,怎么调用方法
		//创建对象
		UserService userService = new UserService();
		// 调用方法
		/*
		 	要素分析:
		 		要素1:userService对象
		 		要素2:login方法名
		 		要素3:实参列表
		 		要素4:返回值
		 */
		boolean f= userService.login("admin", "123");
		System.out.println(f);
		//userService.logout();
		
		// 使用反射机制
		Class c = Class.forName("com.wanho.service.UserService");
		// 创建对象
		Object obj = c.newInstance();
		// 获取方法对象
		Method loginMethod = c.getDeclaredMethod("login", String.class,String.class);
		// 调用方法
		// 调用方法需要4个要素。
		// 反射机制中最最最重要的一个方法,必须记住。
		// 四要素
		/*
		  loginMethod方法
		  obj对象
		  "admin"、"123"实际参数
		  retValue返回值
		 */
		Object retValue = loginMethod.invoke(obj, "admin", "123");//使用invoke调用方法
		System.out.println(retValue);
	}
}

1.9 反射Constructor

1.9.1 反编译Constructor

public class ReflectTest11 {
	public static void main(String[] args) throws Exception{
		StringBuilder s = new StringBuilder();
		Class vipClass = Class.forName("com.wanho.po.Vip");
		// 获取类的修饰符
		s.append(Modifier.toString(vipClass.getModifiers()));
		s.append(" class ");
		// 获取类名
		s.append(vipClass.getSimpleName());
		s.append("{\n");
		// 拼接构造方法
		Constructor[] constructors = vipClass.getDeclaredConstructors();
		for(Constructor constructor : constructors) {
			s.append("\t");
			// 获取构造方法的修饰符
			s.append(Modifier.toString(constructor.getModifiers()));
			s.append(" ");
			s.append(vipClass.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);
	}
}

1.9.2 通过反射机制调用构造方法

public class ReflectTest12 {
	public static void main(String[] args) throws Exception{
		// 不使用反射机制创建对象
		Vip vip = new Vip();
		Vip vip2 = new Vip(1001, "marry", "1999", true);
		
		// 使用反射机制创建对象
		Class vipClass = Class.forName("com.wanho.po.Vip");
		// 调用无参构造
		Object obj = vipClass.newInstance();
		System.out.println(obj);
		// 调用有参构造方法
		// 1.获取构造方法
		Constructor con = vipClass.getDeclaredConstructor(int.class);
		// 2.调用构造方法的new对象
		Object obj2 = con.newInstance(1002);
		System.out.println(obj2);
		
		// 获取无参构造的方法
		Constructor con2 = vipClass.getDeclaredConstructor();
		Object obj3 = con2.newInstance();
		System.out.println(obj3);
		
	}
}

1.10 通过反射获取父类和父接口

public class ReflectTest13 {
	public static void main(String[] args) throws Exception{
		// 获取String类
		Class stringClass = Class.forName("java.lang.String");
		
		// 获取String类的父类
		Class superClass = stringClass.getSuperclass();
		System.out.println("String的父类:"+superClass.getName());
		
		// 获取String类实现的所有接口(一个类可以实现多个接口)
		Class[] interfaces = stringClass.getInterfaces();
		for(Class inter : interfaces) {
			System.out.println("String类实现的接口:"+inter.getName());
		}
	}
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值