Java的反射机制的学习和总结

目录

一、引入Class类

二、定义

目的

三、三种方式获取Class对象【一个class的Class实例】

方式一:

方式二:

方式三:

代码整理:

结果:

结果分析 

四、通过反射可以获取某个实例的信息

 4.1、获取Class类的类信息

 4.2、获取Class类的包信息

4.3.获取接口信息

4.5、获取继承信息

4.5、判断类型

五、创建实例对象

 六、Constructor类   访问构造方法

相关方法

 应用

应用1: 

应用2:获取私有构造方法并调用

 instaceof运算符

 isAssignableForm()方法

 七、Field类  访问字段【成员变量】

Eg1:访问Book类的字段

 Eg2:访问[获取]Book类的字段的值

 Eg3:设置Book类的字段的值

 八、Method类  访问方法

Eg1:调用方法

Eg2:调用静态方法

Eg3:多态问题【目标对象决定调用方法的归处】


 

一、引入Class类

二、定义

对于任意一个类,在运行期间加载类,并允许以编程的方式解剖类中的各个成分 并映射成相应的Java类【位于Java.lang.reflect包中】

各个成分相对应的类
Package
构造方法Constructor
属性Field
方法Method

发生在运行期

【即:将编译好后的字节文件交给计算机执行,把磁盘中的代码放到内存中存起来】

目的

      为了获取某个实例的信息

三、三种方式获取Class对象【一个class的Class实例】


方式一:

Class   c1 = 类名.class

   JVM使用类装载器,将类装入内存(前提是:类还没有装入内存),只加载一次
  不做类的初始化工作,返回Class的对象。 


方式二:

使用类名调用Class提供静态方法:【Public static Class forName(String package)】

     即:Class.forName(“类名字符串”)

装入类,并做类的静态初始化,返回Class的对象

方式三:

Object提供的方法:pubic Class getClass()】

     即:实例对象.getClass() 

 返回引用运行时真正所指的对象所属类的Clas的对象

      真正:(子对象的引用会赋给父对象的引用变量中)

代码整理:

public static void main(String[] args) throws ClassNotFoundException {
	//方式1:通过类名 访问class
	Class  stringCls1 = String.class;
	//方式2:通过实例访问class
	String s = "";
	Class  stringCls2 = s.getClass();
	//方式3:通过Class类的静态方法forName(类名)访问class
	Class  stringCls3 = Class.forName("java.lang.String");
	
	//得到相同的Class对象
	System.out.println(stringCls1.hashCode());
	System.out.println(stringCls2.hashCode());
	System.out.println(stringCls3.hashCode());
}

结果:

结果分析 

因为Class实例在JVM中是唯一的,所以,上面方法获取的Class实例为同一个实例

可以用==来比较两个Class实例是否为同一个

四、通过反射可以获取某个实例的信息

 4.1、获取Class类的类信息

  1. getName()    返回完全限定名【包名+类名】
  2. getSimpleName()  返回类名
  3. getTypeName()  返回类型信息
public class  classInfo {
	public static void main(String[] args) {
		Class clazz = List.class;
		printClassInfo(clazz);
	}
	public static void printClassInfo(Class clazz) {
		System.out.println("名称:"+clazz.getSimpleName());
		System.out.println("完全限定名:"+clazz.getName());
		System.out.println("类型名称:"+clazz.getTypeName());
		System.out.println();			
	}
}

 4.2、获取Class类的包信息

 Package pck = clazz.getPackage();

public class Test {
	public static void main(String[] args) {
		Class clazz = String.class;
		printClassInfo(clazz);
	}

	public static void printClassInfo(Class clazz) {
		Package pck = clazz.getPackage();
		if (pck != null) {
			System.out.println("该类所在的包的名称:" + pck.getName());
		}	
	}
}

4.3.获取接口信息

System.out.println("实现接口列表");
try {
	Class[] inf = String.class.getInterfaces();
	for(Class f :inf) {
		System.out.println(f);
	}
} catch (Exception e) {
	e.printStackTrace();
}

4.5、获取继承信息

获取类,并调用getSuperclass()方法

Class clazz = FileInputStream.class.getSuperclass();

System.out.println("父类:" + clazz.getName());

 

4.5、判断类型

System.out.println("是否为接口:"+clazz.isInterface());
System.out.println("是否是数组:"+clazz.isArray());
System.out.println("是否为枚举:"+clazz.isEnum());
System.out.println("是否为基本类型:"+clazz.isPrimitive());	

五、创建实例对象

newInstance()

public static void main(String[] args) throws IOException {
	//从文件中读取类的名称
	String className = Files.readAllLines(Paths.get("F:\\test\\23.txt")).get(0);
	try {
		//【得到】该类的Class对象
		Class clazz = Class.forName(className);
		//创建3个对象
		
		Object obj1 = clazz.newInstance();
		Object obj2 = clazz.newInstance();
		
		System.out.println(obj1);
		System.out.println(obj2);	
	} catch (ClassNotFoundException e) {
		e.printStackTrace();
	} catch (InstantiationException e) {
		e.printStackTrace();
	} catch (IllegalAccessException e) {
		e.printStackTrace();
	}	
}

 六、Constructor类   访问构造方法

Constructor类封装了构造方法中的所有信息,可以通过Constructor实例来获取

相关方法

 应用

基础类---对该类进行相关方法调用

class Example{
	public Example() {
		System.out.println("Example类的无参构造");
	}
	private Example(String s) {
		System.out.printf("Example类的私有构造:一个参数:s ,s=%d\n",s);
	}
	public Example(int a) {
		System.out.printf("Example类的有参构造:一个参数:a ,a=%d\n",a);
	}
	public Example(int a,double b) {
		System.out.printf("Example类的有参构造:一个参数:a ,a=%d,b=%f\n",a,b);
	}
}

应用1: 

public class Demo{
	public static void main(String[] args) {
		
		Class clazz = Example.class;		
		try {
			//调用无参构造方法创建Example对象
			Example ex1 = (Example)clazz.newInstance();
			System.out.println(ex1);
			System.out.println();
			
			//获取所有的构造方法
			System.out.println("获取所有的构造方法");
			Constructor[] constructors = clazz.getConstructors();
			for(Constructor constructor :constructors) {
				System.out.println(constructor);
			}
			System.out.println();
			System.out.println("使用构造器调用不同参数类型的构造方法来创建Example对象");
			//调用有参构造方法创建Example对象
			Constructor  construct = clazz.getConstructor();
			System.out.println("无参构造:"+construct);
			Constructor construct2 = clazz.getConstructor(int.class);
			System.out.println(construct2);
			
			Constructor construct3 = clazz.getConstructor(int.class,double.class);
			System.out.println(construct3);
			
		}catch (Exception e) {
			//异常处理省略
		}
	}
}

应用2:获取私有构造方法并调用

注意:通过设置setAccessible(true)才能访问非public修饰的构造方法

  从这里可以看出:反射破坏了封装性【硬编码是不能调用私有构造】

public static void main(String[] args) {
	Class clazz = Example.class;
	
	Constructor privateConstruct;
	try {
		privateConstruct = clazz.getDeclaredConstructor(String.class);

        //调用
		privateConstruct.setAccessible(true);
		Example ex = (Example)privateConstruct.newInstance("private-construct");	
	} catch (Exception e) {
		//异常处理省略
	}
}

 instaceof运算符

       解释:判断引用和类型是否匹配

       作用:用来判断继承关系

public static void main(String[] args) {
	String s = "username";
	System.out.println("是否为String类型:"+(s instanceof String));
	System.out.println("");
}

 isAssignableForm()方法

   解释:判断类型与类型之间是否可以赋值

   作用:
             1.子类可以赋值给父类引用
             2.接口可以赋值给实现类

//isAssignableFrom()方法:判断“类型”和类型”之间关系
System.out.println("Integer类型是否可以赋值给Integer类型?"+Integer.class.isAssignableFrom(Integer.class));
System.out.println("Number类型是否可以赋值给Integer类型?"+Integer.class.isAssignableFrom(Number.class));
System.out.println("Integer类型是否可以赋值给Number类型?"+Number.class.isAssignableFrom(Integer.class));
System.out.println("Integer类型是否可以赋值给Double类型?"+Double.class.isAssignableFrom(Integer.class));
System.out.println("Integer类型是否可以赋值给Comparable类型?"+Comparable.class.isAssignableFrom(Integer.class));

 七、Field类  访问字段【成员变量】

在反射中 ,每个字段都会被封装成一个Field对象。

对任意一个Object实例,只要获取了它的class,就可以获取它的一切信息。 

 应用

Eg1:访问Book类的字段

class  Book{
	public String Bookname;
	private int stock;
    private double sale;
    
	//getter and setter 省略

	@Override
	public String toString() {
		return "Book [Bookname=" + Bookname + ", stock=" + stock + ", sale=" + sale + "]";
	}
	
}

注意:"访问修符符:" : Modifier.toString(field.getModifiers()) 

field.getModifiers() 得到的是int类型的值

public class Demo09_Field {
	public static void main(String[] args) {
		Class clazz = Book.class;
		
		Field[] publicfiels = clazz.getFields();
		
		Field[] fields = clazz.getDeclaredFields();
		for(Field field :fields) {
			System.out.println("成员变量访问修饰符【int】:"+field.getModifiers());
			System.out.println("成员变量访问修符:"
                                +Modifier.toString(field.getModifiers()));
			System.out.println("成员变量类型:"+field.getType());
			System.out.println("成员变量名称:"+field.getName());
			System.out.println();
		}
	}
}

 Eg2:访问[获取]Book类的字段的值

    在反射中能访问字段,就是为了能够给该字段赋值

使用方法:Field.get(Object obj)

对于非public修饰的字段,要设置:setAccessible(true)

public class Demo11_getFieldValue {
	public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException {
		Book book =  new Book();
		book.setBookname("一笙有喜");
		book.setStock(500);
		book.setSale(0.75);
		printInfo(book);
	}
	public static void printInfo(Object obj) throws IllegalArgumentException, IllegalAccessException {
		Class clazz = obj.getClass();
		
		Field[] fields = clazz.getDeclaredFields();
		for(Field field : fields) {

			System.out.println("成员变量名称:"+field.getName());
			if(!field.isAccessible()) {
				field.setAccessible(true);
			}
			
			System.out.println("成员变量内容:"+field.get(obj));
			System.out.println();
		}
	}
}

 Eg3:设置Book类的字段的值

Field.set(Object【指定的实例对象】, Object【待修改的值】)

public static void main(String[] args) {

	Class clazz = Book.class;//1.获取Class类的对象	
	try {
	
		Object obj = clazz.newInstance();
		
		Field field = clazz.getDeclaredField("name");
		field.set(obj, "红楼梦");
		System.out.println(obj);
		
	} catch (Exception e) {
		//……
	} 
}

 八、Method类  访问方法

与Constructor类和Field类似,基本方法如下:

一个Method对象 包含一个方法的所有信息:

getModifiers()   返回方法的修饰符 [intl类型]

getName()  返回方法名称

getReturnType()  返回方法返回值类型【一个Class实例】

getParameterTypes() 返回方法的参数类型【一个Class数组】

public static void main(String[] args) {
	Class clazz = Book.class;
	Method[] methods = clazz.getMethods();
	for(Method m: methods) {
		System.out.println("方法的名称:"+m.getName());
		System.out.println("方法的访问修饰符:"+Modifier.toString(m.getModifiers()));
		System.out.println("方法的返回值类型:"+m.getReturnType());
	
		Parameter[] params =m.getParameters();
		for(Parameter p :params) {
			System.out.println("参数名称:"+p.getName());
			System.out.println("参数类型:"+p.getType());
			System.out.println();
		}		
		System.out.println();		
	}	
}

Eg1:调用方法

调用某个对象的方法:Object invoke(Object instance,Object..·parameters)

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

	Class clazz = Base.class;
	Object obj = clazz.newInstance();

	//按照方法名称和"参数类型"获取Method方法对象
	//无参方法
//	Method method = clazz.getMethod("create");
//	int result = (int)method.invoke(obj);

	//有参方法
	Method method = clazz.getMethod("create",int.class);
	int result = (int)method.invoke(obj, 100);
	System.out.println(result);	
}

Eg2:调用静态方法

           获取某个指定数字的位数

public static void main(String[] args) throws Exception {
	//方式1:
	System.out.println((int)Math.log10(1234567765)+1);
	
    //方式2:反射调用
	Class clazz = Math.class;
	Method method = clazz.getMethod("log10", double.class);
	
	int size = Double.valueOf((double)method.invoke(null, 1234567765)).intValue()+1;
	System.out.println(size);		
}

Eg3:多态问题【目标对象决定调用方法的归处】

public class Demo {
	public static void main(String[] args) throws Exception{
		//获取Person的hello方法:
		Method h = Person.class.getMethod("hello");
		//对Student实例调用hello方法:
		h.invoke(new Student());
   }
}
class Person {
	public void hello() {
		System.out.println("Person:hello");
	}
}
class Student extends Perrson {
	public void hello() {
		System.out.println("Student:hello");
	}
}

结果:

 小结:通过反射调用方法时,仍然遵循多态原则 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值