Java学习笔记(四)——反射与泛型

一、反射(一)类Class1 . Class的概念我们知道在Java中除了基本类型之外的其他类型都是class(包括interface),例如:String,Object,Runnable,Exception等。class的本质其实就是数据类型,Java规定class/interface的数据类型是Class。 当我们把一个类型的实例赋值给一个数据类型的变量时,必须严格按照数据类型进...
摘要由CSDN通过智能技术生成

一、反射

(一)类Class

1 . Class的概念

我们知道在Java中除了基本类型之外的其他类型都是class(包括interface),例如:String,Object,Runnable,Exception等。class的本质其实就是数据类型,Java规定class/interface的数据类型是Class。
当我们把一个类型的实例赋值给一个数据类型的变量时,必须严格按照数据类型进行赋值,无继承关系的数据类型无法赋值,编写不符合数据类型要求的代码,编译器就会发生报错。
(1)JVM持有的每一个Class类型都指向一个数据类型(class或interface)
例如,JVM在加载String类时,首先会读取String.class文件,然后为String类创建一个Class实例(这个过程是JVM在其内部实现)
(2)JVM为每个加载的class创建对应的Class实例,并在实例中保存该class的所有信息
(3)如果获取了某个Class实例,则可以获取到该实例对应的class的所有信息
例如:name,package,super,interface,field,method等
(4)通过Class实例获取class信息的方法称为反射(Reflection)

2 . 如何获取class的Class实例

(1)Type.class

Class cls = String.class;
//返回String类对应的Class实例

(2)getClass()

String s = "Hello";
Class cls = s.getClass();
//对实例变量调用getClass方法来返回Class实例

(3)Class.forName()

Class cls = Class.forName("java.lang.String");
//利用Class提供的静态方法forName()传入完整类名得到对应的Class

2 . 比较Class实例

在JVM中,一个class只对应唯一的一个Class实例,可以使用==操作符比较两个Class实例。例如:

Class cls1 = String.class;

String s = "Hello";
Class cls2 = s.getClass();

Class cls3 = Class.forName("java.lang.String");

boolean b1 = cls1 == cls2;//true
boolean b2 = cls2 == cls3;//true

Class实例比较和instanceof比较的差别:
(1)当我们使用instanceof进行比较时,不但匹配当前的类型,还匹配当前类型的子类。例如:

Integer n = new Integer(123);

boolean b3 = n instanceof Integer;//true
boolean b4 = n instanceof Number;//true
//因为Integer是Number的子类,所以判断结果都为true

(2)当我们使用==操作符对Class实例进行比较时,由于Class实例只能精确的判断数据类型,不能进行子类的比较。例如:

boolean b1 = n.getClass() == Integer.class;//true
boolean b2 = n.getClass() == Number.class;//false

3 . 反射的目的

反射的目的是当获得某个Object实例时,我们可以获取该Object的class信息

(1)从Class实例获取class信息:

①获取完整类名:getName()
②获取简单类名:getSimpleName()
③获取包名:getPackage()

Class cls = String.class;

String fname = cls.getName();//"java.lang.String"
String sname = cls.getSimpleName();//"String"
String pkg = cls.getPackage();//"java.lang"
(2)从Class实例判断class类型:

①判断数据类型:isinterface()
②判断枚举类型:isEnum()
③判断数组类型:isArray()
④判断基本类型:isPrimitive()

Runnable.class.isinterface();//true
java.time.Month.class.isEnum();//true
String[].class.isArray();//true
int.classisPrimitive();//true
(3)通过Class创建class实例:
Class cls = String.class;
String s = (String) cls.newInstance();

注意:在这里我们只能调用默认构造方法,带有参数的构造方法无法通过此方式进行调用

(4)动态加载

利用JVM动态加载class的特性:
可以在运行期间根据条件加载不同的类
例如:
如果我们要实现Commons Logging这个功能
首先我们判断org.apache.logging.log4j.Logger这个类是否存在
利用if-else语句,如果这个类存在就优先使用Log4j,如果不存在则使用JVM自带的JDKLog

//Commons Logging优先使用Log4j
LogFactory factory;
if(isClassPresent("org.apache.logging.log4j.Logger")){
    factory = createLog4j;
} else {
    factory = createJDKLog();
}
//在JVM执行这段代码时,并没有引用Log4j的Class本身,在这种情况下即使没有放入Log4j的jar包,代码也依然会执行

而判断一个Class是否存在,我们只需要使用Class.forName()方法,然后捕获Class Not Found异常就可以

boolean isClassPresent(String name) {
    try {
        Class.forName(name);
        return true;
    } catch (Exception e) {
        return false;
    }
}

这就是我们只需要把Log4j的jar包放入classpath中,Commons Logging就可以直接使用Log4j的原因。

(二)访问字段

1 . 通过Class实例获取field信息

(1)获取某个public的field(包括父类):getField(Name)
(2)获取当前某各类的field(不包括父类):getDeclaredField(Name)
(3)获取所有public的field(包括父类):getFields()
(4)获取当前类的所有field(不包括父类):getDeclaredFields()

2 . Field对象包含一个field的所有信息

(1)获得field字段的名称:getName()
(2)获得field字段定义的类型:getType()
(3)获得field字段定义的修饰符:getModifiers()

Integer n = new Integer(123);
Class cls = n.getClass();
Field[] fs = cls.getFields();
for(Field f : fs) {
    f.getName();//field name
    f.getType();//field type
    f.getModifiers();//modifiers
}

3 . 获取和设置field的值

(1)获取一个实例的该字段的值:get(Object)

Integer n = new Integer(123);
Class cls = n.getClass();
Field f = cls.getDeclaredField("Value");
f.get(n);//123,相当于n.Value

(2)设置一个实例的该字段的值:set(Object)

Integer n = new Integer(123);
Class cls = n.getClass();
Field f = cls.getDeclaredField("Value");
f.set(n, 456);//123,相当于n.Value = 456

4 . 访问非public字段

通过反射访问Field需要通过SecurityManager设置的规则。
通过设置setAccessible(true)来访问非public字段。
setAccessible(true)
如果定义了SecurityManager,SecurityManager的规则将会阻止对该field设置Accessible

(三)调用方法

1 . 通过Class实例获取method信息

(1)获取某个public的method(包括父类):getMethod(name,Class…)
(2)获取当前类的某个method(不包括父类):getDeclaredMethod(Name,Class…)
(3)获取所有public的method(包括父类):getMethods()
(4)获取当前类的所有的method(不包括父类):getDeclaredMethods()

2 . Method对象包含一个method的所有信息

(1)返回method的名称:getName()
(2)返回method的返回类型:getReturnType()
(3)返回method的参数类型:getParamaterType()
(4)返回method的修饰符:getModifiers()

Integer n = new Integer(123);
Class cls = n.getClass();
Method[] ms = cls.getMethods();
for(Method m : ms) {
    m.getName();//method name
    //return type:
    Class ret = m.getReturnType();//
    //parameter types:
    Class[] params = m.getParam
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值