Java反射学习总结

Java反射学习总结:
对于对象来说,可以直接使用对象.getClass()或者Class.forName(className);类名.class都可以获取Class实例。


但是我们的基本数据类型,就没有类的权限定名,也没有getClass方法。
问题:那么如何使用Class类来表示基本数据类型的Class实例?
byte,short,int,long,char,float,double,boolean,void关键字
上述8中类型和void关键字,都有class属性。
表示int的Class对象:Class clz = int.class;
表示boolean的Class对象: boolean.class;
void: Class clz = void.class;


所有的数据类型都有class属性,表示都是Class对象。


思考:
int的包装类是Integer
Integer.class    !=   int.class
说明是两份字节码。


Integer和int是同一种数据类型吗?不是


但是在八大基本数据类型的包装类中都有一个常量:TYPE
TYPE表示的是该包装类对应的基本数据类型的Class实例。
如:Integer.TYPE -------> int.class
    Integer.TYPE == int.class ; // YES
Integer.TYPE == Integer.class; // ERROR


数组的Class实例:
String[] sArr1 = {"A", "C"};
String[] sArr2 = {};
String[][] sArr = {};
int[] sArr = {};


表示数组的Class实例:
String[] sArr1 = {"A", "C"};
Class clz = String[].class; //此时clz表示就是一个String类型的一维数组类型

所有具有相同元素类型和维数的数组才共享一份字节码(Class对象);
String[] arr1 = {};
String[] arr2 = {"A", "B"};

Class clz1 = String[].class; 
Class clz2 = arr2.getClass();
Class clz3 = arr1.getClass();
System.out.println(clz2 == clz3); // true

String[][] arr = {};
System.out.println(clz1 == String[][].class); // false

int[] iArr = {};
System.out.println(iArr.getClass() == clz1); // false

注意:和数组中的元素没有一点关系。


获取一个类中的所有构造器:
   1.明确操作的是那一份字节码对象。
   2. 获取构造器
   
 Class类获取构造器方法:
 Constructor类:表示类中构造器的类型,Constructor的实例就是某一个类中的某一个构造器
 public Constructor<?>[] getConstructors():该方法只能获取当前Class所表示类的public修饰的构造器。
 public Constructor<?>[] getDeclaredConstructors(): 获取当前Class所表示类的所有的构造器,和访问权限无关。
 public Constructor<T> getConstructor(Class<?>... parameterTypes) : 获取当前Class所表示类中指定的一个public的构造函数。
参数:parameterTypes表示:构造器参数的Class类型
如:public User(String name)
Constructor c = clz.getConstructor(String.class);
 public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) : 获取当前Class所表示类中指定的一个的构造函数,和访问权限无关。


调用构造器,创建对象
Constructor<T>类:表示类中构造器的类型,Constructor的实例就是某一个类中的某一个构造器


常用方法:
public T newInstance(Object... initargs) :若调用带参数的构造器,只能使用这个方法
参数:initargs:表示调用构造器的实际参数
返回:返回创建的实例,T表示Class所表示类的类型


如果:一个类中的构造器可以直接访问,同时没有参数,那么可以直接使用Class类中的
newInstance方法创建对象。




使用反射获取某一个类中的方法:
1.找到获取方法所在类的字节码对象
2. 找到需要被获取的方法


Class类中常用方法:
public Method[] getMethods():获取包括本身和继承的公共方法


public Method getMethod(String methodName, Class<?>... parameterTypes):表示调用指定的一个公共方法(包括继承)
参数:
     methodName: 表示被调用方法的名字
parameterTypes:表示被调用方法的参数的Class类型如String.class


public Method[] getDeclaredMethods():获取自身所有的方法,不包括继承的,和访问权限无关。


public Method getDeclaredMethod(String name, Class<?>... parameterTypes):表示调用指定的一个本身的方法(不包括继承)。


使用反射调用方法:
1.找到被调用方法所在的字节码
2.获取到被调用的方法对象
3.调用该方法


如何使用反射调用一个方法:
在Method类中有方法:
public Object invoke(Object obj, Object... args):表示调用当前Method所表示的方法
     参数:
      obj:表示被调用方法底层所属对象
  args:表示调用方法传递的实际参数
     返回: 
      底层方法的返回结果


调用私有方法:
在调用私有方法之前:应该设置访问可访问权限。
又因为Method是AccessibleObject子类,所以Method中具有该方法。
sayGoodByeMethod.setAccessible(true);


使用反射调用静态方法:
public Object invoke(Object obj, Object... args)
如果底层方法是静态的,那么可以忽略指定的obj参数。将obj参数设为null.


使用反射调用可变参数的方法:
对于数组类型的引用类型的参数,底层会自动解包,为了解决该问题,我们使用Object一维数组把
实际参数包装起来。


以后使用反射调用invoke方法,在传递实际参数的时候,无论是基本数据类型还是引用数据类型,也无论是
可变参数类型,反正就是一切实际参数都包装在new Object[]{}中,就没问题。
m.invoke(方法底层所属对象,new Object[]{实际参数});通用




使用反射获取字段:
public Field[] getFields(): 获取当前Class所表示类中所有的public的字段,包括继承的字段。
public Field getField(String fieldName); 获取当前Class所表示类中fieldName名字的public 字段,包括继承字段。
public Field[] getDeclaredFields();获取当前Class所表示类中所有的字段,不包括继承的字段。
public Field[] getDeclaredField(String name);获取当前Class所表示类中该fieldName名字的字段,不包括继承的字段。


给某个类的字段设置值
1.找到被操作字段所在类的字节码
2.获取到该被操作的字段对象
3.设置值、获取值


Field类常用方法:
void setXX(Object obj, XX value) :为基本类型字段设置值,XX表示基本数据类型
void set(Object obj, Object value) :表示为引用类型字段设置值


参数:
    obj: 表示字段底层所属对象,若该字段是static的,该值应该设为null
value: 表示将要设置的值

XX getXX(Object obj):获取基本类型字段的值,XX表示基本数据类型
Object get(Object obj) :表示获取引用类型字段的值。
参数: 
    obj: 表示字段底层所属对象,若该字段是static的,该值应该设为null
返回:
返回该字段的值。

反射其他的API:


Class 类中
int getModifiers():获取修饰符
String getName():返回类的全限定名。
Package getPackage():获得该类的包
String getSimpleName():获得该类的简单名字
Class getSuperclass():获得类的父类
boolean isArray():判断该Class实例是否是数组
boolean isEnum():判断该Class实例是否是枚举


int mod = OtherAPIDemo.getClass().getModifiers();
System.out.println(mod);
System.out.println(Modifer.toString(mod));


Constructor,Method,Field的信息:
去查阅相应类的API即可。






获取泛型参数的信息:
1.通过getGenericType方法获取到Type类型(该对象中包含了泛型信息)
2.把getGenericType返回的类型强制转换成ParameterizedType类型:
3.调用ParameterizedType里的Type[] getActualTypeArguments()
4.Type[] 中就保存了参数的泛型信息。


为了能够使其他类也可以使用该方法,我们专门把该方法抽取到一个BeanFactory类中。而该类表示
对象工厂类,专门负责创建对象。而把工厂方法也更名为getBean.(bean就是java对象)




public class BeanFactory{
public static <T> T getBean(String className, Class<T> checkedType){
try{
Class<T> clz = (Class<T>)Class.forName(className)
Object obj = clz.newInstance();
//需要检查checkedType是否是obj的字节码对象
if(!checkedType.isInstance(obj)){
throw new IllegalArgumentException("对象和类型不兼容");
}
return (T)obj;
} catch(Exception e){
e.printStackTrace();
}
return null;
}
}
那么该类就是工厂类。
工厂类往往涉及了另外一个设计模式:单例设计模式
应该把工厂类做出单例的。
改完完后,代码如下:


public enum BeanFactory{
INSTANCE;
public <T> T getBean(String className, Class<T> checkedType){
try{
Class<T> clz = (Class<T>)Class.forName(className);
Object obj = clz.newInstance();
//需要检查checkedType是否是obj的字节码对象
if(!checkedType.isInstance(obj)){
throw new IllegalArgumentException("对象和类型不兼容");
}
return (T)obj;
} catch(Exception e){
e.printStackTrace();
}
return null;
}
}


如何编写工具类:上述的工厂类其实也属于工具
方式一:
1.把工具类做成单例模式的;
2.把工具方法做出非static方法。
那么要调用工具方法,必须先得到工具类对象,再调用方法。
如:上述的BeanFactory方法。
方式二:
1.把工具方法做成static方法。
2.把工具类所有的构造器全部私有化,或者把该工具类加上abstract的。
这么做的目的,就是为了全部是类去调用工具方法。
如:java.util.Arrays就是这么做的。
把BeanFactory改成方式二:
class BeanFactory1{
private BeanFactory1(){}
public static <T> T getBean(String className, Class<T> checkedType){
try{
Class<T> clz = (Class<T>)Class.forName(className);
Object obj = clz.newInstance();
//需要检查checkedType是否是obj的字节码对象
if(!checkedType.isInstance(obj)){
throw new IllegalArgumentException("对象和类型不兼容");
}
return (T)obj;
} catch(Exception e){
e.printStackTrace();
}
return null;
}
}


单例设计模式:在整个应用中某一个类的对象,有且只有一份。
饿汉式:
懒汉式:
建议使用枚举来做。


讲讲Eclipse项目下的.classpath文件:
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<classpathentry kind="lib" path="xxxxxxxx.jar"/>
<classpathentry kind="output" path="bin"/>
</classpath>
kind:类别,类型的意思:kind的值不能修改,常见的值如下:
src:表示source folder:表示源路径,
    特点:放在该类型目录下的文件会被自动编译到classpath目录下。
con: 表示连接,一般表示引用的jre的jar包。
output:表示该src目录下的文件编译之后的输出路径,称之为classpath路径。
lib:表示库文件,一般的表示自行添加的库


path:表示对于kind的路径,可以修改。
以后,做项目往往会更改src的路径值,以及output的路径值。
source folder:其实就是一个src类型的路径,特点:放在该类型目录下的文件会被自动编译到classpath目录下。
package:包。
folder:表示普通的文件夹。


加载资源:
1.文件流
public static void load1() throw Exception{
String fileName = "D:/xxx/x/xxxxxxxx.properties";
Properties p = new Properties();
InputStream inStream = new FileInputStream(fileName);
p.load(inStream);
}
缺点:绝对路径
2.使用相对路径
i): 相对于classpath的根路径(就是output的路径):
此时去加载该资源文件,得使用到一个叫ClassLoader的对象。
在ClassLoader类中主要有两个从classpath路径加载资源文件的方法:
InputStream getResourceAsStream(String name);
static InputStream getSystemResourceAsStream(String name);
两种方法功能相同,第一个使用对象调用,第二个用类调用。

如何获取ClassLoader对象:
(1):Class类中方法:ClassLoader getClassLoader()
(2): Thread类中方法:ClassLoader getContextClassLoader();
上述三种方法都行,推荐使用ClassLoader对象调用

public static void load2() throws Exception{
Properties properties = new Properties();
InputStream inputStream = ClassLoader.getSystemResourceAsStream("db.properties"); // 从classpath里读
properties.load(inputStream);
System.out.println(properties);
}

public static void load2_2() throws Exception{
Properties properties = new Properties();
InputStream inputStream = 
LoadResourcesDemo.class.getClassLoader().getResourceAsStream("db.properties");
//InputStream inputStream = ClassLoader.getSystemResourceAsStream("db.properties"); // 从classpath里读
properties.load(inputStream);
System.out.println(properties);
}

ii) 相对于类路径:
在Class类中有方法:InputStream getResourceAsStream(String name);
此时表示加载资源的路径是当前Class所表示类的路径。
如:类Demo,的包是cn.itcast.cd
Demo.class.getResourceAsStream("db.properties");
此时,表示加载db.properties的路径是:当前Demo的类路径,也就是说从
classpath的根路径下/cn/itcast/cd中去寻找。


转自:


http://edu.csdn.net/course/detail/765/10352?auto_start=1


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值