黑马程序员------Java基础学习------反射机制

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

 


1、反射的概念



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


什么是反射机制?  
        简单的来说,反射机制指的是程序在运行时能够获取自身的信息。在java中,只要给定类的名字,那么就可以通过反射机制来获得类的所有信息。

反射机制的优点与缺点:  
        为什么要用反射机制?直接创建对象不就可以了吗,这就涉及到了动态与静态的概念,  
    静态编译:在编译时确定类型,绑定对象,即通过。  
    动态编译:运行时确定类型,绑定对象。动态编译最大限度发挥了java的灵活性,体现了多态的应用,有以降低类之间的藕合性。  
    一句话,反射机制的优点就是可以实现动态创建对象和编译,体现出很大的灵活性,特别是在J2EE的开发。
它的缺点是对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它满足我们的要求。这类操作总是慢于只直接执行相同的操作。

Class类和Class类实例

Java程序中的各个Java类属于同一类事物,描述这类事物的Java类就是Class类。
对比提问:众多的人用一个什么类表示?众多的Java类用一个什么类表示?
人  Person
Java类  Class
对比提问: Person类代表人,它的实例对象就是张三,李四这样一个个具体的人,Class类代表Java类,它的各个实例对象又分别对应什么呢?
对应各个类在内存中的字节码,例如,Person类的字节码,ArrayList类的字节码,等等;

一个类被类加载器加载到内存中,占用一片存储空间,这个空间里面的内容就是类的字节码,不同的类的字节码是不同的,所以它们在内存中的内容是不同的;


2、获得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();


public class ClassDemo1 {
	public static void main(String[] args) throws Exception {
		//获得Class对象的方法(三种)
		//一:调用属性
		Class<String> c = String.class;
		System.out.println(c);//打印结果:class java.lang.String    				String.class就表示JVM中一份表示String类的字节码
		Class<String> 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
	}
}

3 利用Class获取类的属性信息

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 junereflect624.BaseDemo3

		//得到包名
		System.out.println(c.getPackage());//package junereflect624

		//得到全限定名
		System.out.println(c.getName());//junereflect624.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
	}
}


4 Class中得到构造方法Constructor、方法Method、字段Field


常用方法:
Constructor类用于描述类中的构造方法:
Constructor<T> getConstructor(Class<?>... parameterTypes) 
返回该Class对象表示类的指定的public构造方法; 
Constructor<?>[] getConstructors()
返回该Class对象表示类的所有public构造方法; 
Constructor<T> 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(): 获得类所有的方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法;
import java.lang.reflect.Constructor;

class Emp{
	private String name;
	private int age;
	private Emp() {
	} 
	Emp(String name){
	}
	public Emp(String name,int age){
	}
}

public class ConstructorDemo4 {
	public static void main(String[] args) throws Exception {
		//得到所有的构造器(先得到类)
		Class<Emp> c = Emp.class;
		/**
		 * Constructor<?>[] getConstructors() 
          		返回一个包含某些 Constructor 对象的数组,这些对象反映此 Class 对象所表示的类的所有公共构造方法。 
		 */
		Constructor[] con = c.getConstructors();//前面的修饰符必须是public才可以在这个方法下获取到
		for (Constructor cons : con) {
			System.out.println("c.getConstructors()"+cons);//如果上面的某构造器public去掉,则显示不出
			/**打印
				public junereflect624.Emp(java.lang.String,int)
			 */
		}
		
		//得到指定的构造器,也是必须public
		Constructor c1 = c.getConstructor(String.class,int.class);
		System.out.println(c1);//public junereflect624.Emp(java.lang.String,int)
									 
System.out.println("====================================");
	//现在想获得不受public影响的,getDeclaredConstructors(),暴力反射
		
		con = c.getDeclaredConstructors();
		for (Constructor cons : con) {
						 
System.out.println("c.getDeclaredConstructors()=="+cons);//此时不受修饰符的影响
			/**打印
			 *  public junereflect624.Emp()
				public junereflect624.Emp(java.lang.String)
				public junereflect624.Emp(java.lang.String,int)
			 */
		}
	}
}

5、利用反射创建对象


创建对象:
1、使用Class对象的newInstance()方法创建该Class对象的实例,此时该Class对象必须要有无参数的构造方法。
2、使用Class对象获取指定的Constructor对象,再调用Constructor的newInstance()方法创建对象类的实例,此时可以选择使用某个构造方法。如果这个构造方法被私有化起来,那么必须先申请访问,将可以访问设置为true;

class User{
	/*private User(){//将默认的构造方法私有化的话就不可以再创建对象,两种方法都是这样
	}*/
	public String toString() {
		return "User对象创建成功!";
	}
}

public class NewInstanceDemo6 {
	public static void main(String[] args) throws Exception {
		//传统方式创建对象
		 System.out.println(new User());
		 
		 //使用反射的方式
		 Class<User> c = User.class;
		 User u = c.newInstance();(直接newInstance的话必须保证默认的构造方法正常存在,也就是没有被私有化!这是前提条件)
		 System.out.println(u);
	}
}

6 使用反射调用方法


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

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
	}
}


7 使用反射调用可变参数方法

要把可变参数都当做是其对应的数组类型参数;
如 show(XX... is)作为show(XX[] is)调用;

若可变参数元素类型是引用类型:
JDK内部接收到参数之后,会自动拆包取出参数再分配给该底层方法,为此我们需要把这个数组实参先包装成一个Object对象或把实际参数作为一个Object一维数组的元素再传递。

若可变参数元素类型是基本类型:
JDK内部接收到参数之后,不会拆包,所以可以不必再封装.不过封装了也不会错.所以建议,不管基本类型还是引用类型都使用Object[]封装一层,保证无误.

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"}});//推荐写法
	}
}


8 使用反射操作字段

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);

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);//获取成功
	}
}

9 反射和泛型-反射来获取泛型信息

通过指定对应的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()//返回泛型参数类型;

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培训、Android培训、iOS培训、.Net培训、期待与您交流! -------


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值