14.2 Class 对象
https://github.com/zhaojiatao/javase
1、什么是Class对象,Class对象是用来做什么的?
Class对象是java程序用来创建类的所有常规对象用的;每个类都有一个Class对象;
2、Class对象是如何创建的?
当程序创建第一个对类的静态成员(static修饰的成员以及构造方法)的引用时,就会加载这个类。类加载器首先检查这个类的
Class对象是否已经加载;如果尚未加载,默认的类加载器就会查找.class文件。在加载字节码后会执行安全验证,之后会根
据字节码在内存中创建这个类的Class对象;
3、除了jvm的类加载器会获取某个类的Class对象之外,我们自己如何获取某个类的Class对象的引用?
这个例子会介绍第一种方法:使用Class类的forName()方法,获取类的Class对象的引用;这个方法的副作用是,如果jvm
还未加载这个类的Class对象的话就加载这个类;在类加载的过程中,会初始化static成员;
注意,传递给forName方法的名字,必须是全限定名;
此外,还可以使用类字面常量的方法来生成对类Class对象的引用;
1 package thinkingInJava.chapter_14_classInfo; 2 3 4 /** 5 * @author zhaojiatao 6 * @date 2018/9/9 7 * 8 * 1、什么是Class对象,Class对象是用来做什么的? 9 * Class对象是java程序用来创建类的所有常规对象用的;每个类都有一个Class对象; 10 * 2、Class对象是如何创建的? 11 * 当程序创建第一个对类的静态成员(static修饰的成员以及构造方法)的引用时,就会加载这个类。类加载器首先检查这个类的 12 * Class对象是否已经加载;如果尚未加载,默认的类加载器就会查找.class文件。在加载字节码后会执行安全验证,之后会根 13 * 据字节码在内存中创建这个类的Class对象; 14 * 3、除了jvm的类加载器会获取某个类的Class对象之外,我们自己如何获取某个类的Class对象的引用? 15 * 这个例子会介绍第一种方法:使用Class类的forName()方法,获取类的Class对象的引用;这个方法的副作用是,如果jvm 16 * 还又有加载这个类的Class对象的话就加载这个类;在类加载的过程中,会初始化static成员; 17 * 注意,传递给forName方法的名字,必须是全限定名; 18 * 此外,还可以使用类字面常量的方法来生成对类Class对象的引用; 19 * 20 * 21 */ 22 interface HasBatteries{} 23 24 interface Waterproof{} 25 26 interface Shoots{} 27 28 class Toy{ 29 Toy(){} 30 Toy(int i){} 31 } 32 33 class FancyToy extends Toy implements HasBatteries,Waterproof,Shoots{ 34 FancyToy(){ 35 super(1); 36 } 37 } 38 39 public class Test2 { 40 41 //通过Class对象,可以得到Class对象的多有信息 42 static void printInfo(Class cc){ 43 System.out.println("Class name:"+cc.getName()+"is interface?["+cc.isInterface()+"]"); 44 System.out.println("Simple name:"+cc.getSimpleName()); 45 System.out.println("Canonical name:"+cc.getCanonicalName()); 46 } 47 48 49 public static void main(String[] args) { 50 Class c=null; 51 52 try{ 53 //注意,必须使用全限定名 54 c=Class.forName("thinkingInJava.chapter_14_classInfo.FancyToy"); 55 }catch (ClassNotFoundException e){ 56 System.out.println("Can't find Test2"); 57 System.exit(1); 58 } 59 60 printInfo(c); 61 62 for(Class face:c.getInterfaces()){ 63 printInfo(face); 64 } 65 66 Class up=c.getSuperclass(); 67 Object obj=null; 68 try{ 69 //使用newInstance来创建的类,必须带有默认构造器 70 obj=up.newInstance(); 71 }catch(InstantiationException e){ 72 System.out.println("Cannot instantiate"); 73 System.exit(1); 74 }catch (IllegalAccessException e){ 75 System.out.println("Cannot access"); 76 System.exit(1); 77 } 78 printInfo(obj.getClass()); 79 80 System.out.println(obj instanceof Toy); 81 82 } 83 84 85 }
14.2.1 字面类常量
除了使用class的forName方法获取类的Class对象的引用外,还可以使用类字面常量;
这种方式不仅简单,且更安全,因为会在类编译期就进行安全检查;
注意,使用.class的方式,获取类的Class对象的引用,不会像forName()那样初始化类的Class对象;
.class的方式仅仅是获取了Class对象的引用,只有到了类真正被初始化结束后,Class对象才真正被初始化;即:
只有在对静态方法或非常数静态域进行首次引用时才执行初始化;
1 package thinkingInJava.chapter_14_classInfo; 2 3 import java.util.Random; 4 5 /** 6 * @author zhaojiatao 7 * @date 2018/9/10 8 * 9 * 除了使用class的forName方法获取类的Class对象的引用外,还可以使用类字面常量; 10 * 这种方式不仅简单,且更安全,因为会在类编译期就进行安全检查; 11 * 注意,使用.class的方式,获取类的Class对象的引用,不会像forName()那样初始化类的Class对象; 12 * .class的方式仅仅是获取了Class对象的引用,只有到了类真正被初始化结束后,Class对象才真正被初始化;即: 13 * 只有在对静态方法或非常数静态域进行首次引用时才执行初始化; 14 * 15 */ 16 public class Test2_1 { 17 public static Random rand=new Random(47); 18 19 public static void main(String[] args) throws ClassNotFoundException { 20 Class initable=Initable.class; 21 System.out.println("这个时候仅仅是获取到了Initable类的Class对象引用,Class对象还没初始化"); 22 //对于static final值是编译期常量,则该值无需对类初始化就可以被读取; 23 System.out.println(Initable.staticFinal); 24 //非编译期常量,即使被static和final修饰,也必须先初始化类Class对象,才能读取; 25 System.out.println(Initable.staticFinal2); 26 27 //如果一个static域不是final的,那么在对它访问时,总是要求在它被读取之前,先进行链接(为这个域分配存储空间) 28 //以及初始化(初始化该存储空间) 29 System.out.println(Initable2.staticNonFinal); 30 31 //使用forName方法,就会初始化类 32 Class initable3=Class.forName("thinkingInJava.chapter_14_classInfo.Initable3"); 33 System.out.println(Initable3.staticNonFinal); 34 35 36 37 } 38 39 } 40 41 42 class Initable{ 43 static final int staticFinal=47; 44 static final int staticFinal2=(int)(1+Math.random()*(10-1+1)); 45 static { 46 System.out.println("Initializing Initable"); 47 } 48 } 49 50 class Initable2{ 51 static int staticNonFinal=147; 52 static { 53 System.out.println("Initializing Initable2"); 54 } 55 } 56 57 class Initable3{ 58 static int staticNonFinal=74; 59 static{ 60 System.out.println("Initializing Initable3"); 61 } 62 }
14.2.2 泛化
学习范型在Class引用的使用过程的应用
1 package thinkingInJava.chapter_14_classInfo; 2 3 import org.junit.Test; 4 5 import java.util.ArrayList; 6 import java.util.List; 7 8 /** 9 * @author zhaojiatao 10 * @date 2018/9/11 11 * 12 * 13 * 学习范型在Class引用的使用过程的应用 14 * 15 * 16 */ 17 public class Test2_2 { 18 19 20 @Test 21 public void Test01() { 22 Class intClass=int.class; 23 Class<Integer> genericIntClass=int.class; 24 genericIntClass=Integer.class; 25 //如果将genericIntClass这个Class引用赋值给double.class的话,会由于类型检查失败,是无法编译; 26 //genericIntClass=double.class; 27 //普通类的引用可以赋值为任何其他的Class对象; 28 intClass = double.class; 29 } 30 31 32 //可以使用通配符?代替上例中的<Integer>, 33 @Test 34 public void Test02() { 35 Class<?> intClass=int.class; 36 intClass = double.class; 37 } 38 39 //如果我像创建一个Class对象的引用,并指定这个Class对象的类型为指定类型或其子类型,则需要使用? extend XXX 40 @Test 41 public void Test03() { 42 Class<? extends Number> bounded=int.class; 43 bounded = double.class; 44 bounded = Number.class; 45 } 46 47 //至此,可以得出结论,使用范型语法的目的是为了提供编译期检查; 48 49 @Test 50 public void Test04(){ 51 FilledList<CountedInteger> fl=new FilledList<CountedInteger>(CountedInteger.class); 52 System.out.println(fl.create(15)); 53 } 54 55 56 57 @Test 58 public void Test05() throws IllegalAccessException, InstantiationException { 59 Class<A> a=A.class; 60 A aa=a.newInstance(); 61 //注意,这里如果不写成这样会报错 62 Class<? super A> b=a.getSuperclass(); 63 64 //注意:b.newInstance返回的不是精确值,而是Object; 65 Object bb=b.newInstance(); 66 67 } 68 69 70 71 } 72 73 class CountedInteger{ 74 private static long counter; 75 private final long id=counter++; 76 public String toString(){ 77 return Long.toString(id); 78 } 79 } 80 81 82 //注意,由于这个类中使用了type.newInstance()方法,所以,必须保证T传进来的类有默认构造方法; 83 class FilledList<T>{ 84 private Class<T> type; 85 public FilledList(Class<T> type){ 86 this.type=type; 87 } 88 89 public List<T> create(int nElements){ 90 List<T> result=new ArrayList<T>(); 91 try{ 92 for(int i=0;i<nElements;i++){ 93 //注意,当在type上使用范型,newInstance()方法将产生确定的类型; 94 result.add(type.newInstance()); 95 } 96 }catch (Exception e){ 97 e.printStackTrace(); 98 } 99 return result; 100 } 101 102 } 103 104 105 106 class A{ 107 108 } 109 110 class B extends A{ 111 112 }
14.3 类型转换前先做检查
在类型转换之前先做检查,如果贸然强制转换,可能会抛出ClassCastException异常;
学习使用instance of 和isInstance()进行类型检查;
主要区别就是instance of是编译器进行类型检查;
而 isInstance方法是运行期,动态进行类型检查,可用于反射、泛型中;
1 package thinkingInJava.chapter_14_classInfo; 2 3 /** 4 * @author zhaojiatao 5 * @date 2018/9/11 6 * 7 * 在类型转换之前先做检查,如果贸然强制转换,可能会抛出ClassCastException异常; 8 * 学习使用instance of 和isInstance()进行类型检查; 9 * 主要区别就是instance of是编译器进行类型检查; 10 * 而 isInstance方法是运行期,动态进行类型检查,可用于反射、泛型中; 11 * 12 */ 13 14 public class Test3 { 15 public static boolean DynamicEqual(Object fatherObj,Object sonObj){ 16 return fatherObj.getClass().isInstance(sonObj); // pass 17 // return sonObj.getClass().isInstance(fatherObj); 18 // return sonObj instanceof Father; // pass 19 // return sonObj instanceof (fatherObj.getClass()); //error 20 } 21 22 public static void main(String[] args){ 23 //instance of 编译器类型检查 24 Father father = new Father(); 25 Son son = new Son(); 26 27 System.out.println(son instanceof Son); // true 28 System.out.println(son instanceof Father); // true 29 System.out.println(son instanceof Object); // true 30 System.out.println(null instanceof Object); // false 31 System.out.println(); 32 33 //运行时动态类型检查(括号里的是子类) 34 System.out.println(Son.class.isInstance(son)); // true 35 //很明显是错误的,但编译是可以通过的 36 System.out.println(Integer.class.isInstance(son));//false 37 System.out.println(Father.class.isInstance(son)); // true 38 System.out.println(Object.class.isInstance(son)); // true 39 System.out.println(Object.class.isInstance(null)); // false 40 System.out.println(); 41 42 43 //different using 44 System.out.println(DynamicEqual(father, son)); 45 } 46 47 48 } 49 50 class Father{} 51 52 class Son extends Father{}
14.4注册工厂
工厂设计模式:将对象的创建工作交给类自己去完成;工厂方法可以被多态地调用,从而为你创建恰当类型的对象。
1 package thinkingInJava.chapter_14_classInfo; 2 3 import java.util.ArrayList; 4 import java.util.Collections; 5 import java.util.List; 6 import java.util.Random; 7 8 /** 9 * @author zhaojiatao 10 * @date 2018/9/12 11 * 工厂方法设计模式: 12 * 将对象的创建工作交给类自己去完成;工厂方法可以被多态地调用,从而为你创建恰当类型的对象。 13 */ 14 public class Test4 { 15 16 public static void main(String[] args) { 17 for(int i=0;i<10;i++){ 18 System.out.println(Part.createRandom()); 19 } 20 } 21 22 } 23 24 interface Factory<T>{ 25 T create(); 26 } 27 28 29 class Part{ 30 public String toString(){ 31 return getClass().getSimpleName(); 32 } 33 34 static List<Factory<? extends Part>> partFactories=new ArrayList<>(); 35 36 static { 37 /*partFactories.add(new FuelFilter.Factory()); 38 partFactories.add(new AirFilter.Factory()); 39 partFactories.add(new CabinAirFilter.Factory()); 40 partFactories.add(new OilFilter.Factory()); 41 42 partFactories.add(new FanBelt.Factory()); 43 partFactories.add(new GeneratorBelt.Factory()); 44 partFactories.add(new PowerSteeringBelt.Factory());*/ 45 Collections.addAll(partFactories,new FuelFilter.Factory(),new AirFilter.Factory(),new CabinAirFilter.Factory(), 46 new OilFilter.Factory(),new FanBelt.Factory(),new GeneratorBelt.Factory(),new PowerSteeringBelt.Factory() 47 ); 48 49 } 50 51 private static Random rand=new Random(47); 52 public static Part createRandom(){ 53 int n =rand.nextInt(partFactories.size()); 54 return partFactories.get(n).create(); 55 } 56 57 58 } 59 60 61 class Filter extends Part{} 62 63 class FuelFilter extends Filter{ 64 public static class Factory implements thinkingInJava.chapter_14_classInfo.Factory<FuelFilter>{ 65 @Override 66 public FuelFilter create() { 67 return new FuelFilter(); 68 } 69 } 70 } 71 72 class AirFilter extends Filter{ 73 public static class Factory implements thinkingInJava.chapter_14_classInfo.Factory<AirFilter>{ 74 @Override 75 public AirFilter create() { 76 return new AirFilter(); 77 } 78 } 79 } 80 81 class CabinAirFilter extends Filter{ 82 public static class Factory implements thinkingInJava.chapter_14_classInfo.Factory<CabinAirFilter>{ 83 @Override 84 public CabinAirFilter create() { 85 return new CabinAirFilter(); 86 } 87 } 88 } 89 90 class OilFilter extends Filter{ 91 public static class Factory implements thinkingInJava.chapter_14_classInfo.Factory<OilFilter>{ 92 @Override 93 public OilFilter create() { 94 return new OilFilter(); 95 } 96 } 97 } 98 99 100 class Belt extends Part{ 101 102 } 103 104 class FanBelt extends Belt{ 105 public static class Factory implements thinkingInJava.chapter_14_classInfo.Factory<FanBelt>{ 106 @Override 107 public FanBelt create() { 108 return new FanBelt(); 109 } 110 } 111 } 112 113 class GeneratorBelt extends Belt{ 114 public static class Factory implements thinkingInJava.chapter_14_classInfo.Factory<GeneratorBelt>{ 115 @Override 116 public GeneratorBelt create() { 117 return new GeneratorBelt(); 118 } 119 } 120 } 121 122 class PowerSteeringBelt extends Belt{ 123 public static class Factory implements thinkingInJava.chapter_14_classInfo.Factory<PowerSteeringBelt>{ 124 @Override 125 public PowerSteeringBelt create() { 126 return new PowerSteeringBelt(); 127 } 128 } 129 }
14.5 instanceof 与 Class的等价性
这一章节,其实主要就是讲解instanceof、isInstance()、class对象== 三者的不同;
instanceof和isInstance()是一组,结果是相同的,区别在前文已经说过,前者需要在编译器进行类型检查,后者只在运行时进行类型检查;
而比较class对象的==和equals一组,结果也是相同的,应为equals是比较两个class对象的内存地址是否一致;
但综合起来看,这两组的结果是不一样的。instance和isInstance()考虑了继承的情况,而后一组没有;
1 package thinkingInJava.chapter_14_classInfo; 2 3 /** 4 * @author zhaojiatao 5 * @date 2018/9/12 6 */ 7 public class Test5 { 8 public static void main(String[] args) { 9 FamilyVsExactType.test(new Base()); 10 FamilyVsExactType.test(new Derived()); 11 } 12 } 13 14 15 class Base{} 16 class Derived extends Base{} 17 18 class FamilyVsExactType { 19 static void test(Object x){ 20 System.out.println("Testing x of type "+x.getClass()); 21 System.out.println("x instanceof Base "+(x instanceof Base)); 22 System.out.println("x instanceof Derived "+Derived.class.isInstance(x)); 23 System.out.println("Base.isInstance(x) "+Base.class.isInstance(x)); 24 System.out.println("Derived.isInstance(x) "+Derived.class.isInstance(x)); 25 System.out.println("x.getClass()==Base.class "+(x.getClass()==Base.class)); 26 System.out.println("x.getClass()==Derived.class "+(x.getClass()==Derived.class)); 27 System.out.println("x.getClass().equals(Base.class) "+x.getClass().equals(Base.class)); 28 System.out.println("x.getClass().equals(Derived.class) "+x.getClass().equals(Derived.class)); 29 } 30 }
14.6 反射与内省
14.6.1Java反射机制的适用场景及其利与弊(引用:https://blog.csdn.net/zolalad/article/details/29370565)
一、反射的适用场景是什么?
1).Java的反射机制在做基础框架的时候非常有用,有一句话这么说来着:反射机制是很多Java框架的基石。而一般应用层面很少用,不过这种东西,现在很多开源框架基本都已经给你封装好了,自己基本用不着写。典型的除了Hibernate之外,还有Spring也用到很多反射机制。经典的就是在xml文件或者properties里面写好了配置,然后在Java类里面解析xml或properties里面的内容,得到一个字符串,然后用反射机制,根据这个字符串获得某个类的Class实例,这样就可以动态配置一些东西,不用每一次都要在代码里面去new或者做其他的事情,以后要改的话直接改配置文件,代码维护起来就很方便了,同时有时候要适应某些需求,Java类里面不一定能直接调用另外的方法,这时候也可以通过反射机制来实现。
总的来说,自己写的很少,具体什么时候要用那要看需求,反射机制无非就是根据一个String来得到你要的实体对象,然后调用它原来的东西。但是如果是要自己写框架的话,那就会用得比较多了。
2)当你做一个软件可以安装插件的功能,你连插件的类型名称都不知道,你怎么实例化这个对象呢?因为程序是支持插件的(第三方的),在开发的时候并不知道 。所以无法在代码中 New出来 ,但反射可以,通过反射,动态加载程序集,然后读出类,检查标记之后再实例化对象,就可以获得正确的类实例。
3)在编码阶段不知道那个类名,要在运行期从配置文件读取类名, 这时候就没有办法硬编码new ClassName(),而必须用到反射才能创建这个对象.反射的目的就是为了扩展未知的应用。比如你写了一个程序,这个程序定义了一些接口,只要实现了这些接口的dll都可以作为插件来插入到这个程序中。那么怎么实现呢?就可以通过反射来实现。就是把dll加载进内存,然后通过反射的方式来调用dll中的方法。很多工厂模式就是使用的反射。
二、程序员在自己的业务开发中应该尽量的远离反射
反射:在流行的库如Spring和Hibernate中,反射自然有其用武之地。不过内省业务代码在很多时候都不是一件好事,原因有很多,一般情况下我总是建议大家不要使用反射。
首先是代码可读性与工具支持。打开熟悉的IDE,寻找你的Java代码的内部依赖,很容易吧。现在,使用反射来替换掉你的代码然后再试一下,结果如何呢?如果通过反射来修改已经封装好的对象状态,那么结果将会变得更加不可控。请看看如下示例代码:
如果这样做就无法得到编译期的安全保证。就像上面这个示例一样,你会发现如果getDeclaredField()方法调用的参数输错了,那么只有在运行期才能发现。要知道的是,寻找运行期Bug的难度要远远超过编译期的Bug。
最后还要谈谈代价问题。JIT对反射的优化程度是不同的,有些优化时间会更长一些,而有些甚至是无法应用优化。因此,有时反射的性能损失可以达到几个数量级的差别。不过在典型的业务应用中,你可能不会注意到这个代价。
总结一下,我觉得在业务代码中唯一合理(直接)使用反射的场景是通过AOP。除此之外,你最好远离反射这一特性。
三、性能分析
反射机制是一种程序自我分析的能力。用于获取一个类的类变量,构造函数,方法,修饰符。
优点:运行期类型的判断,动态类加载,动态代理使用反射。
缺点:性能是一个问题,反射相当于一系列解释操作,通知jvm要做的事情,性能比直接的java代码要慢很多。
14.6.2反射的一些常用操作:参考:https://blog.csdn.net/sinat_38259539/article/details/71799078
1 package thinkingInJava.chapter_14_classInfo; 2 3 import org.junit.Test; 4 5 import java.io.InputStream; 6 import java.lang.reflect.Constructor; 7 import java.lang.reflect.Field; 8 import java.lang.reflect.InvocationTargetException; 9 import java.lang.reflect.Method; 10 import java.util.ArrayList; 11 import java.util.Properties; 12 13 /** 14 * @author zhaojiatao 15 * @date 2018/9/13 16 * 17 * 反射的应用场景:https://blog.csdn.net/zolalad/article/details/29370565 18 * 反射的一些常用操作:参考:https://blog.csdn.net/sinat_38259539/article/details/71799078 19 * 20 */ 21 class Student { 22 23 //---------------构造方法------------------- 24 //(默认的构造方法) 25 Student(String str){ 26 System.out.println("(默认)的构造方法 s = " + str); 27 } 28 29 //无参构造方法 30 public Student(){ 31 System.out.println("调用了公有、无参构造方法执行了。。。"); 32 } 33 34 //有一个参数的构造方法 35 public Student(char name){ 36 System.out.println("姓名:" + name); 37 } 38 39 //有多个参数的构造方法 40 public Student(String name ,int age){ 41 System.out.println("姓名:"+name+"年龄:"+ age);//这的执行效率有问题,以后解决。 42 } 43 44 //受保护的构造方法 45 protected Student(boolean n){ 46 System.out.println("受保护的构造方法 n = " + n); 47 } 48 49 //私有构造方法 50 private Student(int age){ 51 System.out.println("私有的构造方法 年龄:"+ age); 52 } 53 54 55 //**********字段*************// 56 public String name; 57 protected int age; 58 char sex; 59 private String phoneNum; 60 61 62 63 64 //**************成员方法***************// 65 public void show1(String s){ 66 System.out.println("调用了:公有的,String参数的show1(): s = " + s); 67 } 68 protected void show2(){ 69 System.out.println("调用了:受保护的,无参的show2()"); 70 } 71 void show3(){ 72 System.out.println("调用了:默认的,无参的show3()"); 73 } 74 private String show4(int age){ 75 System.out.println("调用了,私有的,并且有返回值的,int参数的show4(): age = " + age); 76 return "abcd"; 77 } 78 79 80 81 @Override 82 public String toString() { 83 return "Student [name=" + name + ", age=" + age + ", sex=" + sex 84 + ", phoneNum=" + phoneNum + "]"; 85 } 86 87 88 public static void main(String[] args) { 89 System.out.println("main方法执行了。。。"); 90 } 91 92 public void show(){ 93 System.out.println("is show()"); 94 } 95 96 97 98 99 } 100 101 102 103 104 public class Test6 { 105 /* 106 * 通过Class对象可以获取某个类中的:构造方法、成员变量、成员方法;并访问成员; 107 * 108 * 1.获取构造方法: 109 * 1).批量的方法: 110 * public Constructor[] getConstructors():所有"公有的"构造方法 111 public Constructor[] getDeclaredConstructors():获取所有的构造方法(包括私有、受保护、默认、公有) 112 113 * 2).获取单个的方法,并调用: 114 * public Constructor getConstructor(Class... parameterTypes):获取单个的"公有的"构造方法: 115 * public Constructor getDeclaredConstructor(Class... parameterTypes):获取"某个构造方法"可以是私有的,或受保护、默认、公有; 116 * 117 * 调用构造方法: 118 * Constructor-->newInstance(Object... initargs) 119 * 120 * 2.设置字段的值: 121 * Field --> public void set(Object obj,Object value): 122 * 参数说明: 123 * 1.obj:要设置的字段所在的对象; 124 * 2.value:要为字段设置的值; 125 * 126 * 127 */ 128 129 130 131 //通过反射获取构造方法并使用 132 @Test 133 public void test01() { 134 135 //1.加载Class对象 136 Class clazz = null; 137 try { 138 clazz = Class.forName("thinkingInJava.chapter_14_classInfo.Student"); 139 } catch (ClassNotFoundException e) { 140 e.printStackTrace(); 141 } 142 143 //2.获取所有公有构造方法 144 System.out.println("**********************所有公有构造方法*********************************"); 145 //Returns an array containing Constructor objects reflecting all the public constructors of the class represented by this Class object. 146 Constructor[] conArray = clazz.getConstructors(); 147 for(Constructor c : conArray){ 148 System.out.println(c); 149 } 150 151 System.out.println("************所有的构造方法(包括:私有、受保护、默认、公有)***************"); 152 //Returns an array of Constructor objects reflecting all the constructors declared by the class represented by this Class object. 153 conArray = clazz.getDeclaredConstructors(); 154 for(Constructor c : conArray){ 155 System.out.println(c); 156 } 157 158 System.out.println("*****************获取公有、无参的构造方法*******************************"); 159 Constructor con = null; 160 try { 161 con = clazz.getConstructor(null); 162 } catch (NoSuchMethodException e) { 163 e.printStackTrace(); 164 } 165 //1>、因为是无参的构造方法所以类型是一个null,不写也可以:这里需要的是一个参数的类型,切记是类型 166 //2>、返回的是描述这个无参构造函数的类对象。 167 168 System.out.println("con = " + con); 169 //调用构造方法 170 Object obj = null; 171 try { 172 obj = con.newInstance(); 173 } catch (InstantiationException e) { 174 e.printStackTrace(); 175 } catch (IllegalAccessException e) { 176 e.printStackTrace(); 177 } catch (InvocationTargetException e) { 178 e.printStackTrace(); 179 } 180 System.out.println("obj = " + obj); 181 Student stu = (Student)obj; 182 System.out.println("使用无参构造方法创建的Student对象的实例:stu="+stu.toString()); 183 184 System.out.println("******************获取私有构造方法,并调用*******************************"); 185 try { 186 con = clazz.getDeclaredConstructor(char.class); 187 } catch (NoSuchMethodException e) { 188 e.printStackTrace(); 189 } 190 System.out.println(con); 191 //调用构造方法 192 con.setAccessible(true);//暴力访问(忽略掉访问修饰符) 193 try { 194 obj = con.newInstance('男'); 195 } catch (InstantiationException e) { 196 e.printStackTrace(); 197 } catch (IllegalAccessException e) { 198 e.printStackTrace(); 199 } catch (InvocationTargetException e) { 200 e.printStackTrace(); 201 } 202 203 System.out.println("obj = " + obj); 204 stu = (Student)obj; 205 System.out.println("使用私有构造方法创建的Student对象的实例:stu="+stu.toString()); 206 207 } 208 209 210 211 212 //获取成员变量并调用 213 @Test 214 public void test02() throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, IllegalAccessException, 215 InvocationTargetException, InstantiationException { 216 //1.获取Class对象 217 Class stuClass = Class.forName("thinkingInJava.chapter_14_classInfo.Student"); 218 //2.获取字段 219 System.out.println("************获取所有公有的字段********************"); 220 Field[] fieldArray = stuClass.getFields(); 221 for(Field f : fieldArray){ 222 System.out.println(f); 223 } 224 System.out.println("************获取所有的字段(包括私有、受保护、默认的)********************"); 225 fieldArray = stuClass.getDeclaredFields(); 226 for(Field f : fieldArray){ 227 System.out.println(f); 228 } 229 System.out.println("*************获取公有字段xx并调用***********************************"); 230 Field f = stuClass.getField("name"); 231 System.out.println(f); 232 //获取一个对象 233 Object obj = stuClass.getConstructor().newInstance();//产生Student对象--》Student stu = new Student(); 234 //为字段设置值 235 f.set(obj, "刘德华");//为Student对象中的name属性赋值--》stu.name = "刘德华" 236 //验证 237 Student stu = (Student)obj; 238 System.out.println("验证姓名:" + stu.name); 239 240 241 System.out.println("**************获取私有字段****并调用********************************"); 242 f = stuClass.getDeclaredField("phoneNum"); 243 System.out.println(f); 244 f.setAccessible(true);//暴力反射,解除私有限定 245 f.set(obj, "18888889999"); 246 System.out.println("验证电话:" + stu); 247 248 } 249 250 251 252 //获取成员方法并调用 253 @Test 254 public void test03() throws Exception { 255 256 //1.获取Class对象 257 Class stuClass = Class.forName("thinkingInJava.chapter_14_classInfo.Student"); 258 //2.获取所有公有方法 259 System.out.println("***************获取所有的”公有“方法*******************"); 260 stuClass.getMethods(); 261 Method[] methodArray = stuClass.getMethods(); 262 for(Method m : methodArray){ 263 System.out.println(m); 264 } 265 System.out.println("***************获取所有的方法,包括私有的*******************"); 266 methodArray = stuClass.getDeclaredMethods(); 267 for(Method m : methodArray){ 268 System.out.println(m); 269 } 270 System.out.println("***************获取公有的show1()方法*******************"); 271 Method m = stuClass.getMethod("show1", String.class); 272 System.out.println(m); 273 //实例化一个Student对象 274 Object obj = stuClass.getConstructor().newInstance(); 275 m.invoke(obj, "刘德华"); 276 277 System.out.println("***************获取私有的show4()方法******************"); 278 m = stuClass.getDeclaredMethod("show4", int.class); 279 System.out.println(m); 280 m.setAccessible(true);//解除私有限定 281 Object result = m.invoke(obj, 20);//需要两个参数,一个是要调用的对象(获取有反射),一个是实参 282 System.out.println("返回值:" + result); 283 284 285 286 287 } 288 289 290 //反射main方法 291 @Test 292 public void test04(){ 293 try { 294 //1、获取Student对象的字节码 295 Class clazz = Class.forName("thinkingInJava.chapter_14_classInfo.Student"); 296 297 //2、获取main方法 298 Method methodMain = clazz.getMethod("main", String[].class);//第一个参数:方法名称,第二个参数:方法形参的类型, 299 //3、调用main方法 300 // methodMain.invoke(null, new String[]{"a","b","c"}); 301 //第一个参数,对象类型,因为方法是static静态的,所以为null可以,第二个参数是String数组,这里要注意在jdk1.4时是数组,jdk1.5之后是可变参数 302 //这里拆的时候将 new String[]{"a","b","c"} 拆成3个对象。。。所以需要将它强转。 303 methodMain.invoke(null, (Object)new String[]{"a","b","c"});//方式一 304 //methodMain.invoke(null, new Object[]{new String[]{"a","b","c"}});//方式二 305 306 } catch (Exception e) { 307 e.printStackTrace(); 308 } 309 310 } 311 312 313 314 //反射方法的其它使用之---通过反射运行配置文件内容 315 @Test 316 public void test05() throws Exception { 317 Properties pro = new Properties(); 318 {//此方式要求 配置文件在 src 文件夹 内 319 320 //类名.class.getClassLoader().getResourceAsStream("文件名") 321 InputStream inStream = this.getClass().getClassLoader().getResourceAsStream("pro.properties"); 322 pro.load(inStream); 323 inStream.close(); 324 } 325 326 //通过反射获取Class对象 327 Class stuClass = Class.forName(pro.getProperty("className"));//"cn.fanshe.Student" 328 //2获取show()方法 329 Method m = stuClass.getMethod(pro.getProperty("methodName"));//show 330 //3.调用show()方法 331 m.invoke(stuClass.getConstructor().newInstance()); 332 333 } 334 335 336 337 //反射方法的其它使用之---通过反射越过泛型检查 338 @Test 339 public void test06() throws Exception{ 340 ArrayList<String> strList = new ArrayList<String>(); 341 strList.add("aaa"); 342 strList.add("bbb"); 343 344 // strList.add(100); 345 //获取ArrayList的Class对象,反向的调用add()方法,添加数据 346 Class listClass = strList.getClass(); //得到 strList 对象的字节码 对象 347 //获取add()方法 348 Method m = listClass.getMethod("add", Object.class); 349 //调用add()方法 350 m.invoke(strList, 100); 351 352 //遍历集合 353 for(Object obj : strList){ 354 System.out.println(obj); 355 } 356 357 } 358 359 360 361 362 363 }
14.6.3 内省(参考:http://www.cnblogs.com/peida/archive/2013/06/03/3090842.html)
内省(Introspector) 是Java 语言对 JavaBean 类属性、事件的一种缺省处理方法。
JavaBean是一种特殊的类,主要用于传递数据信息,这种类中的方法主要用于访问私有的字段,且方法名符合某种命名规则。如果在两个模块之间传递信息,可以将信息封装进JavaBean中,这种对象称为“值对象”(Value Object),或“VO”。方法比较少。这些信息储存在类的私有变量中,通过set()、get()获得。
什么时候会用到内省?
在框架设计的时候使用比较多,像mybatis中类与表的映射关系的类的属性与表的列一一对应,框架在赋值和获取值得时候会调用对应的setter和getter方法;
例如spring 中初始化bean的时候取药用到反射的方式实例化bean,在set值的时候会根据变量名获得settter方法,把配置好的值set进去;
1 package thinkingInJava.chapter_14_classInfo; 2 3 import org.apache.commons.beanutils.BeanUtils; 4 import org.apache.commons.beanutils.PropertyUtils; 5 import org.junit.Test; 6 7 import java.beans.BeanInfo; 8 import java.beans.IntrospectionException; 9 import java.beans.Introspector; 10 import java.beans.PropertyDescriptor; 11 import java.lang.reflect.InvocationTargetException; 12 import java.lang.reflect.Method; 13 14 /** 15 * @author zhaojiatao 16 * @date 2018/9/13 17 * 18 * 内省: 19 * http://www.cnblogs.com/peida/archive/2013/06/03/3090842.html 20 * https://blog.csdn.net/u010445297/article/details/60967146 21 * 22 */ 23 public class Test6_Introspector { 24 25 public static void main(String[] args) { 26 UserInfo userInfo=new UserInfo(); 27 userInfo.setUserName("peida"); 28 try { 29 BeanInfoUtil.getProperty(userInfo, "userName"); 30 31 BeanInfoUtil.setProperty(userInfo, "userName"); 32 33 BeanInfoUtil.getProperty(userInfo, "userName"); 34 35 BeanInfoUtil.setPropertyByIntrospector(userInfo, "userName"); 36 37 BeanInfoUtil.getPropertyByIntrospector(userInfo, "userName"); 38 39 BeanInfoUtil.setProperty(userInfo, "age"); 40 //说明:BeanInfoUtil.setProperty(userInfo, "age");报错是应为age属性是int数据类型, 41 //而setProperty方法里面默认给age属性赋的值是String类型。所以会爆出argument type mismatch参数类型不匹配的错误信息。 42 43 } catch (Exception e) { 44 // TODO Auto-generated catch block 45 e.printStackTrace(); 46 } 47 48 } 49 50 51 52 @Test 53 public void testBeanUtils(){ 54 UserInfo userInfo=new UserInfo(); 55 try { 56 BeanUtils.setProperty(userInfo, "userName", "peida"); 57 58 System.out.println("set userName:"+userInfo.getUserName()); 59 60 System.out.println("get userName:"+BeanUtils.getProperty(userInfo, "userName")); 61 62 BeanUtils.setProperty(userInfo, "age", 18); 63 System.out.println("set age:"+userInfo.getAge()); 64 65 System.out.println("get age:"+BeanUtils.getProperty(userInfo, "age")); 66 67 System.out.println("get userName type:"+BeanUtils.getProperty(userInfo, "userName").getClass().getName()); 68 System.out.println("get age type:"+BeanUtils.getProperty(userInfo, "age").getClass().getName()); 69 70 PropertyUtils.setProperty(userInfo, "age", 8); 71 System.out.println(PropertyUtils.getProperty(userInfo, "age")); 72 73 System.out.println(PropertyUtils.getProperty(userInfo, "age").getClass().getName()); 74 75 PropertyUtils.setProperty(userInfo, "age", "8"); 76 } 77 catch (IllegalAccessException e) { 78 e.printStackTrace(); 79 } 80 catch (InvocationTargetException e) { 81 e.printStackTrace(); 82 } 83 catch (NoSuchMethodException e) { 84 e.printStackTrace(); 85 } 86 } 87 88 89 90 } 91 92 93 class BeanInfoUtil { 94 95 public static void setProperty(UserInfo userInfo,String userName){ 96 PropertyDescriptor propDesc= null; 97 try { 98 propDesc = new PropertyDescriptor(userName,UserInfo.class); 99 } catch (IntrospectionException e) { 100 e.printStackTrace(); 101 } 102 Method methodSetUserName=propDesc.getWriteMethod(); 103 try { 104 methodSetUserName.invoke(userInfo, "wong"); 105 } catch (IllegalAccessException e) { 106 e.printStackTrace(); 107 } catch (InvocationTargetException e) { 108 e.printStackTrace(); 109 } 110 System.out.println("set userName:"+userInfo.getUserName()); 111 } 112 113 public static void getProperty(UserInfo userInfo,String userName){ 114 PropertyDescriptor proDescriptor = null; 115 try { 116 proDescriptor = new PropertyDescriptor(userName,UserInfo.class); 117 } catch (IntrospectionException e) { 118 e.printStackTrace(); 119 } 120 Method methodGetUserName=proDescriptor.getReadMethod(); 121 Object objUserName= null; 122 try { 123 objUserName = methodGetUserName.invoke(userInfo); 124 } catch (IllegalAccessException e) { 125 e.printStackTrace(); 126 } catch (InvocationTargetException e) { 127 e.printStackTrace(); 128 } 129 System.out.println("get userName:"+objUserName.toString()); 130 } 131 132 public static void setPropertyByIntrospector(UserInfo userInfo,String userName){ 133 BeanInfo beanInfo= null; 134 try { 135 beanInfo = Introspector.getBeanInfo(UserInfo.class); 136 } catch (IntrospectionException e) { 137 e.printStackTrace(); 138 } 139 PropertyDescriptor[] proDescrtptors=beanInfo.getPropertyDescriptors(); 140 if(proDescrtptors!=null&&proDescrtptors.length>0){ 141 for(PropertyDescriptor propDesc:proDescrtptors){ 142 if(propDesc.getName().equals(userName)){ 143 Method methodSetUserName=propDesc.getWriteMethod(); 144 try { 145 methodSetUserName.invoke(userInfo, "alan"); 146 } catch (IllegalAccessException e) { 147 e.printStackTrace(); 148 } catch (InvocationTargetException e) { 149 e.printStackTrace(); 150 } 151 System.out.println("set userName:"+userInfo.getUserName()); 152 break; 153 } 154 } 155 } 156 } 157 158 public static void getPropertyByIntrospector(UserInfo userInfo,String userName){ 159 BeanInfo beanInfo= null; 160 try { 161 beanInfo = Introspector.getBeanInfo(UserInfo.class); 162 } catch (IntrospectionException e) { 163 e.printStackTrace(); 164 } 165 PropertyDescriptor[] proDescrtptors=beanInfo.getPropertyDescriptors(); 166 if(proDescrtptors!=null&&proDescrtptors.length>0){ 167 for(PropertyDescriptor propDesc:proDescrtptors){ 168 if(propDesc.getName().equals(userName)){ 169 Method methodGetUserName=propDesc.getReadMethod(); 170 Object objUserName= null; 171 try { 172 objUserName = methodGetUserName.invoke(userInfo); 173 } catch (IllegalAccessException e) { 174 e.printStackTrace(); 175 } catch (InvocationTargetException e) { 176 e.printStackTrace(); 177 } 178 System.out.println("get userName:"+objUserName.toString()); 179 break; 180 } 181 } 182 } 183 } 184 } 185 186 187 188 class UserInfo { 189 190 private long userId; 191 private String userName; 192 private int age; 193 private String emailAddress; 194 195 public long getUserId() { 196 return userId; 197 } 198 public void setUserId(long userId) { 199 this.userId = userId; 200 } 201 public String getUserName() { 202 return userName; 203 } 204 public void setUserName(String userName) { 205 this.userName = userName; 206 } 207 public int getAge() { 208 return age; 209 } 210 public void setAge(int age) { 211 this.age = age; 212 } 213 public String getEmailAddress() { 214 return emailAddress; 215 } 216 public void setEmailAddress(String emailAddress) { 217 this.emailAddress = emailAddress; 218 } 219 220 }
14.7 jdk动态代理(参考:https://www.cnblogs.com/xiaoluo501395377/p/3383130.html)
jdk的动态代理依赖接口,有局限性;底层是通过反射,执行method;