thinking in java学习笔记:14章 类型信息

时间:2021-11-04 16:01:49

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对象的引用;

 package thinkingInJava.chapter_14_classInfo;

 /**
* @author zhaojiatao
* @date 2018/9/9
*
* 1、什么是Class对象,Class对象是用来做什么的?
* Class对象是java程序用来创建类的所有常规对象用的;每个类都有一个Class对象;
* 2、Class对象是如何创建的?
* 当程序创建第一个对类的静态成员(static修饰的成员以及构造方法)的引用时,就会加载这个类。类加载器首先检查这个类的
* Class对象是否已经加载;如果尚未加载,默认的类加载器就会查找.class文件。在加载字节码后会执行安全验证,之后会根
* 据字节码在内存中创建这个类的Class对象;
* 3、除了jvm的类加载器会获取某个类的Class对象之外,我们自己如何获取某个类的Class对象的引用?
* 这个例子会介绍第一种方法:使用Class类的forName()方法,获取类的Class对象的引用;这个方法的副作用是,如果jvm
* 还又有加载这个类的Class对象的话就加载这个类;在类加载的过程中,会初始化static成员;
* 注意,传递给forName方法的名字,必须是全限定名;
* 此外,还可以使用类字面常量的方法来生成对类Class对象的引用;
*
*
*/
interface HasBatteries{} interface Waterproof{} interface Shoots{} class Toy{
Toy(){}
Toy(int i){}
} class FancyToy extends Toy implements HasBatteries,Waterproof,Shoots{
FancyToy(){
super(1);
}
} public class Test2 { //通过Class对象,可以得到Class对象的多有信息
static void printInfo(Class cc){
System.out.println("Class name:"+cc.getName()+"is interface?["+cc.isInterface()+"]");
System.out.println("Simple name:"+cc.getSimpleName());
System.out.println("Canonical name:"+cc.getCanonicalName());
} public static void main(String[] args) {
Class c=null; try{
//注意,必须使用全限定名
c=Class.forName("thinkingInJava.chapter_14_classInfo.FancyToy");
}catch (ClassNotFoundException e){
System.out.println("Can't find Test2");
System.exit(1);
} printInfo(c); for(Class face:c.getInterfaces()){
printInfo(face);
} Class up=c.getSuperclass();
Object obj=null;
try{
//使用newInstance来创建的类,必须带有默认构造器
obj=up.newInstance();
}catch(InstantiationException e){
System.out.println("Cannot instantiate");
System.exit(1);
}catch (IllegalAccessException e){
System.out.println("Cannot access");
System.exit(1);
}
printInfo(obj.getClass()); System.out.println(obj instanceof Toy); } }

14.2.1 字面类常量

除了使用class的forName方法获取类的Class对象的引用外,还可以使用类字面常量;

这种方式不仅简单,且更安全,因为会在类编译期就进行安全检查

注意,使用.class的方式,获取类的Class对象的引用,不会像forName()那样初始化类的Class对象

.class的方式仅仅是获取了Class对象的引用,只有到了类真正被初始化结束后,Class对象才真正被初始化;即:

只有在对静态方法或非常数静态域进行首次引用时才执行初始化;

 package thinkingInJava.chapter_14_classInfo;

 import java.util.Random;

 /**
* @author zhaojiatao
* @date 2018/9/10
*
* 除了使用class的forName方法获取类的Class对象的引用外,还可以使用类字面常量;
* 这种方式不仅简单,且更安全,因为会在类编译期就进行安全检查;
* 注意,使用.class的方式,获取类的Class对象的引用,不会像forName()那样初始化类的Class对象;
* .class的方式仅仅是获取了Class对象的引用,只有到了类真正被初始化结束后,Class对象才真正被初始化;即:
* 只有在对静态方法或非常数静态域进行首次引用时才执行初始化;
*
*/
public class Test2_1 {
public static Random rand=new Random(47); public static void main(String[] args) throws ClassNotFoundException {
Class initable=Initable.class;
System.out.println("这个时候仅仅是获取到了Initable类的Class对象引用,Class对象还没初始化");
//对于static final值是编译期常量,则该值无需对类初始化就可以被读取;
System.out.println(Initable.staticFinal);
//非编译期常量,即使被static和final修饰,也必须先初始化类Class对象,才能读取;
System.out.println(Initable.staticFinal2); //如果一个static域不是final的,那么在对它访问时,总是要求在它被读取之前,先进行链接(为这个域分配存储空间)
//以及初始化(初始化该存储空间)
System.out.println(Initable2.staticNonFinal); //使用forName方法,就会初始化类
Class initable3=Class.forName("thinkingInJava.chapter_14_classInfo.Initable3");
System.out.println(Initable3.staticNonFinal); } } class Initable{
static final int staticFinal=47;
static final int staticFinal2=(int)(1+Math.random()*(10-1+1));
static {
System.out.println("Initializing Initable");
}
} class Initable2{
static int staticNonFinal=147;
static {
System.out.println("Initializing Initable2");
}
} class Initable3{
static int staticNonFinal=74;
static{
System.out.println("Initializing Initable3");
}
}

14.2.2 泛化

学习范型在Class引用的使用过程的应用

 package thinkingInJava.chapter_14_classInfo;

 import org.junit.Test;

 import java.util.ArrayList;
import java.util.List; /**
* @author zhaojiatao
* @date 2018/9/11
*
*
* 学习范型在Class引用的使用过程的应用
*
*
*/
public class Test2_2 { @Test
public void Test01() {
Class intClass=int.class;
Class<Integer> genericIntClass=int.class;
genericIntClass=Integer.class;
//如果将genericIntClass这个Class引用赋值给double.class的话,会由于类型检查失败,是无法编译;
//genericIntClass=double.class;
//普通类的引用可以赋值为任何其他的Class对象;
intClass = double.class;
} //可以使用通配符?代替上例中的<Integer>,
@Test
public void Test02() {
Class<?> intClass=int.class;
intClass = double.class;
} //如果我像创建一个Class对象的引用,并指定这个Class对象的类型为指定类型或其子类型,则需要使用? extend XXX
@Test
public void Test03() {
Class<? extends Number> bounded=int.class;
bounded = double.class;
bounded = Number.class;
} //至此,可以得出结论,使用范型语法的目的是为了提供编译期检查; @Test
public void Test04(){
FilledList<CountedInteger> fl=new FilledList<CountedInteger>(CountedInteger.class);
System.out.println(fl.create(15));
} @Test
public void Test05() throws IllegalAccessException, InstantiationException {
Class<A> a=A.class;
A aa=a.newInstance();
//注意,这里如果不写成这样会报错
Class<? super A> b=a.getSuperclass(); //注意:b.newInstance返回的不是精确值,而是Object;
Object bb=b.newInstance(); } } class CountedInteger{
private static long counter;
private final long id=counter++;
public String toString(){
return Long.toString(id);
}
} //注意,由于这个类中使用了type.newInstance()方法,所以,必须保证T传进来的类有默认构造方法;
class FilledList<T>{
private Class<T> type;
public FilledList(Class<T> type){
this.type=type;
} public List<T> create(int nElements){
List<T> result=new ArrayList<T>();
try{
for(int i=0;i<nElements;i++){
//注意,当在type上使用范型,newInstance()方法将产生确定的类型;
result.add(type.newInstance());
}
}catch (Exception e){
e.printStackTrace();
}
return result;
} } class A{ } class B extends A{ }

14.3 类型转换前先做检查

在类型转换之前先做检查,如果贸然强制转换,可能会抛出ClassCastException异常;

学习使用instance of 和isInstance()进行类型检查;

主要区别就是instance of是编译器进行类型检查;

而 isInstance方法是运行期,动态进行类型检查,可用于反射、泛型中;

 package thinkingInJava.chapter_14_classInfo;

 /**
* @author zhaojiatao
* @date 2018/9/11
*
* 在类型转换之前先做检查,如果贸然强制转换,可能会抛出ClassCastException异常;
* 学习使用instance of 和isInstance()进行类型检查;
* 主要区别就是instance of是编译器进行类型检查;
* 而 isInstance方法是运行期,动态进行类型检查,可用于反射、泛型中;
*
*/ public class Test3 {
public static boolean DynamicEqual(Object fatherObj,Object sonObj){
return fatherObj.getClass().isInstance(sonObj); // pass
// return sonObj.getClass().isInstance(fatherObj);
// return sonObj instanceof Father; // pass
// return sonObj instanceof (fatherObj.getClass()); //error
} public static void main(String[] args){
//instance of 编译器类型检查
Father father = new Father();
Son son = new Son(); System.out.println(son instanceof Son); // true
System.out.println(son instanceof Father); // true
System.out.println(son instanceof Object); // true
System.out.println(null instanceof Object); // false
System.out.println(); //运行时动态类型检查(括号里的是子类)
System.out.println(Son.class.isInstance(son)); // true
//很明显是错误的,但编译是可以通过的
System.out.println(Integer.class.isInstance(son));//false
System.out.println(Father.class.isInstance(son)); // true
System.out.println(Object.class.isInstance(son)); // true
System.out.println(Object.class.isInstance(null)); // false
System.out.println(); //different using
System.out.println(DynamicEqual(father, son));
} } class Father{} class Son extends Father{}

14.4注册工厂

工厂设计模式:将对象的创建工作交给类自己去完成;工厂方法可以被多态地调用,从而为你创建恰当类型的对象。

 package thinkingInJava.chapter_14_classInfo;

 import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random; /**
* @author zhaojiatao
* @date 2018/9/12
* 工厂方法设计模式:
* 将对象的创建工作交给类自己去完成;工厂方法可以被多态地调用,从而为你创建恰当类型的对象。
*/
public class Test4 { public static void main(String[] args) {
for(int i=0;i<10;i++){
System.out.println(Part.createRandom());
}
} } interface Factory<T>{
T create();
} class Part{
public String toString(){
return getClass().getSimpleName();
} static List<Factory<? extends Part>> partFactories=new ArrayList<>(); static {
/*partFactories.add(new FuelFilter.Factory());
partFactories.add(new AirFilter.Factory());
partFactories.add(new CabinAirFilter.Factory());
partFactories.add(new OilFilter.Factory()); partFactories.add(new FanBelt.Factory());
partFactories.add(new GeneratorBelt.Factory());
partFactories.add(new PowerSteeringBelt.Factory());*/
Collections.addAll(partFactories,new FuelFilter.Factory(),new AirFilter.Factory(),new CabinAirFilter.Factory(),
new OilFilter.Factory(),new FanBelt.Factory(),new GeneratorBelt.Factory(),new PowerSteeringBelt.Factory()
); } private static Random rand=new Random(47);
public static Part createRandom(){
int n =rand.nextInt(partFactories.size());
return partFactories.get(n).create();
} } class Filter extends Part{} class FuelFilter extends Filter{
public static class Factory implements thinkingInJava.chapter_14_classInfo.Factory<FuelFilter>{
@Override
public FuelFilter create() {
return new FuelFilter();
}
}
} class AirFilter extends Filter{
public static class Factory implements thinkingInJava.chapter_14_classInfo.Factory<AirFilter>{
@Override
public AirFilter create() {
return new AirFilter();
}
}
} class CabinAirFilter extends Filter{
public static class Factory implements thinkingInJava.chapter_14_classInfo.Factory<CabinAirFilter>{
@Override
public CabinAirFilter create() {
return new CabinAirFilter();
}
}
} class OilFilter extends Filter{
public static class Factory implements thinkingInJava.chapter_14_classInfo.Factory<OilFilter>{
@Override
public OilFilter create() {
return new OilFilter();
}
}
} class Belt extends Part{ } class FanBelt extends Belt{
public static class Factory implements thinkingInJava.chapter_14_classInfo.Factory<FanBelt>{
@Override
public FanBelt create() {
return new FanBelt();
}
}
} class GeneratorBelt extends Belt{
public static class Factory implements thinkingInJava.chapter_14_classInfo.Factory<GeneratorBelt>{
@Override
public GeneratorBelt create() {
return new GeneratorBelt();
}
}
} class PowerSteeringBelt extends Belt{
public static class Factory implements thinkingInJava.chapter_14_classInfo.Factory<PowerSteeringBelt>{
@Override
public PowerSteeringBelt create() {
return new PowerSteeringBelt();
}
}
}

14.5 instanceof 与 Class的等价性

这一章节,其实主要就是讲解instanceof、isInstance()、class对象== 三者的不同;

 instanceof和isInstance()是一组,结果是相同的,区别在前文已经说过,前者需要在编译器进行类型检查,后者只在运行时进行类型检查;

而比较class对象的==和equals一组,结果也是相同的,应为equals是比较两个class对象的内存地址是否一致;

  但综合起来看,这两组的结果是不一样的。instance和isInstance()考虑了继承的情况,而后一组没有;

 package thinkingInJava.chapter_14_classInfo;

 /**
* @author zhaojiatao
* @date 2018/9/12
*/
public class Test5 {
public static void main(String[] args) {
FamilyVsExactType.test(new Base());
FamilyVsExactType.test(new Derived());
}
} class Base{}
class Derived extends Base{} class FamilyVsExactType {
static void test(Object x){
System.out.println("Testing x of type "+x.getClass());
System.out.println("x instanceof Base "+(x instanceof Base));
System.out.println("x instanceof Derived "+Derived.class.isInstance(x));
System.out.println("Base.isInstance(x) "+Base.class.isInstance(x));
System.out.println("Derived.isInstance(x) "+Derived.class.isInstance(x));
System.out.println("x.getClass()==Base.class "+(x.getClass()==Base.class));
System.out.println("x.getClass()==Derived.class "+(x.getClass()==Derived.class));
System.out.println("x.getClass().equals(Base.class) "+x.getClass().equals(Base.class));
System.out.println("x.getClass().equals(Derived.class) "+x.getClass().equals(Derived.class));
}
}

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代码的内部依赖,很容易吧。现在,使用反射来替换掉你的代码然后再试一下,结果如何呢?如果通过反射来修改已经封装好的对象状态,那么结果将会变得更加不可控。请看看如下示例代码:

thinking in java学习笔记:14章 类型信息

如果这样做就无法得到编译期的安全保证。就像上面这个示例一样,你会发现如果getDeclaredField()方法调用的参数输错了,那么只有在运行期才能发现。要知道的是,寻找运行期Bug的难度要远远超过编译期的Bug。

最后还要谈谈代价问题。JIT对反射的优化程度是不同的,有些优化时间会更长一些,而有些甚至是无法应用优化。因此,有时反射的性能损失可以达到几个数量级的差别。不过在典型的业务应用中,你可能不会注意到这个代价。

总结一下,我觉得在业务代码中唯一合理(直接)使用反射的场景是通过AOP。除此之外,你最好远离反射这一特性。

三、性能分析

反射机制是一种程序自我分析的能力。用于获取一个类的类变量,构造函数,方法,修饰符。

优点:运行期类型的判断,动态类加载,动态代理使用反射。

缺点:性能是一个问题,反射相当于一系列解释操作,通知jvm要做的事情,性能比直接的java代码要慢很多。

14.6.2反射的一些常用操作:参考:https://blog.csdn.net/sinat_38259539/article/details/71799078

 package thinkingInJava.chapter_14_classInfo;

 import org.junit.Test;

 import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Properties; /**
* @author zhaojiatao
* @date 2018/9/13
*
* 反射的应用场景:https://blog.csdn.net/zolalad/article/details/29370565
* 反射的一些常用操作:参考:https://blog.csdn.net/sinat_38259539/article/details/71799078
*
*/
class Student { //---------------构造方法-------------------
//(默认的构造方法)
Student(String str){
System.out.println("(默认)的构造方法 s = " + str);
} //无参构造方法
public Student(){
System.out.println("调用了公有、无参构造方法执行了。。。");
} //有一个参数的构造方法
public Student(char name){
System.out.println("姓名:" + name);
} //有多个参数的构造方法
public Student(String name ,int age){
System.out.println("姓名:"+name+"年龄:"+ age);//这的执行效率有问题,以后解决。
} //受保护的构造方法
protected Student(boolean n){
System.out.println("受保护的构造方法 n = " + n);
} //私有构造方法
private Student(int age){
System.out.println("私有的构造方法 年龄:"+ age);
} //**********字段*************//
public String name;
protected int age;
char sex;
private String phoneNum; //**************成员方法***************//
public void show1(String s){
System.out.println("调用了:公有的,String参数的show1(): s = " + s);
}
protected void show2(){
System.out.println("调用了:受保护的,无参的show2()");
}
void show3(){
System.out.println("调用了:默认的,无参的show3()");
}
private String show4(int age){
System.out.println("调用了,私有的,并且有返回值的,int参数的show4(): age = " + age);
return "abcd";
} @Override
public String toString() {
return "Student [name=" + name + ", age=" + age + ", sex=" + sex
+ ", phoneNum=" + phoneNum + "]";
} public static void main(String[] args) {
System.out.println("main方法执行了。。。");
} public void show(){
System.out.println("is show()");
} } public class Test6 {
/*
* 通过Class对象可以获取某个类中的:构造方法、成员变量、成员方法;并访问成员;
*
* 1.获取构造方法:
* 1).批量的方法:
* public Constructor[] getConstructors():所有"公有的"构造方法
public Constructor[] getDeclaredConstructors():获取所有的构造方法(包括私有、受保护、默认、公有) * 2).获取单个的方法,并调用:
* public Constructor getConstructor(Class... parameterTypes):获取单个的"公有的"构造方法:
* public Constructor getDeclaredConstructor(Class... parameterTypes):获取"某个构造方法"可以是私有的,或受保护、默认、公有;
*
* 调用构造方法:
* Constructor-->newInstance(Object... initargs)
*
* 2.设置字段的值:
* Field --> public void set(Object obj,Object value):
* 参数说明:
* 1.obj:要设置的字段所在的对象;
* 2.value:要为字段设置的值;
*
*
*/ //通过反射获取构造方法并使用
@Test
public void test01() { //1.加载Class对象
Class clazz = null;
try {
clazz = Class.forName("thinkingInJava.chapter_14_classInfo.Student");
} catch (ClassNotFoundException e) {
e.printStackTrace();
} //2.获取所有公有构造方法
System.out.println("**********************所有公有构造方法*********************************");
//Returns an array containing Constructor objects reflecting all the public constructors of the class represented by this Class object.
Constructor[] conArray = clazz.getConstructors();
for(Constructor c : conArray){
System.out.println(c);
} System.out.println("************所有的构造方法(包括:私有、受保护、默认、公有)***************");
//Returns an array of Constructor objects reflecting all the constructors declared by the class represented by this Class object.
conArray = clazz.getDeclaredConstructors();
for(Constructor c : conArray){
System.out.println(c);
} System.out.println("*****************获取公有、无参的构造方法*******************************");
Constructor con = null;
try {
con = clazz.getConstructor(null);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
//1>、因为是无参的构造方法所以类型是一个null,不写也可以:这里需要的是一个参数的类型,切记是类型
//2>、返回的是描述这个无参构造函数的类对象。 System.out.println("con = " + con);
//调用构造方法
Object obj = null;
try {
obj = con.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
System.out.println("obj = " + obj);
Student stu = (Student)obj;
System.out.println("使用无参构造方法创建的Student对象的实例:stu="+stu.toString()); System.out.println("******************获取私有构造方法,并调用*******************************");
try {
con = clazz.getDeclaredConstructor(char.class);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
System.out.println(con);
//调用构造方法
con.setAccessible(true);//暴力访问(忽略掉访问修饰符)
try {
obj = con.newInstance('男');
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} System.out.println("obj = " + obj);
stu = (Student)obj;
System.out.println("使用私有构造方法创建的Student对象的实例:stu="+stu.toString()); } //获取成员变量并调用
@Test
public void test02() throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, IllegalAccessException,
InvocationTargetException, InstantiationException {
//1.获取Class对象
Class stuClass = Class.forName("thinkingInJava.chapter_14_classInfo.Student");
//2.获取字段
System.out.println("************获取所有公有的字段********************");
Field[] fieldArray = stuClass.getFields();
for(Field f : fieldArray){
System.out.println(f);
}
System.out.println("************获取所有的字段(包括私有、受保护、默认的)********************");
fieldArray = stuClass.getDeclaredFields();
for(Field f : fieldArray){
System.out.println(f);
}
System.out.println("*************获取公有字段xx并调用***********************************");
Field f = stuClass.getField("name");
System.out.println(f);
//获取一个对象
Object obj = stuClass.getConstructor().newInstance();//产生Student对象--》Student stu = new Student();
//为字段设置值
f.set(obj, "刘德华");//为Student对象中的name属性赋值--》stu.name = "刘德华"
//验证
Student stu = (Student)obj;
System.out.println("验证姓名:" + stu.name); System.out.println("**************获取私有字段****并调用********************************");
f = stuClass.getDeclaredField("phoneNum");
System.out.println(f);
f.setAccessible(true);//暴力反射,解除私有限定
f.set(obj, "18888889999");
System.out.println("验证电话:" + stu); } //获取成员方法并调用
@Test
public void test03() throws Exception { //1.获取Class对象
Class stuClass = Class.forName("thinkingInJava.chapter_14_classInfo.Student");
//2.获取所有公有方法
System.out.println("***************获取所有的”公有“方法*******************");
stuClass.getMethods();
Method[] methodArray = stuClass.getMethods();
for(Method m : methodArray){
System.out.println(m);
}
System.out.println("***************获取所有的方法,包括私有的*******************");
methodArray = stuClass.getDeclaredMethods();
for(Method m : methodArray){
System.out.println(m);
}
System.out.println("***************获取公有的show1()方法*******************");
Method m = stuClass.getMethod("show1", String.class);
System.out.println(m);
//实例化一个Student对象
Object obj = stuClass.getConstructor().newInstance();
m.invoke(obj, "刘德华"); System.out.println("***************获取私有的show4()方法******************");
m = stuClass.getDeclaredMethod("show4", int.class);
System.out.println(m);
m.setAccessible(true);//解除私有限定
Object result = m.invoke(obj, 20);//需要两个参数,一个是要调用的对象(获取有反射),一个是实参
System.out.println("返回值:" + result); } //反射main方法
@Test
public void test04(){
try {
//1、获取Student对象的字节码
Class clazz = Class.forName("thinkingInJava.chapter_14_classInfo.Student"); //2、获取main方法
Method methodMain = clazz.getMethod("main", String[].class);//第一个参数:方法名称,第二个参数:方法形参的类型,
//3、调用main方法
// methodMain.invoke(null, new String[]{"a","b","c"});
//第一个参数,对象类型,因为方法是static静态的,所以为null可以,第二个参数是String数组,这里要注意在jdk1.4时是数组,jdk1.5之后是可变参数
//这里拆的时候将 new String[]{"a","b","c"} 拆成3个对象。。。所以需要将它强转。
methodMain.invoke(null, (Object)new String[]{"a","b","c"});//方式一
//methodMain.invoke(null, new Object[]{new String[]{"a","b","c"}});//方式二 } catch (Exception e) {
e.printStackTrace();
} } //反射方法的其它使用之---通过反射运行配置文件内容
@Test
public void test05() throws Exception {
Properties pro = new Properties();
{//此方式要求 配置文件在 src 文件夹 内 //类名.class.getClassLoader().getResourceAsStream("文件名")
InputStream inStream = this.getClass().getClassLoader().getResourceAsStream("pro.properties");
pro.load(inStream);
inStream.close();
} //通过反射获取Class对象
Class stuClass = Class.forName(pro.getProperty("className"));//"cn.fanshe.Student"
//2获取show()方法
Method m = stuClass.getMethod(pro.getProperty("methodName"));//show
//3.调用show()方法
m.invoke(stuClass.getConstructor().newInstance()); } //反射方法的其它使用之---通过反射越过泛型检查
@Test
public void test06() throws Exception{
ArrayList<String> strList = new ArrayList<String>();
strList.add("aaa");
strList.add("bbb"); // strList.add(100);
//获取ArrayList的Class对象,反向的调用add()方法,添加数据
Class listClass = strList.getClass(); //得到 strList 对象的字节码 对象
//获取add()方法
Method m = listClass.getMethod("add", Object.class);
//调用add()方法
m.invoke(strList, 100); //遍历集合
for(Object obj : strList){
System.out.println(obj);
} } }

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进去;

 package thinkingInJava.chapter_14_classInfo;

 import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.PropertyUtils;
import org.junit.Test; 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; /**
* @author zhaojiatao
* @date 2018/9/13
*
* 内省:
* http://www.cnblogs.com/peida/archive/2013/06/03/3090842.html
* https://blog.csdn.net/u010445297/article/details/60967146
*
*/
public class Test6_Introspector { public static void main(String[] args) {
UserInfo userInfo=new UserInfo();
userInfo.setUserName("peida");
try {
BeanInfoUtil.getProperty(userInfo, "userName"); BeanInfoUtil.setProperty(userInfo, "userName"); BeanInfoUtil.getProperty(userInfo, "userName"); BeanInfoUtil.setPropertyByIntrospector(userInfo, "userName"); BeanInfoUtil.getPropertyByIntrospector(userInfo, "userName"); BeanInfoUtil.setProperty(userInfo, "age");
//说明:BeanInfoUtil.setProperty(userInfo, "age");报错是应为age属性是int数据类型,
//而setProperty方法里面默认给age属性赋的值是String类型。所以会爆出argument type mismatch参数类型不匹配的错误信息。 } catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} } @Test
public void testBeanUtils(){
UserInfo userInfo=new UserInfo();
try {
BeanUtils.setProperty(userInfo, "userName", "peida"); System.out.println("set userName:"+userInfo.getUserName()); System.out.println("get userName:"+BeanUtils.getProperty(userInfo, "userName")); BeanUtils.setProperty(userInfo, "age", 18);
System.out.println("set age:"+userInfo.getAge()); System.out.println("get age:"+BeanUtils.getProperty(userInfo, "age")); System.out.println("get userName type:"+BeanUtils.getProperty(userInfo, "userName").getClass().getName());
System.out.println("get age type:"+BeanUtils.getProperty(userInfo, "age").getClass().getName()); PropertyUtils.setProperty(userInfo, "age", 8);
System.out.println(PropertyUtils.getProperty(userInfo, "age")); System.out.println(PropertyUtils.getProperty(userInfo, "age").getClass().getName()); PropertyUtils.setProperty(userInfo, "age", "8");
}
catch (IllegalAccessException e) {
e.printStackTrace();
}
catch (InvocationTargetException e) {
e.printStackTrace();
}
catch (NoSuchMethodException e) {
e.printStackTrace();
}
} } class BeanInfoUtil { public static void setProperty(UserInfo userInfo,String userName){
PropertyDescriptor propDesc= null;
try {
propDesc = new PropertyDescriptor(userName,UserInfo.class);
} catch (IntrospectionException e) {
e.printStackTrace();
}
Method methodSetUserName=propDesc.getWriteMethod();
try {
methodSetUserName.invoke(userInfo, "wong");
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
System.out.println("set userName:"+userInfo.getUserName());
} public static void getProperty(UserInfo userInfo,String userName){
PropertyDescriptor proDescriptor = null;
try {
proDescriptor = new PropertyDescriptor(userName,UserInfo.class);
} catch (IntrospectionException e) {
e.printStackTrace();
}
Method methodGetUserName=proDescriptor.getReadMethod();
Object objUserName= null;
try {
objUserName = methodGetUserName.invoke(userInfo);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
System.out.println("get userName:"+objUserName.toString());
} public static void setPropertyByIntrospector(UserInfo userInfo,String userName){
BeanInfo beanInfo= null;
try {
beanInfo = Introspector.getBeanInfo(UserInfo.class);
} catch (IntrospectionException e) {
e.printStackTrace();
}
PropertyDescriptor[] proDescrtptors=beanInfo.getPropertyDescriptors();
if(proDescrtptors!=null&&proDescrtptors.length>0){
for(PropertyDescriptor propDesc:proDescrtptors){
if(propDesc.getName().equals(userName)){
Method methodSetUserName=propDesc.getWriteMethod();
try {
methodSetUserName.invoke(userInfo, "alan");
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
System.out.println("set userName:"+userInfo.getUserName());
break;
}
}
}
} public static void getPropertyByIntrospector(UserInfo userInfo,String userName){
BeanInfo beanInfo= null;
try {
beanInfo = Introspector.getBeanInfo(UserInfo.class);
} catch (IntrospectionException e) {
e.printStackTrace();
}
PropertyDescriptor[] proDescrtptors=beanInfo.getPropertyDescriptors();
if(proDescrtptors!=null&&proDescrtptors.length>0){
for(PropertyDescriptor propDesc:proDescrtptors){
if(propDesc.getName().equals(userName)){
Method methodGetUserName=propDesc.getReadMethod();
Object objUserName= null;
try {
objUserName = methodGetUserName.invoke(userInfo);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
System.out.println("get userName:"+objUserName.toString());
break;
}
}
}
}
} class UserInfo { private long userId;
private String userName;
private int age;
private String emailAddress; public long getUserId() {
return userId;
}
public void setUserId(long userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getEmailAddress() {
return emailAddress;
}
public void setEmailAddress(String emailAddress) {
this.emailAddress = emailAddress;
} }

14.7 jdk动态代理(参考:https://www.cnblogs.com/xiaoluo501395377/p/3383130.html)

jdk的动态代理依赖接口,有局限性;底层是通过反射,执行method;