反射以及JAVA 常用的但是就像我一样的菜鸟不懂的玩意

反射1


反射的基石就是Class类,C是一个大写的字母,Class 出现在1.2版本。java程序中的各个java类属于同一个事务,描述这类事务的java 类名就是Class。

用一描述java类的单词就是Class

java.lang
类 Class<T>

java.lang.Object
  继承者 java.lang.Class<T>

类型参数:
T - 由此 Class 对象建模的类的类型。例如, String.class 的类型是 Class<String>。如果将被建模的类未知,则使用 Class<?>
所有已实现的接口:
Serializable, AnnotatedElement, GenericDeclaration, Type

public final class Class<T>
   
   
    
    extends 
    
    Object
   
   
   
   
    
    implements 
    
    Serializable, 
    
    GenericDeclaration, 
    
    Type, 
    
    AnnotatedElement
   
   

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

Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的。

以下示例使用 Class 对象来显示对象的类名:

     void printClassName(Object obj) {
         System.out.println("The class of " + obj +
                            " is " + obj.getClass().getName());
     }
 

还可以使用一个类字面值(JLS Section 15.8.2)来获取指定类型(或 void)的 Class 对象。例如:

     System.out.println("The name of class Foo is: "+Foo.class.getName());
 
从以下版本开始:
JDK1.0
另请参见:
ClassLoader.defineClass(byte[], int, int), 序列化表格

方法摘要
<U> Class<? extends U>
asSubclass(Class<U> clazz)
强制转换该 Class 对象,以表示指定的 class 对象所表示的类的一个子类。
Tcast(Object obj)
将一个对象强制转换成此 Class 对象所表示的类或接口。
booleandesiredAssertionStatus()
如果要在调用此方法时将要初始化该类,则返回将分配给该类的断言状态。
static Class<?>forName(String className)
返回与带有给定字符串名的类或接口相关联的 Class 对象。
static Class<?>forName(String name, boolean initialize,ClassLoader loader)
使用给定的类加载器,返回与带有给定字符串名的类或接口相关联的 Class 对象。
<A extends Annotation>
A
getAnnotation(Class<A> annotationClass)
如果存在该元素的指定类型的注释,则返回这些注释,否则返回 null。
Annotation[]getAnnotations()
返回此元素上存在的所有注释。
StringgetCanonicalName()
返回 Java Language Specification 中所定义的底层类的规范化名称。
Class<?>[]getClasses()
返回一个包含某些 Class 对象的数组,这些对象表示属于此 Class 对象所表示的类的成员的所有公共类和接口。
ClassLoadergetClassLoader()
返回该类的类加载器。
Class<?>getComponentType()
返回表示数组组件类型的 Class
Constructor<T>getConstructor(Class<?>... parameterTypes)
返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法。
Constructor<?>[]getConstructors()
返回一个包含某些 Constructor 对象的数组,这些对象反映此 Class 对象所表示的类的所有公共构造方法。
Annotation[]getDeclaredAnnotations()
返回直接存在于此元素上的所有注释。
Class<?>[]getDeclaredClasses()
返回 Class 对象的一个数组,这些对象反映声明为此 Class 对象所表示的类的成员的所有类和接口。
Constructor<T>getDeclaredConstructor(Class<?>... parameterTypes)
返回一个 Constructor 对象,该对象反映此 Class 对象所表示的类或接口的指定构造方法。
Constructor<?>[]getDeclaredConstructors()
返回 Constructor 对象的一个数组,这些对象反映此 Class 对象表示的类声明的所有构造方法。
FieldgetDeclaredField(String name)
返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段。
Field[]getDeclaredFields()
返回 Field 对象的一个数组,这些对象反映此 Class 对象所表示的类或接口所声明的所有字段。
MethodgetDeclaredMethod(String name,Class<?>... parameterTypes)
返回一个 Method 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明方法。
Method[]getDeclaredMethods()
返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。
Class<?>getDeclaringClass()
如果此 Class 对象所表示的类或接口是另一个类的成员,则返回的 Class 对象表示该对象的声明类。
Class<?>getEnclosingClass()
返回底层类的立即封闭类。
Constructor<?>getEnclosingConstructor()
如果该 Class 对象表示构造方法中的一个本地或匿名类,则返回 Constructor 对象,它表示底层类的立即封闭构造方法。
MethodgetEnclosingMethod()
如果此 Class 对象表示某一方法中的一个本地或匿名类,则返回 Method 对象,它表示底层类的立即封闭方法。
T[]getEnumConstants()
如果此 Class 对象不表示枚举类型,则返回枚举类的元素或 null。
FieldgetField(String name)
返回一个 Field 对象,它反映此 Class 对象所表示的类或接口的指定公共成员字段。
Field[]getFields()
返回一个包含某些 Field 对象的数组,这些对象反映此 Class 对象所表示的类或接口的所有可访问公共字段。
Type[]getGenericInterfaces()
返回表示某些接口的 Type,这些接口由此对象所表示的类或接口直接实现。
TypegetGenericSuperclass()
返回表示此 Class 所表示的实体(类、接口、基本类型或 void)的直接超类的 Type
Class<?>[]getInterfaces()
确定此对象所表示的类或接口实现的接口。
MethodgetMethod(String name,Class<?>... parameterTypes)
返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。
Method[]getMethods()
返回一个包含某些 Method 对象的数组,这些对象反映此 Class 对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共member 方法。
intgetModifiers()
返回此类或接口以整数编码的 Java 语言修饰符。
StringgetName()
String 的形式返回此 Class 对象所表示的实体(类、接口、数组类、基本类型或 void)名称。
PackagegetPackage()
获取此类的包。
ProtectionDomaingetProtectionDomain()
返回该类的 ProtectionDomain
URLgetResource(String name)
查找带有给定名称的资源。
InputStreamgetResourceAsStream(String name)
查找具有给定名称的资源。
Object[]getSigners()
获取此类的标记。
StringgetSimpleName()
返回源代码中给出的底层类的简称。
Class<? superT>getSuperclass()
返回表示此 Class 所表示的实体(类、接口、基本类型或 void)的超类的 Class
TypeVariable<Class<T>>[]getTypeParameters()
按声明顺序返回 TypeVariable 对象的一个数组,这些对象表示用此 GenericDeclaration 对象所表示的常规声明来声明的类型变量。
booleanisAnnotation()
如果此 Class 对象表示一个注释类型则返回 true。
booleanisAnnotationPresent(Class<? extendsAnnotation> annotationClass)
如果指定类型的注释存在于此元素上,则返回 true,否则返回 false。
booleanisAnonymousClass()
当且仅当底层类是匿名类时返回 true
booleanisArray()
判定此 Class 对象是否表示一个数组类。
booleanisAssignableFrom(Class<?> cls)
判定此 Class 对象所表示的类或接口与指定的 Class 参数所表示的类或接口是否相同,或是否是其超类或超接口。
booleanisEnum()
当且仅当该类声明为源代码中的枚举时返回 true。
booleanisInstance(Object obj)
判定指定的 Object 是否与此 Class 所表示的对象赋值兼容。
booleanisInterface()
判定指定的 Class 对象是否表示一个接口类型。
booleanisLocalClass()
当且仅当底层类是本地类时返回 true
booleanisMemberClass()
当且仅当底层类是成员类时返回 true
booleanisPrimitive()
判定指定的 Class 对象是否表示一个基本类型。
booleanisSynthetic()
如果此类是复合类,则返回 true,否则 false
TnewInstance()
创建此 Class 对象所表示的类的一个新实例。
StringtoString()
将对象转换为字符串。
从类 java.lang.Object 继承的方法
clone,equals,finalize,getClass,hashCode,notify,notifyAll,wait,wait,wait


Class cls1 = Data.class/字节码1;

class cls2 = 字节码2;

字节码就是在内存中的类的实例化的二进制数。每一份字节码代表字节码的实例对象。

对象点getClass(); 就能得到对象的字节码。

Class.forName("java.lang.String");制定一个类的完整的名称。得到String 类的字节码。作用就是返回字节码,

得到字节码的方法三种.

方法1 类名点class 例如

System.class

方法2 对象点getClass();

new Data().getClass();

方法3 Class点forName("类名")

Class.forName("java.util.Date");

做反射的时候多用第三种。

数组的话就用这种方法

        sop(int[].class.isArray());//true; isArray 就是检查是不是数组。

在写程序的时候第三种的括号里写变量的名字。

八个基本类型有着相对的class 对象,void也有class 对象


反射2


反射就是把java类中的各种成分映射成相应的java类,例如,一个java类中用一个Class类的对象来表示,一个类中的组成部分,成员变量,方法,构造方法,包等等信息也用一个个的java类来表示,就像汽车是一个类,汽车中的发动机也表示一个个的类。表示java类的Class类显然要提供一系列的方法,来获取其中的变量,方法,构造方法,修饰符,包等信息。

假设Sytem内有很多方法,这些方法对应的是方法1 ,方法2 等,但是他们都是方法。我们要利用反射的方法得到各个成分所对应的对象。

Constructor类代表某个类中的构造方法。

得到某个类所有的构造方法:

例子Contructor[ ] constructors = Class.forName("java.lang.String").getConstructors();

得到一个构造方法

Constructor constructor = Class.forName("java.lang.String").getConstructor(StringBuffer.class);

or

Constructor constructor1 = String.class.getConstructor(StringBuffer.class);//获得方法时要用到的类型和定义的类型应该一致,在这里就都应该是StringBuffer

String str2 = (String)constructor1.newInstance(new StringBuffer("abc"));可以加String的强转是constructor1初始化的时候并没有告知是什么类型的所以要强转

String str2 = (String)constructor1.newInstance("abc");不可以。

//调用获得的方法时要用到上面相同类型的实例对象。

class.netInstance()方法:

例子:String obj =(String)Class.forName("java.lang.String").newInstance();

该方法内部先得到默认的构造方法,然后用该构造方法创建实例对象,该方法内的具体代码是怎么写的呢,用到了缓存机制来保存默认构造方法的实例对象。

实例:反射的应用

package 高新技术;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class ReflectTest {

	public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
		
		
		String str1 = "abc";
		Class cls1 = str1.getClass();
		Class cls2 = String.class;
		Class cls3 = Class.forName("java.lang.String");
		/*	
		sop("cls1 == cls2:"+(cls1 == cls2) );
		sop("cls1 == cls3:"+(cls1 == cls3) );
		sop("cls2 == cls3:"+(cls2 == cls3) );
			cls1 == cls2:true
			cls1 == cls3:true
			cls2 == cls3:true
		 */
		//基本类型的字节码就是基本类型的
		
		/*sop(cls1.isPrimitive());//false 
		sop(int.class.isPrimitive());//true
		sop(int.class == Integer.class);//false
		sop(int.class == Integer.TYPE);//true TYPE就是为了让包装类返回原始类的基本类型字节码
		sop(int[].class.isPrimitive());//false
		sop(int[].class.isArray());//true; isArray 就是检查是不是数组。
		*/
		//new String(new StringBuffer("abc"));//如何利用反射的方式得到同样的效果
	Constructor constructor1 = String.class.getConstructor(StringBuffer.class);//我们可以在括号里写一个参数或者多个参数这是1。5之后的新特性得到的是构造函数的
	String str2 = (String)constructor1.newInstance(new StringBuffer("abc"));//输出c
//String str2 = (String)constructor1.newInstance("abc");//出现java.lang.IllegalArgumentException错误因为前面构造constructor 的时候写的是SB

	System.out.println(str2.charAt(2));
	//为啥加(String)在constructor 之前呢,因为编译的时候不把 String.class.getConstructor(StringBuffer.class)赋值给左边的constructor1
	//在运行时候到了第二行的时候才把右边的赋值给constructor
	}
	public static void sop(Object obj)
	{
		System.out.println(obj);
	}
}


Field类


Field类在反射中代表某个类中的一个成员变量。也许是int 也许是string 也许是long 不确定

field的方法

1field.getType); 返回基本单位和包装类的class类。

2field.get(obj); 返回类obj中的字段变量的实例

3field.set(obj,value).返回类型空,给obj中的field 安装成value。

ReflectPoint pt1 = new ReflectPoint(3,5);
	Field fieldY = pt1.getClass().getField("y");
	//fieldY 的值不是5 fieldY 代表变量不是变量的值
	System.out.println(fieldY.get(pt1));//输出得到5 fieldY.get(pt1)才能得到pt1 中的那个y的数值写pt2 的话有pt2的值
	
	//Field fieldX = pt1.getClass().getField("x");// java.lang.NoSuchFieldException: 因为x在Reflect中是私有的不是共有的
	Field fieldX = pt1.getClass().getDeclaredField("x");//这样不报错但是不能直接打印,就是拿不到所以要使用暴力反射
	
	fieldX.setAccessible(true);//这就是暴力反射的行为
	
	System.out.println(fieldX.get(pt1));
输出结果:

fieldY.get(pt1): 5
fieldX.get(pt1): 3

实例: 现在一个对象对象中有多个String 类型的成员变量我们想把这些成员变量中的“b” 该成“a”怎么办。

/*
	 * 换一个类中的所有的字符串中的某一个字母
	 * 1 创建目标类
	 * 2创建改变方法参数是目标类
	 * 	取出所有的字符串的field类
	 * 	便利所有的field,把每一个field 变成字符串
	 * 	把获取得到的每一个字符串改变
	 * 	使用field set方法把新换的字符串放进目标类中
	 */
	ReflectPoint pt1 = new ReflectPoint(3,5);
	changeStringValue(pt1);
	sop(pt1);
	}
	private static void changeStringValue(Object obj) throws IllegalArgumentException, IllegalAccessException
	{
		Field[] fields = obj.getClass().getFields();//得到所有传进来的类的String的字节码
		for(Field field: fields)//field 是一个字段 代表所有的类中的成员变量
		{
			if(field.getType() == String.class)//双等号因为是同一个字节码,作用是判断是不是String 类
			{
				//加入你是String 的字节码我要去的你的值
				String oldValue = (String)field.get(obj);
				String newValue = oldValue.replace('b','a');//替换所有的旧的 前边的是旧的
				field.set(obj,newValue);
			}
		}
	}
if(field.getType() == String.class)//双等号因为是同一个字节码,作用是判断是不是String 类,field其实还可以是
fi: public int 高新技术.ReflectPoint.y
fi: public java.lang.String 高新技术.ReflectPoint.str1
fi: public java.lang.String 高新技术.ReflectPoint.str2
fi: public java.lang.String 高新技术.ReflectPoint.str3

所以要判断是不是String 类,如果是的话就替换如果不是的话你不能替换。


Method 类


代表类里方法的字节码,不能用method 调用方法,method 只能等于某种特定的方法。

我们可以在str1点charAt 调用方法,方法和对象其实没关系,先得到方法再根据某个对象调用方法。

Jdk1.4的话不能写可变方法,只能写数组,1.5 就能

Method 方法

invoke(object obj ,object... args)

package 高新技术;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class 调用字符串中的某个方法演示Method类 {
	/*
	 *  str1 = abc 上边有
	 *  使用反射的方法利用Method 类获取一个字符串的String类的方法charAt 来获取 第一个字母
	 *  
	 */
	public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {

		
		String str1  = "abc";
		Method mo = String.class.getMethod("charAt",int.class);
	        sop(mo.invoke(str1, 1));
	        char ch = (char) mo.invoke(str1, 1);
	}
	
	public static void sop(Object obj)
	{
		System.out.println(obj);
	}
}

输出结果:

b

实例2
		String str = "abc";
		Method mo = String.class.getMethod("compareTo",String.class);

		int bp = (int) mo.invoke(str, "b");
		int ap = (int) mo.invoke(str, "a");

		//"b" 是-1 = 0 - 1  (this.charAt(k)-anotherString.charAt(k))因为b的位置和abc中b第一次出现的位置不同
		//"a" 是   2 = 3-1  this.length()-anotherString.length() 因为a 的位置都是在0处所以是长度相减
		sop(bp);
		sop(ap);

输出结果:

-1
2

Method 是类中的所有的方法的字节码,在getMehod后面加上锚定方法的名字和次方法的返回类型之后调用method 的实例化对象

此对象使用invoke方法在其中添加该类的实例化对象和该方法的实参就能起到使用该实例化对象的锚定法的效果。

invoke

public Object invoke(Object obj,
                     Object... args)
              throws IllegalAccessException,
                     IllegalArgumentException,
                     InvocationTargetException
对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。个别参数被自动解包,以便与基本形参相匹配,基本参数和引用参数都随需服从方法调用转换。

如果底层方法是静态的,那么可以忽略指定的 obj 参数。该参数可以为 null。

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

如果底层方法是实例方法,则使用动态方法查找来调用它,这一点记录在 Java Language Specification, Second Edition 的第 15.12.4.4 节中;在发生基于目标对象的运行时类型的重写时更应该这样做。

如果底层方法是静态的,并且尚未初始化声明此方法的类,则会将其初始化。

如果方法正常完成,则将该方法返回的值返回给调用者;如果该值为基本类型,则首先适当地将其包装在对象中。但是,如果该值的类型为一组基本类型,则数组元素 被包装在对象中;换句话说,将返回基本类型的数组。如果底层方法返回类型为 void,则该调用返回 null。


参数:
obj - 从中调用底层方法的对象
args - 用于方法调用的参数
返回:
使用参数 argsobj 上指派该对象所表示方法的结果
抛出:
IllegalAccessException - 如果此 Method 对象强制执行 Java 语言访问控制,并且底层方法是不可访问的。
IllegalArgumentException - 如果该方法是实例方法,且指定对象参数不是声明底层方法的类或接口(或其中的子类或实现程序)的实例;如果实参和形参的数量不相同;如果基本参数的解包转换失败;如果在解包后,无法通过方法调用转换将参数值转换为相应的形参类型。
InvocationTargetException - 如果底层方法抛出异常。
NullPointerException - 如果指定对象为 null,且该方法是一个实例方法。
ExceptionInInitializerError - 如果由此方法引起的初始化失败。


用反射的方法获取main中的函数

/*
	 * 用反射的方法调用main函数里的内容
	 * 
	 */
	//TestArguments.main(new String[]{"111","222","333"});//用普通方式调出来main的内容函数
	String startingClassName = args[0];
	Method mainMethod = Class.forName(startingClassName).getMethod("main", String[].class);
	mainMethod.invoke(null,(Object)new String[]{"12","23","34"});//想办法把数组打包成一个object不让拆包
	
输出结果:

12
23
34

数组的反射

每一个数组的反射如果是相同的维度和相同的参数类型的话就是同一种Class 都是属于java.lang.Object

实例:需求如何利用数组的反射做出一个打印程序,如果不论传进去的是数组还是基本类型都能够全部打印

          int[] a1 = {1,2,3};
    
     int[] a2 = new int[4];
     int[] []a3= new int[2][3];
     String [] a4 = new String[4];
     sop(a1.getClass() == a2.getClass());
     sop(a1.getClass().getSuperclass().getName());
     printObject(a1);
     printObject("hello");

private static void printObject(Object obj) {
        //如果给出的是数组也要打印出来数组中的每一个元素,如果是一个也要打印出来
            //    首先判断是数组还是一个
        Class clazz = obj.getClass();
        if(clazz.isArray())
        {
            int len = Array.getLength(obj);
            for(int i=0;i<len;i++)
            {
                sop(Array.get(obj, i));
            }
        }
        else{
            sop(obj);
        }
    }


输出:

true
java.lang.Object
1
2
3
hello

首先数组a1 和a2 的长度维度一样所以输出的是true 都说是 Object 的子类

在制作输出数组的方法的时候先判断进入的是不是数组

obj.getClass();

然后判断此Class是不是数组类的class然后利用数组本身的方法

Array.get(array,int)来获取其中的元素然后依次打印。


集合的反射

ArrayList内放的变量的地址,HashSet放进去的时候先比较,如果相同就不放,如果不相同就放。

但是如果使用哈希集合的话会有可能产生内存泄露的问题比如

package 高新技术;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;

public class ReflectTest2 {

	public static void main(String[] args) {
		Collection collections = new HashSet();
	    ReflectPoint pt1 = new ReflectPoint(3,3);
	    ReflectPoint pt2 = new ReflectPoint(5,5);
	    ReflectPoint pt3 = new ReflectPoint(3,3);
	    collections.add(pt1);
	    collections.add(pt2);
	    collections.add(pt3);
	    collections.add(pt1);
	  //  pt1.y = 7;
	    collections.remove(pt1);
	    sop(collections.size());
	}
	public static void sop(Object obj)
	{
		System.out.println(obj);
	}
}
输出

1

如果把pt1.y =7 那行的注释删掉,的话输出就是2 ,因为此pt1的类本身的哈希值判定方法依赖其x和y值,而你已经把pt放入了集合中然后再改他们决定哈希值的x 和 y 的话,集合就不能去删除pt1因为他的哈希值变化了。

应用时候注意,如果对象的实例已经被放入了哈希集合中,不要再把判定哈希值的变量随意改动,以防止内存泄露。


框架


框架是在一个可以调用工具类的东西,框架在变程序之前就已经被编写好了,当程序开始编写的时候很多类和工具是当时为了特定的环境来编写的而框架就是为了适应和调配这些新编制的特定的工具和类的一种结构。

举个例子,一个框架也许是20年前编写的当时还不存在hashSet 这种工具,但是我为了预计将来会出现一种集合类名字叫做啥也不知道,我就比需编写一个框架来为将来可能出现的集合类的应用打好结构。这种东西就是框架。不然的话程序员在每次编写程序的时候都要设计一种新的框架就太麻烦了

例子:我现在应用框架结构去调用hashSet

1 制作配置文件,配置文件就是一个类的名字,然后客户可以自定义自己的需要的类名然后加载到这些配置文件中,之后我们懂得java程序就能兼容客户自己的类或者将来最新出现的类。

首先在我的默认文件夹内,也就是java程序所在的文件包中点击右键,new,File 然后制作配置文件,文件的名字自定,里边的类名自定,将来用户使用这个配置文件的时候可以自行编制他们自己的类的名字。这里的HashSet 就是一个演示假定整个框架是在HashSet 被制作出来之前完成的然后我们现在要用HashSet 了,所以我们要加载HashSet 到配置文件中去。


config.properties 放在哪里比较重要,我们需要用getRealPath 得到整个原件在硬盘上的位置,你的配置文件要放在硬盘的位置上。

2 编写java程序

package 高新技术;
import java.io.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Properties;

public class ReflectTest2 {

	public static void main(String[] args) throws IOException, InstantiationException, IllegalAccessException, ClassNotFoundException {
		//InputStream ips =new FileInputStream("config.properties");//加载配置文件1
		//类加载器能加载类也能加载config 文件
		//InputStream ips =	ReflectTest2.class.getClassLoader().getResourceAsStream("高新技术\\config.properties");//通过类加载方式加载配置文件1
		InputStream ips =	ReflectTest2.class.getResourceAsStream("config.properties");//通过类加载方式加载配置文件2

		//获取property文件
		Properties props = new Properties();
		//获取配置文件中的自定义的config。properties 的文件输入流
		props.load(ips);
		//关闭文件输入流
		ips.close();
		//获取加载文件中的某个类的名字 这里边的className是配置文件中的设定的名字
		String className = props.getProperty("className");
		//获取改类的实例
		Collection collections = (Collection) Class.forName(className).newInstance();
		//使用该类,方法和效果等同于使用HashSet
	//	Collection collections = new HashSet();
	    ReflectPoint pt1 = new ReflectPoint(3,3);
	    ReflectPoint pt2 = new ReflectPoint(5,5);
	    ReflectPoint pt3 = new ReflectPoint(3,3);
	    collections.add(pt1);
	    collections.add(pt2);
	    collections.add(pt3);
	    collections.add(pt1);
	  
	    sop(collections.size());
	}
	public static void sop(Object obj)
	{
		System.out.println(obj);
	}
}

输出结果是

2

也就是说我们并没有实例化HashSet 但是我们确实使用了HashSet 去放置类ReflectPoint了。


实例2

package heimaceshiti;

import java.io.FileInputStream;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Properties;


/*
(1) 写一个Properties格式的配置文件,配置类的完整名称。
(2) 写一个程序,读取这个Properties配置文件,获得类的完整名称并加载这个类,用反射 的方式运行run方法。
*/
public class Test18 {
	public static void main(String[] args) throws Exception {

	InputStream ips =    Test18.class.getResourceAsStream("18Tconfig.properties");
		Properties prop = new Properties();//创建一个读取配置文件(键=值)的对象
		prop.load(ips);//把读取流添加到读取配置文件的对象中
		String className = prop.getProperty("className");//获取key对应的value值
		Class<?> clszz = Class.forName(className);//通过反射的方式获取该类的字节码文件
		Method method = clszz.getMethod("sp");//获取反射的方法
		method.invoke(clszz.newInstance());//运行方法。
	}
}
输出:

18 题的类的方法

上边的程序调用了一个叫做18Tconfig.properties的配置文件,该文件的内容是

className = heimaceshiti.Test18Class

这个配置文件调用了一个类叫做Test18Class

该类的程序在这

package heimaceshiti;

public class Test18Class {

	public static void main(String[] args) {}
	
	public static void sp(){
		System.out.println("18 题的类的方法");
	}
}

一个简单的小程序就是输出一段话。

注意点是,配置文件的类名之前只要写上一层的文件夹的名字就可以了。


内省


IntroSpector 内部视察的意思,就是对内部进行检查,主要用于对JAVA bean进行操作,javaBean 是一个特殊的java类,其方法的名称符合特定的规则

比如

getAge()是返回一个int 类型的

void setAge(int age)其中就要有一个参数,方法的名称都和服规则前面有get或者set

如果你的方法符合特定的规则,我把你当作javabean操作,如果我愿意的话。

如果一个Person有getAge 和 setAge 方法,则age 就是这个类的javabean 的属性,因为Age 中g是小写的所以 Age 中的A也比需是小写的所以age 才是属性。

一个类通过javabeen 处理有方便的方面,通过一些API 会简单很多,在开发中很多要使用javaBeen 比如两个模块之间如果想传递信息的话就把信息封装到jb 中,这种jb的实例对象称之为value object 简称VO 

实例1 通过内省的方式获取某个类的成员变量的值和改变其值。要求在不实例化这个类的情况下做到

package 高新技术;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class ItroSpectorTest {

	public static void main(String[] args) throws IntrospectionException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {

		//类叫做ReflectPoint 初始化实例pt1
		ReflectPoint pt1 = new ReflectPoint(3,5);
		//设置一个字符串其中的内容是x
		String propertyName = "x";
		Object retVal = getProperties(pt1,propertyName);
		
		//输出object
		System.out.println(retVal);//retVal = 3;
		
		Object value = 7;
		setProperties(pt1,propertyName,value);
		System.out.println(pt1.getX());

	}
	private static void setProperties(Object pt1,String propertyName,
			Object value) throws IntrospectionException, IllegalAccessException, IllegalArgumentException, InvocationTargetException
	{
		PropertyDescriptor pd2 = new PropertyDescriptor(propertyName,pt1.getClass());
				//获取一个写入的方法写入能够写入的值
				Method methodSetX = pd2.getWriteMethod();
				//调用写入方法,再使用invoke 填写类和值。
				methodSetX.invoke(pt1,7);
	}
	private static Object getProperties(Object pt1,String propertyName) throws IntrospectionException, IllegalAccessException, IllegalArgumentException, InvocationTargetException
	{
//				//实例化PropertyDescriptor 的实例 pd 构造中添加 字符串x 和 pt1的字节码
//				PropertyDescriptor pd = new PropertyDescriptor(propertyName,pt1.getClass());
//				//实例化 Method 叫做methodGetX 其中的是阅读器对value 的阅读器
//				Method methodGetX = pd.getReadMethod();
//				// 阅读器去阅读pt1中能够阅读的程序,使用invoke 去调用pt1返回一个Object 
//				Object retVal = methodGetX.invoke(pt1);
	//		Method methodGetX = pd.getReadMethod();
		
		//方法2..................................................................
			BeanInfo beanInfo = Introspector.getBeanInfo(pt1.getClass());
			PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
			Object retVal = null;
			for(PropertyDescriptor pd: pds)
			{
				Method methodGetX = pd.getReadMethod();
				retVal = methodGetX.invoke(pt1);
			}
		return retVal;
	}
}
输出结果:

5
7

实例2 有一个类其中还有多个成员变量通过内省的方式给变量赋值。

package heimaceshiti;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
public class Test24存在一个javaBean它包含以下几个属性1Booleanboolean {
//存在一个javabean,设置string,Boolean,double,integer的默认初值为www.itheima.com,true,0.01D,100
	public static void main(String[] args)throws Exception {
		// TODO Auto-generated method stub
	testBean tb = new testBean();
		Class<?> clazz = tb.getClass();
		Object bean = clazz.newInstance();
		BeanInfo beanInfo = Introspector.getBeanInfo(clazz);
		PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
		//遍历PropertyDescriptor 的所有的 实例
		for(PropertyDescriptor pd : propertyDescriptors ){
			//获区不是叫做class 的字符串,获取文件的字节码
			Object name = pd.getName();
			Object type = pd.getPropertyType();
			Method getMethod = pd.getReadMethod();
			Method setMethod = pd.getWriteMethod();
			if(!"class".equals(name)){
				if(setMethod!=null){
					//如果函数是boolean 类的话就 要变成true 
					if(type==Boolean.class||type==boolean.class){
						setMethod.invoke(bean, true);
					}
					if(type==String.class){
						setMethod.invoke(bean, "www.itheima.com");
					}
					if(type==int.class||type==Integer.class){
						setMethod.invoke(bean, 100);
					}
					if(type==Double.class||type==double.class){
						setMethod.invoke(bean, 0.01D);
					}
				}
				if(getMethod!=null){
					System.out.println(type+" "+name+"="+getMethod.invoke(bean, null));
				}
			}
		}
								
	}

}
class testBean{
	private boolean b;
	private String str;
	private Integer i;
	private double d;

	
	public boolean isB() {
		return b;
	}
	public void setB(boolean b) {
		this.b = b;
	}
	public String getStr() {
		return str;
	}
	public void setStr(String str) {
		this.str = str;
	}
	public Integer getI() {
		return i;
	}
	public void setI(Integer i) {
		this.i = i;
	}
	public double getD() {
		return d;
	}
	public void setD(double d) {
		this.d = d;
	}
}
输出结果:

boolean b=true
double d=0.01
class java.lang.Integer i=100
class java.lang.String str=www.itheima.com

从上面可以看出来内省可以在不知道类的成员变量的情况下直接给其赋值或者调用这些值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值