微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Java反射之深入理解

这篇文章主要介绍了Java反射机制的深入理解,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

目录

一、Java反射机制概述

二、理解Class类并获取Class实例

关于java.lang.class类的理解

三、通过反射创建对应的运行时类的对象(反射的应用1)

四、获取运行时类的完整结构(反射的应用2)

1.获取当前运行时类的属性结构

2.获取运行时类的方法结构

3.获取运行时类的其他结构

五、调用运行时类的指定结构(反射的应用3)

一、Java反射机制概述

//反射之前,对于Person的操作 @Test public void test1(){ //1.创建Person类的对象 Person p1 = new Person("Tom",12); //2.通过对象,调用其内部的属性方法 p1.age = 10; System.out.println(p1.toString());//Person{name='Tom', age=10} p1.show();//你好,我是一个人 //在Person类外部,不可以通过Person类的对象调用其内部私有结构。 //比如:name、showNation()以及私有的构造器 } //反射之后,对于Person的操作 @Test public void test2() throws Exception { Class clazz = Person.class; //1.通过反射,创建Person类的对象 Constructor cons = clazz.getConstructor(String.class, int.class); Object obj = cons.newInstance("Tom", 12); Person p = (Person)obj; System.out.println(p.toString()); //2.通过反射,调用对象指定的属性方法 //调用属性 Field age = clazz.getDeclaredField("age"); age.set(p,10); System.out.println(p.toString()); //调用方法 Method show = clazz.getDeclaredMethod("show"); show.invoke(p); System.out.println("*********************************************"); //通过反射,可以调用Person类的私有结构。比如:私有的构造器、方法属性 //调用私有的构造器 Constructor cons1 = clazz.getDeclaredConstructor(String.class); cons1.setAccessible(true); Person p1 =(Person) cons1.newInstance("Jerry"); System.out.println(p1); //调用私有的属性 Field name = clazz.getDeclaredField("name"); name.setAccessible(true); name.set(p1,"HanMeimei"); System.out.println(p1); //调用私有的方法 Method showNation = clazz.getDeclaredMethod("showNation", String.class); showNation.setAccessible(true); String nation = (String) showNation.invoke(p1, "中国");//相当于String nation = p1.showNation("中国") System.out.println(nation); }

疑问1:通过直接new的方式过反射的方式都可以调用公共的结构,开发中到底用那个?

建议:直接new的方式

什么时候后使用:反射的方式?

(反射的特征:动态性)在编译时不能确定要造那个类的对象时,使用反射。

疑问2:反射的机制与面向对象中的封装性是不是矛盾的?如何看待两个技术?

不矛盾。我的理解:开发中,封装性给我们的提示是,私有的属性不建议直接修改,而是通过get、set方法去做。反射给我们的的提示是,不建议直接修该的私有属性,如果非要修改,也是可以通过反射修改的。总结:反射解决的是能不能调用的问题,封装性解决的是建议我们如何掉的问题。

二、理解Class类并获取Class实例

关于java.lang.class类的理解

//获取Class的实力的方式(前三种方式需要掌握) @Test public void test3() throws ClassNotFoundException { //方式一:调用运行时类的属性:class Class clazz1 = Person.class; System.out.println(clazz1);//class zx_reflection.Person //方式二:通过运行时类的对象,调用getClass() Person p1 = new Person(); Class clazz2 = p1.getClass(); System.out.println(clazz2);//class zx_reflection.Person //方式三:调用Class的静态方法:forName(String classpath)(这种方式常用) Class clazz3 = Class.forName("class zx_reflection.Person"); System.out.println(clazz3);//class zx_reflection.Person System.out.println(clazz1 == clazz2);//true System.out.println(clazz1 == clazz3);//true //方式四:使用类的加载器:ClassLoader(了解) ClassLoader classLoader = ReflectionTest.class.getClassLoader(); Class clazz4 = classLoader.loadClass("class zx_reflection.Person"); System.out.println(clazz4); } //Class实例可以时那些结构的说明: @Test public void test4(){ Class c1 = Object.class; Class c2 = Comparable.class; Class c3 = String[].class; Class c4 = int[][].class; Class c5 = ElementType.class; Class c6 = Override.class; Class c7 = int.class; Class c8 = void.class; Class c9 = Class.class; int[] a = new int[10]; int[] b = new int[100]; Class c10 = a.getClass(); Class c11 = b.getClass(); //只要数组的元素类型与维度一样,就是同一个Class System.out.println(c10 == c11);//true }

1.类的加载的过程:

程序经过javac.exe命令以后,会生成一个或多个字节码文件(.class结尾)。

接着我们使用java.exe命令对某个字节码文件进行解释运行。相当于将某个字节码文件加载到内存中。此过程就称为类的加载。

加载到内存中的类,我们就称为运行时类,此运行时类,就作为Class的一个实例。

2.换句话说,Class的实例就对应着一个加载到内存中的运行时类。

3.加载到内存中的一个运行时类,会缓存一段时间;在此时间之内 ,我们可以通过不同的方式来获取此运行时类。

@Test public void test1(){ //对于自定义类,使用系统类加载器进行加载 ClassLoader classLoader = ClassLoaderTest.class.getClassLoader(); System.out.println(classLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2 //调用系统类加载器的getParent():获取扩展类加载器 ClassLoader classLoader1 = classLoader.getParent(); System.out.println(classLoader1);//sun.misc.Launcher$ExtClassLoader@54bedef2 //调用扩展类加载器的getParent():无法获取引导类加载器 //引导类加载器主要负责加载java的核心类库,无法加载自定义类的。 ClassLoader classLoader2 = classLoader1.getParent(); System.out.println(classLoader2);//null ClassLoader classLoader3 = String.class.getClassLoader(); System.out.println(classLoader3);//null } Properties:用来读取配置文件 @Test public void test2() throws Exception { Properties pros = new Properties(); //此时的文件认在当前的module下 //读取配置文件的方式一: // FileInputStream fis = new FileInputStream("jdbc.properties"); // pros.load(fis); //读取配置文件的方式二:使用ClassLoader //配置文件认识别为:当前module的src下 ClassLoader classLoader = ClassLoaderTest.class.getClassLoader(); InputStream is = classLoader.getResourceAsstream("jdbc1.properties"); pros.load(is); String user = pros.getProperty("user"); String password = pros.getProperty("password"); System.out.println("user = " + user + ",password" + password);//user = 吴飞,passwordabc123 }

三、通过反射创建对应的运行时类的对象(反射的应用1)

public class NewInstanceTest { @Test public void test1() throws illegalaccessexception, InstantiationException { Class clazz = Person.class; /* newInstance():调用方法,创建对应的运行时类的对象,内部调用了运行时类的空参构造器 要想此方法正常的创建运行时类的对象,要求: 1.运行时类必须提供空参的构造器 2.空参的构造器的访问权限得够。通常,设置为public 在javabean中要求提供一个public的空参构造器。原因: 1.便于通过反射,创建运行时类的对象 2.便于子类继承此运行时类时,调用super()时,保证父类有此构造器 */ Person obj = clazz.newInstance(); System.out.println(obj);//Person{name='null', age=0} } //体会反射的动态性 @Test public void test2() { int num = new Random().nextInt(3);//0,1,2 String classpath = ""; switch(num){ case 0: classpath = "java.util.Date"; break; case 1: classpath = "java.lang.Object"; break; case 2: classpath = "zx_reflection/Person.java"; break; try { Object obj = getInstance(classpath); } catch (Exception e) { e.printstacktrace(); } } } /* 创建一个指定类的对象 classpath:指定类的全类名 */ public Object getInstance(String classpath) throws Exception { Class clazz = Class.forName(classpath); return clazz.newInstance(); } }

总结:创建类的对象的方式?

方式一:new + 构造器

方式二:要创建Xxx类的对象,可以考虑:Xxx、Xxxs、XxxBuilder类中查看是否有静态方法的存在。可以调用其静态方法,创建Xxx对象。

方式三:通过反射。(使用频率、先后顺序与书写顺序一致)

四、获取运行时类的完整结构(反射的应用2)

1.获取当前运行时类的属性结构

@Test public void test1(){ Class clazz = Person.class; //获取属性结构 //getFields():获取当前运行时及其父类中声明为public访问权限的属性 Field[] fields = clazz.getFields(); for(Field f : fields){ System.out.println(f); } System.out.println(); //getDeclaredFields():获取当前运行时类中声明的所有属性。(不包含父类中声明的属性) Field[] declaredFields = clazz.getDeclaredFields(); for(Field f : declaredFields){ System.out.println(f); } } @Test public void test2(){ Class clazz = Person.class; Field[] declaredFields = clazz.getDeclaredFields(); for(Field f : declaredFields){ //1.权限修饰符 int modifiers = f.getModifiers(); System.out.print(Modifier.toString(modifiers) + "t"); //2.数据类型 Class type = f.getType(); System.out.print(type.getName() + "t"); //3.变量名 String fName = f.getName(); System.out.print(fName); } }

2.获取运行时类的方法结构

@Test public void test1(){ Class clazz = Person.class; //getmethods():获取当前运行时类及所有父类中声明为public权限的方法 Method[] methods = clazz.getmethods(); for(Method m : methods){ System.out.println(m); } System.out.println(); //getDeclaredMethods():获取当前运行时类中声明的所有方法。(不包含父类中声明的方法) Method[] declaredMethods = clazz.getDeclaredMethods(); for(Method m : declaredMethods){ System.out.println(m); } } /* @Xxxx 权限修饰符 返回值类型 方法名(参数类型1 形参名1,...) throws XxxException{} */ @Test public void test2(){ Class clazz = Person.class; Method[] declaredMethods = clazz.getDeclaredMethods(); for(Method m : declaredMethods){ //1.获取方法声明的注释 Annotation[] annos = m.getAnnotations(); for(Annotation a : annos){ System.out.println(a); } //2.权限修饰符 System.out.print(Modifier.toString(m.getModifiers()) + "t"); //3.返回值类型 System.out.print(m.getReturnType().getName() + "t"); //4.方法名 System.out.print(m.getName()); System.out.print("("); //5.形参列表 Class[] parameterTypes = m.getParameterTypes(); if(!(parameterTypes == null && parameterTypes.length == 0)){ for(int i = 0;i 0){ System.out.print("throws "); for(int i = 0;i

3.获取运行时类的其他结构

/* 获取构造器结构 */ @Test public void test1(){ Class clazz = Person.class; //getConstructors():获取当前运行时类中声明为public的构造器 Constructor[] constructors = clazz.getConstructors(); for(Constructor c : constructors){ System.out.println(c); } System.out.println(); //getDeclaredConstructors():获取当前运行时类中声明的所有的构造器 Constructor[] declaredConstructors = clazz.getDeclaredConstructors(); for(Constructor c : declaredConstructors){ System.out.println(c); } } /* 获取运行时类的父类 */ @Test public void test2(){ Class clazz = Person.class; Class superclass = clazz.getSuperclass(); System.out.println(superclass);//class zx_reflection1.Creature } /* 获取运行时类的带泛型的父类 */ @Test public void test3(){ Class clazz = Person.class; Type genericSuperclass = clazz.getGenericSuperclass(); System.out.println(genericSuperclass);//zx_reflection1.Creature } /* 获取运行时类的带泛型的父类的泛型 */ @Test public void test4(){ Class clazz = Person.class; Type genericSuperclass = clazz.getGenericSuperclass(); ParameterizedType paramType = (ParameterizedType)genericSuperclass; //获取泛型类型 Type[] actualTypeArguments = paramType.getActualTypeArguments(); System.out.println(actualTypeArguments[0].getTypeName());//java.lang.String } /* 获取运行时类实现的接口 */ @Test public void test5(){ Class clazz = Person.class; Class[] interfaces = clazz.getInterfaces(); for(Class c : interfaces){ System.out.println(c); } System.out.println(); //获取运行时类的父类实现的接口 Class[] interfaces1 = clazz.getSuperclass().getInterfaces(); for(Class c : interfaces1){ System.out.println(c); } } /* 获取运行时类所在的包 */ @Test public void test6(){ Class clazz = Person.class; Package pack = clazz.getPackage(); System.out.println(pack);//package zx_reflection1 } /* 获取运行时类声明的注解 */ @Test public void test7(){ Class clazz = Person.class; Annotation[] annotations = clazz.getAnnotations(); for(Annotation annos : annotations){ System.out.println(annos);//@zx_reflection1.MyAnnotation(value=hi) } }

五、调用运行时类的指定结构(反射的应用3)

@Test public void testField() throws Exception { Class clazz = Person.class; //创建运行时类的对象 Person p = (Person)clazz.newInstance(); //获取指定的属性:要求运行时类中属性声明为public(通常不采用此方法,因为属性很少声明为public,常用的在下面) Field id = clazz.getField("id"); /* 设置当前属性的值 set():参数1:指明设置那个对象的属性 参数2:将此属性值设置为多少 */ id.set(p,1001); /* 获取当前属性的值 get():参数1:获取那个对象的当前属性值 */ int pId = (int)id.get(p); System.out.println(pId); } /* 如何操作运行时类中的指定的属性----需要掌握 */ @Test public void testField1() throws Exception { Class clazz = Person.class; //创建运行时类的对象 Person p = (Person)clazz.newInstance(); //1.getDeclaredField(String fieldName):获取运行时类中指定变量名的属性 Field name = clazz.getDeclaredField("name"); //2.保证当前属性是可访问的 name.setAccessible(true); //3.获取、设置指定对象的此属性值 name.set(p,"Tom"); System.out.println(name.get(p)); } /* 如何操作运行时类中的指定的方法 -- 需要掌握 */ @Test public void testMethod() throws Exception { Class clazz = Person.class; //创建运行时类的对象 Person p = (Person)clazz.newInstance(); /* 1.获取指定的某个方法 getDeclaredMethod():参数1:指明获取方法名称 参数2:指明获取方法的形参列表 */ Method show = clazz.getDeclaredMethod("show", String.class); //2.保证当前方法是可访问的 show.setAccessible(true); /* 2.调用方法的invoke():参数1:方法调用者 参数2:给方法形参赋值的实参 invoke()的返回值即为对应类中调用方法的返回值 */ Object returnValue = show.invoke(p, "CHN");//String nation = p.show() System.out.println(returnValue); System.out.println("*******************如何调用静态方法*********************************"); Method showDesc = clazz.getDeclaredMethod("showDesc"); showDesc.setAccessible(true); //如果调用的运行时类中的方法没有返回值,则此invoke()返回null Object returnVal = showDesc.invoke(Person.class); System.out.println(returnVal);//null } /* 如何调用运行时类中的指定的构造器 */ @Test public void testConstructor() throws NoSuchMethodException { Class clazz = Person.class; /* 1.获取指定的构造器 getDeclaredConstructor():参数:指明构造器的参数列表 */ Constructor constructor = clazz.getDeclaredConstructor(String.class); //2.保证此构造器是可访问的 constructor.setAccessible(true); }

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐