1.Class对象

1.1普通的Class对象

Class对象是一个特殊的对象,是用来创建其它对象的对象(这里的其他对象就是指:java类的实例)。其实Class对象就是java类编译后生成的.class文件,它包含了与类有关的信息。

每当第一次使用一个类时,JVM必须使用“类加载器”子系统加载该类对象的Class对象。一旦这个类的Class对象被载入内存,它就被用来创建这个类的所有对象。当我们使用new关键字创建一个类的第一个对象的时候,JVM会帮助我们加载该类Class对象,但是当我们想自己加载这个类的Class对象怎么办呢?实际上有3种方法:

  1. Class.forName("类名字符串")  (注意:类名字符串必须是全称,包名+类名)
  2. 类字面常量法:类名.class
  3. 实例对象.getClass()
 class Candy{
     static{System.out.println("Loading Candy");}
 }
 class Gum{
     static{System.out.println("Loading Gum");}
 }
 class Cookie{
     static{System.out.println("Loading Cookie");}
     public Cookie(){
         System.out.println("initializing Cookie");
     }
 }
 public class SweetShop {
     public static void main(String[] args){
         Class classType;
         System.out.println("inside main");
         try{
             classType=Class.forName("typeInfo.Gum");
         }catch(ClassNotFoundException e){
             System.out.println("Couldn't not find Gum");
         }
         System.out.println("After creating Class.forName(\"Gum\")");
         classType=Candy.class;
         System.out.println("After creating Candy");
         Cookie cookie=new Cookie();
         classType=cookie.getClass();
         System.out.println("After creating Cookie");
     }
 }

从输出中可以看出,Class对象仅在需要的时候才被加载(我们在Java 对象及其内存控制一文中说过:static初始化是在类加载时进行的)。

为什么没有打印出“Loading Candy”呢?因为,使用类字面常量法创建对Class对象的引用时,不会自动的初始化该Class对象。

1.2泛化的Class对象

由于普通Class引用指向的是它所指向的对象的确切类型。在Java引入泛型的概念之后,Java SE5的设计者将Class引用的类型通过使用泛型限定变得更具体了。再看我们上面的程序,一个Class classType既可以指向Candy类的Class对象也可以指向Gum类的Class对象还可以指向Cookie类的Class对象。这就很像我们编程时使用Object作为引用变量的类型一样。当我们用泛型限定了上面代码的classType之后,便会有错误出现了:

与使用普通的Class对象相比,使用泛型的Class对象,在调用newIntance方法时返回的不在是一个Object类型而是该对象的确切类型:

现在当我们需要放宽条件,即需要创建一个Class引用,它被限定为某种类型,或者该类型的任何子类型,这是我们就需要使用通配符"?"与extends关键字相结合,创建一个“范围”:

 public class NumberClassObj {
     public static void main(String[] args) {
         Class<? extends Number> numType=int.class;
         numType=double.class;
         numType=Number.class;
     }
 }

当然,在这里调用newIntance方法的话返回的就是Number对象了。

到了这里,是不是有些朋友就会想,那我们是不是就可以使用泛型创建一个Class引用,它指向“某个类,它是一个类的超类”?当然可以:

 public class SuperClassObj {
     interface Inter{
         public void sayHello();
     }
     class Base{

     }
     class Sub extends Base implements Inter{
         @Override
         public void sayHello() {
             System.out.println("hello everyone");
         }
     }
     public static void main(String[] args) throws InstantiationException, IllegalAccessException {
         Class<? super Sub> superOfSub=Base.class;
         superOfSub=Inter.class;
     }
 }

但是这样创建出来的Class对象,在调用newInstance方法时将返回Object对象:

这是为什么呢?我的理解是:因为它表示的是一个很模糊的概念,编译器找不到一个合适的类型与之对应,并且没有这样一个.class文件。(不知道理解的是否合适,请各位大神不吝赐教)

2.类型转换前先做检查

2.1instanceof 运算符的陷阱

instanceof运算符的前一个操作数通常是引用类型的变量,后面一个操作数通常是一个类(也可以是接口),它用于判断前面的对象是否是后面的类或其子类、实现类的实例。是则返回true;否则,返回false。

Java规范,使用instanceof运算符有一个限制:instanceof运算符前面操作数的编译时类型必须是如下3种情况:

①与后面的类相同;

②是后面类的父类;

③是后面类的子类。

如果前面操作数的编译时类型与后面的类型没有任何关系,程序将没法通过编译,只有通过编译后才能考虑它的运算结果是true还是false。

对于上面这段代码,是不是有点晕了,为什么Math math=(Math)str;这里没有出现编译错误,而是在下面出现了编译错误呢?其实是这样的:当编译器编译Java程序时,编译器无法检查引用变量实际引用对象的类型,它只检查该变量的编译类型。对于math instanceof String而言math编译类型为Math,Math既不是String类型,也不是String类型的父类更不是String类型的子类,因此程序没法通过编译。至于math实际引用对象的类型是什么,编译器并不关心。至于Math math=(Math)str;没有出现编译错误,这和强制类型转换机制有关。对于Java的强制类型转换而言,也可以分为编译、运行两个阶段类分析它。

在编译阶段:强制类型转换要求被转换变量的编译时类型必须是如下3种情况:

①被转换变量的编译时类型与目标类型相同;

②被转换变量的编译时类型是目标类型的父类;

③被转换变量的编译时类型是目标类型的子类,这种情况下可以自动向上转型,无须强制转换。

Math math=(Math)str;没有提示编译错误的原因是,str编译时的类型是目标类型(Math)的父类,所以编译是正确的,可见,强制类型转换的编译阶段只关心引用变量的编译时类型,至于该引用变量实际引用对象的类型,编译器并不关心,也没法关心。

在运行阶段:被转换变量所引用对象的实际类型必须是目标类型的实例,或者是目标类型的子类的实例、实现类的实例,否则在运行时将引发java.lang.ClassCastException异常。

再如:

 Object obj=new Integer(10);
 String str=(String)obj;
 System.out.println(str);

这段代码中由于obj变量实际引用的变量的类型是Integer,而Integer既不是String的子类也不是父类,所以在运行阶段抛出了java.lang.ClassCastException异常。

Instanceof有一个额外的功能:它可以确保第一个操作数所引用的对象不是null,当第一个操作数所引用的对象为null时,instanceof运算符返回false,而不会报异常。

2.2 Class.isInstance()

Class.isInstance方法提供了一种动态测试对象的途径。它与instanceof表达式的区别是:

Class.isInstance方法更加适合泛类型的检测(如代理,接口,抽象类等规则),常与泛化Class对象出现,而instanceof表达式适合直接类型的检查,常与普通的Class对象出现。

3.反射

前面讲了获取(手动加载)一个类的Class对象一些方法,我们在使用new创建一个对象的时候JVM首先会检查该类的Class对象是否被加载,没有的话,JVM会自动帮我们加载,那我们为什么还要手动加载呢?原因就是创建一个对象的方法不止使用new这一种,本节我们讲述的反射就是另外一种创建类实例的方法,但是JVM不会为反射创建对象自动加载Class对象。因此我们需要手动加载。

大家都知道,要让Java程序能够运行,那么就得让Java类要被Java虚拟机加载。Java类如果不被Java虚拟机加载,是不能正常运行的。现在我们运行的所有的程序都是在编译期的时候就已经知道了你所需要的那个类的已经被加载了。

Java的反射机制是在编译并不确定是哪个类被加载了,而是在程序运行的时候才加载、探知、自审。使用在编译期并不知道的类,直到运行时才得知名称的class,这样的特点就是反射。

反射在使用JDBC、开发第三方插件、开发框架的时候用的比较多。

Java反射机制所需要的类、接口等都在java.lang.reflect包中。仔细查看该包的内容相信一定会熟练运用该技能的。这里就不展开讲解各个API了。

3.1动态代理

说到了反射,我们就顺便再了解一下动态代理。它是以反射为基础的。

什么是代理呢?代理就是用来代替“实际”对象的对象。这里的“实际”对象被称为委托类。它主要用在:不允许直接访问某些类时;对访问要做特殊处理时等。或者,要对原方法进行统一的扩展,例如加入日志记录。

代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等,因此它充当着“中间人”的角色,它与委托类具有相同点接口。一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。

根据代理的创建方式,可以将代理类分为:

静态代理:由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。

动态代理:通过反射机制动态生成。

我们先来看一个静态代理:

 public interface Interface {
     void doSomething();
     void someElse(String str);
 }
 public class RealObject implements Interface {
     @Override
     public void doSomething() {
         System.out.println("doing something");
     }
     @Override
     public void someElse(String str) {
         System.out.println("someElse "+str);
     }
 }
 public class SimpleProxy implements Interface {
     private Interface delegate;
     public SimpleProxy(Interface delegate){
         this.delegate=delegate;
     }
     @Override
     public void doSomething() {
         System.out.println("SimpleProxy doSomething");
         delegate.doSomething();
     }

     @Override
     public void someElse(String str) {
         System.out.println("SimpleProxy someElse "+str);
         delegate.someElse(str);
     }
 }
 public class Business {
     public static void consumer(Interface inter){
         inter.doSomething();
         inter.someElse("bonobo");
     }
     public static void main(String[] args) {
         consumer(new RealObject());
         System.out.println("-----------------------");
         consumer(new SimpleProxy(new RealObject()));
     }
 }

从上面代码中我们可以发现:相比SimpleProxy,RealObject类更专注于特定的功能,它将一些“额外”的操作(在这里指的就是System.out.println("SimpleProxy doSomething");和System.out.println("SimpleProxy someElse "+str);)从代码中分离出来,这就使得这些“额外”的操作的变化非常容易(比如我要变为System.out.println("AA");或者直接使用RealObject定义的功能),这也体现了设计模式的核心思想:封装变化点。RealObject类的核心功能是不变的,变的是那些“额外”的操作,因此,将这些“额外”的操作封装起来不就可以了。

再来看一下动态代理:

 public interface Interface {
     void doSomething();
     void someElse(String str);
 }
 public class RealObject implements Interface {
     @Override
     public void doSomething() {
         System.out.println("doing something");
     }
     @Override
     public void someElse(String str) {
         System.out.println("someElse "+str);
     }
 }
 public class DynamicProxyHandler implements InvocationHandler {
     private Object proxied;
     public DynamicProxyHandler(Object proxied){
         this.proxied=proxied;
     }
     @Override
     public Object invoke(Object proxy, Method method, Object[] args)
             throws Throwable {
         System.out.println("proxy: "+proxy.getClass()+".method: "+method+",args: "+args);
         if(args!=null)
             for(Object arg : args)
                 System.out.println("  "+arg);
         return method.invoke(proxied, args);
     }
 }
 public class SimpleDynamicProxy {
     public static void consumer(Interface inter){
         inter.doSomething();
         inter.someElse("bonobo");
     }
     public static void main(String[] args) {
         RealObject realObj=new RealObject();
         consumer(realObj);
         System.out.println("-----------------");
         Interface proxy=(Interface)Proxy.newProxyInstance(
                 Interface.class.getClassLoader(),
                 new Class[]{Interface.class},
                 new DynamicProxyHandler(realObj));
         consumer(proxy);
     }
 }

查看JDK:

java Class对象的更多相关文章

  1. 规则引擎集成接口(九)Java类对象

    Java类对象 右键点击“对象库” —“添加java类对象”,如下图: 弹出窗体,在文本框中输入类的全名“com.flagleader.test.Test”,选择该类型后确定,如下: 显示如下,勾选上 ...

  2. java类 对象 和构造方法

    github地址:https://github.com/lily1010/java_learn/tree/master/dog java中对象和类 java中万物皆对象,比如说动物,里面有猫,狗,鱼等 ...

  3. java integer对象判断两个数字是否相等

    java integer对象判断两个数字是否相等,不一定对 问题发生的背景:javaweb的项目,起先,因为在java中实体类中的int类型在对象初始化之后会给int类型的数据默认赋值为0,这样在很多 ...

  4. 转载---Java集合对象的深度复制与普通复制

    原博文:http://blog.csdn.net/qq_29329775/article/details/49516247 最近在做算法作业时出现了错误,原因是没有弄清楚java集合的深度复制和浅度复 ...

  5. spring mvc返回json字符串数据,只需要返回一个java bean对象就行,只要这个java bean 对象实现了序列化serializeable

    1.spring mvc返回json数据,只需要返回一个java bean对象就行,只要这个java bean 对象实现了序列化serializeable 2. @RequestMapping(val ...

  6. [原创]java WEB学习笔记81:Hibernate学习之路--- 对象关系映射文件(.hbm.xml):hibernate-mapping 节点,class节点,id节点(主键生成策略),property节点,在hibernate 中 java类型 与sql类型之间的对应关系,Java 时间和日期类型的映射,Java 大对象类型 的 映射 (了解),映射组成关系

    本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...

  7. Gson把json串转换成java实体对象

    Gson把json串转换成java实体对象的方法如下: 1.首先导入Gson的jar包,网上可以下载. java实体对象如下: public class Model { private double ...

  8. net.sf.json在处理json对象转换为普通java实体对象时的问题和解决方案

    我使用的net.sf.json是json-lib-2.4-jdk15.jar,把json对象转换为普通java实体对象时候有个问题,josn对象转换为java对象之后,json串里面的那几个小数点的值 ...

  9. Java中对象的深复制和浅复制详解

    1.浅复制与深复制概念 ⑴浅复制(浅克隆) 被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象.换言之,浅复制仅仅复制所考虑的对象,而不复制它所引用的对象. ⑵ ...

  10. EBS OAF开发中的Java 实体对象(Entity Object)验证功能补充

    EBS OAF开发中的Java 实体对象(Entity Object)验证功能补充 (版权声明,本人原创或者翻译的文章如需转载,如转载用于个人学习,请注明出处:否则请与本人联系,违者必究) EO理论上 ...

随机推荐

  1. Hibernate component mapping

    A Component is a containted object that is be persisted value type and not an entity.But you can emb ...

  2. Linux磁盘管理

    本系列的博客来自于:http://www.92csz.com/study/linux/ 在此,感谢原作者提供的入门知识 这个系列的博客的目的在于将比较常用的liunx命令从作者的文章中摘录下来,供自己 ...

  3. android 退出机制

    android sdk 退出机制的研究 有多种, 方法一.用list保存activity实例,然后逐一干掉 上代码: import java.util.LinkedList; import java. ...

  4. jQuery Ready 与 Window onload 的区别(转)

    “我们都知道,很多时候,在页面加载完后都需要做一些相应的初始化动作.例如,运行某些js特效,设置表单等等.怎么知道页面加载完了呢?一 般情况下都是设置body标签的onload监听window的loa ...

  5. Windows Azure 服务器时间问题

    最近一直在做学校的一个小项目,前期在没有服务器端的情况下意淫做出来了手机客户端.在寒假里使用ASP.NET快速做了一个网站并且设计好了需要使用其他内容,在Windows Azure上测试评估,为学校的 ...

  6. objective-c(内存管理)

    本文主要记录objective-c 内存管理的知识点: 1.objective-c的对象都是分配内存在堆上,与C的mallock和C++的new类似,只有int等系统变量分配内存在栈上: 2.obje ...

  7. DOM+CSS3实现小游戏SwingCopters

    前些日子看到了一则新闻,flappybird原作者将携新游戏SwingCopters来袭,准备再靠这款姊妹篇游戏引爆大众眼球.就是下面这个小游戏: 前者的传奇故事大家都有耳闻,至于这第二个游戏能否更加 ...

  8. Linq动态条件

    很多情况下,我们开发程序,需要动态拼接SQL查询语句; 比如  select top 1 * from User where age= 18  and  name = 'renruiquan' 其中红 ...

  9. undefined function openssl_x509_read

    打开php.ini,找到这一行 ;extension=php_openssl.dll,将前面的";"去掉 再重启apache或者iis即可

  10. DOM_04之常用对象及BOM

    1.添加:①var a=document.createElement("a"):②设置关键属性:③将元素添加到DOM树:a.parent.appendChild(a):b.pare ...