讲完.class,Class之后,继续。

1)泛化的Class引用

Class也能够增加泛型,增加之后会进行类型检查。

贴一下书上原话,Class<?>优于Class,尽管他们是等价的,Class<?>的优点是碰巧或疏忽使用了一个非详细的类引用。我搞不懂这个所谓非详细是什么?

后面弄懂了,事实上<?>作为通配符,就是未知的。直接写结论的话不能写个详细类型吧。作者的意思事实上就是说加了泛型的Class就是选择了非详细的版本号。

增加泛型的原因是提供编译期间的类型检查,操作失误的话便会显示错误。可是使用普通的Class的时候。就要到等到执行的时候。

还有这个Class<T>,JDK文档中:

T - the type of the class modeled by thisClass object. For example, the type ofString.class isClass<String>. UseClass<?

> if the class being modeled is unknown.

<T>是使用泛型类的声明。包含:

Interface List<E>

  • Type Parameters:
    E - the type of elements in this list

一样也是声明。

2)转型语法

SE5加入的用于Class引用转型的语法。

class Gun{
    int price = 1;
}
class DeathGun extends Gun{
    int price = 2;
}
public class TestCast {
    public static void main(String[] args) {
        Gun g = new DeathGun();
        Class<DeathGun> d = DeathGun.class;
        //泛型的用处 类型不匹配
        //Class<DeathGun> dd = Gun.class;  
        DeathGun gg = d.cast(g);
        System.out.println(gg.price);
    }
} cast源代码:
public T cast(Object obj) {
if (obj != null && !isInstance(obj))
throw new ClassCastException(cannotCastMsg(obj));
return (T) obj;
}

3)类型转换前的检查

if(x instanceof TV){
((TV)x).show();
}

检查对象类型。向下转型时假设有错,则抛出ClassCastException。所以要先使用instanceOf。

instaceOf与Class的等价性。

class Father{}
class Son extends Father{}
public class Test {
static void test(Object o ){
System.out.println("type: "+o.getClass());
System.out.println("o instanceof Father: "+(o instanceof Father));
System.out.println("o instanceof Son: "+(o instanceof Son));
System.out.println("Father.isInstanceOf(o)"+Father.class.isInstance(o));
System.out.println("Son.isInstanceOf(o)"+Son.class.isInstance(o));
System.out.println("o.getClass()==Father.class:"+(o.getClass()==Father.class));
System.out.println("o.getClass()==Son.class:"+(o.getClass()==Son.class));
System.out.println("o.getClass().equals(Father.class):"+(o.getClass().equals(Father.class)));
System.out.println("o.getClass().equals(Son.class):"+(o.getClass().equals(Son.class)));
} public static void main(String[] args) {
test(new Father());
test(new Son());
}
} result:
type: class son.Father
o instanceof Father: true
o instanceof Son: false
Father.isInstanceOf(o)true
Son.isInstanceOf(o)false
o.getClass()==Father.class:true
o.getClass()==Son.class:false
o.getClass().equals(Father.class):true
o.getClass().equals(Son.class):false type: class son.Son
o instanceof Father: true
o instanceof Son: true
Father.isInstanceOf(o)true
Son.isInstanceOf(o)true
o.getClass()==Father.class:false
o.getClass()==Son.class:true
o.getClass().equals(Father.class):false
o.getClass().equals(Son.class):true

instanceof指的是你是这个类吗?你是这个类的派生类吗?

4)反射

书上讲得好简单,3页左右。

RTTI。执行时类型信息能够告诉你对象的详细类型,可是,这个类型必须在编译时必须已知,这样RTTI才干识别。

即在编译时,编译器必须知道要通过RTTI来处理的类。

这个看起来不是限制,可是置身于大规模的编程中,可能编译时程序无法获知这个对象所属的类。

RTTI和反射真正的差别仅仅有:RTTI,编译器在编译时检查和打开.class文件,对于反射,.class文件编译时不可获取。是在执行时打开和检查.class文件。

直接用书上的样例。贴个代码:

public class ShowMethods {
  private static String usage =
    "usage:\n" +
    "ShowMethods qualified.class.name\n" +
    "To show all methods in class or:\n" +
    "ShowMethods qualified.class.name word\n" +
    "To search for methods involving 'word'";
  private static Pattern p = Pattern.compile("\\w+\\.");
  public static void main(String[] args) {
    if(args.length < 1) {
      System.out.println(usage);
      System.exit(0);
    }
    int lines = 0;
    try {
      Class<?> c = Class.forName(args[0]);
      Method[] methods = c.getMethods();
      Constructor[] ctors = c.getConstructors();
      if(args.length == 1) {
        for(Method method : methods)
          System.out.println(
            p.matcher(method.toString()).replaceAll(""));
        for(Constructor ctor : ctors)
          System.out.println(p.matcher(ctor.toString()).replaceAll(""));
        lines = methods.length + ctors.length;
      } else {
//事实上作者这样写是。method匹配会匹配。假设我java ShowMethods ShowMethods ShowMethods
//本来的话是提取当中的一个,可是args[1]为ShowMethods,刚刚好匹配到的就是ShowMethods本身自带的方法。也就是main方法。
        for(Method method : methods)
          if(method.toString().indexOf(args[1]) != -1) {
            System.out.println(
              p.matcher(method.toString()).replaceAll(""));
            lines++;
          }
        for(Constructor ctor : ctors)
          if(ctor.toString().indexOf(args[1]) != -1) {
            System.out.println(p.matcher(
              ctor.toString()).replaceAll(""));
            lines++;
          }
      }
    } catch(ClassNotFoundException e) {
      System.out.println("No such class: " + e);
    }
  }
} F:\>java ShowMethods ShowMethods
public static void main(String[])
public final native Class getClass()
public native int hashCode()
public boolean equals(Object)
public String toString()
public final native void notify()
public final native void notifyAll()
public final native void wait(long) throws InterruptedException
public final void wait(long,int) throws InterruptedException
public final void wait() throws InterruptedException
public ShowMethods()

慢慢看Matcher的replaceAll方法,不同于String的replaceAll,后者有两个參数。一个替换内容,一个是要被替换的内容。而前者仅仅有一个參数。

public String replaceAll(String replacement)
Replaces every subsequence of the input sequence that matches the pattern with the given replacement string.
public class TestString {
public static void main(String[] args) {
String s = "0898((*(*))(";
//正則表達式编译进Pattern
 Pattern p = Pattern.compile("\\W");
//p.matcher返回的是Matcher对象,Matcher的replaceAll方法是能够将匹配的字符串
//替换成方法參数中的内容。
 System.out.println(p.matcher(s).replaceAll("-"));
}
}
result:0898--------

假设没有正則表達式的替换,那么结果是:

public static void ShowMethods.main(java.lang.String[])
public final native java.lang.Class java.lang.Object.getClass()
public native int java.lang.Object.hashCode()
public boolean java.lang.Object.equals(java.lang.Object)
public java.lang.String java.lang.Object.toString()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
public final native void java.lang.Object.wait(long) throws java.lang.Interrupte
dException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedEx
ception
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public ShowMethods()

事实上就是替换掉类前面的包名。

有没有发现,forName的结果在编译时不可知,我们是在执行的时候传进Class參数的。尽管刚開始的时候没用到这么高级的东西。可是后面学得深入了之后就发现有些东西在使用,如Spring。

getMethods拿到的是类的全部共同拥有方法,包含自身,从父类继承。从接口实现的public方法。

另一个是getDeclaredMethods拿到的是类自身声明的方法。不止是public方法,protected也能拿到。甚至是private。(刚開始我也是非常疑惑,不是常常强调封装。私有不让人家看到吗。怎么能够直接拿到private的东西的,事实上能够这样看。什么东西有个度,可是这个度并非一尘不破。有一定的后门)。

getConstructors返回的是public的构造器。

试了一下,私有的构造器不会显示。书上说到这里就停了,继续扩展:

曾经看过Java反射教程里面介绍的非常好。

一、未知的方法调用

public class TestRefl {
public static void main(String[] args) {
UnKnown uk = new UnKnown();
Method m = null;
try {
m = uk.getClass().getMethod("print", new Class<?>[0]);
m.invoke(uk);
} catch (NoSuchMethodException | InvocationTargetException| SecurityException |IllegalAccessException e) {
e.printStackTrace();
}
}
} class UnKnown{
public void print(){
System.out.println("haha");
}
}

之前我特地将方法改成private,发现是找不到方法的,仅仅有改为public。

二、对象的创建

public class TestRefl {
public static void main(String[] args) {
Class c = null;
try {
c = c.forName("son.UnKnown");
System.out.println(c.getName());
} catch (ClassNotFoundException e) {
System.out.println("not found");
}
try {
UnKnown u = (UnKnown) c.newInstance();
u.print();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
} class UnKnown{
static void print(){
System.out.println("haha");
}
}

通过构造函数获取对象。

public class TestRefl {
public static void main(String[] args) {
Class c = null;
try {
c = c.forName("son.UnKnown");
} catch (ClassNotFoundException e) {
System.out.println("not found");
}
Constructor<?> ct[] = c.getConstructors();
try {
UnKnown u = (UnKnown) ct[0].newInstance();
UnKnown u2 = (UnKnown) ct[1].newInstance(1);
u.print();
u2.print();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
} class UnKnown {
public UnKnown() {
} public UnKnown(int i) {
} static void print() {
System.out.println("haha");
}
}

构造函数数组有顺序,0是无參的那个构造函数,1为传參,在方法中直接传參就可以。

当然私有的构造方法是拿不到的。

解释到RTTI和反射的不同的时候,反过来能够说明反射的作用。就是执行时检查对象的类型。随意调用对象的方法(Spring和servlet中都用到了,注意到没有,我们在xml配置,然后属性会依据xml注入),同一时候能够知道方法參数和属性。

反射还是非常强大。接下来会介绍动态代理,曾经被虐过的一个设计模式。

Java编程思想(十五) —— 类型信息之反射的更多相关文章

  1. Java编程思想学习笔记——类型信息

    前言 运行时类型信息(RTTI:Runtime Type Information)使得我们可以在程序运行时发现和使用类型信息. Java在运行时识别对象和类的信息的方式: (1)一种是RTTI,它假定 ...

  2. 关于 java编程思想第五版 《On Java 8》

    On Java 8中文版 英雄召集令 这是该项目的GITHUB地址:https://github.com/LingCoder/OnJava8 广招天下英雄,为开源奉献!让我们一起来完成这本书的翻译吧! ...

  3. Java编程思想学习(五)----第5章:初始化与清理

    随着计算机革命的发展,“不安全”的编程方式已逐渐成为编程代价高昂的主因之一. C++引入了构造嚣(constructor)的概念,这是一个在创建对象时被自动调用的特殊方法.Java中也采用了构造器,并 ...

  4. java编程思想第五章初始化与清理

    5.1使用构造器确保初始化: 构造器与一般方法一样,但是没有返回值,且其方法名与类名完全相同. 不接受任何参数的构造器成为默认构造器,也叫无参构造器. 5.2 方法重载: 为什么会有方法重载? 构造器 ...

  5. Java编程思想学习(五) 复用类

    1.继承与组合 复用类的方法有两种:继承与组合.继承就不多说了,组合就是直接在类中new一个对象. 数组也是对象,使用数组也是组合的一种. 2.初始化基类 当创建一个导出类的对象时,该对象包含一个基类 ...

  6. java编程思想-第五章-某些练习题

    参考https://blog.csdn.net/caroline_wendy/article/details/46844651 10&11 finalize()被调用的条件 Java1.6以下 ...

  7. 《Java编程思想》笔记 第十四章 类型信息

    1.RTTI:在运行时识别一个对象类型 JAVA在运行时 有时要 识别对象和类的信息这个机制叫RTTI.Java提供了两种机制去做这件事.传统的RTTI 和 反射. 传统的RTTI  假定编译时就已经 ...

  8. Java 编程思想 Chapter_14 类型信息

    本章内容绕不开一个名词:RTTI(Run-time Type Identification) 运行时期的类型识别 知乎上有人推断作者是从C++中引入这个概念的,反正也无所谓,理解并能串联本章知识才是最 ...

  9. 《java编程思想》读书笔记(一)开篇&第五章(1)

    2017 ---新篇章  今天终于找到阅读<java编程思想>这本书方法了,表示打开了一个新世界. 第一章:对象导论 内容不多但也有20页,主要是对整本书的一个概括.因为已经有过完整JAV ...

随机推荐

  1. 聊聊JVM的年轻代(转)

    聊聊JVM的年轻代 本文转自http://ifeve.com/jvm-yong-generation/ 1.为什么会有年轻代 我们先来屡屡,为什么需要把堆分代?不分代不能完成他所做的事情么?其实不分代 ...

  2. .NET:鲜为人知的 “Load Context”

    背景 任何一门语言都要了解其类型加载过程,如:Java 的 Class Loader,NodeJS 的搜索方式等,本文概述一下我对 CLR 如何加载程序集,重点说一下 Load Context. 其编 ...

  3. 企业应用:C/S 开发需要考虑的事项

    备注 几乎没有做过 C/S 方面的开发(有 RIA 方面的开发经验),此文纯属个人胡思乱想,写下来是希望朋友们多给点意见. C/S 开发注意事项 C/S 开发需要注意如下几点: 采用何种模式组织 UI ...

  4. Selenium2+python自动化60-异常后截图(screenshot)

    前言 在执行用例过程中由于是无人值守的,用例运行报错的时候,我们希望能对当前屏幕截图,留下证据. 在写用例的时候,最后一步是断言,可以把截图的动作放在断言这里,那么如何在断言失败后截图呢? 一.截图方 ...

  5. Weblogic下启用Gzip压缩

    一.首先,去http://sourceforge.net/projects/filterlib网站下载tk-filters-1.0.1.zip. 二.解压这个tk-filters-1.0.1.zip压 ...

  6. pandas过滤包含特定字符串的行

    ~df.col3.str.contains('u|z')也就是在条件前面加~号,表示not

  7. OTL翻译(8) -- otl_long_string/otl_long_unicode_string类

    otl_long_string/olt_long_unicode_string 这两个类主要用来处理大对象数据.从OTL4.0版本开始,otl_long_string还可以处理任何类型的RAW/BIA ...

  8. C语言条件编译

    使用与平台有关的C语言函数,可能会使得程序不具有可移植性.比如Socket编程.多线程编程等是与平台有关的. 若想将程序做成平台无关的就需要用到与平台相关的条件编译. 下面转自:http://blog ...

  9. 细聊MySQL的分区功能

    此篇主要介绍下MySQL的分区功能.我们分别从分区的概念.分区对于MySQL应用的优点.分区的类别及设置来和大家一起探讨下MySQL的分区. 什么是分区? MySQL在未启用分区功能时,数据库的单个表 ...

  10. Longest Substring Without Repeating Characters leetcode java

    题目: Given a string, find the length of the longest substring without repeating characters. For examp ...