黑马程序员——基础加强反射

-----------android培训java培训、java学习型技术博客、期待与您交流!------------


JAVA-Reflect

1.Class类的使用:There is a class name is Class.

在面向对象的世界里,万事万物皆对象,但是在java语言中静态成员、普通数据类型不是面向对象的(如int a=5,静态的东西它不是属于某个对象的它是属于某个类的)

我们写的每一个类是java.lang.Class类的实例对象(如String类、Integer类都属于Class类的实例对象)

<span style="font-size:18px;color:#FF0000;"><span style="color:#000000;">/**
 * 
 */
package haHa;

/**
 * @author dell1
 *
 */
public class ClassDemo {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO 自动生成的方法存根
		//Foo类的对象如何表示
		Foo foo1=new Foo();
		//任何一个类都是Class类的实例对象,有三种表示方式
		//第一种表达方式 ----->实际上告诉我们任何一个类都有一个隐含的静态成员变量class
		Class c1=Foo.class;
		
		//第二种表达方式:通过该类的对象的getClass方法
		Class c2=foo1.getClass();
		//官网表示了c1、c2表示了Foo类的类类型(class type)
		//类是Class的对象这个对象被称为该类的类类型。
		//不管c1或c2都代表了Foo类的类类型,一个类只可能是Class类的体格实例对象
		System.out.println(c1==c2);
		//第三种表达方式
		Class c3=null;
		try {
			c3=Class.forName("cn.heima.day9-13.src.haHa.Foo");
		} catch (ClassNotFoundException e) {
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		}
		System.out.println(c2==c3);//可以看到c2、c3也是表示的同一个类类型。
		//可以通过类的类型创建该类对象实例--->通过c1、c2创建该类的实例对象
		try {
			Foo foo=(Foo)c1.newInstance();//因为c1是Class类类型的变量所以这里需要强制类型转换
			//需要有无参数的构造方法,所以Foo类中一定要有无参数的构造方法
			foo.print();
		} catch (InstantiationException e) {
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		}
		
	}

}
class Foo{
	void print(){
		System.out.println("foo");
	}
}</span></span>
Class.forName("类的全称")

1》不仅表示了类的类类型,还代表了动态加载类

2》编译时刻加载类是静态加载类、运行时刻记载类是动态加载类

(new创建的对象是静态加载类,在编译时刻就需要加载所有可能使用到的类)

<span style="font-size:18px;color:#FF0000;"><span style="color:#000000;">class  OfferAbleDemo1
{
	public static void main(String[] args) 
	{
		if("Word".equals(args[0]))
		{
			Word w=new Word();
			w.start();
		}
		if("Excel".equals(args[0]))
		{
			Excel e=new Excel();
			e.start();
		}
	}
}
比如说这一个程序因为没有创建Word类和Excel类而无法编译通过
</span></span>
通过动态加载类可以解决该问题(动态加载类在运行时加载)
<span style="font-size:18px;color:#FF0000;"><span style="color:#000000;">class  OfferAbleDemo1
{
	public static void main(String[] args) throws Exception
	{
		args=new String[1];
		args[0]="Word";
		if("Word".equals(args[0]))
		{
			Class w=Class.forName("Word");//动态加载,在运行时如果需要Word类再加载进来
			//创建对象
			Word w1=(Word)w.newInstance();//强制类型转换,建立Word的实例对象
			w1.start();
		}
		if("Excel".equals(args[0]))
		{
			Class e=Class.forName("Excel");
			Excel e1=(Excel)e.newInstance();
			e1.start();
		}
	}
}
//注:虚拟机在找类的时候是先通过windows系统的系统环境变量path来找的,如果path设置的是先找当前目录(并且Word
和Excel和OfficeAble在同一个目录下那么在forName()的括号内可以直接写类的名字否者不可以,只能写完整路径)
</span></span>
思想升级:如果想要执行的方法不确定那么可以这样
<span style="font-size:18px;color:#FF0000;"><span style="color:#000000;">class  OfferAbleDemo1
{
	public static void main(String[] args) throws Exception
	{
		//args=new String[1];
		//args[0]="Word";
//		if("Word".equals(args[0]))
//		{
//			Class w=Class.forName("Word");
//			//创建对象
//			office of=(office)w.newInstance();
//			of.start();
//		}
//		if("Excel".equals(args[0]))
//		{
//			Class e=Class.forName("Excel");
//			office of1=(office)e.newInstance();
//			of1.start();
//		}
		Class w=Class.forName(args[0]);//当主函数被虚拟机调用时,传入的是new args[0]
		office of=(office)w.newInstance();//抽象类不可以用new构造器创建实例对象,但这里是反射啊,哈哈
		of.start();
	}
}
</span></span>
<span style="font-size:18px;color:#FF0000;"><span style="color:#000000;">class Word implements office
{
	public void start(){
		System.out.println("Word--------Start");
	}
}
</span></span>

class Excel implements office
{
	public void start(){
		System.out.println("Excel------------start");
	}
}

interface  office 
{
	public void start();
}
备注:在运行时用到了主函数传值的知识


2.方法的反射:获取某个类的方法的信息

package haHa;


import java.lang.reflect.*;

/*
 * 获取一个类中的方法的信息
 * 类中的方法包括:1、构造方法getConstructors() 
 * 			  2、成员方法getDeclaredMethods()(公共、保护、私有、默认的方法都可以得到) 、getMethods()(仅可得到公共的,包括继承的方法)
 * 			  3、方法所属的类getDeclaringClass()
 * 			  4、方法中的形参类型getGenericParameterTypes()
 * 			  5、方法的还回值结果类型getGenericReturnType() \getParameterTypes()
 * 			  6、方法名称getName()
 * 			  
 * */

public class ClassDemo1 {

	public static void main(String[] args) {
		// TODO 自动生成的方法存根
		Methods("abc");
	}
	public static void Methods(Object obj){
		//的到obj对象所属的类
		Class c=obj.getClass();
		//得到该类的所有公共构造方法
		Constructor[] con=c.getConstructors();
		for(int m=0;m<con.length;m++){
			Class[] c2=con[m].getParameterTypes();
			System.out.print(con[m].getName());
			for(int n=0;n<c2.length;n++){
				System.out.print("(");
				System.out.print(c2[n].getName());
			}
			System.out.println(")");
		}
		//得到该类的所有成员方法
		Method[] m=c.getMethods();
		//得到该每个方法中的返回值类型
		for(int i=0;i<m.length;i++){
			Class[] c1=m[i].getParameterTypes();
			System.out.print(m[i].getGenericReturnType()+"----");//方法的返回值类型
			System.out.print(m[i].getName());//方法的名字
			for(int j=0;j<c1.length;j++){
				System.out.print("(");
				System.out.print(c1[j].getName());
			}
			System.out.println(")");
			
		}
	}

}
3.构造的反射

学习方法同上

4.Java类加载机制

学习方法同上

注:java中集合的泛型,是防止错误输入的,只在编译阶段有效,绕过编译就无效了

验证:我们可以通过方法的反射来操作,绕过编译

编译之后集合是去泛型化的。

/**
 * 
 */
package haHa;

import java.lang.reflect.Method;
import java.util.ArrayList;

/**
 * @author dell1
 * 泛型简单应用操作练习
 *
 */
public class Demo002 {

	/**
	 * @param args
	 */
	public static void main(String[] args) throws Exception {
		// TODO 自动生成的方法存根
		ArrayList<String> arr=new ArrayList<String>();//这个集合arr是带泛型的也就是只能向集合内添加String类型的元素
		arr.add("haha");
		//我想在集合arr中添加数字0
		Method m=arr.getClass().getMethod("add",Object.class);//用反射的方式获得ArrayList的add方法
		m.invoke(arr,20);/*向arr集合中添加int类型的元素*/
		System.out.println(arr);
	}

}


/**
 * 
 */
package haHa;

import java.lang.reflect.*;

/**
 * @author dell1
 * 作业:将任意一个对象中的所有String类型的成员变量所对应的字符串内容中的"b"改成"a"。
 * 知识点一:一个类中只要是声明的(定义的)字段都可以用getDeclaredField(String name) 和getDeclaredFields() 访问到
 * 		其中变量name为要访问的字段的字符串类型的简称(比如他要访问Integer类中的字段int,那么name就要写成int,然后返回值类型为Field
 * 		然后可以通过Field提供的一些方法来得到该字段的被实例化对象赋予的值,得到该字段的所属类型)
 * 知识点二:要仅要得到一个类中被(public修饰)的字段可以用getField(String name) 和getFields(String name)来得到



 *
 */
public class RefectDemo3 {

	/**
	 * @param args
	 */
	public static void main(String[] args)throws Exception {
		// TODO 自动生成的方法存根
		Class<A> aclass=A.class;//创建A的类类型
		//A a=(A)aclass.newInstance();//通过A的无参数构造方法来创建实例对象
		A a1=(A)aclass.getConstructor(String.class,int.class).newInstance("sheng",24);//用指定构造方法创建对象
		//验证a1对象是否创建成功
		a1.getAge();
		a1.getName();//从输出结果上看这种创建方法是可以的
		//获取A类的所有字段
		Field af=aclass.getDeclaredField("a");
		//获取字段的所属类型
		String str1=af.getGenericType().toString();
		System.out.println(str1);
		//获取A类中的所有公共声明的字段
		Field af1=aclass.getField("str");//将str变量的权限修饰符分别改为public、默认、private发现只有public不报错,说明该方法只能访问到被公开的字段
		String str2=af1.getGenericType().toString();
		System.out.println(str2);
		//将指定字段中的所有字符'a'替换为字符'b';用反射的方法
		Field af2=aclass.getDeclaredField("str2");
		Type t1=af2.getGenericType();
		System.out.println(t1+"---------------"+"str2");
		a1.set("aaa");
		af2.setAccessible(true);//将此对象的 accessible 标志设置为指示的布尔值。值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。值为 false 则指示反射的对象应该实施 Java 语言访问检查。 
		boolean b=af2.isAccessible();//获得此对象Accessbille标识
		System.out.println(b);
		System.out.println(af2.get(a1).toString());
		System.out.println("=============看了视频以后的从新理解=============");
		//创建一个Afield对象
		Afield field=new Afield(3,5);
		//获取Afield类中的字段
		Field fieldy=field.getClass().getField("y");
		//获取成员变量的被实例对象赋予的值(因为该成员变量对应的对象有很多,如果获取获取的是哪个对象给它赋的值呢?所以要指明)
		Object o1=fieldy.get(field);
		//将值打印出来
		System.out.println(o1);//可以看到结果为5
		//如果对x变量也进行上诉操作将如何呢?
		
		Field fieldx=field.getClass().getDeclaredField("x");//先看见变量x以后才能对其操作刚才getField()就获取不到x所以没法进行后续操作
		//暴力更改(不让虚拟检查)
		fieldx.setAccessible(true);//分析:fieldx是Field类型的一个实例对象,该对象用来操作所有类的成员变量的(也就是说每个类的成员变量都是该所映射)
		Object o2=fieldy.get(field);
		//将值打印出来
		System.out.println(o2);//可以看到报出java.lang.NoSuchFieldException异常,这是因为x成员变量是被private修饰的,无法被外界访问
		//既然y是被public修饰的那么能否对他的值进行操作呢
		//1.先获取该字段是什么类型的
		Class c1=fieldy.getType();
		System.out.println(c1.getName());//从结果上可以看到该字段属于int类型
		//2.用相应的赋值方法给变量改值
		fieldy.setInt(field, 3);
		System.out.println(fieldy.get(field));//可以看到y的值已经改为了3
		//那是否也可以改x的值呢?
		//1.先获取该字段是什么类型的
				//Field fieldx=field.getClass().getField("x");
				
				Class c2=fieldx.getType();
				System.out.println(c2.getName());//从结果上可以看到该字段属于int类型
				//2.用相应的赋值方法给变量改值
				fieldx.setInt(field, 4);
				System.out.println(fieldx.get(field));//java.lang.NoSuchFieldException可以看到还是这个异常,表明我就不让你看到你如何改,想改看玩笑
		System.out.println("+++++++++++++++++++作业:将任意一个对象中的所有String类型的成员变量所对应的字符串内容中的b改成a。+++++++++++");
		//第一步获取该类的所有String类型的字段
		Field[] f1=field.getClass().getDeclaredFields();
		//用for循环进行遍历
		
		for(Field x:f1){
			//判断x是否为String类型
			if(x.getType()==String.class){
			String s1=(String)x.get(field);
			String s2=s1.replace('a','b');
			//打印出来
			System.out.println(s2);
			}
		}
				
		
	}
	

}
class A{
	private String str2;
	//setAccessible(true);
	 public String str;
	public int a;
	A(){}
	public A(String str, int a) {
		super();
		this.str = str;
		this.a = a;
	}
	void getName(){
		System.out.println(str);
	}
	void getAge(){
		System.out.println(a);
	}
	String set(String str2){
		return this.str2=str2;
	}
	 
	
	
}
class Afield{
	public String stra="aabb1";
	public String strb="bbaa2";
	public String strc="aabbcc";
	private int x;
	public int y;
	Afield(int x,int y){
		this.x=x;
		this.y=y;
	}
	
}

字段的常用方法:

toString ()返回一个描述此 Field 的字符串。

set(Object  obj,Object  value)将指定对象变量上此 Field 对象表示的字段设置为指定的新值。

getType()返回一个 Class 对象,它标识了此 Field 对象所表示字段的声明类型。

getName()返回此 Field 对象表示的字段的名称。

注:如果此字段是私有的那么如果要想用set(Object  obj,Object  value)设置此字段的字那么就要用setAccessible(Boolean boolean);方法(将此对象的 accessible 标志设置为指示的布尔值。值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。值为 false 则指示反射的对象应该实施 Java 语言访问检查。)

package com.it.heima;
/*3、 写一个方法,此方法可将obj对象中名为propertyName的属性的值设置为value.   

public void setProperty(Object obj, String propertyName, Object value){   

}*/
import java.lang.reflect.*;

public class Test3 {

	public static void main(String[] args)throws Exception {
		// TODO 自动生成的方法存根
		System.out.println("=============更改前=============");
		Person p=new Person();
		Test3.setProperty(p, "propertyName", "value");
		System.out.println("==============更改后=============");
		System.out.println(p.getValue());	
	}
	static void setProperty(Object obj, String propertyName, Object value){  
		 Class c=obj.getClass();//获得对象的类类型
		 
		 Field f=null;
		 try {
			f=c.getDeclaredField(propertyName);
			f.setAccessible(true);
			f.set(obj, value);
			
		} catch (NoSuchFieldException e) {
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		} catch (SecurityException e) {
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		}
		 

	}

}
class Person{
	
	private String propertyName;
	Person(){
		
		System.out.println(propertyName);
	}
	public String getValue(){
		return propertyName;
	}
	
}



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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值